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

forgeyn-sdk

v0.2.1

Published

Forgeyn Base SDK — universal data-plane client + central auth, server/admin management client, and React hooks. Zero runtime dependencies.

Readme

forgeyn-sdk

The official Forgeyn Base SDK. Zero runtime dependencies — fetch only. Works in browsers, Node 18+, Bun, Deno and edge runtimes.

npm install forgeyn-sdk

Version 0.2 ships three entry points:

| Entry point | Import | For | | --- | --- | --- | | Universal client | forgeyn-sdk (or forgeyn-sdk/client) | Browsers, mobile, edge — data plane via the public gateway + end-user auth via central GoTrue | | Server / admin client | forgeyn-sdk/node | Servers & agents — project provisioning, SQL, tables/rows, gateway apps, app end-users (X-Api-Key) | | React hooks | forgeyn-sdk/react | React 18/19 apps — ForgeynProvider, useForgeynClient, useAuth, useTable |

// browser / app code
import { createClient } from "forgeyn-sdk";
// server code
import { createAdminClient } from "forgeyn-sdk/node";
// react
import { ForgeynProvider, useAuth, useTable } from "forgeyn-sdk/react";

1. Universal client — forgeyn-sdk / forgeyn-sdk/client

import { createClient } from "forgeyn-sdk";

const fb = createClient({
  appId: "app_xxxx",       // from the /v1 credentials block (credentials.app_id) or console → API keys
  clientKey: "pk_ey…",     // publishable — safe in the browser
  gatewayUrl: "https://pupagsqvhhshecxmbgay.supabase.co/functions/v1/gateway", // optional, this is the default
  apiUrl: "https://api.db.forgeyn.com",                                        // optional, this is the default
});

Data plane — fb.from(table) (edge gateway, x-app-id + pk_ client key)

Structured JSON actions only — the gateway never sees raw SQL, every operation runs with bind parameters on your project's private Postgres.

await fb.from("todos").insert({ title: "Ship 0.2", done: false });

const { rows } = await fb.from("todos").select({
  columns: ["id", "title", "done"],
  where: { done: false },          // equality shorthand; operators: { eq, ne, gt, gte, lt, lte, like, ilike, in }
  orderBy: "-created_at",          // string shorthand; or [{ column: "created_at", direction: "desc" }]
  limit: 20,
  offset: 0,
});

await fb.from("todos").update({ where: { id }, values: { done: true } });
await fb.from("todos").delete({ where: { id } });   // alias: .remove(...)
const open = await fb.from("todos").count({ where: { done: false } });

// gateway reachability probe → { ok, service: "baas-gateway", configured }
await fb.health();

Rows on tables with a user_id column are forced to the Bearer token's user by the gateway. Attach a gateway tenant token with fb.setToken(token); calls without a token run as the anonymous (pk_) identity — public tables work out of the box.

End-user auth — fb.auth (central GoTrue at {apiUrl}/auth/v1)

Tier-2 end-user auth is the platform's central GoTrue service, exposed Supabase-style on the API origin.

const { user, session } = await fb.auth.signUp("[email protected]", "password123");
// session: { access_token, refresh_token, token_type, expires_in, user } — null when email confirmation is required
// (production has mailer_autoconfirm on, so sign-up returns a session immediately)

const res = await fb.auth.signIn("[email protected]", "password123"); // → { user, session }
const me   = await fb.auth.getUser();   // GET /auth/v1/user with the session's Bearer token
await fb.auth.signOut();                // POST /auth/v1/logout + local session cleared

fb.auth.getSession();  // stored AuthSession or null
fb.auth.getToken();    // current access_token or null
fb.auth.setSession(s); // restore a session (e.g. from your own storage)

Session persistence (opt-in)

Sessions live in memory by default. To survive reloads, opt in explicitly:

const fb = createClient({
  appId: "app_xxxx",
  clientKey: "pk_…",
  persistSession: true, // writes localStorage["forgeyn:session:<appId>"] and restores on load
});

Honest security note: raw tokens in localStorage are readable by any XSS payload. For production apps, back persistence with YOUR server (e.g. an httpOnly cookie via a /session endpoint) and call fb.auth.setSession(...) on boot. persistSession is a convenience for prototypes.


