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

@anchrd/gate-sdk

v0.39.0

Published

The typed client and CLI for **gate** — login and permissions as a finished product. The only package a customer edge installs.

Readme

@anchrd/gate-sdk

The typed client and CLI for gate — login and permissions as a finished product. The only package a customer edge installs.

  • authorize(bearer) → can(interface, function) — one remote call per request, then local permission checks.
  • gate CLI — register your service and generate typed code.
  • The shared wire formats as Zod schemas, under @anchrd/gate-sdk/contract.
  • Runtime-neutral — standard fetch only; runs in Cloudflare Workers, Node, Bun, and the browser.

Where this package stops. It needs a reachable gate (GATE_URL) and the right key for the door it knocks on — see the two-token rule below. It administers nothing: it cannot create users, assign roles or grant permissions, and it only ever sees the interfaces of the one service its key belongs to. It holds no session and caches no permissions — authorize() is one call per request, and everything after it is local.

Install

npm i @anchrd/gate-sdk

Quick start

1. Register your service (once, as an admin — the app key lives in GATE_ADMIN_KEY):

export GATE_URL=https://gate.your-customer.dev
export GATE_ADMIN_KEY=gate_ak_…            # admin application key (x-api-key)
gate register dashboard --fn read,export   # → service key is written to .env (GATE_KEY)

This creates a service named dashboard — the unit that owns exactly one key — together with its interface of the same name. Further interfaces use that same key and can be managed in the admin UI or declared by the service itself with gate.interfaces.set().

If .env already carries a GATE_KEY, gate register stops before talking to the gate. The key is handed out exactly once, so creating a second service here would leave one behind whose key nobody ever sees.

2. Generate types for your service:

gate init   # writes gate.gen.ts (deterministic, never edit by hand)

/schema is scoped to the service your key belongs to: gate.gen.ts contains exactly your own interfaces. Another service's handle is not in there — and therefore a compile error at your call site, not a rule that silently never matches.

3. Authorize in your edge (~5 lines):

import { createGateClient } from "@anchrd/gate-sdk";

const gate = createGateClient({
  url: process.env.GATE_URL!,
  serviceKey: process.env.GATE_KEY!,   // from the .env written by `gate register`
});

// per request:
const result = await gate.authorize(userBearer);
if (!result.ok) return new Response(null, { status: result.status }); // 401 / 403 / 5xx
if (!result.can("dashboard", "read")) return new Response(null, { status: 403 });
// … result.identity is the user (id, email)

Client

createGateClient({ url, serviceKey, fetch?, timeoutMs? }) returns a client. Service-key requests time out after 10 seconds by default; timeoutMs overrides that limit.

| | | |---|---| | authorize(bearer) | One call to /api/v1/authorization. The response is validated. An invalid or expired bearer is a structured result ({ ok: false, status, problem }), not an exception — the edge can tell 401 from 500. | | result.can(handle, fn) | Local, no further network call (rules[handle]?.includes(fn)). | | result.identity · result.rules | Who the user is · their flattened permission set. | | gate.interfaces.list() | List this service's own interfaces. | | gate.interfaces.set(handle, functions) | Idempotently create or replace one own interface. This never grants a user access. Gate's internal interfaces cannot be managed this way. | | gate.interfaces.remove(handle) | Remove one own interface and its grants. |

The gate.interfaces methods throw GateInterfaceError ({ status, problem }) on errors, including 401/403 responses and 502 when gate breaks the response contract. Unlike authorize, they do not return a structured failure result.

After gate init, can is typed: can("dashboard", "read") — an unknown interface or function is a compile error, with no string escape hatch.

fetch is injectable (mocking in tests means passing a fetch, not an HTTP framework). The service key never appears in logs, errors, or results.

CLI

| Command | | |---|---| | gate register <name> --fn a,b,c | Create a service plus its interface of the same name (admin). The service key is written to .env exactly once (GATE_KEY) — never to stdout. <name> is a lowercase slug (a-z, 0-9, -). | | gate init | Generate types from /api/v1/schema — the interfaces of your service (gate.gen.ts). | | gate build | Apply gate.json to the local admin UI and build it. Pure file work — no key, no network. |

