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

@mochabug/adapt-sdk

v2.0.0-beta.19

Published

Application APIs, routers, browser utilities, and shared contracts for Mochabug Adapt v2

Readme

@mochabug/adapt-sdk

Application APIs, routers, and browser utilities for Mochabug Adapt v2.

npm install @mochabug/adapt-sdk
npm install -D @mochabug/adapt-builder

Adaptkit scaffolds the project and its TypeScript environments. Adapt Builder owns runtime declarations and builds the plugin. The SDK wraps runtime services and provides application utilities; it does not install ambient types.

| Import | Environment | Purpose | | --- | --- | --- | | @mochabug/adapt-builder/runtime | Adapt backend | Types for handlers, injected environments, HTTP, TCP, datagrams, and the three native mb:* modules | | @mochabug/adapt-sdk/api | Adapt backend | Executor/configurator APIs and native Connect transport | | @mochabug/adapt-sdk/router | Adapt backend | HTTP, Connect RPC, WebSocket, WebTransport, tunnels, lifecycle | | @mochabug/adapt-sdk/browser | Browser | Plugin token and frame resizing | | @mochabug/adapt-sdk/react | Browser | React components; CSS at @mochabug/adapt-sdk/react/styles.css | | @mochabug/adapt-sdk/jtd | Any | RFC 8927 schema compilation, exact raw JSON validation, and compatibility checks | | @mochabug/adapt-sdk/proto | Any | Generated protobuf messages and service descriptors | | @mochabug/adapt-sdk/cel, /signals, /openai, /anthropic, /gemini | Any | Expressions, signals, and message conversion |

Backend TypeScript uses lib: ["ES2024", "DOM", "DOM.Iterable"] and types: ["@mochabug/adapt-builder/runtime"], which includes Node 24 declarations. Browser TypeScript uses lib: ["ES2024", "DOM", "DOM.Iterable"] and types: ["vite/client"]. Config/SSG files have their own host-tools project.

TypeScript's DOM library supplies standard Fetch types such as HeadersInit, which Connect RPC requires. Native interfaces are explicit imports from @mochabug/adapt-builder/runtime; neither package adds standard global aliases. Server rendering uses the same backend configuration. DOM declarations make browser globals visible to TypeScript but do not provide window or document in the runtime. Keep browser API use in browser code. All environments are checked with skipLibCheck: false. There is no SDK root import or builder browser-types wrapper.

Protobuf and Connect service definitions are shared unchanged between server and browser. Only the transport changes: the runtime uses native HttpBinding; browsers use @connectrpc/connect-web. Neutral SDK entries need neither the builder nor Node types.

JTD schemas

/jtd owns schema and value validation; it has no third-party JTD dependency. Compile a schema once with compile(schema), then reuse .validate(value), .validateJson(source) or .subsumes(target). An empty error array means a match. Invalid schemas, invalid JSON, and resource limits throw distinct errors. Use raw JSON validation when integer precision matters: JavaScript numbers may already have been rounded before .validate(value) receives them.

The generated JTDSchemaSchema accepts ordinary JTD JSON with fromJson or fromJsonString using their defaults. JTD JSON uses camelCase keywords, including optionalProperties and additionalProperties; default protobuf JSON output uses the same names. Empty properties/optionalProperties maps retain their presence through binary and JSON round trips. Known schema keywords set to null are treated as omitted, including nested schemas; unknown keywords, null schema-map entries, and null enum members remain invalid. Opaque metadata and instance nulls retain their meaning. normalize(schema) validates and omits null schema keywords, preserving metadata and user-defined property, definition, and tag names. Numbers inside opaque metadata use standard ProtoJSON/Struct double precision; round trips do not preserve arbitrary numeric precision there. Instance validation uses the raw JSON validator to retain numeric lexemes.

Validation bounds are exported as limits. They include 16 MiB of JSON, schema and reference depth 32, JSON depth 128, 16,384 schema nodes, 1,048,576 JSON values and validation work units, and 100 diagnostics totaling at most 64 KiB. Limit exhaustion never indicates success. Timestamps use RFC 3339 calendar and offset rules and the 27 announced leap seconds through IERS Bulletin C 72 (July 2026); a new leap announcement requires updating the SDK and platform tables together.

Beta.11 changes the JTD protobuf wire representation. Rebuild plugins against this version; SDK code does not read legacy persisted messages.

HTTP, TCP, and datagram exports

In src/<vertex>/executor.ts, export the handlers enabled by vertex.json. Each receives (streamOrSocket, env, ctx); its environment is inferred from the explicit handler type:

// runtime/native.ts
import type { ExecutorEnvironment, ExportedHandler } from '@mochabug/adapt-builder/runtime';

export const external_http = {
  async http(stream) {
    if (stream.method !== 'GET' || stream.path !== '/health') {
      await stream.respond({ status: 404 }).end();
      return;
    }
    await stream.respond({ headers: { 'content-type': 'application/json' } }).json({ ok: true });
  },
} satisfies ExportedHandler<ExecutorEnvironment>;

export const external_connect = {
  async connect(socket) {
    await socket.readable.pipeTo(socket.writable);
  },
} satisfies ExportedHandler<ExecutorEnvironment>;

export const external_datagram = {
  async datagram(socket) {
    for (;;) {
      const packet = await socket.receive();
      if (packet === null) return;
      await socket.send(packet);
    }
  },
} satisfies ExportedHandler<ExecutorEnvironment>;

TCP connect is a raw socket handler. Connect RPC is an HTTP service.

Credit to Cloudflare for the TCP Socket interface design, familiar from Workers' connect() API. This attribution applies specifically to the interface design.

