--- url: /packages/fetch.md --- # @zap-studio/fetch A type-safe fetch wrapper with Standard Schema validation. ## Why @zap-studio/fetch? **Before:** ```typescript const response = await fetch("/api/users/1"); const data = await response.json(); const user = data as User; // 😱 Unsafe type assertion ``` **After:** ```typescript const user = await api.get("/api/users/1", UserSchema); // ✨ Typed, validated, and safe! ``` ## Features * 🎯 **Type-safe requests** with automatic type inference * 🛡️ **Runtime validation** using Standard Schema (Zod, Valibot, ArkType, etc.) * ⚡️ **Convenient API methods** (GET, POST, PUT, PATCH, DELETE) * 🏭 **Factory pattern** for creating pre-configured instances with base URLs * 🚨 **Custom error handling** with FetchError and ValidationError classes * 📘 **Full TypeScript support** with zero configuration ## Installation ```bash pnpm add @zap-studio/fetch # or npm install @zap-studio/fetch ``` ## Quick Start ```typescript import { z } from "zod"; import { api } from "@zap-studio/fetch"; // Define your schema const UserSchema = z.object({ id: z.number(), name: z.string(), email: z.string().email(), }); // Make a type-safe request const user = await api.get("https://api.example.com/users/1", UserSchema); // user is fully typed and validated! ✨ console.log(user.name); // TypeScript knows this is a string ``` ## What's Next? * [API Methods](/packages/fetch/api-methods) - Learn about GET, POST, PUT, PATCH, DELETE * [Using $fetch](/packages/fetch/fetch-function) - Direct fetch usage with and without validation * [Factory Pattern](/packages/fetch/create-fetch) - Create pre-configured instances * [Error Handling](/packages/fetch/errors) - Handle HTTP and validation errors * [Validation](/packages/fetch/validation) - Deep dive into schema validation --- --- url: /packages/waitlist.md --- # @zap-studio/waitlist ::: warning Coming Soon The documentation for `@zap-studio/waitlist` is currently under development. Stay tuned for updates! ::: ## What is @zap-studio/waitlist? `@zap-studio/waitlist` is a complete waitlist management solution for TypeScript applications. It provides everything you need to implement a waitlist system with referrals, analytics, and more. ## Features (Preview) * 📝 **Easy Integration** - Simple API for adding users to your waitlist * 🔗 **Referral System** - Built-in referral tracking and rewards * 📊 **Analytics** - Track waitlist growth and engagement * 🎨 **Customizable** - Flexible configuration to match your needs ## Installation ```bash pnpm add @zap-studio/waitlist # or npm install @zap-studio/waitlist ``` ## Stay Updated Join our [Discord community](https://discord.gg/8Ke3VCjjMf) to stay updated on the latest developments. --- --- url: /packages/webhooks.md --- # @zap-studio/webhooks ::: warning Coming Soon The documentation for `@zap-studio/webhooks` is currently under development. Stay tuned for updates! ::: ## What is @zap-studio/webhooks? `@zap-studio/webhooks` is a type-safe webhook handling library for TypeScript applications. It provides a simple and secure way to receive, validate, and process webhooks from various services. ## Features (Preview) * 🔒 **Secure Signature Verification** - Built-in signature validation for popular providers * 🎯 **Type-Safe Event Handling** - Full TypeScript support with type inference * ⚡ **Framework Agnostic** - Works with any Node.js framework * 🔌 **Extensible** - Easy to add custom webhook providers ## Installation ```bash pnpm add @zap-studio/webhooks # or npm install @zap-studio/webhooks ``` ## Stay Updated Join our [Discord community](https://discord.gg/8Ke3VCjjMf) to stay updated on the latest developments. --- --- url: /about.md --- # About ## Our Mission **We're on a mission to make developers 10x more productive.** Millions of hours are wasted every year rebuilding the same infrastructure: auth, payments, emails, APIs. We're fixing that by creating the building blocks that let developers focus on what makes their product unique. ## What We Believe * **Speed is a feature.** The faster you can ship, the faster you can learn. * **Open source wins.** Transparency builds trust. Community drives innovation. * **Developer experience matters.** Great tools should feel invisible. * **Quality over quantity.** We'd rather ship one excellent tool than ten mediocre ones. ## What We're Building ### Zap.ts — Ship in Days, Not Months [Zap.ts](./zap-ts/index.md) is our flagship product: a production-ready Next.js starter that handles all the boring stuff so you can focus on your product. Teams using Zap.ts go from zero to deployed in under an hour. ### @zap-studio Packages — Modern Primitives We're building a suite of composable packages under the `@zap-studio` scope: | Package | What it does | |---------|-------------| | [@zap-studio/fetch](./packages/fetch/index.md) | Type-safe HTTP client with runtime validation | | @zap-studio/waitlist | Launch waitlists in minutes | | @zap-studio/webhooks | Secure webhook handling with zero config | ## Our Approach We build in public. Everything we ship is open source under permissive licenses. We believe the best developer tools are built by developers who use them every day. ## Get Involved We're just getting started. Star us on [GitHub](https://github.com/zap-studio), try our tools, and let us know what you think. --- --- url: /packages/fetch/api-methods.md --- # API Methods The `api` object provides convenient methods for common HTTP verbs. All methods require a schema for validation and return fully typed, validated data. ## Available Methods ### `api.get(url, schema, options?)` Performs a GET request with schema validation. ```typescript import { z } from "zod"; import { api } from "@zap-studio/fetch"; const UserSchema = z.object({ id: z.number(), name: z.string(), email: z.string().email(), }); const user = await api.get("https://api.example.com/users/1", UserSchema); // user is typed as { id: number; name: string; email: string; } ``` ### `api.post(url, schema, options?)` Performs a POST request. The `body` is automatically JSON-stringified and `Content-Type: application/json` header is set. ```typescript const UserSchema = z.object({ id: z.number(), name: z.string(), email: z.string().email(), }); const newUser = await api.post("https://api.example.com/users", UserSchema, { body: { name: "John Doe", email: "john@example.com", }, }); // newUser is typed as { id: number; name: string; email: string; } ``` ### `api.put(url, schema, options?)` Performs a PUT request for full resource replacement. ```typescript const updated = await api.put("https://api.example.com/users/1", UserSchema, { body: { name: "Jane Doe", email: "jane@example.com", }, }); ``` ### `api.patch(url, schema, options?)` Performs a PATCH request for partial updates. ```typescript const patched = await api.patch("https://api.example.com/users/1", UserSchema, { body: { email: "newemail@example.com", }, }); ``` ### `api.delete(url, schema, options?)` Performs a DELETE request. ```typescript const DeleteResponseSchema = z.object({ success: z.boolean(), message: z.string(), }); const result = await api.delete("https://api.example.com/users/1", DeleteResponseSchema); ``` ## Request Options All methods accept an optional `options` parameter with the following properties: | Option | Type | Default | Description | | ------------------------ | --------------------------------- | ------- | ----------------------------------------------------- | | `body` | `BodyInit \| Record` | - | Request body (auto-stringified for objects) | | `headers` | `HeadersInit` | - | Request headers | | `throwOnFetchError` | `boolean` | `true` | Throw `FetchError` on non-2xx responses | | `throwOnValidationError` | `boolean` | `true` | Throw `ValidationError` on schema validation failures | Plus all standard [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) options. ## Example with Headers ```typescript const user = await api.get("https://api.example.com/users/me", UserSchema, { headers: { Authorization: "Bearer your-token", "X-Custom-Header": "value", }, }); ``` ## Note on Raw Responses The `api.*` methods always require a schema for validation. If you need raw responses without validation, use [`$fetch`](/packages/fetch/fetch-function) directly. --- --- url: /packages/fetch/errors.md --- # Error Handling `@zap-studio/fetch` provides two specialized error classes for granular error handling: `FetchError` for HTTP errors and `ValidationError` for schema validation failures. ## Importing Error Classes ```typescript import { FetchError, ValidationError } from "@zap-studio/fetch/errors"; ``` ## FetchError Thrown when the HTTP response status is not ok (non-2xx status codes) and `throwOnFetchError` is `true` (default). ### Properties | Property | Type | Description | | ---------- | ---------- | ------------------------------ | | `name` | `string` | Always `"FetchError"` | | `message` | `string` | Error message with status info | | `status` | `number` | HTTP status code | | `response` | `Response` | The full Response object | ### Example ```typescript import { api } from "@zap-studio/fetch"; import { FetchError } from "@zap-studio/fetch/errors"; try { const user = await api.get("/api/users/999", UserSchema); } catch (error) { if (error instanceof FetchError) { console.error(`HTTP Error ${error.status}: ${error.message}`); // Access the full response const body = await error.response.text(); console.error("Response body:", body); // Handle specific status codes if (error.status === 404) { console.log("User not found"); } else if (error.status === 401) { console.log("Unauthorized - please log in"); } } } ``` ## ValidationError Thrown when schema validation fails and `throwOnValidationError` is `true` (default). ### Properties | Property | Type | Description | | --------- | ------------------------- | ------------------------------------- | | `name` | `string` | Always `"ValidationError"` | | `message` | `string` | JSON-formatted validation issues | | `issues` | `StandardSchemaV1.Issue[]` | Array of validation issues | ### Issue Structure Each issue in the `issues` array follows the Standard Schema format: ```typescript interface Issue { message: string; path?: PropertyKey[]; } ``` ### Example ```typescript import { api } from "@zap-studio/fetch"; import { ValidationError } from "@zap-studio/fetch/errors"; try { const user = await api.get("/api/users/1", UserSchema); } catch (error) { if (error instanceof ValidationError) { console.error("Validation failed!"); for (const issue of error.issues) { const path = issue.path?.join(".") || "root"; console.error(` - ${path}: ${issue.message}`); } } } ``` ## Combined Error Handling Handle both error types in a single try-catch: ```typescript import { api } from "@zap-studio/fetch"; import { FetchError, ValidationError } from "@zap-studio/fetch/errors"; async function getUser(id: number) { try { return await api.get(`/api/users/${id}`, UserSchema); } catch (error) { if (error instanceof FetchError) { if (error.status === 404) { return null; // User not found } throw new Error(`API error: ${error.status}`); } if (error instanceof ValidationError) { console.error("Invalid response from API:", error.issues); throw new Error("API returned invalid data"); } throw error; // Re-throw unexpected errors } } ``` ## Disabling Error Throwing You can disable automatic error throwing to handle errors manually. ### Disabling FetchError ```typescript const response = await $fetch("/api/users/999", { throwOnFetchError: false, }); // Check status manually if (!response.ok) { console.log("Request failed:", response.status); } ``` ### Disabling ValidationError When disabled, validation returns a Result object instead of throwing: ```typescript const result = await $fetch("/api/users/1", UserSchema, { throwOnValidationError: false, }); if (result.issues) { // Validation failed console.error("Validation issues:", result.issues); } else { // Validation succeeded console.log("User:", result.value); } ``` ### Result Type When `throwOnValidationError` is `false`, the return type is a Standard Schema Result: ```typescript type Result = { value: T; issues?: undefined; } | { value?: undefined; issues: Issue[]; }; ``` ## Error Handling Best Practices 1. **Always handle both error types** when making API calls 2. **Use specific error handlers** for different status codes 3. **Log validation issues** to help debug API response changes 4. **Consider disabling throws** for expected error cases (like 404s) 5. **Re-throw unexpected errors** to avoid silently swallowing issues --- --- url: /packages/fetch/create-fetch.md --- # Factory Pattern with createFetch The `createFetch` function allows you to create pre-configured fetch instances with base URLs and default headers. This is perfect for building API clients. ## Basic Usage ```typescript import { z } from "zod"; import { createFetch } from "@zap-studio/fetch"; // Create a configured instance const { $fetch, api } = createFetch({ baseURL: "https://api.example.com", headers: { Authorization: "Bearer your-token", "X-API-Key": "your-api-key", }, }); const UserSchema = z.object({ id: z.number(), name: z.string(), }); // Now use relative paths - baseURL is prepended automatically const user = await api.get("/users/1", UserSchema); // POST with auto-stringified body const newUser = await api.post("/users", UserSchema, { body: { name: "John Doe" }, }); ``` ## Factory Options | Option | Type | Default | Description | | ------------------------ | ------------- | ------- | ----------------------------------------------------- | | `baseURL` | `string` | `""` | Base URL prepended to relative paths only | | `headers` | `HeadersInit` | - | Default headers included in all requests (can be overridden per request) | | `searchParams` | `URLSearchParams \| Record \| string \| [string, string][]` | - | Default query/search params included in all requests (can be overridden per request) | | `throwOnFetchError` | `boolean` | `true` | Throw `FetchError` on non-2xx responses | | `throwOnValidationError` | `boolean` | `true` | Throw `ValidationError` on schema validation failures | ## URL Handling ### Relative URLs Relative URLs are automatically prefixed with the `baseURL`: ```typescript const { api } = createFetch({ baseURL: "https://api.example.com" }); // Fetches https://api.example.com/users const users = await api.get("/users", UsersSchema); // Leading slash is optional const user = await api.get("users/1", UserSchema); ``` ### Absolute URLs Absolute URLs (starting with `http://`, `https://`, or `//`) ignore the `baseURL`: ```typescript const { api } = createFetch({ baseURL: "https://api.example.com" }); // Fetches https://other-api.com/data (ignores baseURL) const data = await api.get("https://other-api.com/data", DataSchema); ``` ## Header Merging Default headers from the factory are merged with per-request headers. Per-request headers take precedence: ```typescript const { api } = createFetch({ baseURL: "https://api.example.com", headers: { Authorization: "Bearer default-token", "Content-Type": "application/json", }, }); // This request will have: // - Authorization: Bearer override-token (overridden) // - Content-Type: application/json (from defaults) // - X-Custom: value (new header) const user = await api.get("/users/1", UserSchema, { headers: { Authorization: "Bearer override-token", "X-Custom": "value", }, }); ``` ## Search Params Merging Default search params from the factory are merged with per-request search params. Per-request search params take precedence: ```typescript const { api } = createFetch({ baseURL: "https://api.example.com", searchParams: { locale: "en", page: "1" }, }); // This request will have: // - locale: en (from defaults) // - page: 2 (overridden) // - q: alex (new param) const user = await api.get("/users/1", UserSchema, { searchParams: { page: "2", q: "alex" }, }); ``` Query params in the URL are also merged with factory and per-request search params. The priority order is (highest priority last): 1. **Factory defaults** — lowest priority 2. **URL params** — override factory defaults 3. **Per-request params** — highest priority, override everything ```typescript const { api } = createFetch({ baseURL: "https://api.example.com", searchParams: { locale: "en", page: "1" }, }); // This request will have: // - locale: en (from factory defaults) // - sort: asc (from URL) // - page: 2 (overridden by per-request) // - q: alex (new param from per-request) const user = await api.get("/users?sort=asc&page=0", UserSchema, { searchParams: { page: "2", q: "alex" }, }); ``` ## Multiple API Clients You can create separate fetch instances for different APIs: ```typescript import { createFetch } from "@zap-studio/fetch"; // GitHub API client const github = createFetch({ baseURL: "https://api.github.com", headers: { Authorization: "Bearer github-token" }, }); // Internal API client const internal = createFetch({ baseURL: "https://internal.example.com/api", headers: { "X-Internal-Key": "secret" }, }); // Stripe API client const stripe = createFetch({ baseURL: "https://api.stripe.com/v1", headers: { Authorization: "Bearer sk_test_..." }, }); // Use them independently const repo = await github.api.get("/repos/owner/repo", RepoSchema); const data = await internal.api.get("/data", DataSchema); const customer = await stripe.api.get("/customers/cus_123", CustomerSchema); ``` ## Configuring Error Behavior You can configure default error throwing behavior at the factory level: ```typescript // Never throw validation errors by default const { api } = createFetch({ baseURL: "https://api.example.com", throwOnValidationError: false, }); // All requests will return Result objects instead of throwing const result = await api.get("/users/1", UserSchema); if (result.issues) { console.error("Validation failed:", result.issues); } else { console.log("Success:", result.value); } ``` You can still override this per-request: ```typescript // Override to throw for this specific request const user = await api.get("/users/1", UserSchema, { throwOnValidationError: true, }); ``` ## Return Type `createFetch` returns an object with both `$fetch` and `api`: ```typescript const { $fetch, api } = createFetch({ baseURL: "https://api.example.com", }); // Use api.* for convenience methods const user = await api.get("/users/1", UserSchema); // Use $fetch for raw responses or more control const response = await $fetch("/health"); console.log("Status:", response.status); ``` --- --- url: /packages/fetch/fetch-function.md --- # Using $fetch The `$fetch` function provides more control over your requests. You can use it with or without schema validation. ## With Schema Validation When you pass a schema, `$fetch` validates the response and returns typed data: ```typescript import { z } from "zod"; import { $fetch } from "@zap-studio/fetch"; const UserSchema = z.object({ id: z.number(), name: z.string(), }); const user = await $fetch("https://api.example.com/users/1", UserSchema, { headers: { Authorization: "Bearer token", }, }); // user is typed as { id: number; name: string; } console.log(user.name); ``` ## Without Schema Validation When you don't pass a schema, `$fetch` returns the raw `Response` object: ```typescript import { $fetch } from "@zap-studio/fetch"; const response = await $fetch("https://api.example.com/users/1", { method: "GET", }); // response is a standard Response object const data = await response.json(); ``` This is useful when: * You need access to response headers or status * You're working with non-JSON responses (binary, text, etc.) * You want to handle the response manually ## Function Signatures `$fetch` has two overloaded signatures: ```typescript // With schema - returns validated data async function $fetch( resource: string, schema: TSchema, options?: ExtendedRequestInit ): Promise>; // Without schema - returns raw Response async function $fetch( resource: string, options?: ExtendedRequestInit ): Promise; ``` ## Extended Request Options | Option | Type | Default | Description | | ------------------------ | --------------------------------- | ------- | ----------------------------------------------------- | | `body` | `BodyInit \| Record` | - | Request body (auto-stringified when schema is present) | | `throwOnFetchError` | `boolean` | `true` | Throw `FetchError` on non-2xx responses | | `throwOnValidationError` | `boolean` | `true` | Throw `ValidationError` on schema validation failures | Plus all standard [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) options. ## Examples ### POST with JSON body ```typescript const user = await $fetch("https://api.example.com/users", UserSchema, { method: "POST", body: { name: "John Doe", email: "john@example.com", }, }); ``` ### GET with query parameters ```typescript const url = new URL("https://api.example.com/users"); url.searchParams.set("page", "1"); url.searchParams.set("limit", "10"); const users = await $fetch(url.toString(), UsersSchema); ``` ### Handling non-JSON responses ```typescript const response = await $fetch("https://api.example.com/file.pdf"); if (response.ok) { const blob = await response.blob(); // Handle the binary data } ``` ### Custom headers ```typescript const user = await $fetch("https://api.example.com/users/1", UserSchema, { headers: { Authorization: "Bearer token", "Accept-Language": "en-US", "X-Request-ID": crypto.randomUUID(), }, }); ``` --- --- url: /packages/fetch/validation.md --- # Validation `@zap-studio/fetch` uses [Standard Schema](https://standardschema.dev/) for runtime validation, which means it works with any schema library that implements the Standard Schema specification. ## Supported Schema Libraries Any library implementing Standard Schema v1 is supported, including: * [Zod](https://zod.dev/) * [Valibot](https://valibot.dev/) * [ArkType](https://arktype.io/) * [TypeBox](https://github.com/sinclairzx81/typebox) * And more... ## Using Different Schema Libraries ### Zod ```typescript import { z } from "zod"; import { api } from "@zap-studio/fetch"; const UserSchema = z.object({ id: z.number(), name: z.string(), email: z.string().email(), createdAt: z.string().datetime(), }); const user = await api.get("/users/1", UserSchema); ``` ### Valibot ```typescript import * as v from "valibot"; import { api } from "@zap-studio/fetch"; const UserSchema = v.object({ id: v.number(), name: v.string(), email: v.pipe(v.string(), v.email()), createdAt: v.pipe(v.string(), v.isoTimestamp()), }); const user = await api.get("/users/1", UserSchema); ``` ### ArkType ```typescript import { type } from "arktype"; import { api } from "@zap-studio/fetch"; const UserSchema = type({ id: "number", name: "string", email: "email", createdAt: "string", }); const user = await api.get("/users/1", UserSchema); ``` ## The standardValidate Helper For standalone validation needs, you can use the `standardValidate` helper function: ```typescript import { standardValidate } from "@zap-studio/fetch/validator"; import { z } from "zod"; const UserSchema = z.object({ id: z.number(), name: z.string(), }); // Throwing validation (default) const user = await standardValidate(UserSchema, data, true); // Returns validated data or throws ValidationError // Non-throwing validation const result = await standardValidate(UserSchema, data, false); // Returns { value: T } or { issues: Issue[] } if (result.issues) { console.error("Validation failed:", result.issues); } else { console.log("Valid user:", result.value); } ``` ## isStandardSchema Type Guard Check if a value is a Standard Schema: ```typescript import { isStandardSchema } from "@zap-studio/fetch/validator"; const schema = z.object({ name: z.string() }); if (isStandardSchema(schema)) { // TypeScript knows schema is StandardSchemaV1 const result = schema["~standard"].validate(data); } ``` ## Validation Flow When you pass a schema to `$fetch` or `api.*` methods: 1. The HTTP request is made 2. The response JSON is parsed 3. The data is validated against your schema 4. If valid, the typed data is returned 5. If invalid and `throwOnValidationError: true`, a `ValidationError` is thrown 6. If invalid and `throwOnValidationError: false`, a Result object is returned ```typescript // Simplified internal flow const response = await fetch(url, options); const rawData = await response.json(); const validatedData = await standardValidate(schema, rawData, throwOnValidationError); return validatedData; ``` ## Handling Validation Results ### Throwing Mode (Default) ```typescript import { ValidationError } from "@zap-studio/fetch/errors"; try { const user = await api.get("/users/1", UserSchema); // user is fully typed: { id: number; name: string; email: string; } } catch (error) { if (error instanceof ValidationError) { // Handle validation errors for (const issue of error.issues) { console.error(`${issue.path?.join(".")}: ${issue.message}`); } } } ``` ### Non-Throwing Mode ```typescript const result = await api.get("/users/1", UserSchema, { throwOnValidationError: false, }); if (result.issues) { // Handle validation failure result.issues.forEach((issue) => { console.error(`${issue.path?.join(".")}: ${issue.message}`); }); } else { // Use validated data console.log("User name:", result.value.name); } ``` ## Type Inference Types are automatically inferred from your schema: ```typescript const UserSchema = z.object({ id: z.number(), name: z.string(), email: z.string().email(), roles: z.array(z.enum(["admin", "user"])), }); const user = await api.get("/users/1", UserSchema); // TypeScript infers: // { // id: number; // name: string; // email: string; // roles: ("admin" | "user")[]; // } ``` ## Best Practices 1. **Define schemas once, reuse everywhere** - Create schema files for your API types 2. **Use strict schemas** - Validate exactly what you expect from the API 3. **Handle validation errors gracefully** - APIs can change unexpectedly 4. **Consider non-throwing mode** - For expected variations in API responses 5. **Use schema transforms** - Parse dates, numbers, etc. directly in your schema ### Schema Organization Example ```typescript // schemas/user.ts import { z } from "zod"; export const UserSchema = z.object({ id: z.number(), name: z.string(), email: z.string().email(), createdAt: z.string().transform((s) => new Date(s)), }); export type User = z.infer; // api/users.ts import { api } from "@/lib/fetch"; import { UserSchema, type User } from "@/schemas/user"; export async function getUser(id: number): Promise { return api.get(`/users/${id}`, UserSchema); } ``` --- --- url: /introduction.md --- # What is Zap Studio? We're building the infrastructure layer for modern web development. Zap Studio provides open-source tools and primitives that help developers ship production-ready applications in days, not months. ## The Problem Every developer starting a new project faces the same challenges: setting up authentication, configuring databases, handling payments, managing emails. This boilerplate work costs teams weeks of development time and introduces countless opportunities for security vulnerabilities. ## Our Solution We're solving this by building a suite of modular, production-ready tools that just work: ### Zap.ts Our flagship product is [Zap.ts](https://zapstudio.dev/zap-ts) — the fastest way to go from idea to production. It's a modular starter kit that includes everything you need to build and scale a web application. **What's included:** * 🔐 Authentication with Better Auth * 🗄️ Database with Drizzle ORM * 🎨 UI with Tailwind CSS + shadcn/ui * 📧 Transactional emails with Resend * 💳 Payments with Polar * 🚀 Deploy anywhere ### Developer Infrastructure We're also building a suite of packages that solve common problems with modern, composable APIs. **Our engineering principles:** * **ESM First** — Native ES modules for modern JavaScript. No legacy baggage. * **100% Type Safe** — Full TypeScript with strict types. Catch bugs before they ship. * **Modular Architecture** — Core + adapter pattern. Use only what you need. * **Tree Shakeable** — Zero bloat. Your bundle stays lean. | Package | Description | Status | |---------|-------------|--------| | [@zap-studio/fetch](./packages/fetch/index.md) | Type-safe fetch wrapper with validation | ✅ Available | | @zap-studio/waitlist | Waitlist management solution | 🚧 Coming Soon | | @zap-studio/webhooks | Webhook handling with signature verification | 🚧 Coming Soon | ## Why Now? The JavaScript ecosystem has matured. TypeScript is the standard. ESM is native. Edge runtimes are everywhere. The tools developers use should reflect this new reality — not carry the weight of a decade of backwards compatibility. We're building the next generation of developer tools from the ground up. --- --- url: /zap-ts.md --- # Zap.ts ::: warning Coming Soon Zap.ts documentation is currently under development. Stay tuned for updates! ::: ## What is Zap.ts? Zap.ts is a modern, full-stack TypeScript monorepo starter kit designed to help you build fast with ease. It comes with everything you need to launch your next project. ## Features * **Authentication** - Complete auth system with multiple providers using Better Auth * **Email** - Transactional emails with React Email templates and Resend * **Payments** - Polar integration for subscriptions and billing * **Database** - Type-safe database access with Drizzle ORM * **UI Components** - Pre-built components with shadcn/ui * **Analytics** - User tracking with PostHog * **Internationalization** - Built-in i18n support * **Deployment** - Deploy anywhere with Vercel or Docker ## Stay Updated Join our [Discord community](https://discord.gg/8Ke3VCjjMf) to stay updated on the latest developments and be the first to know when Zap.ts is ready for public release. Follow us on [GitHub](https://github.com/zap-studio) for updates and to star the project!