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

@hyper.authio/admin

v0.10.0

Published

Authio Admin API client for Node.js and edge runtimes: every operation generated from the server's OpenAPI document, grouped by area and typed by path, with account credentials, envelopes, errors, paging and Retry-After.

Readme

@hyper.authio/admin

The Authio Admin API client for Node.js 20.19+ and edge runtimes. Every operation under /api/v1/admin/ (359 of them) is generated from the server's own OpenAPI document, typed end to end, and grouped by area; authentication with an account credential, the envelopes, the errors, paging and Retry-After are handled for you.

npm install @hyper.authio/admin

It depends on @hyper.authio/client (account credentials, envelopes, errors) and openapi-fetch (the typed fetch by path).

Configure

import { createAuthioAdminClient } from "@hyper.authio/admin";

const admin = createAuthioAdminClient({
  authority: "https://auth.example.com",
  credential: process.env.AUTHIO_ADMIN_CREDENTIAL, // ak_…, pat_… or cs_… (with credentialId)
});

| Option | Default | | | --- | --- | --- | | authority | | Required. Authio's base URL; the Admin API is under {authority}/api/v1/admin. HTTPS, except on loopback or with requireHttps: false. | | credential | | An account credential. ak_… (API key) and pat_… (personal access token) are sent directly; cs_… (client secret) is exchanged first. | | credentialId | | Required with a cs_ secret: it is exchanged at {authority}/realms/authio with client_credentials and the client_id cred_{id in format N}. | | presentAs | bearer | How ak_ and pat_ are sent: Authorization: Bearer or x-api-key. | | exchangePersonalAccessToken | false | Exchanges a pat_ with the personal_access_token grant instead of sending it. | | scopes | | Narrows an exchanged token to admin:{permission}:{method} scopes. | | tokenRefreshSkew | 60 s | How early an exchanged token is renewed (at most half its lifetime). Exchanged tokens are cached, and exchanged again once after a 401. | | accessToken | | Instead of credential: an access token, or a function returning one, called for every request. | | maxRetries | 1 | Times a GET, HEAD, PUT or DELETE answered 429 or 503 with Retry-After is sent again, each after waiting it. 0 turns it off. | | httpTimeout | 30 s | Bounds every call, combined with the call's signal (streams excepted). | | fetch, clock | global fetch, system clock | Replace them in tests. |

Option names follow the shared conformance/options.md; there are no aliases.

Call it

Operations are grouped by the controller their operationId names: Realm_GetAllPaginated is admin.realm.getAllPaginated, Client_Get is admin.client.get. Path and query parameters are passed by name in one object, and the request body as body; the second argument takes signal, requestId (sent as X-Request-Id) and headers. Each method answers what the envelope carries:

  • the object (data) for most operations;
  • the page { items, totalCount, pageNumber, pageSize } the envelope's data holds for a listing;
  • nothing for 204;
  • the raw Response for a stream (admin.accessLog.stream(), Server-Sent Events).
const realm = await admin.realm.getByRealmId({ realm: "acme" });          // AuthioAdminSchema<"RealmResponse">
const page = await admin.realmClient.getClientsPaginated({ realm: "acme", pageSize: 50, searchTerm: "web" });

Every refusal throws AuthioAdminError, the same class @hyper.authio/client exports:

import { AuthioAdminError } from "@hyper.authio/admin";

try {
  await admin.realm.create({ body: { name: "Acme", realmId: "acme" } });
} catch (error) {
  if (error instanceof AuthioAdminError) {
    error.status;            // the HTTP status (never the envelope's statusCode)
    error.errorCode;         // "ValidationFailed", "RealmNotFound", …: the top-level errorCode
    error.notifications;     // [{ message, field?, code? }]
    error.requestId;         // from the envelope, else X-Request-Id
    error.retryAfterSeconds; // from Retry-After, on 429 and 503
  }
}

Every page

for await (const client of admin.paginate(admin.realmClient.getClientsPaginated, { realm: "acme", pageSize: 100 })) {
  console.log(client.clientId);
}

for await (const page of admin.pages(admin.realm.getAllPaginated)) console.log(page.totalCount);

paginate starts at page 1 and stops at totalCount, or at an empty page.

The typed fetch by path

admin.api is an openapi-fetch client over the same document, authenticated and retried the same way. It answers { data, error, response } with the raw envelopes (ApiResult… on success, ErrorResult on failure):