Env, per command: register needs GATE_URL + GATE_ADMIN_KEY (app key); init needs GATE_URL + GATE_KEY (service key); build needs none — it never talks to a gate.

The CLI does not grant permissions. The former gate grant went away with the grants slice (GATE-65/66): a permission belongs to a role, and roles are edited in the admin UI (the role matrix) or through the API. A CLI command would have been a second place where access is handed out.

Branding the admin UI — gate.json

Put a gate.json next to your package.json. gate build reads it, writes two files into the local admin UI (it travels inside @anchrd/gate) and builds it; the output is the static asset directory your gate worker serves (see the @anchrd/gate README).

{
  "ui": {
    "theme": "./branding/theme.css",
    "defaultLanguage": "en",
    "languages": { "de": "./i18n/de.json" }
  }
}

| Field | | |---|---| | ui.theme | A CSS file with your design tokens (:root { --primary: … }). It is imported after the UI's own stylesheet, so your tokens win — no component is touched. | | ui.languages | Language code → catalog file. Every listed file is checked against the UI's en.json (the key source). | | ui.defaultLanguage | Which of them is built in. Defaults to "en" — the UI's own catalog. |

Three things are deliberate:

  • Unknown fields are rejected, not ignored. A typo (langauges) aborts the build naming the field, instead of silently doing nothing.
  • An incomplete translation aborts the build, naming the file and the missing keys. There is no silent fallback — you would not see it until a user did, mid-screen, in the wrong language.
  • No gate.json is fine. gate build then builds the defaults (English, stock tokens) — the same output as before this feature existed.

Everything happens at build time. The running UI loads no config, picks no language and switches no theme: it finds exactly one stylesheet and exactly one catalog.

gate build works against the admin UI already present locally, which travels inside @anchrd/gate (install it: the UI ships as source precisely so that this build can apply your config). The output lands where it belongs to you:

| the UI found in | output | |---|---| | node_modules/@anchrd/gate/ui | .gate/ui in your project root — point assets.directory there | | packages/gate/ui (this workspace) | packages/gate/ui/dist, unchanged since GATE-45 |

⚠️ The installed case never builds into node_modules: the next npm ci would wipe the output while your wrangler.jsonc still pointed at it — a worker serving nothing, with nobody having touched a thing. The two mount points (src/theme/custom.css, src/i18n/custom.json) do live in node_modules, and may: gate build rewrites both every run.

The wire formats: @anchrd/gate-sdk/contract

Until 0.26.0 these lived in a package of their own, @anchrd/gate-contract. Since 0.28.0 they are a subpath of this one, so a consumer installs one package instead of two — that is the whole reason for the move. The schemas are unchanged.

import { AuthorizationResponse, type Identity, type Rules } from "@anchrd/gate-sdk/contract";

const { identity, rules } = AuthorizationResponse.parse(await res.json());
//      ^ Identity            ^ Rules = Record<interface, function[]>

| Schema | | |---|---| | Identity | Who the authenticated user is: { id, email, name? }. | | Rules | Record<interface, function[]> — the flattened can() set. | | AuthorizationResponse | Response of POST /api/v1/authorization: { identity, rules, resource? }. | | SchemaResponse | Response of GET /api/v1/schema — the basis for codegen (gate init). | | SchemaInterface · ServiceInterfaceRemoveResponse | Service-scoped interface self-management. | | ProblemDetails | The single outward error shape (RFC 9457, application/problem+json). |

Formats only, no behavior. Semver-sacred: a breaking change here hits every customer edge at once.

Where the contract stops. It covers the wire formats a customer edge meets — authorization, schema, interface self-management, and the one error shape. It is deliberately not a complete type mirror of gate's administration surface: the admin routes belong to the admin UI and to /mcp, and pinning their shapes here would make every screen change a semver event for every customer.

⚠️ rules are trimmed to the service whose key asked. A service sees its own interfaces and nothing about what the same person may do elsewhere.

The two-token rule

Never confuse them:

  • Service key (GATE_KEY, in the edge's .env) — only says "this code may talk to gate".
  • User bearer (per request from the client) — the only thing can() checks. Permissions always come from the resolved token, never from an argument.

Related

  • @anchrd/gate — gate itself: the Cloudflare Worker and the admin UI. That is what an operator installs; this package is what a consumer of a running gate installs.