accounts
Version:
Tempo Accounts SDK
125 lines (103 loc) • 4.5 kB
text/typescript
import { hc } from 'hono/client'
import type { ExtractSchema } from 'hono/types'
import { http, type Chain, type Transport } from 'viem'
import { tempo, tempoModerato } from 'viem/chains'
import { describe, expectTypeOf, test } from 'vp/test'
import * as CliAuth from './CliAuth.js'
import * as Handler from './Handler.js'
describe('codeAuth options', () => {
test('supports chain-agnostic chains/transports configuration', () => {
expectTypeOf<Handler.codeAuth.Options>().toMatchTypeOf<{
chains?: readonly [Chain, ...Chain[]] | undefined
transports?: Record<number, Transport> | undefined
}>()
})
test('accepts derived clients from chains/transports', () => {
void Handler.codeAuth({
chains: [tempo, tempoModerato],
transports: {
[tempo.id]: http('https://rpc.tempo.xyz'),
[tempoModerato.id]: http('https://rpc.moderato.tempo.xyz'),
},
})
})
test('accepts a shared rate limiter', () => {
void Handler.codeAuth({
rateLimit: CliAuth.RateLimit.memory({ max: 120, windowMs: 60_000 }),
})
void Handler.codeAuth({
rateLimit: false,
})
void Handler.codeAuth({
rateLimitKey(request) {
return request.headers.get('x-forwarded-for') ?? 'unknown'
},
})
})
})
describe('compose', () => {
// Two ad-hoc schema-typed handlers to exercise schema merging.
function makeAlpha() {
const router = Handler.from()
const app = router.post('/alpha/ping', async (c) => c.json({ pong: 'alpha' as const }))
return app as Handler.Handler<typeof app>
}
function makeBeta() {
const router = Handler.from()
const app = router.get('/beta/echo', async (c) => c.json({ echo: 'beta' as const }))
return app as Handler.Handler<typeof app>
}
test('merges route schemas under the mount path', () => {
const composed = Handler.compose([makeAlpha(), makeBeta()], { path: '/api' })
type Schema = ExtractSchema<typeof composed>
type AlphaPost = Schema['/api/alpha/ping']['$post']
type BetaGet = Schema['/api/beta/echo']['$get']
// Both routes survive the merge.
expectTypeOf<AlphaPost>().toBeObject()
expectTypeOf<BetaGet>().toBeObject()
})
test('typed client exposes both handlers via direct property access', () => {
const composed = Handler.compose([makeAlpha(), makeBeta()], { path: '/api' })
const client = hc<typeof composed>('http://localhost')
// Direct, autocomplete-friendly access.
expectTypeOf(client.api.alpha.ping.$post).toBeFunction()
expectTypeOf(client.api.beta.echo.$get).toBeFunction()
})
test('default mount path `/` exposes routes at the root', () => {
const composed = Handler.compose([makeAlpha(), makeBeta()])
const client = hc<typeof composed>('http://localhost')
expectTypeOf(client.alpha.ping.$post).toBeFunction()
expectTypeOf(client.beta.echo.$get).toBeFunction()
})
test('response bodies stay typed end-to-end after compose', async () => {
const composed = Handler.compose([makeAlpha(), makeBeta()], { path: '/api' })
const client = hc<typeof composed>('http://localhost')
const alphaRes = await client.api.alpha.ping.$post()
if (alphaRes.status === 200) {
const body = await alphaRes.json()
expectTypeOf(body).toMatchTypeOf<{ pong: 'alpha' }>()
}
const betaRes = await client.api.beta.echo.$get()
if (betaRes.status === 200) {
const body = await betaRes.json()
expectTypeOf(body).toMatchTypeOf<{ echo: 'beta' }>()
}
})
test('different mount paths are reflected in the composed schema', () => {
const a = Handler.compose([makeAlpha()], { path: '/v1' })
const b = Handler.compose([makeBeta()], { path: '/v2' })
type SchemaA = ExtractSchema<typeof a>
type SchemaB = ExtractSchema<typeof b>
expectTypeOf<SchemaA['/v1/alpha/ping']['$post']>().toBeObject()
expectTypeOf<SchemaB['/v2/beta/echo']['$get']>().toBeObject()
})
test('handlers without a route schema (legacy `Handler`) compose without errors', () => {
// `Handler.codeAuth` returns plain `Handler` (default `Hono`), no route
// schema. Mixing it with typed handlers must not break inference for the
// typed siblings.
const codeAuth = Handler.codeAuth() as unknown as Handler.Handler
const composed = Handler.compose([codeAuth, makeAlpha()], { path: '/api' })
const client = hc<typeof composed>('http://localhost')
expectTypeOf(client.api.alpha.ping.$post).toBeFunction()
})
})