🚀 TypeScript emitter with namespace structure and Ky HTTP client generator for TypeSpec.
Transform your TypeSpec definitions into production-ready, type-safe HTTP clients with zero runtime overhead.
- 🏗️ Namespace-Aware - Preserves your TypeSpec namespace hierarchy in generated TypeScript code
- 🔒 Fully Type-Safe - Complete end-to-end type safety from API definition to client usage
- ⚡ Zero Runtime Overhead - Generates lightweight clients with minimal bundle impact
- 🎯 Modern & Fast - Built on Ky for modern fetch-based HTTP requests
- 🛠️ Developer Experience - IntelliSense, auto-completion, and compile-time error checking
- 📦 Production Ready - Used in production environments with robust error handling
Perfect for teams who want to maintain API contracts while building fast, reliable TypeScript applications.
- @ube-tsp/ky-emitter - TypeSpec emitter generating namespace-structured TypeScript
- @ube-tsp/ky-client - Type-safe HTTP client generator
npm install @ube-tsp/ky-emitter# tspconfig.yaml
emit:
- "@ube-tsp/ky-emitter"tsp compile .npm install @ube-tsp/ky-client kyimport ky from "ky";
import { createClient } from "@ube-tsp/ky-client";
import { operationMap, type OperationMap } from "./generated/operation-map.js";
const kyInstance = ky.create({ prefixUrl: "https://api.example.com" });
const client = createClient<OperationMap>(kyInstance, operationMap);
// Use nested methods based on your TypeSpec namespaces
await client.PetStore.getPet({ params: { path: { petId: 123 } } });Pass additional Ky options for advanced scenarios:
// Custom timeout and headers
const result = await client.PetStore.getPet(
{ params: { path: { petId: 123 } } },
{
timeout: 5000,
headers: { Authorization: "Bearer token" },
retry: { limit: 3 },
},
);
// Global configuration
const kyInstance = ky.create({
prefixUrl: "https://api.example.com",
timeout: 10000,
hooks: {
beforeRequest: [
(request) => {
request.headers.set("User-Agent", "MyApp/1.0");
},
],
},
});// For deeply nested TypeSpec namespaces
await client.Store.Inventory.Products.getProduct({
params: { path: { productId: "abc123" } },
});
await client.Admin.Users.Permissions.grantPermission({
params: {
path: { userId: 456, permissionId: 789 },
body: { reason: "Promotion to manager" },
},
});The emitter generates:
- Namespace-structured TypeScript types - Mirrors your TypeSpec namespace hierarchy
- Runtime operation map - Contains HTTP method, path, and status code information
- Type-safe client methods - Automatically creates nested client structure from flat operation keys
For a TypeSpec like:
@service({
title: "Pet Store API",
})
namespace PetStore {
@route("/pets/{petId}")
op getPet(@path petId: int32): Pet | NotFoundError;
}The emitter generates:
generated/
├── Spec.ts # Namespace-structured types
├── operation-map.ts # Runtime operation mapping
└── Spec/
└── PetStore.ts # PetStore namespace types
The operation map contains:
export const operationMap = {
"PetStore.getPet": {
path: "/pets/{petId}",
method: "GET",
statusCodes: [200, 404],
},
};The client transforms this into:
// Typed as: (params: { path: { petId: number } }) => Promise<ApiResponse>
client.PetStore.getPet({ params: { path: { petId: 123 } } });npm run build # Build all packages
npm run test # Test all packages
npm run lint # Lint all packages
npm run format # Format codeBased on typespec-zod implementation patterns. Built with AI assistance from Claude Code.
MIT