const { data, error } = await admin.api.GET("/api/v1/admin/realms/{realm}", { params: { path: { realm: "acme" } } });

admin.fetch is a plain fetch with the same authentication and retries, for anything else.

Examples

const realm = "acme";

// Realms
await admin.realm.create({ body: { name: "Acme", realmId: realm } });
await admin.realm.update({ realm, body: { name: "Acme Corp", description: "Production tenants" } });
const realms = await admin.realm.getAllPaginated({ pageSize: 20 });

// Clients
const created = await admin.realmClient.create({ realm, body: { name: "Storefront", pkce: true, redirectUri: "https://shop.example.com/callback" } });
const client = await admin.client.get({ realm, client: "storefront" });
await admin.client.updateStatus({ realm, client: "storefront", body: { status: "Inactive" } });
const rotated = await admin.clientSecret.rotate({ realm, client: "storefront", body: { previousValueValidForMinutes: 60 } });

// Roles: a claim is a permission, its type the resource and its value the method
await admin.realmRole.create({ realm, body: { name: "auditor", claims: [{ claimType: "orders", claimValue: "read" }] } });
await admin.clientRole.create({ realm, client: "storefront", body: { name: "support", claims: [{ claimType: "tickets", claimValue: "write" }] } });
await admin.realmUser.addRoles({ realm, userId: "6f7a8b9c-0d1e-4f2a-9b3c-4d5e6f7a8b9c", body: { roles: ["auditor"] } });

// Groups (per client)
const group = await admin.group.create({ realm, client: "storefront", body: { name: "Finance", isActive: true } });
await admin.group.addMember({ realm, client: "storefront", groupId: group.id!, body: { userId: "6f7a8b9c-0d1e-4f2a-9b3c-4d5e6f7a8b9c" } });

// Scopes
await admin.realmScope.create({ realm, body: { name: "orders:read", protocol: "openid-connect", assignedType: "Optional" } });
await admin.clientScope.create({ realm, client: "storefront", body: { name: "orders:write", protocol: "openid-connect" } });

// Resource servers (APIs)
await admin.resourceServer.create({
  realm,
  body: { identifier: "https://orders.example.com", name: "Orders API", scopes: [{ name: "orders:read" }], clientIds: [created.id!] },
});

// Consumers (machine callers of a client): Authio generates the credential, and consumer.value is the one place it is shown
const consumer = await admin.clientConsumer.create({ realm, client: "storefront", body: { name: "billing-worker", type: "ApiKey" } });

// Account credentials (ak_, cs_, pat_): the answer is the only one that carries the value
const credential = await admin.secret.create({ body: { name: "ci", type: "PersonalAccessToken", allowedScopeIds: [] } });
await admin.secret.rotate({ id: credential.id!, body: { previousValueValidForMinutes: 15 } });

// Webhooks, for a client or for the whole realm
const hook = await admin.webhook.create({
  realm,
  client: "storefront",
  body: { name: "Audit", url: "https://hooks.example.com/authio", subscribedEvents: ["user_created", "login_failed"] },
});
await admin.webhook.sendTest({ realm, client: "storefront", webhookId: hook.id! });
await admin.realmWebhook.create({ realm, body: { name: "SIEM", url: "https://siem.example.com/authio", subscribedEvents: ["attack_detected"] } });

The full list is in the AuthioAdminAreas type (each method's summary, method and path are in its doc comment) and in authioAdminOperations. The request and answer types are exported as AuthioAdminSchema<"Name">, AuthioAdminParams<"OperationId">, AuthioAdminPaths, AuthioAdminComponents and AuthioAdminOperations.

Generated from the server

openapi/v1.json is a copy of authio.api's openapi/v1.json, which the server regenerates and checks on every build; openapi/SOURCE names the commit it was copied from (also exported as authioAdminOpenApiCommit). From it, scripts/generate.mjs keeps the paths under /api/v1/admin/ and the schemas they reach, writes the types with openapi-typescript (src/generated/schema.ts) and the grouped operations (src/generated/operations.ts). The generated code is committed, and CI fails when running the generator would change it.

npm run sync-openapi -w @hyper.authio/admin -- ../authio.api origin/master   # copy, record the commit, regenerate
npm run generate -w @hyper.authio/admin                                      # regenerate from the copy
npm run generate -w @hyper.authio/admin -- --check                           # what CI runs

Area and method names come from the operationIds ({Controller}_{Action}), which the server keeps stable; a renamed controller or action is a breaking change of this package.