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

@rexeus/typeweaver

v0.3.1

Published

🧡✨ Typeweaver CLI. Entry point into the Typeweaver framework to scaffold, validate, and generate API assets.

Readme

🧡✨ @rexeus/typeweaver

npm version License TypeScript Node.js Deno Bun

Typeweaver is a type-safe HTTP API framework built for API-first development with a focus on developer experience. Use typeweaver to specify your HTTP APIs in TypeScript and Zod, and generate clients, validators, routers, and more ✨


πŸ“₯ Installation

# Node.js (npm)
npm install -D @rexeus/typeweaver
npm install @rexeus/typeweaver-core

# Node.js (pnpm)
pnpm add -D @rexeus/typeweaver
pnpm add @rexeus/typeweaver-core

# Deno
deno add npm:@rexeus/typeweaver npm:@rexeus/typeweaver-core

# Bun
bun add -D @rexeus/typeweaver
bun add @rexeus/typeweaver-core

Now you are ready to start building! Check out Quickstart

🎯 Why typeweaver?

  • πŸ“ Define once, generate everything: API contracts in Zod become clients, servers, validators, and docs.
  • πŸ“‚ Resource-based architecture: APIs organized by resources (like user, todo, project, tag, blog-post, etc.), each with its operations and generated components (e.g. clients). Scale naturally as your API grows.
  • πŸ”’ Real type safety: From API definition to client usage, every request and response is fully typed. No more any types sneaking in.
  • βœ… Automatic validation: Invalid requests never reach your code.
  • πŸ”Œ Bring your own framework: Ready-made adapters for popular frameworks, extensible plugin system for everything else.
  • 😊 Finally, DX that doesn't suck: One schema, no duplication, pure TypeScript.

πŸ”Œ Available Plugins

| Package | Description | Version | | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | | @rexeus/typeweaver-types | Plugin for request/response types and validation - the foundation for all other plugins and always included | npm | | @rexeus/typeweaver-clients | Plugin for HTTP clients using Axios | npm | | @rexeus/typeweaver-hono | Plugin for Hono routers | npm | | @rexeus/typeweaver-aws-cdk | Plugin for AWS CDK constructs for API Gateway V2 | npm |

More plugins are planned. If you want to build your own, check out the plugin system

Plugin system.


⌨️ CLI

Generate TypeScript code from your API definitions:

# Node.js (npm)
npx typeweaver generate --input ./api/definition --output ./api/generated --plugins clients

# Node.js (pnpm)
pnpx typeweaver generate --input ./api/definition --output ./api/generated --plugins clients

# Deno
deno run -A npm:@rexeus/typeweaver generate --input ./api/definition --output ./api/generated --plugins clients

# Bun
bunx typeweaver generate --input ./api/definition --output ./api/generated --plugins clients

Note: Deno may require the --sloppy-imports flag or equivalent configuration in deno.json when your API definitions use extensionless TypeScript imports.

βš™οΈ Options

  • --input, -i <path>: Input directory containing API definitions (required)
  • --output, -o <path>: Output directory for generated code (required)
  • -s, --shared <path>: Shared directory for reusable schemas (optional, defaults to <input-path>/shared)
  • --config, -c <path>: Configuration file path (optional)
  • --plugins, -p <plugins>: Comma-separated list of plugins to use (e.g., "clients,hono" or "all" for all plugins)
  • --prettier / --no-prettier: Enable/disable code formatting with Prettier (default: true)
  • --clean / --no-clean: Enable/disable output directory cleaning (default: true)

πŸ“ Configuration File

Create a config file (e.g. typeweaver.config.js) for more complex configurations:

export default {
  input: "./api/definition",
  output: "./api/generated",
  plugins: ["clients", "hono", "aws-cdk"],
  prettier: true,
  clean: true,
};

Then run:

npx typeweaver generate --config ./typeweaver.config.js

Replace npx with pnpx, deno run -A npm:@rexeus/typeweaver, or bunx depending on your runtime.

🌱 Get Started

πŸ“ Project Structure

