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

@wy-ai-labs/gates

v0.1.0

Published

Gate culture as a tool: manifest-driven gate runner (tiers static/core/full/live, cycle detection, env skips, exit-2 environment-blocked convention) plus shared gate checks and bins — lock hygiene, ESM paths, path deps, architecture direction, deny-list,

Downloads

156

Readme

@wy-ai-labs/gates

Gate culture as a tool — a manifest-driven gate runner (tiers, transitive expansion, cycle rejection, env skips, the exit-2 "environment blocked" convention) plus shared gate checks and bins that report every finding and never let a gate disappear silently.

npm License: MIT Node ≥22 runtime deps: 0

Part of the wy-ai-labs parts monorepo (packages/gates) · contract v1 · no Python twin

Install

npm install --save-dev @wy-ai-labs/gates      # Node ≥22.11, ESM only (import — no require), zero runtime dependencies

30-second usage

  1. Put a gates.json at the repository root (copy the one below and delete what you do not have).
  2. Wire the tiers into package.json:
{
  "scripts": {
    "test": "node --test \"test/**/*.test.mjs\"",
    "gates:core": "wy-gates run core",
    "gates:full": "wy-gates run full --no-stop",
    "gates:live": "wy-gates run live",
    "gates:inventory": "wy-gates inventory"
  }
}
  1. Run them:
npx wy-gates run core                 # before every commit / PR: stops at the first failure
npx wy-gates run full --no-stop       # nightly: runs everything, lists every failure
npx wy-gates list                     # what each tier expands to
npx wy-gates inventory                # Markdown tables for docs/gates-inventory.md
npx wy-check-lock --expected-node 22  # any check is also a standalone bin (exit 0 / 1 / 2)

The same thing from JavaScript — pure functions, no process.exit, no output unless you ask for it:

import { runManifest, createConsoleReporter, checkLock, checkEsmPaths } from '@wy-ai-labs/gates';

const summary = await runManifest('gates.json', { tier: 'core', reporter: createConsoleReporter() });
// summary = { ok, tier, results: [{ id, group, command, status: 'pass'|'fail'|'skip', ms, exitCode, reason? }], durationMs, counts, stoppedAt }
process.exitCode = summary.ok ? 0 : 1;

const lock = checkLock({ cwd: process.cwd(), expectedNode: 22 });   // { ok, problems: string[], summary }
const paths = checkEsmPaths({ cwd: process.cwd(), exclude: ['test/**'] });

gates.json — copy-paste starting point

{
  "version": 1,
  "tiers": {
    "static": ["lock", "esm-paths", "no-path-deps", "architecture", "denylist"],
    "core":   ["static", "unit"],
    "full":   ["core", "error-passthrough", "render"],
    "live":   ["full", "e2e-live"]
  },
  "groups": {
    "lock":         { "commands": ["wy-check-lock --expected-node 22"], "description": "package-lock hygiene: lockfileVersion 3, no libc fields" },
    "esm-paths":    { "commands": ["wy-check-esm-paths --exclude \"test/**\""], "description": "dynamic import() of a raw filesystem path (Windows crash class)" },
    "no-path-deps": { "commands": ["wy-check-no-path-deps"], "description": "registry dependency specs only — no file:/link:/workspace:/git/URL" },
    "architecture": { "commands": ["wy-check-architecture --config gates/architecture.json"], "description": "layer direction + entry-file line budgets" },
    "denylist":     { "commands": ["wy-check-denylist --list gates/denylist.txt --list gates/denylist.local.txt --redact"], "description": "public-safety deny-list: 0 hits" },
    "unit":         { "commands": ["npm test"], "description": "unit + contract tests, offline" },
    "error-passthrough": { "commands": ["wy-check-error-passthrough --exclude \"test/**\""], "description": "error text is never cut below 500 chars at capture" },
    "build":        { "commands": ["npm run build"] },
    "render":       { "commands": ["node scripts/check-render.mjs"], "requires": ["build"],
                      "skipIf": { "envMissing": ["CHROME_PATH"] }, "allowEnvironmentSkip": true, "timeoutMs": 600000,
                      "description": "pixel render check — needs a browser, skips (never fails) without one" },
    "e2e-live":     { "commands": ["node scripts/e2e-live.mjs"], "skipIf": { "envMissing": ["LLM_BASE_URL"] }, "allowEnvironmentSkip": true,
                      "description": "real provider round-trip — manual only, never in CI" }
  },
  "when": { "live": "manual, before a release" }
}