Datagrams are RFC 9221 QUIC DATAGRAM, with one socket per connection. Keep the handler pending for the entire session. Await sends; a send may transfer the buffer, so do not reuse it afterwards. maxDatagramSize is available after socket.opened resolves.

All executors also need internal_http for platform lifecycle RPCs:

// runtime/lifecycle.ts
import { createIERouter } from '@mochabug/adapt-sdk/router';

export const internal_http = createIERouter((router) => {
  router.onStart(async (_request, api) => {
    console.log(await api.getConfig());
  });
});

env.assets.fetch() reads managed assets. env.httpproxy, env.plugin, env.fileforge, and the role's env.executor or env.configurator expose .http(). SDK API classes wrap these bindings; router callbacks receive the appropriate API instance. Import outbound http from mb:http, raw TCP connect from mb:connect, and QUIC datagram from mb:datagram. Each module exports only its named function; see the builder's native examples.

Native calls use binding.http(url, { method, headers, signal }) or http(url, options). The returned exchange owns the upload through end(body), write(), or writable; it snapshots metadata and preserves the supplied signal. The SDK's userHttpProxy() and systemHttpProxy() keep their Fetch-compatible (input, init) interface, including Request bodies and cancellation. Their adapter forwards metadata directly and allows a response to arrive during an unfinished upload.

HTTP routers

Router factories return the native { http } handler directly. This executor example combines HTTP, SSE, Connect RPC, WebSocket, WebTransport, and tunnels. EchoService is your Buf-generated service descriptor with unary echo and server-streaming watch methods, each using messages with a text field.

| Factory | Role | Configuration | | --- | --- | --- | | createEERouter | External executor | Middleware and routes | | createIERouter | Internal executor | Middleware and onStart, onStop, onExchange, onCron | | createECRouter | External configurator | Middleware and routes | | createICRouter | Internal configurator | No callback; no incoming application protocol |

// runtime/routes.ts
import { setTimeout as delay } from 'node:timers/promises';
import { createEERouter } from '@mochabug/adapt-sdk/router';
import { EchoService } from '../gen/echo_pb.js';

export const external_http = createEERouter((router) => {
  router.useH3Upgrade();
  router.use(async (exchange, next) => {
    exchange.response.headers.set('x-content-type-options', 'nosniff');
    await next();
  });
  router.addRoute('GET', '/users/:id', async (_request, api, route) => {
    return Response.json({ id: route.params.id, config: await api.getConfig() });
  });
  router.addRoute('GET', '/events', (request) => {
    const cancelled = new AbortController();
    const signal = AbortSignal.any([request.signal, cancelled.signal]);
    const encoder = new TextEncoder();
    const events = new ReadableStream<Uint8Array>({
      async pull(controller) {
        await delay(1000, undefined, { signal });
        controller.enqueue(encoder.encode(`event: tick\ndata: ${Date.now()}\n\n`));
      },
      cancel() { cancelled.abort(); },
    });
    return new Response(events, {
      headers: { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' },
    });
  });
  router.addConnect(EchoService, {
    echo: (request) => ({ text: request.text }),
    async *watch(request, context) {
      for (let i = 1; i <= 3; i++) {
        context.signal.throwIfAborted();
        yield { text: `${request.text}:${i}` };
      }
    },
  }, { grpc: true, grpcWeb: true, connect: true });
  router.addWebSocket('/socket', async (socket) => {
    for (;;) {
      const message = await socket.receive();
      if (message === null) return;
      await socket.send(message.data, { type: message.type });
    }
  });
  router.addWebTransport('/transport', async (transport) => {
    for (;;) {
      const packet = await transport.datagrams.receive();
      if (packet === null) return;
      await transport.datagrams.send(packet);
    }
  });
  router.addTunnel('/tunnel', async (tunnel) => {
    await tunnel.readable.pipeTo(tunnel.writable);
  }, { protocol: 'bytestream' });
});

HTTP middleware covers ordinary requests, RPCs, and accepted HTTP sessions. Set response headers before next(); call it once or return a Response to reject the request. useH3Upgrade() advertises HTTP/3. Add router.useBearerAuthorization(['/private']) to authorize matching paths before user middleware or transport acceptance. Routes and protected prefixes are case-sensitive. Bearer credentials use the canonical Authorization: Bearer <token> form. The platform's x-mochabug-adapt-plugin-token supplies trusted route context; caller authorization is separate. Raw TCP and datagram exports bypass HTTP middleware. See middleware and lifecycle details.

Plugin services always use the trusted platform token. Executor configuration and persistent stores also use it, including inside lifecycle callbacks. Session stores, signals, sends, and exchanges use the session caller token; getAuthHeader() returns only that caller header. Each API instance verifies a caller against its trusted route before the first caller-scoped RPC.

Direct configurator construction accepts separate credentials: new ConfiguratorApi(env, platformToken, callerAuthorizationHeader). Configuration, incoming signals, and configurator stores retain the caller's permissions. Omitting the third argument permits plugin services, assets, and caller verification, but configuration operations fail with Unauthenticated. All backend API constructors require a valid, nonempty platform token.

RPC failures

SDK API calls do not automatically replay failed RPCs. Returned application statuses, including ResourceExhausted and Unavailable, propagate to the caller. A lost reply can mean the operation already ran. Only the native transport can recover a request it knows was unprocessed.

Browser UI

Call init({ resize: true }) from /browser once per document. Appearance follows standard CSS color-scheme; see browser appearance and resizing. sample/ is the runnable React showcase (npm run sample). The usage examples above live only in this README and are compiled and exercised by tests.

Development

npm run build generates protos, checks the separate environments, runs tests, and builds ESM bundles and declarations. npm run test:types checks packed package consumers in browser and Node environments, plus the README examples.

License

Apache-2.0