Your API definition must follow this structure:

  • Each resource needs its own directory under the specified input directory (e.g. input dir: api/definition contains user/, post/ subdirectories)
    • The directory name defines the resource name (e.g. user, post)
    • The structure inside a resource directory can be nested to provide better organization (e.g. user/errors/..., user/mutations/...)
  • Inside a resource directory, each operation or response definition gets its own file (e.g. CreateUserDefinition.ts, UserNotFoundErrorDefinition.ts)
  • An operation definition file must include one default export of a HttpOperationDefinition instance (e.g. export default new HttpOperationDefinition({...}))
  • It is recommended to specify separate schemas for requests and responses, but this is not strictly required.
    • If separating schemas, Zod utilities can be used to apply general schemas case-specifically (useful Zod utilities: pick, omit, merge...)
  • A response definition file must include one default export of a HttpResponseDefinition instance (e.g. export default new HttpResponseDefinition({...}))
  • Responses shared across operations are possible, but need to be placed in the shared directory.
    • The shared directory can be specified using the --shared option, but must be located within the input directory
    • Default shared directory is <input-path>/shared
    • The shared directory is suitable not only as a place for responses but also for shared schemas

As you can see, the structure of the input directory is essential. However, you are completely free to choose the structure and nesting within resource directories.

Important: All definition files and their dependencies (like separate schemas etc.) must be self-contained within the input directory. Generated code creates an immutable snapshot of your definitions, so any external imports (relative imports outside the input directory) will not work. NPM package imports continue to work normally.

api/definition/
β”œβ”€β”€ user/                                  # Resource directory
β”‚   β”œβ”€β”€ errors/                            # Resource-specific error definitions
β”‚   β”‚   β”‚                                  # -> Because they are inside a resource directory,
β”‚   β”‚   β”‚                                  # they can only be used within this resource
β”‚   β”‚   └── UserNotFoundErrorDefinition.ts
β”‚   β”‚   └── UserStatusTransitionInvalidErrorDefinition.ts
β”‚   β”œβ”€β”€ CreateUserDefinition.ts            # Operation definitions
β”‚   β”œβ”€β”€ GetUserDefinition.ts
β”‚   β”œβ”€β”€ ListUserDefinition.ts
β”‚   β”œβ”€β”€ UpdateUserDefinition.ts
β”‚   └── userSchema.ts                      # Schema for the resource, can be reused across operations
β”œβ”€β”€ post/
β”‚   β”œβ”€β”€ errors/
β”‚   β”œβ”€β”€ CreatePostDefinition.ts
β”‚   β”œβ”€β”€ GetPostDefinition.ts
β”‚   β”œβ”€β”€ ...
β”œβ”€β”€ ...
└── shared/                                # Shared responses and schemas
   β”‚                                       # -> While it doesn't matter where schemas are defined
   β”‚                                       # inside the input directory, responses can only be
   β”‚                                       # shared across resources if they are located in the
   β”‚                                       # shared directory
   β”œβ”€β”€ ConflictErrorDefinition.ts
   β”œβ”€β”€ ForbiddenErrorDefinition.ts
   β”œβ”€β”€ InternalServerErrorDefinition.ts
   β”œβ”€β”€ NotFoundErrorDefinition.ts          # Like BaseApiErrors, can be extended to be resource-specific
   β”œβ”€β”€ TooManyRequestsErrorDefinition.ts
   β”œβ”€β”€ UnauthorizedErrorDefinition.ts
   β”œβ”€β”€ ValidationErrorDefinition.ts
   └── sharedResponses.ts                  # Collection of responses relevant for every operation

πŸ’» Sample Definitions

// api/definition/user/userSchema.ts
import { z } from "zod";

// General schema for user status
export const userStatusSchema = z.enum(["ACTIVE", "INACTIVE", "SUSPENDED"]);

// General user schema, can be reused across operations
export const userSchema = z.object({
  id: z.uuid(),
  name: z.string(),
  email: z.email(),
  status: userStatusSchema,
  createdAt: z.iso.date(),
  updatedAt: z.iso.date(),
});
// api/definition/user/GetUserDefinition.ts
import { HttpOperationDefinition, HttpMethod, HttpStatusCode } from "@rexeus/typeweaver-core";
import { z } from "zod";
import { sharedResponses } from "../shared/sharedResponses";
import { userSchema } from "./userSchema";
import UserNotFoundErrorDefinition from "./errors/UserNotFoundErrorDefinition";