| Manifest field | Meaning | |---|---| | tiers.<name> | ordered list of tier or group names; tiers expand transitively (core = everything in static + unit), duplicates run once, a cycle is rejected at load by name (tier cycle: full -> core -> full) | | groups.<name>.commands | shell command lines, run in order in the manifest's directory with node_modules/.bin on PATH; an entry may be { "command", "name", "env" } | | requires | groups pulled in before this one (e.g. render requires build) — cycles rejected | | skipIf.envMissing | skip the whole group with reason env X missing when any listed variable is unset or empty — the runner is never invoked | | allowEnvironmentSkip | treat exit code 2 from a command as skip — environment blocked (exit 2) instead of fail (the source's ENVIRONMENT BLOCKED convention); default false | | timeoutMs / env | per-command timeout (a timeout is a fail with error: "timeout after N ms") and extra env for the group | | when | optional "runs when" text per tier for the inventory (defaults: static/core → every PR, full → nightly, live → manual) |

Every child process also receives WY_GATES_TIER, WY_GATES_GROUP and WY_GATES_ID. Unknown keys (steps, command, skipIf.platform, …) are rejected by name — a typo never degrades into a silently empty gate.

Checks and bins

Every check is a function returning { ok, problems: string[], summary } (cwd-relative paths, every finding, sorted) and a bin exiting 0 OK · 1 problems (listed on stderr) · 2 usage / config error; all bins take --cwd, --json, --quiet, --help.