2. Server / admin client — forgeyn-sdk/node

Management-plane access with a Forgeyn API key (X-Api-Key: fbk_live_…). Account-wide or project-bound; scopes like projects:read, projects:write, sql:run, keys:write. Bearer tokens are better-auth console sessions — this client never sends them. Server-side only.

import { createAdminClient } from "forgeyn-sdk/node";

const admin = createAdminClient({
  apiKey: process.env.FORGEYN_API_KEY!,        // fbk_live_…
  apiUrl: "https://api.db.forgeyn.com",        // optional, this is the default
});

Provisioning flow (the Studio /v1 contract)

const created = await admin.projects.create({
  owner_external_id: "user_42",              // YOUR canonical user id → shadow platform user
  name: "my-app-db",
  idempotency_key: "user_42:my-app-db",      // retries are safe: same key → same project (200 + full record)
  owner_email: "[email protected]",          // optional — unifies with an existing platform account
  owner_name: "Ada Lovelace",                // optional
  region: "aws-us-east-1",                   // optional (GET /api/meta/regions)
});
// → 202 { project_id, status: "provisioning", poll_url }

const project = await admin.projects.pollUntilReady(created.project_id, {
  timeoutMs: 120_000,   // default; Neon provisioning usually takes 5–15s
  intervalMs: 2_000,    // default
});
// resolves the FINAL state: "ready" (with credentials) or "failed" (with error).

The credentials block

project.credentials on a ready project (and on idempotent replay):

| Field | Meaning | Where it may live | | --- | --- | --- | | api_url | Forgeyn API origin — management plane + central GoTrue ({api_url}/auth/v1) | any trusted runtime | | gateway_url | Public edge-gateway URL — the browser data plane | browsers included | | app_id | Gateway app id (app_…) — sent as x-app-id | browsers included | | pk_key | Publishable client key (pk_…) — sent as x-client-key | browsers safe (signed ES256 JWT) | | sk_key | Gateway secret admin key (sk_…) — bypasses per-user row scoping | SERVER-ONLY. Never in a browser, never in client bundles, never in NEXT_PUBLIC_* | | fbk_key | Project-bound Forgeyn API key (fbk_live_…, scopes projects:read + sql:run) — MCP/CLI/agent surface | server-side |

Note: gateway_url is populated from the API deployment's GATEWAY_PUBLIC_URL env. If a deployment hasn't set it, the field is null — pass gatewayUrl explicitly to createClient (the SDK defaults to the production gateway).

Which key drives which surface (verified live): management-plane endpoints (sql, tables, rows, auth/users) authorize the project's owner. Projects provisioned through /v1/projects are owned by the shadow user — so drive their schema/rows with the bundled project-bound fbk_key (scopes projects:read + sql:run), and keep your service key for provisioning, status polling and lifecycle (delete). The apps() keys surface resolves the owner too, but for /v1 projects the bundle already carries app_id/pk_key/sk_key, so nothing extra needs minting.

Wire them into the universal client:

const fb = createClient({
  appId: project.credentials.app_id,
  clientKey: project.credentials.pk_key,
  gatewayUrl: project.credentials.gateway_url ?? undefined,
  apiUrl: project.credentials.api_url,
});

Everything else on the admin client

await admin.health();                                   // → { ok, service: "forgeyn-api", version, db }

const mine = await admin.projects.list();               // management-plane list (this key's projects)
const p      = await admin.projects.get(id);             // GET /v1/projects/:id (contract shape)
const { status } = await admin.projects.remove(id);     // DELETE → { status: "deleting" } (async teardown)

// SQL (scope: sql:run) — rows capped at 500 per query, 15s statement timeout
const r = await admin.sql(projectId, "select 1 as ok");          // → { columns, rows, rowCount, durationMs }
await admin.sql(projectId, "select * from t", { readonly: true }); // READ ONLY transaction

