[
  {
    "path": ".changeset/README.md",
    "content": "# Changesets\n\nHello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works\nwith multi-package repos, or single-package repos to help you version and publish your code. You can\nfind the full documentation for it [in our repository](https://github.com/changesets/changesets)\n\nWe have a quick list of common questions to get you started engaging with this project in\n[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)\n"
  },
  {
    "path": ".changeset/config.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/@changesets/config@2.3.0/schema.json\",\n  \"changelog\": \"@changesets/cli/changelog\",\n  \"commit\": false,\n  \"fixed\": [],\n  \"linked\": [],\n  \"access\": \"public\",\n  \"baseBranch\": \"main\",\n  \"updateInternalDependencies\": \"patch\",\n  \"ignore\": []\n}\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\non:\n  push:\n    branches:\n      - \"**\"\n  pull_request:\n    branches:\n      - \"**\"\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: pnpm/action-setup@v2\n        with:\n          version: 7\n      - uses: actions/setup-node@v3\n        with:\n          node-version: 16.x\n          cache: \"pnpm\"\n\n      - run: pnpm install --frozen-lockfile\n      - run: pnpm run ci\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish\non:\n  push:\n    branches:\n      - \"main\"\n\nconcurrency: ${{ github.workflow }}-${{ github.ref }}\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: pnpm/action-setup@v2\n        with:\n          version: 7\n      - uses: actions/setup-node@v3\n        with:\n          node-version: 16.x\n          cache: \"pnpm\"\n\n      - run: pnpm install --frozen-lockfile\n      - name: Create Release Pull Request or Publish\n        id: changesets\n        uses: changesets/action@v1\n        with:\n          publish: pnpm run release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\ndist"
  },
  {
    "path": ".npmignore",
    "content": "src\nnode_modules\npnpm-lock.yaml\ntsconfig.json"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# untypeable\n\n## 0.2.0\n\n### Minor Changes\n\n- 9db7f9b: Made it so you don't need to pass an empty object to the client if the input is entirely partial.\n\n## 0.1.0\n\n### Minor Changes\n\n- 0441952: Initial release of untypeable - check the readme for details!\n"
  },
  {
    "path": "docs/swapi-example/swapi-example.ts",
    "content": "import { createTypeLevelClient, initUntypeable } from \"../../src/index\";\nimport { Person, Paginated, Planet, Film, Vehicle } from \"./types\";\n\nconst u = initUntypeable();\n\n/**\n * Create the router\n */\nconst router = u.router({\n  \"/people/:id\": u.input<{ id: string }>().output<Person>(),\n  \"/people\": u.output<Paginated<Person>>(),\n  \"/planets/:id\": u.input<{ id: string }>().output<Planet>(),\n  \"/planets\": u.output<Paginated<Planet>>(),\n  \"/films/:id\": u.input<{ id: string }>().output<Film>(),\n  \"/films\": u.output<Paginated<Film>>(),\n  \"/vehicles/:id\": u.input<{ id: string }>().output<Vehicle>(),\n  \"/vehicles\": u.output<Paginated<Vehicle>>(),\n});\n\n/**\n * Create the client, using the zero-bundle method\n */\nexport const fetchFromSwapi = createTypeLevelClient<typeof router>(\n  (path, input = {}) => {\n    // Replace dynamic path params in url\n    const pathWithParams = path.replace(\n      /:([a-zA-Z0-9_]+)/g,\n      (_, key) => input[key],\n    );\n\n    return fetch(`https://swapi.dev/api${pathWithParams}`, {\n      method: \"GET\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n    }).then((res) => {\n      if (!res.ok) {\n        throw new Error(`HTTP error! status: ${res.status}`);\n      }\n\n      return res.json();\n    });\n  },\n);\n"
  },
  {
    "path": "docs/swapi-example/types.ts",
    "content": "export type Person = {\n  name: string;\n  height: string;\n  mass: string;\n  hair_color: string;\n  skin_color: string;\n  birth_year: string;\n  gender: string;\n  homeworld: string;\n  films: string[];\n};\n\nexport type Planet = {\n  climate: string;\n  diameter: string;\n  gravity: string;\n  name: string;\n  orbital_period: string;\n  population: string;\n  residents: string[];\n};\n\nexport type Film = {\n  title: string;\n  episode_id: number;\n  opening_crawl: string;\n  director: string;\n  producer: string;\n  release_date: string;\n  characters: string[];\n  planets: string[];\n  starships: string[];\n  vehicles: string[];\n};\n\nexport type Vehicle = {\n  name: string;\n  model: string;\n  manufacturer: string;\n  cost_in_credits: string;\n  length: string;\n  max_atmosphering_speed: string;\n};\n\nexport type Paginated<T> = {\n  count: number;\n  next: string | null;\n  previous: string | null;\n  results: T[];\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"untypeable\",\n  \"version\": \"0.2.1\",\n  \"description\": \"Get type-safe access to any API, with a zero-bundle size option.\",\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.mjs\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"import\": \"./dist/index.mjs\",\n      \"require\": \"./dist/index.js\"\n    },\n    \"./client\": {\n      \"types\": \"./dist/client.d.ts\",\n      \"import\": \"./dist/client.mjs\",\n      \"require\": \"./dist/client.js\"\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"scripts\": {\n    \"build\": \"tsup\",\n    \"lint\": \"tsc\",\n    \"ci\": \"npm run build && npm run lint && npm run test\",\n    \"prepublish\": \"npm run ci\",\n    \"test\": \"vitest run\",\n    \"dev\": \"vitest\",\n    \"release\": \"npm run ci && changeset publish\"\n  },\n  \"keywords\": [],\n  \"author\": \"Matt Pocock\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"@changesets/cli\": \"^2.26.0\",\n    \"tsup\": \"^6.6.3\",\n    \"typescript\": \"^4.9.5\",\n    \"vite\": \"^4.1.4\",\n    \"vitest\": \"^0.29.2\",\n    \"zod\": \"^3.21.4\"\n  }\n}\n"
  },
  {
    "path": "readme.md",
    "content": "# Untypeable\n\nGet type-safe access to any API, with a zero-bundle size option.\n\n## The Problem\n\nIf you're lucky enough to use [tRPC](https://trpc.io/), [GraphQL](https://graphql.org/), or [OpenAPI](https://www.openapis.org/), you'll be able to get **type-safe access to your API** - either through a type-safe RPC or codegen.\n\nBut **what about the rest of us**?\n\nWhat do you do if **your API has no types**?\n\n## Solution\n\nEnter `untypeable` - a first-class library for typing API's you don't control.\n\n- 🚀 Get **autocomplete on your entire API**, without needing to set up a single generic function.\n- 💪 **Simple to configure**, and extremely **flexible**.\n- 🤯 Choose between two modes:\n  - **Zero bundle-size**: use `import type` to ensure `untypeable` adds nothing to your bundle.\n  - **Strong types**: integrates with libraries like [Zod](https://zod.dev/) to add runtime safety to the types.\n- ✨ **Keep things organized** with helpers for merging and combining your config.\n- ❤️ You bring the fetcher, we bring the types. There's **no hidden magic**.\n\n## Quickstart\n\n`npm i untypeable`\n\n```ts\nimport { initUntypeable, createTypeLevelClient } from \"untypeable\";\n\n// Initialize untypeable\nconst u = initUntypeable();\n\ntype User = {\n  id: string;\n  name: string;\n};\n\n// Create a router\n// - Add typed inputs and outputs\nconst router = u.router({\n  \"/user\": u.input<{ id: string }>().output<User>(),\n});\n\nconst BASE_PATH = \"http://localhost:3000\";\n\n// Create your client\n// - Pass any fetch implementation here\nconst client = createTypeLevelClient<typeof router>((path, input) => {\n  return fetch(BASE_PATH + path + `?${new URLSearchParams(input)}`).then(\n    (res) => res.json(),\n  );\n});\n\n// Type-safe data access!\n// - user is typed as User\n// - { id: string } must be passed as the input\nconst user = await client(\"/user\", {\n  id: \"1\",\n});\n```\n\n## SWAPI Example\n\nWe've added a [full example](./docs/swapi-example/swapi-example.ts) of typing `swapi.dev`.\n\n## Zero-bundle mode\n\nYou can set up `untypeable` to run in zero-bundle mode. This is great for situations where you trust the API you're calling, but it just doesn't have types.\n\nTo set up zero-bundle mode, you'll need to:\n\n1. Define your router in a file called `router.ts`.\n2. Export the type of your router: `export type MyRouter = typeof router;`\n\n```ts\n// router.ts\n\nimport { initUntypeable } from \"untypeable\";\n\nconst u = initUntypeable();\n\ntype User = {\n  id: string;\n  name: string;\n};\n\nconst router = u.router({\n  \"/user\": u.input<{ id: string }>().output<User>(),\n});\n\nexport type MyRouter = typeof router;\n```\n\n3. In a file called `client.ts`, import `createTypeLevelClient` from `untypeable/type-level-client`.\n\n```ts\n// client.ts\n\nimport { createTypeLevelClient } from \"untypeable/client\";\nimport type { MyRouter } from \"./router\";\n\nexport const client = createTypeLevelClient<MyRouter>(() => {\n  // your implementation...\n});\n```\n\n### How does this work?\n\nThis works because `createTypeLevelClient` is just an identity function, which directly returns the function you pass it. Most modern bundlers are smart enough to [collapse identity functions](https://github.com/evanw/esbuild/pull/1898) and erase type imports, so you end up with:\n\n```ts\n// client.ts\n\nexport const client = () => {\n  // your implementation...\n};\n```\n\n## Runtime-safe mode\n\nSometimes, you just don't trust the API you're calling. In those situations, you'll often like to _validate_ the data you get back.\n\n`untypeable` offers first-class integration with [Zod](https://zod.dev). You can pass a Zod schema to `u.input` and `u.output` to ensure that these values are validated with Zod.\n\n```ts\nimport { initUntypeable, createSafeClient } from \"untypeable\";\nimport { z } from \"zod\";\n\nconst u = initUntypeable();\n\nconst router = u.router({\n  \"/user\": u\n    .input(\n      z.object({\n        id: z.string(),\n      }),\n    )\n    .output(\n      z.object({\n        id: z.string(),\n        name: z.string(),\n      }),\n    ),\n});\n\nexport const client = createSafeClient(router, () => {\n  // Implementation...\n});\n```\n\nNow, every call made to client will have its `input` and `output` verified by the zod schemas passed.\n\n## Configuration & Arguments\n\n`untypeable` lets you be extremely flexible with the shape of your router.\n\nEach level of the router corresponds to an argument that'll be passed to your client.\n\n```ts\n// A router that looks like this:\nconst router = u.router({\n  github: {\n    \"/repos\": {\n      GET: u.output<string[]>(),\n      POST: u.output<string[]>(),\n    },\n  },\n});\n\nconst client = createTypeLevelClient<typeof router>(() => {});\n\n// Will need to be called like this:\nclient(\"github\", \"/repos\", \"POST\");\n```\n\nYou can set up this argument structure using the methods below:\n\n### `.pushArg`\n\nUsing the `.pushArg` method when we `initUntypeable` lets us add new arguments that must be passed to our client.\n\n```ts\nimport { initUntypeable, createTypeLevelClient } from \"untypeable\";\n\n// use .pushArg to add a new argument to\n// the router definition\nconst u = initUntypeable().pushArg<\"GET\" | \"POST\" | \"PUT\" | \"DELETE\">();\n\ntype User = {\n  id: string;\n  name: string;\n};\n\n// You can now optionally specify the\n// method on each route's definition\nconst router = u.router({\n  \"/user\": {\n    GET: u.input<{ id: string }>().output<User>(),\n    POST: u.input<{ name: string }>().output<User>(),\n    DELETE: u.input<{ id: string }>().output<void>(),\n  },\n});\n\n// The client now takes a new argument - method, which\n// is typed as 'GET' | 'POST' | 'PUT' | 'DELETE'\nconst client = createTypeLevelClient<typeof router>((path, method, input) => {\n  let resolvedPath = path;\n  let resolvedInit: RequestInit = {};\n\n  switch (method) {\n    case \"GET\":\n      resolvedPath += `?${new URLSearchParams(input as any)}`;\n      break;\n    case \"DELETE\":\n    case \"POST\":\n    case \"PUT\":\n      resolvedInit = {\n        method,\n        body: JSON.stringify(input),\n      };\n  }\n\n  return fetch(resolvedPath, resolvedInit).then((res) => res.json());\n});\n\n// This now needs to be passed to client, and\n// is still beautifully type-safe!\nconst result = await client(\"/user\", \"POST\", {\n  name: \"Matt\",\n});\n```\n\nYou can call this as many times as you want!\n\n```ts\nconst u = initUntypeable()\n  .pushArg<\"GET\" | \"POST\" | \"PUT\" | \"DELETE\">()\n  .pushArg<\"foo\" | \"bar\">();\n\nconst router = u.router({\n  \"/\": {\n    GET: {\n      foo: u.output<string>,\n    },\n  },\n});\n```\n\n### `.unshiftArg`\n\nYou can also add an argument at the _start_ using `.unshiftArg`. This is useful for when you want to add different base endpoints:\n\n```ts\nconst u = initUntypeable().unshiftArg<\"github\", \"youtube\">();\n\nconst router = u.router({\n  github: {\n    \"/repos\": u.output<{ repos: { id: string }[] }>(),\n  },\n});\n```\n\n### `.args`\n\nUseful for when you want to set the args up manually:\n\n```ts\nconst u = initUntypeable().args<string, string, string>();\n\nconst router = u.router({\n  \"any-string\": {\n    \"any-other-string\": {\n      \"yet-another-string\": u.output<string>(),\n    },\n  },\n});\n```\n\n## Organizing your routers\n\n### `.add`\n\nYou can add more detail to a router, or split it over multiple calls, by using `router.add`.\n\n```ts\nconst router = u\n  .router({\n    \"/\": u.output<string>(),\n  })\n  .add({\n    \"/user\": u.output<User>(),\n  });\n```\n\n### `.merge`\n\nYou can merge two routers together using `router.merge`. This is useful for when you want to combine multiple routers (perhaps in different modules) together.\n\n```ts\nimport { userRouter } from \"./userRouter\";\nimport { postRouter } from \"./postRouter\";\n\nexport const baseRouter = userRouter.merge(postRouter);\n```\n"
  },
  {
    "path": "src/client.ts",
    "content": "import {\n  ArgsFromRouter,\n  LooseUntypeableHandler,\n  RoutesFromRouter,\n  UntypeableHandler,\n  UntypeableRouter,\n} from \"./types\";\n\n/**\n * Creates an API client that does not check the input or output\n * at runtime. For runtime checking, use createSafeClient.\n *\n * If you're using createTypeLevelClient, we recommend importing\n * it from 'untypeable/client' to minimize bundle size.\n *\n * @example\n *\n * const client = createTypeLevelClient<MyRouterType>((path) => {\n *   return fetch(path).then(res => res.json());\n * });\n */\nexport const createTypeLevelClient = <\n  TRouter extends UntypeableRouter<any, any>,\n  TArgs extends readonly string[] = ArgsFromRouter<TRouter>,\n  TRoutes extends Record<string, any> = RoutesFromRouter<TRouter>,\n>(\n  handler: LooseUntypeableHandler<TArgs>,\n): UntypeableHandler<TArgs, TRoutes> => {\n  return handler as any;\n};\n"
  },
  {
    "path": "src/constants.ts",
    "content": "export const SEPARATOR = \"__\";\n"
  },
  {
    "path": "src/index.ts",
    "content": "export * from \"./client\";\nexport * from \"./types\";\nexport * from \"./untypeable\";\nexport * from \"./safe-client\";\n"
  },
  {
    "path": "src/playground.ts",
    "content": "import { initUntypeable, createTypeLevelClient } from \"untypeable\";\n\n// Initialize untypeable\nconst u = initUntypeable();\n\ntype User = {\n  id: string;\n  name: string;\n};\n\n// Create a router\n// - Add typed inputs and outputs\nconst router = u.router({\n  \"/user\": u.input<{ id: string }>().output<User>(),\n});\n\n// Create your client\n// - Pass any fetch implementation here\nconst client = createTypeLevelClient<typeof router>((path, input) => {\n  return fetch(path + `?${new URLSearchParams(input)}`).then((res) =>\n    res.json(),\n  );\n});\n\n// Type-safe data access!\n// - user is typed as User\n// - { id: string } must be passed as the input\nconst user = client(\"/user\", {\n  id: \"1\",\n});\n"
  },
  {
    "path": "src/safe-client.ts",
    "content": "import { SEPARATOR } from \"./constants\";\nimport {\n  AcceptedParser,\n  ArgsFromRouter,\n  LooseUntypeableHandler,\n  RoutesFromRouter,\n  Schemas,\n  UntypeableHandler,\n  UntypeableRouter,\n} from \"./types\";\n\nexport const createSafeClient = <\n  TRouter extends UntypeableRouter<any, any>,\n  TArgs extends ArgsFromRouter<TRouter>,\n  TRoutes extends RoutesFromRouter<TRouter>,\n>(\n  router: TRouter,\n  handler: LooseUntypeableHandler<TArgs>,\n): UntypeableHandler<TArgs, TRoutes> => {\n  return (async (...args: any[]) => {\n    let schemas: Schemas;\n\n    const argsExceptLast = args.slice(0, -1);\n\n    const matchingSchemas = router._schemaMap.get(\n      argsExceptLast.join(SEPARATOR),\n    );\n\n    const secondSchemaMatchAttempt = router._schemaMap.get(\n      args.join(SEPARATOR),\n    );\n    let shouldCheckInput: boolean;\n\n    if (matchingSchemas) {\n      schemas = matchingSchemas;\n      shouldCheckInput = true;\n    } else if (secondSchemaMatchAttempt) {\n      schemas = secondSchemaMatchAttempt;\n      shouldCheckInput = false;\n    } else {\n      throw new Error(\n        `No matching schema found for args: ${argsExceptLast.join(\", \")}`,\n      );\n    }\n\n    const inputSchema = resolveParser(schemas.input);\n    const outputSchema = resolveParser(schemas.output);\n\n    let output;\n\n    if (shouldCheckInput) {\n      const input = args[args.length - 1];\n      const parsedInput = inputSchema(input);\n\n      output = await (handler as any)(...argsExceptLast, parsedInput);\n    } else {\n      output = await (handler as any)(...args);\n    }\n\n    return outputSchema(output);\n  }) as any;\n};\n\nconst resolveParser = <T>(parser: AcceptedParser<T> | undefined) => {\n  if (typeof parser === \"function\") {\n    return parser;\n  } else if (typeof parser === \"undefined\") {\n    return (x: any) => x;\n  } else {\n    return parser.parse;\n  }\n};\n"
  },
  {
    "path": "src/tests/add.test.ts",
    "content": "import { expect, it, vitest } from \"vitest\";\nimport { z } from \"zod\";\nimport { createSafeClient } from \"../safe-client\";\nimport { initUntypeable } from \"../untypeable\";\n\nconst u = initUntypeable();\n\nconst router = u\n  .router({\n    inputNeeded: u.input(z.string()).output(z.object({ hello: z.string() })),\n  })\n  .add({\n    inputNotNeeded: u.output(z.object({ hello: z.string() })),\n  });\n\nit('Should pick up schemas added by the \"add\" method', async () => {\n  const fn = vitest.fn();\n  const client = createSafeClient(router, fn);\n\n  fn.mockResolvedValueOnce({ hello: \"world\" });\n\n  await client(\"inputNeeded\", \"hello\");\n\n  expect(fn).toHaveBeenLastCalledWith(\"inputNeeded\", \"hello\");\n\n  fn.mockResolvedValueOnce({ hello: \"world\" });\n\n  await client(\"inputNotNeeded\");\n});\n"
  },
  {
    "path": "src/tests/dynamic-args.test.ts",
    "content": "import { it, vitest } from \"vitest\";\nimport { createTypeLevelClient } from \"../client\";\nimport { initUntypeable } from \"../untypeable\";\n\nit(\"Should let you specify 2 args\", async () => {\n  const u = initUntypeable().args<\"GET\" | \"POST\", \"SOMETHING\" | \"ELSE\">();\n\n  const router = u.router({\n    GET: {\n      ELSE: u.output<string>(),\n    },\n  });\n\n  const client = createTypeLevelClient<typeof router>(vitest.fn());\n\n  await client(\"GET\", \"ELSE\");\n\n  // @ts-expect-error\n  await client(\"GET\", \"SOMETHING\");\n\n  // @ts-expect-error\n  await client(\"POST\", \"ELSE\");\n});\n\nit(\"Should default to only requiring one arg\", async () => {\n  const u = initUntypeable();\n\n  const router = u.router({\n    GET: u.output<string>(),\n  });\n\n  const client = createTypeLevelClient<typeof router>(vitest.fn());\n\n  await client(\"GET\");\n\n  // @ts-expect-error\n  await client(\"POST\");\n});\n"
  },
  {
    "path": "src/tests/merge.test.ts",
    "content": "import { expect, it, vitest } from \"vitest\";\nimport { z } from \"zod\";\nimport { createSafeClient } from \"../safe-client\";\nimport { initUntypeable } from \"../untypeable\";\n\nconst u = initUntypeable();\n\nconst router1 = u.router({\n  inputNeeded: u.input(z.string()).output(z.object({ hello: z.string() })),\n});\n\nconst router2 = u.router({\n  inputNotNeeded: u.output(z.object({ hello: z.string() })),\n});\n\nconst router = router2.merge(router1);\n\nit('Should pick up schemas added by the \"merge\" method', async () => {\n  const fn = vitest.fn();\n  const client = createSafeClient(router, fn);\n\n  fn.mockResolvedValueOnce({ hello: \"world\" });\n\n  await client(\"inputNeeded\", \"hello\");\n\n  expect(fn).toHaveBeenLastCalledWith(\"inputNeeded\", \"hello\");\n\n  fn.mockResolvedValueOnce({ hello: \"world\" });\n\n  await client(\"inputNotNeeded\");\n});\n"
  },
  {
    "path": "src/tests/repl.test.ts",
    "content": "import { it } from \"vitest\";\nimport { initUntypeable } from \"../untypeable\";\n\nit(\"REPL\", () => {\n  const u = initUntypeable().pushArg<\"GET\" | \"POST\">();\n\n  const router = u.router({\n    something: {\n      GET: u.output<string>(),\n      POST: u.input<string>().output<string>(),\n    },\n  });\n});\n"
  },
  {
    "path": "src/tests/router-definition.test.ts",
    "content": "import { describe, expect, it } from \"vitest\";\nimport { initUntypeable } from \"../untypeable\";\n\nit(\"Should not let you pass an input directly to a route\", () => {\n  const u = initUntypeable();\n\n  expect(() =>\n    u.router({\n      // @ts-expect-error\n      inputNeeded: u.input<string>(),\n    }),\n  ).toThrowError();\n});\n\nit(\"Should let you pass an output without an input\", () => {\n  const u = initUntypeable();\n\n  u.router({\n    noInputNeeded: u.output<string>(),\n  });\n});\n"
  },
  {
    "path": "src/tests/type-only.test.ts",
    "content": "import { describe, expect, it, vitest } from \"vitest\";\nimport { createTypeLevelClient } from \"../client\";\nimport { initUntypeable } from \"../untypeable\";\nimport { Equal, Expect } from \"./utils\";\n\nconst u = initUntypeable();\n\nconst router = u.router({\n  inputNeeded: u.input<string>().output<number>(),\n  noInputNeeded: u.output<boolean>(),\n  partialInputOnly: u.input<{ a?: string }>().output<{ a: string }>(),\n});\n\ndescribe(\"input types\", () => {\n  it(\"Should require an input of the correct type if one has been specified\", async () => {\n    const fn = vitest.fn();\n\n    const client = createTypeLevelClient<typeof router>(fn);\n\n    // @ts-expect-error\n    await client(\"inputNeeded\");\n    expect(fn).toHaveBeenLastCalledWith(\"inputNeeded\");\n\n    await client(\"inputNeeded\", \"hello\");\n    expect(fn).toHaveBeenLastCalledWith(\"inputNeeded\", \"hello\");\n\n    // @ts-expect-error\n    await client(\"inputNeeded\", 0);\n    expect(fn).toHaveBeenLastCalledWith(\"inputNeeded\", 0);\n  });\n\n  it(\"Should not require an input if one has not been specified\", async () => {\n    const fn = vitest.fn();\n\n    const client = createTypeLevelClient<typeof router>(fn);\n\n    await client(\"noInputNeeded\");\n    expect(fn).toHaveBeenLastCalledWith(\"noInputNeeded\");\n\n    // @ts-expect-error\n    await client(\"noInputNeeded\", \"hello\");\n\n    expect(fn).toHaveBeenLastCalledWith(\"noInputNeeded\", \"hello\");\n  });\n\n  it(\"Should not require an input if all properties of the input are optional\", async () => {\n    const fn = vitest.fn();\n\n    const client = createTypeLevelClient<typeof router>(fn);\n\n    await client(\"partialInputOnly\");\n    expect(fn).toHaveBeenLastCalledWith(\"partialInputOnly\");\n\n    await client(\"partialInputOnly\", {});\n    expect(fn).toHaveBeenLastCalledWith(\"partialInputOnly\", {});\n  });\n});\n\ndescribe(\"Output types\", () => {\n  it(\"Should return an output of the correct type\", async () => {\n    const fn = vitest.fn();\n\n    const client = createTypeLevelClient<typeof router>(fn);\n\n    const numResult = await client(\"inputNeeded\", \"adwawd\");\n    const boolResult = await client(\"noInputNeeded\");\n\n    type tests = [\n      Expect<Equal<typeof numResult, number>>,\n      Expect<Equal<typeof boolResult, boolean>>,\n    ];\n  });\n});\n"
  },
  {
    "path": "src/tests/utils.ts",
    "content": "export type Expect<T extends true> = T;\nexport type ExpectTrue<T extends true> = T;\nexport type ExpectFalse<T extends false> = T;\nexport type IsTrue<T extends true> = T;\nexport type IsFalse<T extends false> = T;\n\nexport type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <\n  T,\n>() => T extends Y ? 1 : 2\n  ? true\n  : false;\nexport type NotEqual<X, Y> = true extends Equal<X, Y> ? false : true;\n\n// https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360\nexport type IsAny<T> = 0 extends 1 & T ? true : false;\nexport type NotAny<T> = true extends IsAny<T> ? false : true;\n\nexport type Debug<T> = { [K in keyof T]: T[K] };\nexport type MergeInsertions<T> = T extends object\n  ? { [K in keyof T]: MergeInsertions<T[K]> }\n  : T;\n\nexport type Alike<X, Y> = Equal<MergeInsertions<X>, MergeInsertions<Y>>;\n\nexport type ExpectExtends<VALUE, EXPECTED> = EXPECTED extends VALUE\n  ? true\n  : false;\nexport type ExpectValidArgs<\n  FUNC extends (...args: any[]) => any,\n  ARGS extends any[],\n> = ARGS extends Parameters<FUNC> ? true : false;\n\nexport type UnionToIntersection<U> = (\n  U extends any ? (k: U) => void : never\n) extends (k: infer I) => void\n  ? I\n  : never;\n\nexport const doNotExecute = (func: () => any) => {};\n"
  },
  {
    "path": "src/tests/with-schemas.test.ts",
    "content": "import { expect, it, vitest } from \"vitest\";\nimport { z } from \"zod\";\nimport { createSafeClient } from \"../safe-client\";\nimport { initUntypeable } from \"../untypeable\";\n\nit(\"Should let you define inputs and outputs as schemas\", async () => {\n  const u = initUntypeable();\n  const router = u.router({\n    inputNeeded: u.input(z.string()).output(z.object({ hello: z.string() })),\n  });\n\n  const fn = vitest.fn();\n\n  const client = createSafeClient(router, fn);\n\n  fn.mockResolvedValueOnce({ hello: \"world\" });\n\n  await client(\"inputNeeded\", \"hello\");\n});\n\nit(\"Should error if you do not provide the correct input\", async () => {\n  const u = initUntypeable();\n  const router = u.router({\n    inputNeeded: u.input(z.string()).output(z.object({ hello: z.string() })),\n  });\n\n  const fn = vitest.fn();\n\n  const client = createSafeClient(router, fn);\n\n  fn.mockResolvedValueOnce({ hello: \"world\" });\n\n  await client(\"inputNeeded\", \"hello\");\n});\n\nit(\"Should error if the fetcher does not provide the correct output WITHOUT an input\", async () => {\n  const u = initUntypeable();\n  const router = u.router({\n    inputNotNeeded: u.output(z.object({ hello: z.string() })),\n  });\n\n  const fn = vitest.fn();\n\n  const client = createSafeClient(router, fn);\n\n  fn.mockResolvedValueOnce({ hello: 124 });\n\n  await expect(() => client(\"inputNotNeeded\")).rejects.toThrowError();\n});\n\nit(\"Should error if the fetcher does not provide the correct output WITH an input\", async () => {\n  const u = initUntypeable();\n  const router = u.router({\n    inputNeeded: u.input(z.string()).output(z.object({ hello: z.string() })),\n  });\n\n  const fn = vitest.fn();\n\n  const client = createSafeClient(router, fn);\n\n  fn.mockResolvedValueOnce({ hello: 124 });\n\n  await expect(() => client(\"inputNeeded\", \"124123123\")).rejects.toThrowError();\n});\n"
  },
  {
    "path": "src/types.ts",
    "content": "type DefaultArgs = [string];\n\nexport type AcceptedParser<T> =\n  | ((input: unknown) => T)\n  | {\n      parse: (input: unknown) => T;\n    };\n\nexport type SchemaMap = Map<string, Schemas>;\n\nexport type Schemas = {\n  input: AcceptedParser<any> | undefined;\n  output: AcceptedParser<any> | undefined;\n};\n\nexport interface UntypeableBase<TArgs extends readonly string[] = DefaultArgs> {\n  pushArg: <TArg extends string>() => UntypeableBase<[...TArgs, TArg]>;\n  unshiftArg: <TArg extends string>() => UntypeableBase<[TArg, ...TArgs]>;\n  args<TArg1 extends string>(): UntypeableBase<[TArg1]>;\n  args<TArg1 extends string, TArg2 extends string>(): UntypeableBase<\n    [TArg1, TArg2]\n  >;\n  args<\n    TArg1 extends string,\n    TArg2 extends string,\n    TArg3 extends string,\n  >(): UntypeableBase<[TArg1, TArg2, TArg3]>;\n  args<\n    TArg1 extends string,\n    TArg2 extends string,\n    TArg3 extends string,\n    TArg4 extends string,\n  >(): UntypeableBase<[TArg1, TArg2, TArg3, TArg4]>;\n  args<TArgs extends readonly string[]>(): UntypeableBase<TArgs>;\n  router: <\n    TNewRoutes extends StringArrayToObject<TArgs, UntypeableOutput<any, any>>,\n  >(\n    routes: TNewRoutes,\n  ) => UntypeableRouter<TArgs, Prettify<TNewRoutes>>;\n  input: <TInput>(parser?: AcceptedParser<TInput>) => UntypeableInput<TInput>;\n  output: <TOutput>(\n    parser?: AcceptedParser<TOutput>,\n  ) => UntypeableOutput<{}, TOutput>;\n}\n\nexport type Prettify<T> = {\n  [K in keyof T]: T[K];\n} & {};\n\nexport interface UntypeableInput<TInput> {\n  __type: \"input\";\n  output: <TOutput>(\n    schema?: AcceptedParser<TOutput>,\n  ) => UntypeableOutput<TInput, TOutput>;\n  inputSchema: AcceptedParser<TInput> | undefined;\n}\n\nexport interface UntypeableOutput<TInput, TOutput> {\n  __type: \"output\";\n  outputSchema: AcceptedParser<TOutput> | undefined;\n  inputSchema: AcceptedParser<TInput> | undefined;\n}\n\nexport interface UntypeableRouter<\n  TArgs extends readonly string[] = DefaultArgs,\n  TRoutes extends Record<string, any> = {},\n> {\n  _schemaMap: SchemaMap;\n  add: <\n    TNewRoutes extends StringArrayToObject<TArgs, UntypeableOutput<any, any>>,\n  >(\n    routes: TNewRoutes,\n  ) => UntypeableRouter<TArgs, Prettify<TRoutes & TNewRoutes>>;\n\n  merge: <TRoutes2 extends Record<string, any>>(\n    router: UntypeableRouter<TArgs, TRoutes2>,\n  ) => UntypeableRouter<TArgs, Prettify<TRoutes & TRoutes2>>;\n}\n\nexport type LooseUntypeableHandler<TArgs extends readonly string[]> = (\n  ...args: [...args: TArgs, input: any]\n) => Promise<any>;\n\nexport type UntypeableHandler<\n  TArgs extends readonly string[] = DefaultArgs,\n  TRoutes extends Record<string, any> = {},\n> = TArgs[\"length\"] extends 1\n  ? <T1 extends keyof TRoutes>(\n      firstArg: T1,\n      ...args: ArgsFromRoutes<TRoutes[T1]>\n    ) => TRoutes[T1] extends UntypeableOutput<any, infer TOutput>\n      ? Promise<TOutput>\n      : never\n  : TArgs[\"length\"] extends 2\n  ? <T1 extends keyof TRoutes, T2 extends keyof TRoutes[T1]>(\n      firstArg: T1,\n      secondArg: T2,\n      ...args: ArgsFromRoutes<TRoutes[T1][T2]>\n    ) => TRoutes[T1][T2] extends UntypeableOutput<any, infer TOutput>\n      ? Promise<TOutput>\n      : never\n  : TArgs[\"length\"] extends 3\n  ? <\n      T1 extends keyof TRoutes,\n      T2 extends keyof TRoutes[T1],\n      T3 extends keyof TRoutes[T1][T2],\n    >(\n      firstArg: T1,\n      secondArg: T2,\n      thirdArg: T3,\n      ...args: ArgsFromRoutes<TRoutes[T1][T2][T3]>\n    ) => TRoutes[T1][T2][T3] extends UntypeableOutput<any, infer TOutput>\n      ? Promise<TOutput>\n      : never\n  : TArgs[\"length\"] extends 4\n  ? <\n      T1 extends keyof TRoutes,\n      T2 extends keyof TRoutes[T1],\n      T3 extends keyof TRoutes[T1][T2],\n      T4 extends keyof TRoutes[T1][T2][T3],\n    >(\n      firstArg: T1,\n      secondArg: T2,\n      thirdArg: T3,\n      fourthArg: T4,\n      ...args: ArgsFromRoutes<TRoutes[T1][T2][T3][T4]>\n    ) => TRoutes[T1][T2][T3][T4] extends UntypeableOutput<any, infer TOutput>\n      ? Promise<TOutput>\n      : never\n  : never;\n\nexport type ArgsFromRoutes<TRoutes> = TRoutes extends UntypeableOutput<\n  infer TInput,\n  any\n>\n  ? TInput extends Record<string, never>\n    ? []\n    : Record<string, undefined> extends TInput\n    ? [input?: TInput]\n    : [input: Prettify<TInput>]\n  : never;\n\nexport type StringArrayToObject<\n  T extends readonly string[],\n  TValue,\n> = T extends [\n  infer THead extends string,\n  ...infer TTail extends readonly string[],\n]\n  ? { [K in THead]?: StringArrayToObject<TTail, TValue> }\n  : TValue;\n\nexport type ArgsFromRouter<TRouter extends UntypeableRouter<any, any>> =\n  TRouter extends UntypeableRouter<infer TArgs, any> ? TArgs : never;\n\nexport type RoutesFromRouter<TRouter extends UntypeableRouter<any, any>> =\n  TRouter extends UntypeableRouter<any, infer TRoutes> ? TRoutes : never;\n"
  },
  {
    "path": "src/untypeable.ts",
    "content": "import { SEPARATOR } from \"./constants\";\nimport {\n  UntypeableBase,\n  UntypeableInput,\n  UntypeableOutput,\n  UntypeableRouter,\n  AcceptedParser,\n  SchemaMap,\n} from \"./types\";\n\nconst isOutput = (value: {\n  __type?: string;\n}): value is UntypeableOutput<any, any> => {\n  return value.__type === \"output\";\n};\n\nconst isInput = (value: {\n  __type?: string;\n}): value is UntypeableOutput<any, any> => {\n  return value.__type === \"input\";\n};\n\nconst combineMaps = <K, V>(map1: Map<K, V>, map2: Map<K, V>) => {\n  const newMap = new Map(map1);\n\n  for (const [key, value] of map2) {\n    newMap.set(key, value);\n  }\n\n  return newMap;\n};\n\nconst initRouter = <TRoutes extends Record<string, any> = {}>(\n  routes: TRoutes,\n  schemaMap: SchemaMap = new Map(),\n): UntypeableRouter => {\n  const collectRoutes = (\n    routes: Record<string, UntypeableOutput<any, any> | Record<string, any>>,\n    path: string[] = [],\n  ) => {\n    for (const [key, value] of Object.entries(routes)) {\n      if (isOutput(value)) {\n        schemaMap.set([...path, key].join(SEPARATOR), {\n          input: value.inputSchema,\n          output: value.outputSchema,\n        });\n      } else if (isInput(value)) {\n        throw new Error(\"An input cannot be passed directly to a router.\");\n      } else {\n        collectRoutes(value, [...path, key]);\n      }\n    }\n  };\n\n  collectRoutes(routes);\n\n  return {\n    add: (newRoutes) => initRouter(newRoutes, schemaMap),\n    merge: (router) =>\n      initRouter({}, combineMaps(router._schemaMap, schemaMap)),\n    _schemaMap: schemaMap,\n  };\n};\n\nconst initInput = (\n  inputSchema: AcceptedParser<any> | undefined,\n): UntypeableInput<any> => {\n  return {\n    output: (outputSchema) => initOutput(inputSchema, outputSchema),\n    inputSchema,\n    __type: \"input\",\n  };\n};\n\nconst initOutput = (\n  inputSchema: AcceptedParser<any> | undefined,\n  outputSchema: AcceptedParser<any> | undefined,\n): UntypeableOutput<any, any> => {\n  return {\n    __type: \"output\",\n    inputSchema,\n    outputSchema,\n  };\n};\n\nexport const initUntypeable = (): UntypeableBase => {\n  return {\n    pushArg: () => initUntypeable() as any,\n    unshiftArg: () => initUntypeable() as any,\n    args: () => initUntypeable() as any,\n    input: initInput,\n    output: (outputSchema) => initOutput(undefined, outputSchema),\n    router: (routes) => initRouter(routes),\n  };\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    /* Visit https://aka.ms/tsconfig to read more about this file */\n    /* Projects */\n    // \"incremental\": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */\n    // \"composite\": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */\n    // \"tsBuildInfoFile\": \"./.tsbuildinfo\",              /* Specify the path to .tsbuildinfo incremental compilation file. */\n    // \"disableSourceOfProjectReferenceRedirect\": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */\n    // \"disableSolutionSearching\": true,                 /* Opt a project out of multi-project reference checking when editing. */\n    // \"disableReferencedProjectLoad\": true,             /* Reduce the number of projects loaded automatically by TypeScript. */\n    /* Language and Environment */\n    \"target\": \"es2016\", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */\n    // \"lib\": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */\n    // \"jsx\": \"preserve\",                                /* Specify what JSX code is generated. */\n    // \"experimentalDecorators\": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */\n    // \"emitDecoratorMetadata\": true,                    /* Emit design-type metadata for decorated declarations in source files. */\n    // \"jsxFactory\": \"\",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */\n    // \"jsxFragmentFactory\": \"\",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */\n    // \"jsxImportSource\": \"\",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */\n    // \"reactNamespace\": \"\",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */\n    // \"noLib\": true,                                    /* Disable including any library files, including the default lib.d.ts. */\n    // \"useDefineForClassFields\": true,                  /* Emit ECMAScript-standard-compliant class fields. */\n    // \"moduleDetection\": \"auto\",                        /* Control what method is used to detect module-format JS files. */\n    /* Modules */\n    \"module\": \"NodeNext\", /* Specify what module code is generated. */\n    // \"rootDir\": \"./\",                                  /* Specify the root folder within your source files. */\n    // \"moduleResolution\": \"node\",                       /* Specify how TypeScript looks up a file from a given module specifier. */\n    // \"baseUrl\": \"./\",                                  /* Specify the base directory to resolve non-relative module names. */\n    // \"paths\": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */\n    // \"rootDirs\": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */\n    // \"typeRoots\": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */\n    // \"types\": [],                                      /* Specify type package names to be included without being referenced in a source file. */\n    // \"allowUmdGlobalAccess\": true,                     /* Allow accessing UMD globals from modules. */\n    // \"moduleSuffixes\": [],                             /* List of file name suffixes to search when resolving a module. */\n    // \"resolveJsonModule\": true,                        /* Enable importing .json files. */\n    // \"noResolve\": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */\n    /* JavaScript Support */\n    // \"allowJs\": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */\n    // \"checkJs\": true,                                  /* Enable error reporting in type-checked JavaScript files. */\n    // \"maxNodeModuleJsDepth\": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */\n    /* Emit */\n    // \"declaration\": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */\n    // \"declarationMap\": true,                           /* Create sourcemaps for d.ts files. */\n    // \"emitDeclarationOnly\": true,                      /* Only output d.ts files and not JavaScript files. */\n    // \"sourceMap\": true,                                /* Create source map files for emitted JavaScript files. */\n    // \"outFile\": \"./\",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */\n    // \"outDir\": \"./\",                                   /* Specify an output folder for all emitted files. */\n    // \"removeComments\": true,                           /* Disable emitting comments. */\n    \"noEmit\": true, /* Disable emitting files from a compilation. */\n    // \"importHelpers\": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */\n    // \"importsNotUsedAsValues\": \"remove\",               /* Specify emit/checking behavior for imports that are only used for types. */\n    // \"downlevelIteration\": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */\n    // \"sourceRoot\": \"\",                                 /* Specify the root path for debuggers to find the reference source code. */\n    // \"mapRoot\": \"\",                                    /* Specify the location where debugger should locate map files instead of generated locations. */\n    // \"inlineSourceMap\": true,                          /* Include sourcemap files inside the emitted JavaScript. */\n    // \"inlineSources\": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */\n    // \"emitBOM\": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */\n    // \"newLine\": \"crlf\",                                /* Set the newline character for emitting files. */\n    // \"stripInternal\": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */\n    // \"noEmitHelpers\": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */\n    // \"noEmitOnError\": true,                            /* Disable emitting files if any type checking errors are reported. */\n    // \"preserveConstEnums\": true,                       /* Disable erasing 'const enum' declarations in generated code. */\n    // \"declarationDir\": \"./\",                           /* Specify the output directory for generated declaration files. */\n    // \"preserveValueImports\": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */\n    /* Interop Constraints */\n    // \"isolatedModules\": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */\n    // \"allowSyntheticDefaultImports\": true,             /* Allow 'import x from y' when a module doesn't have a default export. */\n    \"esModuleInterop\": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */\n    // \"preserveSymlinks\": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */\n    \"forceConsistentCasingInFileNames\": true, /* Ensure that casing is correct in imports. */\n    /* Type Checking */\n    \"strict\": true, /* Enable all strict type-checking options. */\n    // \"noImplicitAny\": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */\n    // \"strictNullChecks\": true,                         /* When type checking, take into account 'null' and 'undefined'. */\n    // \"strictFunctionTypes\": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */\n    // \"strictBindCallApply\": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */\n    // \"strictPropertyInitialization\": true,             /* Check for class properties that are declared but not set in the constructor. */\n    // \"noImplicitThis\": true,                           /* Enable error reporting when 'this' is given the type 'any'. */\n    // \"useUnknownInCatchVariables\": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */\n    // \"alwaysStrict\": true,                             /* Ensure 'use strict' is always emitted. */\n    // \"noUnusedLocals\": true,                           /* Enable error reporting when local variables aren't read. */\n    // \"noUnusedParameters\": true,                       /* Raise an error when a function parameter isn't read. */\n    // \"exactOptionalPropertyTypes\": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */\n    // \"noImplicitReturns\": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */\n    // \"noFallthroughCasesInSwitch\": true,               /* Enable error reporting for fallthrough cases in switch statements. */\n    // \"noUncheckedIndexedAccess\": true,                 /* Add 'undefined' to a type when accessed using an index. */\n    // \"noImplicitOverride\": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */\n    // \"noPropertyAccessFromIndexSignature\": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */\n    // \"allowUnusedLabels\": true,                        /* Disable error reporting for unused labels. */\n    // \"allowUnreachableCode\": true,                     /* Disable error reporting for unreachable code. */\n    /* Completeness */\n    // \"skipDefaultLibCheck\": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */\n    \"skipLibCheck\": true /* Skip type checking all .d.ts files. */\n  },\n  \"exclude\": [\n    \"dist\"\n  ]\n}"
  },
  {
    "path": "tsup.config.ts",
    "content": "import { defineConfig } from \"tsup\";\n\nconst config = defineConfig({\n  format: [\"cjs\", \"esm\"],\n  dts: true,\n  entry: {\n    index: \"src/index.ts\",\n    client: \"src/client.ts\",\n  },\n});\n\nexport default config;\n"
  }
]