| Function / bin | Catches | Options | |---|---|---| | checkLock / wy-check-lock | package-lock.json missing, lockfileVersion ≠ 3, "libc" fields (npm versions disagree about them → two machines rewrite the lock in turns, dirty tree, blocked git pull); optional --expected-node 22 when run on another Node major | lockFile, expectedNode | | checkEsmPaths / wy-check-esm-paths | import(path.join(...)), import(__dirname + …), import("C:\\…"), import(require.resolve(…)) without pathToFileURL() — fine on POSIX, ERR_UNSUPPORTED_ESM_URL_SCHEME on Windows | include (default **/*.mjs\|js\|cjs), exclude, hardcodedSlash (opt-in dir + "/x" heuristic) | | checkNoPathDeps / wy-check-no-path-deps | file: / link: / workspace: / git / github: / URL / ../ specs in any dependency block of package.json and packages/*/package.json | packagesDir, blocks | | checkArchitecture / wy-check-architecture --config f.json | imports against the allowed layer directions (srcserver, both → shared), forbidden bare imports per layer (sharednode:), entry files over their line budget | config { layers, allow, entryBudget, exclude, extensions, aliases, forbiddenImports } | | checkDenylist / wy-check-denylist --list f [--list …] | regex deny-list hits over the tree (file:line: match [list:line]), same walk/skip rules as the monorepo's scan-text.mjs; no built-in list — bring your own | lists, patterns, redact (hide the matched text in CI logs), quiet, skipDirs, maxBytes | | checkErrorPassthrough / wy-check-error-passthrough | .slice(0, N) / .substring(0, N) / .substr(0, N) with N < 500 on values that look like error text (err, error, message, text, body, stderr, detail) — the "do not lose the original error" gate; tails (.slice(-N)) and comment lines are fine, // gates: allow-truncate is the reviewable opt-out | include, exclude, minKeep, identifiers, allow | | checkPattern / wy-check-pattern --config f.json | any forbidden pattern over included files (line by line) and any required pattern in a named file (whole text) — the shape of a repository's "check-no-.mjs" scripts: no ".env editing" instructions in user docs, no hard-coded account-specific model id, shutdown handlers still wired | include, exclude, forbidden: [{ pattern, flags, message }], required: [{ file, pattern, flags, message }] |

wy-check-architecture config example:

{
  "layers": [{ "name": "src", "dirs": ["src"] }, { "name": "server", "dirs": ["server"] }, { "name": "shared", "dirs": ["shared"] }],
  "allow": { "src": ["shared"], "server": ["shared"] },
  "forbiddenImports": { "shared": ["node:", "express"] },
  "entryBudget": { "files": ["server/index.ts", "src/main.ts"], "maxLines": 200 },
  "exclude": ["**/*.test.*"],
  "aliases": { "@/": "src/" }
}

docs/gates-inventory.md — template

Keep one inventory per repository. The policy paragraph is hand-written; the tables are pasted from wy-gates inventory whenever the manifest changes.

# Gate inventory — <repository>

Gates are the safety net that unit tests cannot be: environment, platform, lifecycle and log regressions.
Policy (do not revert):
- `npm test` stays unit/contract tests only, seconds, offline. Heavy gates never move back into `pretest`.
- **static / core** run on every PR (`gates:core`, before every commit) · **full** runs nightly and before a release
  (`gates:full --no-stop`) · **live** (real providers, devices, networks) runs manually only, never in CI.
- **Gates are never deleted — only quarantined or demoted.** A gate that became slow, flaky or environment-bound moves
  to a heavier tier or gets a `skipIf` / `requires` guard; the bug class it once caught stays covered. Note the real
  bug each gate caught (commit, issue, version) next to it — that history is what justifies its cost.
- A new gate is added to the manifest *and* to this file: cheap + production-bug-catching → `core`; heavy or
  environment-dependent → `full`; live → `live`.

<!-- everything below is generated: `npx wy-gates inventory` -->
## Gate inventory
…

Contract

Public API, invariants and error model are fixed in CONTRACT.md; the tests in test/ are the contract suite (one test per numbered invariant). Three invariants to know before depending on this part:

  1. Cycles and unknown references fail at load, by name — a tier that includes itself (transitively), a requires loop, a tier entry naming nothing, or an unknown manifest key throws before any command runs (manifest_cycle / manifest_unknown_ref / invalid_argument with the offender in the message).
  2. Exit codes are the status and skips are never silent — exit 0 → pass; exit 2 → skip only when the group says allowEnvironmentSkip: true; anything else → fail; every skip carries a reason and an env-missing skip never invokes the runner. stopOnFail (default) reports the rest as skip — stopped after <id> failed so the summary is always complete.
  3. Checks report every finding and throw only for bad input{ ok, problems } lists everything (cwd-relative, sorted, deterministic); invalid_argument / not_found are raised for options and config only, never for what is in the tree; no check embeds a hostname, model id, org name or built-in deny-list.

An incompatible change bumps CONTRACT_VERSION and the major version together (CONTRACT.md → Compatibility).

Mined from

Extracted from three private source repositories — CodeReviewWar (modules scripts/run-test-manifest.mjs, scripts/test-manifest.json, scripts/check-architecture.mjs, docs/gates-inventory.md), LLM_GATEWAY (scripts/check-lock.mjs, check-esm-paths.mjs, check-error-passthrough.mjs, check-no-env-edit.mjs, check-shutdown.mjs, docs/gates-inventory.md) and AgentRAGKnowledge (scripts/check-lock.mjs, check-shutdown.mjs, check-no-default-model.mjs), developed 2026-07-13 – 2026-08-12 — generalized and re-tested for publication. What was kept, generalized and stripped: PROVENANCE.md.

Used by

| Client | Role of this part there | |---|---| | llm-gateway | gates.json with core (lock, esm-paths, error-passthrough, unit) and full (+ packaging / render checks); docs/gates-inventory.md generated by wy-gates inventory | | code-review-war | tiered manifest (static → PR, full → nightly, browser gates skipIf CHROME_PATH + exit-2 skips), wy-check-architecture for the src / server / shared direction and entry-file budgets | | every other wy-ai-labs client and the parts monorepo itself | wy-check-no-path-deps, wy-check-denylist, wy-check-pattern as the shared public-safety and principle gates |

Dependencies & budget

  • Runtime dependencies: 0 (budget 0) — enforced by scripts/check-budget.mjs.
  • Allowed: other @wy-ai-labs/* parts, pinned as caret ranges from the registry. Never a client, never file: / link: / git URLs / ../.
  • No private host, model id, org name or deny-list inside the part: the manifest, the architecture config and the deny-lists are the caller's files; defaults are conventions only (tier names, the 500-char error rule, exit code 2).

Gates

| Command | What | When | |---|---|---| | npm test | contract suite (node:test), 68 tests, offline, temp-dir fixtures, ~25 s (real child processes for the bins) | every save | | npm run gates:core | dependency budget + independence + engines.node + tests | before every commit / PR | | npm run gates:full | everything above (no environment-dependent gates in this part) | nightly / before release |

CI only calls these scripts (wy-ai-labs/.githubnode-gates.yml). Releases: Keep a Changelog + npm publish --provenance.

License

MIT © 2026 waneekim

한국어 요약

  • @wy-ai-labs/gates — 게이트 문화의 도구화: 티어(static/core/full/live)를 가진 매니페스트 기반 게이트 러너(전이적 확장, 순환 거부, env 기반 스킵, 종료코드 2 = "환경 차단" 규약)와 공용 게이트 체크·bin(lock 위생, ESM 경로, 경로 의존성, 아키텍처 방향, deny-list, 에러 전문 보존, 금지/필수 패턴). 공개 API·불변식·오류 모델은 CONTRACT.md에 고정되어 있고, 테스트가 곧 계약 스위트입니다.
  • 설치 npm install --save-dev @wy-ai-labs/gates(Node ≥22, ESM). gates.json을 두고 wy-gates run core(커밋·PR 전), wy-gates run full --no-stop(야간), wy-gates inventory(docs/gates-inventory.md 표). 런타임 의존성 0, 사설 호스트·모델명·내장 deny-list 없음.
  • 원칙: 게이트는 삭제하지 않는다 — 격리·강등만. 비공개 저장소 CodeReviewWar·LLM_GATEWAY·AgentRAGKnowledge에서 추출·일반화했습니다(PROVENANCE.md). 커밋 전 npm run gates:core.