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

@two80/sdk

v0.3.1

Published

Identity and platform API primitives for applications running on 280.

Readme

@two80/sdk

The only identity and platform-access code a 280 app ever contains. Your app holds no auth, no sessions, no provider credentials, and no user table. The 280 gateway authenticates the caller, gates the route, and forwards one short-lived signed identity header; this SDK decodes it and gives you a typed client for each platform capability.

npm install @two80/sdk

The SDK reads the platform-supplied TWO80_API origin from the environment. Never override it. The container reaches only the 280 API host; the API authorizes every call for the current app and user.

Request scoping

Everything is request-scoped: pass the incoming request so the SDK forwards the caller's identity. Nothing is global or cached across requests. "The request" is anything that exposes its headers: a Fetch Request (identity(request)) or Next's headers() result (identity(await headers())).

import { identity } from "@two80/sdk";

// Next.js route handler, Server Action, or any handler with the request in scope.
export async function GET(request: Request) {
  const { user, can, scope, role, anonymous } = await identity(request);

  user.email;             // resolved by the gateway, never by app code
  can("approvals.edit");  // true when the viewer holds that feature role
  scope("salaries");      // advisory data scope, or null
  role;                   // '' | owner | admin | editor | viewer
  anonymous;              // true for a public app's no-session visitor
}

identity(request) never throws when no identity header is present: it resolves to a safe absent viewer with present: false, empty user/role/title, and can()/scope() returning false/null. Branch on present instead of wrapping the call in try/catch:

const viewer = await identity(request);
if (!viewer.present) return <SignInPrompt />;

A malformed token is a genuine failure (the gateway signs valid tokens and is the container's only ingress) and still throws IdentityError. The token is not re-verified here; the gateway already verified it.

Before writes or per-user rows in a public app, branch on anonymous:

if (identity.anonymous) return new Response("Sign in required", { status: 401 });

Integrations

Each integration is a factory that takes the incoming request (same shapes as identity: a Fetch Request or await headers()) and returns a typed client. The 280 API authorizes every call for the current app and user; your app never sees provider credentials.

Not-ready vs. genuine failure

The platform has expected not-ready states where a human still needs to act: the integration is not connected yet, the bound resource was removed, or the owner must re-authorize. On these, calls do not throw. They resolve to a safe result carrying an optional notReady code, so an app that just renders the data shows an empty state instead of crashing:

const { values, notReady } = await sheets.read({ resource, range });
values.map(...);          // [] when not-ready — renders "nothing yet"
if (notReady) { /* optionally prompt the owner to connect Google */ }

Writes (append/update/deleteRows) return the same-shaped result with zeroed counts and notReady set; they never throw on a not-ready integration. notReady is one of not_connected, resource_not_found, or reauthorization_required.

Every genuine failure still throws IntegrationRequestError with { code, message, status, retryable }: provider_error (502), provider_unavailable (503, retryable), invalid_request (400), unauthenticated (401), internal_error (500).

Declare each integration the app uses in 280.json so push gates the deploy until the owner connects it:

{ "integrations": ["google-sheets"] }

Google Sheets — googleSheets(request)

import { googleSheets } from "@two80/sdk";

const sheets = googleSheets(request);
await sheets.read({ resource, range });            // -> { range, majorDimension, values }
await sheets.append({ resource, range, values });  // -> { updatedRange, updatedRows, updatedCells }
await sheets.update({ resource, range, values });  // -> { updatedRange, updatedRows, updatedCells }

resource is the spreadsheet id, range is A1 notation (e.g. Sheet1!A1:C10), and values is a 2D array of cell values.

Capability reference

The authoritative list of supported capabilities and operations, generated from the 280 capability catalog, lives at https://www.280apps.com/capabilities.md. If an operation you need is not listed, it is unsupported: report it rather than working around the network boundary.