// Schema + rows
const tables = await admin.tables(projectId);           // [{ name, columns, rowCount, sizeBytes }]
const page   = await admin.rows(projectId, "todos").list({ limit: 50, offset: 0 });
const row    = await admin.rows(projectId, "todos").insert({ title: "hi", done: false });
const upd    = await admin.rows(projectId, "todos").update({ id: row.id }, { done: true }); // pk → values
const gone   = await admin.rows(projectId, "todos").delete({ id: row.id });                    // → deleted count

// Gateway app credentials (dual-key pk_/sk_ pairs) — GET/POST/DELETE /api/projects/:id/keys
const apps  = await admin.apps(projectId).list();        // [{ appId, name, status, createdAt, revokedAt }]
const app   = await admin.apps(projectId).create({ name: "web" }); // { appId, clientKey, secretKey } — secret shown once
await admin.apps(projectId).revoke(app.appId);

// App end-users (POST /api/projects/:id/auth/users)
const { user } = await admin.appUsers.create(projectId, {
  email: "[email protected]",
  password: "correct-horse-battery",   // optional
  email_confirm: true,                 // optional
  user_metadata: { plan: "free" },     // optional
  app_metadata: { role: "member" },    // optional
});
// 201 → { user: { id, email, … } }; duplicate email → 409.
// NOTE: this endpoint ships independently of the SDK — while a deployment
// doesn't have it yet it answers 404; treat that as "not available here".

3. React — forgeyn-sdk/react

Peer dependency: react ^18 || ^19. No other dependencies, no JSX required (the provider is createElement-based).

import { createClient } from "forgeyn-sdk";
import { ForgeynProvider, useAuth, useForgeynClient, useTable } from "forgeyn-sdk/react";

const fb = createClient({ appId: "app_xxxx", clientKey: "pk_…", persistSession: true });

export default function Root({ children }) {
  return <ForgeynProvider client={fb}>{children}</ForgeynProvider>;
}

function Todos() {
  const { user, signIn, signOut, loading } = useAuth();
  const todos = useTable("todos", { orderBy: "-created_at", limit: 50 });
  const client = useForgeynClient();

  return (
    <>
      <p>{user ? `Hi ${user.email}` : "Anonymous"}</p>
      <button onClick={() => signIn("[email protected]", "password123")} disabled={loading}>Sign in</button>
      <button onClick={signOut}>Sign out</button>
      {todos.loading && <p>Loading…</p>}
      <ul>{todos.rows.map((r) => <li key={String(r.id)}>{String(r.title)}</li>)}</ul>
      <button onClick={() => todos.refresh()}>Refresh</button>
      <button onClick={() => client.from("todos").insert({ title: "new", done: false }).then(todos.refresh)}>Add</button>
    </>
  );
}
  • useAuth() → { user, loading, error, signUp, signIn, signOut, refreshUser }
  • useTable(table, opts) → { rows, loading, error, refresh }
  • useForgeynClient() → the client from the provider (throws without a provider)

Errors

Every non-2xx response throws ForgeynError ({ name, code, status, details, message }) — the class is exported from every entry point (forgeyn-sdk, forgeyn-sdk/client, forgeyn-sdk/node), and instanceof works across entries. Both platform envelopes are parsed: the API envelope { error: { code, message, details } } and the GoTrue envelope { code, error_code, msg }. Network failures throw code: "network_error", status: 0.

Migrating from 0.1.x

  • createClient({ url, appId, clientKey }) → createClient({ appId, clientKey, gatewayUrl }) (url renamed).
  • fb.db.select/insert/update/remove/count(table, …) → fb.from(table).select/insert/update/delete/count(…).
  • fb.auth now targets central GoTrue at {apiUrl}/auth/v1 (Supabase-shaped { user, session }, signUp/signIn/signOut/getUser). The 0.1 gateway auth (/auth/signup per-project accounts) is replaced per the platform auth contract; attach gateway tenant tokens with fb.setToken(token).
  • The persist hook is replaced by the persistSession boolean flag.
  • createAdminClient moved to forgeyn-sdk/node and is now a management-plane client (X-Api-Key: provisioning, SQL, tables/rows, apps, app users). The 0.1 sk_-based data-plane admin client is available by passing adminKey to createClient (server runtimes only — the constructor refuses adminKey in browsers).

License

MIT