export default new HttpOperationDefinition({
  operationId: "GetUser",
  method: HttpMethod.GET,
  path: "/users/:userId",
  request: {
    param: z.object({
      userId: z.uuid(),
    }),
  },
  responses: [
    // - the only success response in this operation is defined inline
    // - the response could also be defined in a separate file and be imported here
    // - generally also multiple success responses could be defined
    // - in this case the "general" user schema is imported and used
    {
      statusCode: HttpStatusCode.OK,
      description: "User successfully retrieved",
      header: z.object({
        "Content-Type": z.literal("application/json"),
      }),
      body: userSchema,
    },
    UserNotFoundErrorDefinition, // Resource specific response
    ...sharedResponses, // Commonly used responses across all operations, e.g. 401, 403, 500...
  ],
});
// api/definition/user/UpdateUserDefinition.ts
import { HttpOperationDefinition, HttpMethod, HttpStatusCode } from "@rexeus/typeweaver-core";
import { z } from "zod";
import { sharedResponses } from "../shared/sharedResponses";
import { userSchema } from "./userSchema";
import UserNotFoundErrorDefinition from "./errors/UserNotFoundErrorDefinition";
import UserStatusTransitionInvalidErrorDefinition from "./errors/UserStatusTransitionInvalidErrorDefinition";

export default new HttpOperationDefinition({
  operationId: "UpdateUser",
  method: HttpMethod.PATCH,
  path: "/users/:userId",
  request: {
    param: z.object({
      userId: z.uuid(),
    }),
    // general user schema is processed via zod's pick and partial methods
    // to match the update operation's requirements
    body: userSchema
      .pick({
        name: true,
        email: true,
        status: true,
      })
      .partial(),
  },
  responses: [
    {
      statusCode: HttpStatusCode.OK,
      description: "User successfully updated",
      header: z.object({
        "Content-Type": z.literal("application/json"),
      }),
      body: userSchema,
    },
    UserNotFoundErrorDefinition, // Resource specific response
    UserStatusTransitionInvalidErrorDefinition, // Resource specific response
    ...sharedResponses, // Commonly used responses across all operations, e.g. 401, 403, 500...
  ],
});
// api/definition/user/errors/UserNotFoundErrorDefinition.ts
import { z } from "zod";
import { NotFoundErrorDefinition } from "../../shared";

// - uses the shared NotFoundErrorDefinition as "base" and extends it
// - adds a specific message and code for the user resource
export default NotFoundErrorDefinition.extend({
  name: "UserNotFoundError",
  description: "User not found",
  body: z.object({
    message: z.literal("User not found"),
    code: z.literal("USER_NOT_FOUND_ERROR"),
    actualValues: z.object({
      userId: z.uuid(),
    }),
  }),
});
// api/definition/user/errors/UserStatusTransitionInvalidErrorDefinition.ts
import { HttpResponseDefinition, HttpStatusCode } from "@rexeus/typeweaver-core";
import { z } from "zod";
import { userStatusSchema } from "../userSchema";

// could also extend the shared ConflictErrorDefinition:
// export default ConflictErrorDefinition.extend({...});

// or in this case does not extend a BaseApiError and defines everything itself
export default new HttpResponseDefinition({
  name: "UserStatusTransitionInvalidError",
  description: "User status transition is conflicting with current status",
  body: z.object({
    message: z.literal("User status transition is conflicting with current status"),
    code: z.literal("USER_STATUS_TRANSITION_INVALID_ERROR"),
    context: z.object({
      userId: z.uuid(),
      currentStatus: userStatusSchema,
    }),
    actualValues: z.object({
      requestedStatus: userStatusSchema,
    }),
    expectedValues: z.object({
      allowedStatuses: z.array(userStatusSchema),
    }),
  }),
});
// api/definition/shared/sharedResponses.ts
import ForbiddenErrorDefinition from "./ForbiddenErrorDefinition";
import InternalServerErrorDefinition from "./InternalServerErrorDefinition";
import TooManyRequestsErrorDefinition from "./TooManyRequestsErrorDefinition";
import UnauthorizedErrorDefinition from "./UnauthorizedErrorDefinition";
import UnsupportedMediaTypeErrorDefinition from "./UnsupportedMediaTypeErrorDefinition";
import ValidationErrorDefinition from "./ValidationErrorDefinition";

