{"id":50108962,"url":"https://github.com/usings/openapi-shape","last_synced_at":"2026-05-23T12:04:30.095Z","repository":{"id":354571124,"uuid":"1221767799","full_name":"usings/openapi-shape","owner":"usings","description":"Generate TypeScript API contract types from OpenAPI 3.x JSON.","archived":false,"fork":false,"pushed_at":"2026-05-15T13:25:08.000Z","size":203,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-15T15:20:04.075Z","etag":null,"topics":["dts","openapi","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/usings.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-26T16:52:50.000Z","updated_at":"2026-05-15T13:25:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/usings/openapi-shape","commit_stats":null,"previous_names":["usings/openapi-shape"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/usings/openapi-shape","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usings%2Fopenapi-shape","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usings%2Fopenapi-shape/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usings%2Fopenapi-shape/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usings%2Fopenapi-shape/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/usings","download_url":"https://codeload.github.com/usings/openapi-shape/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usings%2Fopenapi-shape/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33394677,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T04:15:53.637Z","status":"ssl_error","status_checked_at":"2026-05-23T04:15:53.242Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dts","openapi","typescript"],"created_at":"2026-05-23T12:04:05.933Z","updated_at":"2026-05-23T12:04:30.090Z","avatar_url":"https://github.com/usings.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# openapi-shape\n\n[![npm version][npm-version-src]][npm-version-href]\n[![npm downloads][npm-downloads-src]][npm-downloads-href]\n[![bundle][bundle-src]][bundle-href]\n[![License][license-src]][license-href]\n\nGenerate TypeScript declarations from OpenAPI 3.x JSON, plus optional typed request shapes for your own HTTP client.\n\nUse `openapi-shape` when OpenAPI is your type contract, but your app should still own fetch/axios/ky/ofetch, auth, retries, caching, and response parsing.\n\n## Quick Start\n\nRun without installing:\n\n```sh\nnpx openapi-shape ./openapi.json -o src/api.d.ts\n```\n\nGenerate from a URL:\n\n```sh\nnpx openapi-shape https://api.example.com/openapi.json -o src/api.d.ts\n```\n\nInstall in a project when you regenerate types often:\n\n```sh\npnpm add -D openapi-shape\npnpm exec openapi-shape ./openapi.json -o src/api.d.ts\n```\n\nInstall as a runtime dependency only if you use the optional client:\n\n```sh\npnpm add openapi-shape\n```\n\nRequires Node \u003e= 22 and TypeScript \u003e= 5.\n\n## What It Generates\n\nThe generated file is plain TypeScript declarations:\n\n```ts\nexport interface Endpoints {\n  \"GET /pets\": {\n    params: void\n    query: { limit?: number }\n    body: void\n    response: { \"200\": Schemas.Pet[] }\n  }\n  \"POST /pets\": {\n    params: void\n    query: void\n    body: Schemas.CreatePet\n    response: { \"201\": Schemas.Pet }\n  }\n  \"GET /pets/{petId}\": {\n    params: { petId: string }\n    query: void\n    body: void\n    response: { \"200\": Schemas.Pet }\n  }\n}\n\nexport namespace Schemas {\n  export interface Pet {\n    id: number\n    name: string\n  }\n\n  export interface CreatePet {\n    name: string\n  }\n}\n```\n\nKey ideas:\n\n- `Endpoints` is keyed by `\"METHOD /path\"`.\n- Each endpoint has `params`, `query`, `body`, and `response`.\n- `response` is a map keyed by OpenAPI response keys such as `\"200\"`, `\"404\"`, or `\"default\"`.\n- OpenAPI `components.schemas` are grouped under `Schemas`.\n- `void` means that slot has no value.\n- The file is safe to commit or regenerate in CI.\n\n### Webhooks\n\nOpenAPI 3.1 `webhooks` are emitted as a parallel `Webhooks` interface:\n\n```ts\nexport interface Webhooks {\n  \"POST pet.created\": {\n    query: void\n    payload: Schemas.Pet\n    reply: void\n  }\n}\n```\n\nWebhook entries use the receiving side's vocabulary:\n\n- `payload` is the incoming request body.\n- `reply` is the handler's outgoing response.\n- `params` is omitted because webhook names do not have URL templates.\n- `query` and `headers` describe what the third party sends.\n\nExample handler type:\n\n```ts\nimport type { Webhooks } from \"./api\"\n\nfunction onPetCreated(payload: Webhooks[\"POST pet.created\"][\"payload\"]) {\n  payload.id\n}\n```\n\n## CLI\n\n```text\nUSAGE openapi-shape [OPTIONS] \u003cSOURCE\u003e --output=\u003coutput\u003e\n\nARGUMENTS\n\n  SOURCE    Path to OpenAPI JSON file or HTTP(S) URL\n\nOPTIONS\n\n  -o, --output=\u003coutput\u003e    Output file path\n                --check    Exit non-zero if --output is missing or stale\n              --headers    Emit typed header parameters per endpoint/webhook\n```\n\nTypical package script:\n\n```json\n{\n  \"scripts\": {\n    \"gen:api\": \"openapi-shape ./openapi.json -o src/api.d.ts\",\n    \"check:api\": \"openapi-shape ./openapi.json -o src/api.d.ts --check\"\n  }\n}\n```\n\n## Optional Client\n\n`openapi-shape/client` gives you one typed request function over the generated `Endpoints` map. It builds adapter input; your adapter owns the HTTP call and response parsing.\n\n```ts\nimport { createClient, type Adapter } from \"openapi-shape/client\"\nimport type { Endpoints } from \"./api\"\n\nconst adapter: Adapter = async ({ method, url, body, headers }) =\u003e {\n  const response = await fetch(url, { method, body, headers })\n  if (!response.ok) throw new Error(`${response.status} ${response.statusText}`)\n  if (response.status === 204) return undefined\n  return response.json()\n}\n\nexport const api = createClient\u003cEndpoints\u003e(adapter, {\n  baseURL: \"https://api.example.com\",\n})\n```\n\nCalls are checked at compile time:\n\n```ts\nconst pets = await api(\"GET /pets\", {\n  query: { limit: 10 },\n})\n\nconst created = await api(\"POST /pets\", {\n  body: { name: \"Buddy\" },\n})\n```\n\nClient return types are inferred from response maps:\n\n- `SuccessOf\u003cT\u003e` is the union of all `2xx` entries.\n- If there is no `2xx`, `SuccessOf\u003cT\u003e` uses `default` only when it is the sole response key.\n- `ResultOf\u003cT, Status\u003e` extracts one exact status key.\n\n```ts\nimport type { ResultOf, SuccessOf } from \"openapi-shape/client\"\nimport type { Endpoints } from \"./api\"\n\ntype ListPets = SuccessOf\u003cEndpoints[\"GET /pets\"]\u003e\ntype NotFound = ResultOf\u003cEndpoints[\"GET /pets/{petId}\"], \"404\"\u003e\n```\n\nAdapter-specific options stay typed:\n\n```ts\ntype AdapterOptions = { timeout?: number }\n\nconst adapter: Adapter\u003cAdapterOptions\u003e = async ({ method, url, body, headers, options }) =\u003e {\n  const controller = new AbortController()\n  const timeout = setTimeout(() =\u003e controller.abort(), options?.timeout ?? 30_000)\n  try {\n    const response = await fetch(url, { method, body, headers, signal: controller.signal })\n    return response.status === 204 ? undefined : response.json()\n  } finally {\n    clearTimeout(timeout)\n  }\n}\n\nexport const api = createClient\u003cEndpoints, AdapterOptions\u003e(adapter, {\n  options: { timeout: 5000 },\n})\n\nawait api(\"GET /pets\", {\n  query: { limit: 10 },\n  options: { timeout: 1000 },\n})\n```\n\n## Request Building\n\nThe optional client builds adapter input with these rules:\n\n| Field     | Behavior                                                                                                                                                                                                                                                      |\n| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `method`  | Read from the endpoint key, such as `GET /pets`.                                                                                                                                                                                                              |\n| `url`     | `baseURL` plus path params and query string. Path params are URL-encoded. Query arrays become repeated keys, for example `tags=a\u0026tags=b`. `null` and `undefined` query values are skipped. Absolute `http://` and `https://` endpoint paths bypass `baseURL`. |\n| `body`    | `undefined` stays `undefined`. `string`, `FormData`, `URLSearchParams`, `Blob`, `ArrayBuffer`, typed arrays, and `ReadableStream` pass through unchanged. Other defined bodies are JSON-stringified.                                                          |\n| `headers` | JSON bodies get `content-type: application/json`. Passthrough bodies get no automatic content type. Per-call headers override automatic headers case-insensitively. Adapter headers use lowercase names.                                                      |\n| `options` | Passed through to your adapter after default/per-call merging. Object options are shallow-merged; non-object options are replaced by the per-call value.                                                                                                      |\n\nCustomize serialization when your API does not use the defaults:\n\n```ts\nexport const api = createClient\u003cEndpoints\u003e(adapter, {\n  baseURL: \"https://api.example.com\",\n  serializeQuery(query) {\n    const params = new URLSearchParams()\n    for (const [name, value] of Object.entries(query)) {\n      if (value == null) continue\n      params.set(name, Array.isArray(value) ? value.join(\",\") : String(value))\n    }\n    return params\n  },\n  serializeBody(body) {\n    if (typeof body === \"string\") {\n      return { body, headers: { \"Content-Type\": \"text/plain\" } }\n    }\n\n    return {\n      body: JSON.stringify(body),\n      headers: { \"Content-Type\": \"application/json\" },\n    }\n  },\n})\n```\n\n- `serializeQuery` receives the raw query object and returns a query string or `URLSearchParams`.\n- `serializeBody` receives each non-`undefined` body and returns the adapter body plus optional headers.\n- Per-call headers still override headers returned by `serializeBody`.\n\n## Integration Examples\n\nThese examples are recipes. They map the same adapter input to fetch or third-party HTTP clients; keep auth, retries, hooks, and error handling in your adapter or HTTP client.\n\n\u003cdetails\u003e\n\u003csummary\u003eMore complete fetch adapter\u003c/summary\u003e\n\nThis version handles auth headers, typed HTTP errors, empty responses, and content-type based parsing:\n\n```ts\nimport { createClient, type Adapter } from \"openapi-shape/client\"\nimport type { Endpoints } from \"./api\"\n\nclass HttpError extends Error {\n  constructor(\n    public readonly status: number,\n    public readonly body: string,\n    public readonly response: Response,\n  ) {\n    super(`HTTP ${status} ${response.statusText}: ${body.slice(0, 200)}`)\n    this.name = \"HttpError\"\n  }\n}\n\ndeclare function getToken(): string\n\nconst adapter: Adapter = async ({ method, url, body, headers }) =\u003e {\n  const response = await fetch(url, {\n    method,\n    body,\n    headers: { ...headers, authorization: `Bearer ${getToken()}` },\n  })\n\n  if (!response.ok) {\n    const errorBody = await response.text()\n    throw new HttpError(response.status, errorBody, response)\n  }\n\n  const contentLength = response.headers.get(\"content-length\")\n  if (response.status === 204 || contentLength === \"0\") {\n    return undefined\n  }\n\n  const contentType = response.headers.get(\"content-type\")?.toLowerCase() ?? \"\"\n  if (/^application\\/(.*\\+)?json/.test(contentType)) return response.json()\n  if (contentType.startsWith(\"text/\")) return response.text()\n  return response.blob()\n}\n\nexport const api = createClient\u003cEndpoints\u003e(adapter, {\n  baseURL: \"https://api.example.com\",\n})\n```\n\n\u003c/details\u003e\n\nFor third-party adapters, use `Omit\u003c...\u003e` so callers cannot override fields owned by the generated request (`method`, `url`, `body`/`data`, `headers`).\n\n\u003cdetails\u003e\n\u003csummary\u003eaxios adapter\u003c/summary\u003e\n\n```ts\nimport axios, { type AxiosRequestConfig } from \"axios\"\nimport { createClient, type Adapter } from \"openapi-shape/client\"\nimport type { Endpoints } from \"./api\"\n\ntype AdapterOptions = Omit\u003cAxiosRequestConfig, \"method\" | \"url\" | \"data\" | \"headers\"\u003e\n\nconst adapter: Adapter\u003cAdapterOptions\u003e = async ({ method, url, body, headers, options }) =\u003e {\n  const response = await axios.request({ ...options, method, url, data: body, headers })\n  return response.data\n}\n\nexport const api = createClient\u003cEndpoints, AdapterOptions\u003e(adapter)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eky adapter\u003c/summary\u003e\n\n```ts\nimport ky, { type Options as KyOptions } from \"ky\"\nimport { createClient, type Adapter } from \"openapi-shape/client\"\nimport type { Endpoints } from \"./api\"\n\ntype AdapterOptions = Omit\u003cKyOptions, \"method\" | \"body\" | \"headers\"\u003e\n\nconst adapter: Adapter\u003cAdapterOptions\u003e = async ({ method, url, body, headers, options }) =\u003e {\n  return ky(url, { ...options, method, body, headers }).json()\n}\n\nexport const api = createClient\u003cEndpoints, AdapterOptions\u003e(adapter)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eofetch adapter\u003c/summary\u003e\n\n```ts\nimport { ofetch, type FetchOptions } from \"ofetch\"\nimport { createClient, type Adapter } from \"openapi-shape/client\"\nimport type { Endpoints } from \"./api\"\n\ntype AdapterOptions = Omit\u003cFetchOptions, \"method\" | \"body\" | \"headers\"\u003e\n\nconst adapter: Adapter\u003cAdapterOptions\u003e = async ({ method, url, body, headers, options }) =\u003e {\n  return ofetch(url, { ...options, method, body, headers })\n}\n\nexport const api = createClient\u003cEndpoints, AdapterOptions\u003e(adapter)\n```\n\n\u003c/details\u003e\n\n### Typed Query Keys\n\nThe generated `Endpoints` map can also type cache keys for libraries such as TanStack Query. `openapi-shape` does not generate hooks; keep cache policy, invalidation, and optimistic updates in your app.\n\n\u003cdetails\u003e\n\u003csummary\u003eTanStack Query example\u003c/summary\u003e\n\n```ts\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\"\nimport type { Endpoints } from \"./api\"\nimport { api } from \"./client\"\n\ntype NonNullish = NonNullable\u003cunknown\u003e\ntype EmptyObject = Record\u003cPropertyKey, never\u003e\ntype HasOnlyOptionalProps\u003cT\u003e = T extends object ? (NonNullish extends T ? true : false) : false\n\ntype QueryKeyEndpoint = keyof Endpoints \u0026 string\n\ntype QueryKeyParamsPart\u003cK extends QueryKeyEndpoint\u003e = Endpoints[K][\"params\"] extends void\n  ? NonNullish\n  : { params: Endpoints[K][\"params\"] }\n\ntype QueryKeyQueryPart\u003cK extends QueryKeyEndpoint\u003e = Endpoints[K][\"query\"] extends void\n  ? NonNullish\n  : HasOnlyOptionalProps\u003cEndpoints[K][\"query\"]\u003e extends true\n    ? { query?: Endpoints[K][\"query\"] }\n    : { query: Endpoints[K][\"query\"] }\n\ntype QueryKeyInput\u003cK extends QueryKeyEndpoint\u003e = QueryKeyParamsPart\u003cK\u003e \u0026 QueryKeyQueryPart\u003cK\u003e\n\ntype QueryKeyArgs\u003cK extends QueryKeyEndpoint\u003e = keyof QueryKeyInput\u003cK\u003e extends never\n  ? [input?: EmptyObject]\n  : NonNullish extends QueryKeyInput\u003cK\u003e\n    ? [input?: QueryKeyInput\u003cK\u003e]\n    : [input: QueryKeyInput\u003cK\u003e]\n\nexport const apiKeys = {\n  /** Endpoint-level key for broad invalidation, e.g. all GET /pets queries. */\n  endpoint: \u003cK extends QueryKeyEndpoint\u003e(endpoint: K) =\u003e [endpoint] as const,\n  /** Request-level key including params/query input for exact query caching. */\n  request: \u003cK extends QueryKeyEndpoint\u003e(endpoint: K, ...[input]: QueryKeyArgs\u003cK\u003e) =\u003e\n    input === undefined ? ([endpoint] as const) : ([endpoint, input] as const),\n}\n\nexport function usePets(limit?: number) {\n  const query = limit === undefined ? {} : { limit }\n\n  return useQuery({\n    queryKey: apiKeys.request(\"GET /pets\", { query }),\n    queryFn: () =\u003e api(\"GET /pets\", { query }),\n  })\n}\n\nexport function useCreatePet() {\n  const queryClient = useQueryClient()\n\n  return useMutation({\n    mutationFn: (body: Endpoints[\"POST /pets\"][\"body\"]) =\u003e api(\"POST /pets\", { body }),\n    onSuccess: () =\u003e {\n      queryClient.invalidateQueries({\n        queryKey: apiKeys.endpoint(\"GET /pets\"),\n      })\n    },\n  })\n}\n```\n\n\u003c/details\u003e\n\n## Programmatic API\n\nUse the generator directly from build scripts, custom CLIs, or tests:\n\n```ts\nimport { generate } from \"openapi-shape\"\nimport { writeFile } from \"node:fs/promises\"\n\nconst code = await generate(\"./openapi.json\", {\n  headers: true,\n  formats: { \"date-time\": \"Date\", \"uuid\": \"UUID\" },\n})\n\nawait writeFile(\"src/api.d.ts\", code)\n```\n\n`generate(source)` is async for file paths and URLs. `generate(doc)` is synchronous for already-parsed OpenAPI objects:\n\n```ts\nimport { generate } from \"openapi-shape\"\n\nconst code = generate(openapi)\n```\n\nOptions:\n\n| Option    | Default | Description                                                                                                                                                                                                                                           |\n| --------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `formats` | `{}`    | Maps OpenAPI `format` values to raw TypeScript type expressions. Applies to schemas with `type: \"string\" \\| \"number\" \\| \"integer\"` and nullable variants such as `[\"string\", \"null\"]`. User mappings override the built-in `binary`/`byte` -\u003e `Blob`. |\n| `headers` | `false` | Adds a typed `headers` field to each endpoint/webhook from `in: header` parameters. When false, callers may still pass arbitrary runtime headers through the client.                                                                                  |\n\n## Supported OpenAPI Features\n\nOpenAPI 3.0 and 3.1 JSON documents are supported.\n\n| Feature                              | Output / behavior                                                                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |\n| `components.schemas`                 | Named declarations in `export namespace Schemas`, emitted as `interface` for object models or `type` for aliases/unions/primitives.     |\n| Schema `$ref`                        | Named TypeScript references to `Schemas.*`; schema refs are preserved instead of inlined.                                               |\n| Component `$ref`                     | `$ref` parameters, request bodies, responses, and path items are resolved before endpoint generation.                                   |\n| `oneOf` / `anyOf`                    | TypeScript unions, with duplicate members collapsed where possible.                                                                     |\n| `allOf`                              | TypeScript intersections.                                                                                                               |\n| `discriminator` on `oneOf` / `anyOf` | Required string literal discriminator properties injected into branch schemas for narrowable unions.                                    |\n| `enum` / `const`                     | Literal types and literal unions.                                                                                                       |\n| OpenAPI 3.0 `nullable`               | Normalized to OpenAPI 3.1-style nullable type arrays.                                                                                   |\n| OpenAPI 3.1 `type: [\"T\", \"null\"]`    | TypeScript unions with `null`.                                                                                                          |\n| Arrays and tuples                    | `items` becomes arrays; `prefixItems` becomes tuples, with optional rest from `items`.                                                  |\n| `additionalProperties`               | `Record\u003cstring, T\u003e` for dictionary objects, or an index signature on objects with explicit properties.                                  |\n| `patternProperties`                  | Folded into the same index signature; multiple patterns become a union of value types.                                                  |\n| OpenAPI 3.1 `webhooks`               | A parallel `Webhooks` interface with `payload` and `reply` entries.                                                                     |\n| `requestBody.required`               | Missing or `false` emits `body?: T`; `true` emits `body: T`.                                                                            |\n| Response maps                        | Responses are emitted as maps keyed by OpenAPI response keys, e.g. `\"200\"`, `\"4XX\"`, and `\"default\"`.                                   |\n| Client success type                  | `SuccessOf\u003cT\u003e` is the union of all `2xx` response entries; when there is no `2xx`, sole `default` is used as the fallback success type. |\n\nIdentifier handling:\n\n- Invalid object property and parameter names are quoted, for example `\"user-id\"?: string`.\n- Invalid or reserved schema names are sanitized, for example `User-Profile` -\u003e `User_Profile` and `class` -\u003e `_class`.\n- Schema name collisions after sanitization throw an error.\n\n## Not Supported Yet\n\n- Swagger 2.0. Convert to OpenAPI 3 first.\n- YAML input.\n- `readOnly` / `writeOnly` request and response variants.\n- External `$ref` targets such as remote URLs or separate files.\n\n## License\n\n[MIT](./LICENSE) License\n\n\u003c!-- Badges --\u003e\n\n[npm-version-src]: https://img.shields.io/npm/v/openapi-shape?style=flat\u0026colorA=080f12\u0026colorB=1fa669\n[npm-version-href]: https://npmx.dev/package/openapi-shape\n[npm-downloads-src]: https://img.shields.io/npm/dm/openapi-shape?style=flat\u0026colorA=080f12\u0026colorB=1fa669\n[npm-downloads-href]: https://npmx.dev/package/openapi-shape\n[bundle-src]: https://img.shields.io/bundlephobia/minzip/openapi-shape?style=flat\u0026colorA=080f12\u0026colorB=1fa669\u0026label=minzip\n[bundle-href]: https://bundlephobia.com/result?p=openapi-shape\n[license-src]: https://img.shields.io/github/license/usings/openapi-shape.svg?style=flat\u0026colorA=080f12\u0026colorB=1fa669\n[license-href]: https://github.com/usings/openapi-shape/blob/main/LICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusings%2Fopenapi-shape","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fusings%2Fopenapi-shape","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusings%2Fopenapi-shape/lists"}