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

@runku/client

v0.5.4

Published

Typed browser and server client for Runku Public Protocol v1

Readme

@runku/client

Framework-independent TypeScript ESM client for Runku HTTP and Realtime protocols. It uses native fetch, AbortSignal, Web Crypto, and browser WebSocket; there are no runtime dependencies. Node can inject a WebSocket factory.

npm install @runku/[email protected]

Client 0.5.0 retains Public Protocol v1 compatibility and the runtime Function-reference contract required by @runku/[email protected].

Create and type a client

import { RunkuClient, type CodeTarget } from "@runku/client"
import { api } from "./runku/_generated/api.js"

export const runku = new RunkuClient({
  baseUrl: process.env.RUNKU_URL!,
  target: process.env.RUNKU_TARGET! as CodeTarget,
  applicationKey: process.env.RUNKU_KEY!,
  getBearer: async () => currentUserToken(),
})

RunkuClient accepts generated Function references such as api.notes.list. api excludes internal and service-auth Functions; runku/_generated/server.js contains the separate server tree. The legacy typedClient string view remains available. The SDK does not read environment variables or detect frameworks; application code passes configuration explicitly.

baseUrl requires HTTPS except loopback development. Credentials in URL/userinfo are rejected.

Targets

target is mandatory:

"workspace:local"
"release:rel_01..."
"channel:stable"

There is no latest. A per-call options.target may select another explicit target. A request pins the resolved Release/Dev Revision.

Typed calls and result envelopes

const created = await runku.mutation(api.notes.create, { title: "Read runbook" })
const noteId = created.value.id
const loaded = await runku.query(api.notes.get, { id: noteId })
const image = await runku.action(api.images.render, { width: 64n, height: 64n })

Each call returns:

  • requestId and resolved releaseId;
  • canonical Function value;
  • kind-specific metadata: Query snapshot, Mutation commit/replay/attempts, or Action schedules.

Generated contracts restrict public Function names by kind and preserve argument/result/document ID types. Runtime/server validation remains authoritative.

Canonical values

| Runku | JavaScript | |---|---| | i64 | bigint | | float64 | finite number | | bytes | Uint8Array | | timestamp | RunkuTimestamp with signed microseconds | | typed ID | RunkuId / DocumentId<"table"> | | array/object | recursively bounded readonly values |

Validate untrusted route/form IDs with documentId("notes", value) before calling a typed Function. Values are checked before network encoding.

Application and functional identity

applicationKey is always required. Use rk_pub_* in distributable clients and rk_sec_* only in trusted server configuration. rk_dev_* is not accepted by the invocation protocol.

getBearer is optional and evaluated on every attempt/reconnect so the application can refresh a guest/user/service JWT. Never persist bearer tokens in URLs. Authentication and Application Key are independent checks.

Retry and cancellation

  • Query and Mutation retry only transport or explicitly retryable server errors.
  • Mutation generates one operation ID and preserves it across attempts. Supply { operationId: "opn_..." } to reconcile the same intent across application restart.
  • Action is never retried automatically because an external effect may have happened.
  • AbortSignal cancels client waiting; it does not prove remote/Action effects did not occur.
  • Timeouts/attempts/delay are bounded in RunkuClientConfig.

RunkuError exposes stable code, retryable, HTTP status, and optional requestId. Branch on code/retryable, not message text.

Realtime

const realtime = runku.realtime({
  reconnectInitialDelayMs: 250,
  reconnectMaximumDelayMs: 10_000,
})

const subscription = realtime.subscribe("notes.get", { id: noteId }, {
  onValue: ({ value, deliveryRevision, releaseId }) => {
    render(value, deliveryRevision, releaseId)
  },
  onError: (error) => report(error.code, error.requestId),
})

const initial = await subscription.ready
await subscription.unsubscribe()
realtime.close()

Realtime supports public Queries only. Authentication occurs in a WebSocket frame, never the URL. Reconnect preserves target and refreshes bearer. resync_required obtains another authoritative Query result; intermediate frame replay is not promised.

Browser uses native WebSocket. Node/test runtimes may pass webSocketFactory implementing the documented interface.

File upload and download

An authorized Action first returns a one-shot FileUploadGrant or short-lived FileDownloadGrant. Pass the structural generated result through fileUploadGrant(...) or fileDownloadGrant(...) to validate and refine its IDs and paths. Transfer through the client so the secret remains an Authorization header and the path cannot escape the configured origin:

const grant = fileUploadGrant(
  (await runku.action("files.beginUpload", { size: BigInt(file.size) })).value,
)
const metadata = await runku.uploadFile(grant, file, { contentType: "image/png" })

const readGrant = fileDownloadGrant(
  (await runku.action("files.beginDownload", { fileId: metadata.fileId })).value,
)
const response = await runku.downloadFile(readGrant)
await response.body?.pipeTo(destination)

Uploads are one-shot and never automatically retried. Downloads expose a streaming Response; the configured deadline and optional AbortSignal remain active until the stream completes or is cancelled. One inclusive-exclusive range may be supplied. The SDK validates metadata, checksum ETag, lengths, range response, attachment policy, media type, and same-origin canonical path. It does not send the Application Key or user bearer to a raw transfer route because the grant is the delegated credential. See Application file storage.

Direct/untyped use

RunkuClient.query/mutation/action<T>() is available without a generated registry. It still validates canonical values but cannot type-check Function names/contracts at compile time. Prefer typedClient in application code.

Development

pnpm --dir packages/client check

The check builds, runs unit/protocol/retry/Realtime tests, and type conformance. Public changes also require gateway/protocol vectors and executable example gates.