// various error responses which are relevant for every operation
// can be spread in the responses array of an HttpOperationDefinition
export const sharedResponses = [
  ForbiddenErrorDefinition,
  InternalServerErrorDefinition,
  TooManyRequestsErrorDefinition,
  UnauthorizedErrorDefinition,
  UnsupportedMediaTypeErrorDefinition,
  ValidationErrorDefinition,
];

πŸ”§ Generate using plugins

# Generate with plugins:
# - Hono: to easily provide a web server
# - Clients: to get fitting API clients
npx typeweaver generate --input ./api/definition --output ./api/generated --plugins clients,hono

🌐 Create Hono web server

// api/user-handlers.ts
import { HttpResponse, HttpStatusCode } from "@rexeus/typeweaver-core";
import {
  type UserApiHandler,
  type IGetUserRequest,
  GetUserResponse,
  GetUserSuccessResponse,
  type ICreateUserRequest,
  CreateUserResponse,
  type IUpdateUserRequest,
  UpdateUserResponse,
  type IListUserRequest,
  ListUserResponse,
} from "./generated";

export class UserHandlers implements UserApiHandler {
  public constructor() {}

  public async handleGetUserRequest(request: IGetUserRequest): Promise<GetUserResponse> {
    // Simulate fetching user data
    const fetchedUser = {
      id: request.param.userId,
      name: "John Doe",
      email: "[email protected]",
      status: "ACTIVE",
      createdAt: new Date("2023-01-01").toISOString(),
      updatedAt: new Date("2023-01-01").toISOString(),
    };

    return new GetUserSuccessResponse({
      statusCode: HttpStatusCode.OK,
      header: {
        "Content-Type": "application/json",
      },
      body: fetchedUser,
    });
  }

  public handleCreateUserRequest(request: ICreateUserRequest): Promise<CreateUserResponse> {
    throw new Error("Not implemented");
  }

  public handleUpdateUserRequest(request: IUpdateUserRequest): Promise<UpdateUserResponse> {
    throw new Error("Not implemented");
  }

  public handleListUserRequest(request: IListUserRequest): Promise<ListUserResponse> {
    throw new Error("Not implemented");
  }
}
// api/server.ts
import { serve } from "@hono/node-server";
import { Hono } from "hono";
// an index file exporting all generated components is automatically provided
import { UserHandlers } from "./user-handlers";
import { PostHandlers } from "./post-handlers"; // Implement similarly to UserHandlers
import { UserHono, PostHono } from "./generated";

const app = new Hono();

const userHandlers = new UserHandlers();
const postHandlers = new PostHandlers();

// you have further config options, e.g. custom error response handling
// (useful for mapping validation errors to your specific response format)
const userRouter = new UserHono({
  requestHandlers: userHandlers,
});
const postRouter = new PostHono({
  requestHandlers: postHandlers,
});

app.route("/", userRouter);
app.route("/", postRouter);

// Start server on port 3000
serve(
  {
    fetch: app.fetch,
    port: 3000,
  },
  () => {
    console.log("Server is running on http://localhost:3000");
  }
);
# Start your server locally
tsx api/server.ts

πŸ”— Communicate by using Clients

// api/client-test.ts
import { UserClient, GetUserRequestCommand, UserNotFoundErrorResponse } from "./generated";

const client = new UserClient({ baseUrl: "http://localhost:3000" });

try {
  const getUserRequestCommand = new GetUserRequestCommand({ param: { userId: "123" } });
  const result = await client.send(getUserRequestCommand);

  console.log("Successfully fetched user:", result.body);
} catch (error) {
  if (error instanceof UserNotFoundErrorResponse) {
    console.error("User not found:", error.body);
  } else {
    console.error("Other error occurred:", error);
  }
}
# Call your created Hono server
tsx api/client-test.ts

πŸ“„ License

Apache 2.0 Β© Dennis Wentzien 2025