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

@smithy-hono/test-kit

v0.2.6

Published

Testing toolkit for smithy-hono services. Drive the GENERATED typed client against your router/pipeline in-process (no network): unit harness (router + stand-in principal), integration harness (full security pipeline + in-memory stores), auth helpers (log

Downloads

389

Readme

@smithy-hono/test-kit

Testing toolkit for smithy-hono services. It makes unit and integration testing of a generated service ridiculously easy by driving the generated typed client against your router/pipeline in-process (no network, no ports), plus auth helpers, builders, and assertions.

Everything is Web-standard only (ARCH-01), so it runs in any test environment (node, workers, jsdom) and under any runner (vitest, node:test, jest).

npm i -D @smithy-hono/test-kit

Peers you already have in a smithy-hono service: @smithy-hono/security-core, hono.


The two harnesses

| | mountRouter (unit) | createTestHarness (integration) | |---|---|---| | What runs | just the generated router | full createSecurityPipeline → router | | Auth | a stand-in principal you set | real authenticate (cookies), loginAs/asService | | Stores | none | fresh in-memory Session/RateLimit/Nonce/Secret per harness | | Use for | handler logic, validators, the authorize hook | auth, CSRF, rate-limit, headers, signing, e2e flows |

Both hand you the generated typed client wired to the app — calls are type-safe and modeled errors come back as the same generated error classes the server throws.


Unit: mountRouter

import { mountRouter, expectError } from '@smithy-hono/test-kit'
import { createTodoRouter, createTodoClient, TodoNotFound } from '../generated'
import { OPERATIONS } from '../generated/registry.gen'

const { client, app } = mountRouter({
  router: createTodoRouter(myOps),
  createClient: createTodoClient,
  operations: OPERATIONS,        // default principal = a superuser that reaches every route
})

const { item } = await client.CreateTodo({ body: { title: 'x' } })   // typed I/O
await expectError(() => client.GetTodo({ id: 'nope' }), TodoNotFound) // typed error

principal controls the stand-in identity:

mountRouter({ router, createClient, principal: principal({ permissions: ['todos.read'] }) })
mountRouter({ router, createClient, principal: null })   // simulate an unauthenticated request

Use the returned app (raw app.request) for cases the typed client can't express — a malformed JSON body, a missing required field, etc.


Integration: createTestHarness

import { createTestHarness, principal } from '@smithy-hono/test-kit'
import { createTodoRouter, createTodoClient } from '../generated'
import { OPERATIONS } from '../generated/registry.gen'

const h = createTestHarness({
  operations: OPERATIONS,
  router: createTodoRouter(myOps),
  createClient: createTodoClient,
  // config?: Partial<PipelineConfig>   // merged over test defaults (https, json, pinnable IP)
})

h.client      // unauthenticated client (anonymous routes only)
h.stores      // { session, rateLimit, nonce, secrets } — seed / assert
h.app         // the assembled Hono app (escape hatch for raw requests)

loginAs — cookie + CSRF, handled for you

const { client } = await h.loginAs()                                   // superuser by default
await client.CreateTodo({ body: { title: 'via pipeline' } })           // cookie + CSRF attached

const { client: reader } = await h.loginAs(principal({ permissions: ['todos.read'] }))

loginAs seeds a real session in h.stores.session and returns a client whose every request carries the session cookie and the matching X-CSRF-Token.

asService — HMAC request signing (@sigv4Hmac ops)

const svc = await h.asService({ keyId: 'key-1', secret: 'shared-secret-…' })
await svc.ImportThings({ body: { items: [] } })   // each request is SH-HMAC-SHA256 signed

Registers the key in h.stores.secrets and signs every outgoing request via the in-memory transport.


Builders

import { principal, sessionRecord, fakeContext } from '@smithy-hono/test-kit'

principal({ permissions: ['todos.write'], id: 'u1', kind: 'user', tenantId: 't1' })
sessionRecord({ principal: principal(), csrfToken: 'tok', ttlSeconds: 3600 })

// Unit-test a handler directly (no HTTP):
const c = fakeContext({ principal: principal({ id: 'u1' }) })
await myOps.GetTodo({ id: 'x' }, c)

Assertions (runner-agnostic)

import { expectError, catchError, expectStatus } from '@smithy-hono/test-kit'

const err = await expectError(() => client.GetTodo({ id: 'x' }), TodoNotFound)
expect(err.$statusCode).toBe(404)

await expectStatus(() => app.request('/todos', { method: 'POST', body: 'not-json' }), 400)

Authz rejections ({ code: 'Unauthorized' | 'AccessDenied' }) aren't modeled errors, so the generated client throws a SmithyError carrying the status — assert on $statusCode:

import { SmithyError } from '../generated'
const e = await expectError(() => client.CreateTodo({ body: { title: 'x' } }), SmithyError)
expect(e.$statusCode).toBe(403)

Prefer your runner's native matchers? They work too: await expect(client.GetTodo({ id: 'x' })).rejects.toBeInstanceOf(TodoNotFound).


MCP

import { createMcpClient } from '@smithy-hono/test-kit'

const mcp = createMcpClient(myMcpApp)                       // injects https + JSON-RPC envelope
await mcp.listTools()                                       // public discovery
await mcp.callTool('CreateTodo', { body: { title: 'x' } }, { token: 'writer' })

How it works

The generated client takes a fetch-shaped function. The kit supplies one built on Hono's in-memory app.request (inMemoryFetch), and that single function is where cookies, CSRF tokens, and HMAC signatures are injected — the client stays oblivious. Because the same codegen emits the server and the client, they agree on the wire contract by construction.