diff --git a/.changeset/mean-walls-lick.md b/.changeset/mean-walls-lick.md new file mode 100644 index 0000000..2a03e4c --- /dev/null +++ b/.changeset/mean-walls-lick.md @@ -0,0 +1,5 @@ +--- +"typed-openapi": minor +--- + +Add response headers in endpoint types diff --git a/packages/typed-openapi/src/generator.ts b/packages/typed-openapi/src/generator.ts index 6255068..486bb77 100644 --- a/packages/typed-openapi/src/generator.ts +++ b/packages/typed-openapi/src/generator.ts @@ -70,29 +70,29 @@ export const generateFile = (options: GeneratorOptions) => { ctx.runtime === "none" ? (file: string) => file : (file: string) => { - const model = Codegen.TypeScriptToModel.Generate(file); - const transformer = runtimeValidationGenerator[ctx.runtime as Exclude]; - // tmp fix for typebox, there's currently a "// todo" only with Codegen.ModelToTypeBox.Generate - // https://github.com/sinclairzx81/typebox-codegen/blob/44d44d55932371b69f349331b1c8a60f5d760d9e/src/model/model-to-typebox.ts#L31 - const generated = ctx.runtime === "typebox" ? Codegen.TypeScriptToTypeBox.Generate(file) : transformer(model); - - let converted = ""; - const match = generated.match(/(const __ENDPOINTS_START__ =)([\s\S]*?)(export type __ENDPOINTS_END__)/); - const content = match?.[2]; - - if (content && ctx.runtime in replacerByRuntime) { - const before = generated.slice(0, generated.indexOf("export type __ENDPOINTS_START")); - converted = - before + - replacerByRuntime[ctx.runtime as keyof typeof replacerByRuntime]( - content.slice(content.indexOf("export")), - ); - } else { - converted = generated; - } + const model = Codegen.TypeScriptToModel.Generate(file); + const transformer = runtimeValidationGenerator[ctx.runtime as Exclude]; + // tmp fix for typebox, there's currently a "// todo" only with Codegen.ModelToTypeBox.Generate + // https://github.com/sinclairzx81/typebox-codegen/blob/44d44d55932371b69f349331b1c8a60f5d760d9e/src/model/model-to-typebox.ts#L31 + const generated = ctx.runtime === "typebox" ? Codegen.TypeScriptToTypeBox.Generate(file) : transformer(model); + + let converted = ""; + const match = generated.match(/(const __ENDPOINTS_START__ =)([\s\S]*?)(export type __ENDPOINTS_END__)/); + const content = match?.[2]; + + if (content && ctx.runtime in replacerByRuntime) { + const before = generated.slice(0, generated.indexOf("export type __ENDPOINTS_START")); + converted = + before + + replacerByRuntime[ctx.runtime as keyof typeof replacerByRuntime]( + content.slice(content.indexOf("export")), + ); + } else { + converted = generated; + } - return converted; - }; + return converted; + }; const file = ` ${transform(schemaList + endpointSchemaList)} @@ -132,6 +132,24 @@ const parameterObjectToString = (parameters: Box | Record, ctx: GeneratorContext) => { + let str = "{"; + for (const [key, responseHeader] of Object.entries(responseHeaders)) { + const value = ctx.runtime === "none" + ? responseHeader.recompute((box) => { + if (Box.isReference(box) && !box.params.generics && box.value !== "null") { + box.value = `Schemas.${box.value}`; + } + + return box; + }).value + : responseHeader.value + str += `${wrapWithQuotesIfNeeded(key.toLowerCase())}: ${value},\n`; + } + return str + "}"; +} + const generateEndpointSchemaList = (ctx: GeneratorContext) => { let file = ` ${ctx.runtime === "none" ? "export namespace Endpoints {" : ""} @@ -145,39 +163,42 @@ const generateEndpointSchemaList = (ctx: GeneratorContext) => { path: "${endpoint.path}", requestFormat: "${endpoint.requestFormat}", ${ - endpoint.meta.hasParameters - ? `parameters: { + endpoint.meta.hasParameters + ? `parameters: { ${parameters.query ? `query: ${parameterObjectToString(parameters.query)},` : ""} ${parameters.path ? `path: ${parameterObjectToString(parameters.path)},` : ""} ${parameters.header ? `header: ${parameterObjectToString(parameters.header)},` : ""} ${ parameters.body ? `body: ${parameterObjectToString( - ctx.runtime === "none" - ? parameters.body.recompute((box) => { - if (Box.isReference(box) && !box.params.generics) { - box.value = `Schemas.${box.value}`; - } - return box; - }) - : parameters.body, - )},` + ctx.runtime === "none" + ? parameters.body.recompute((box) => { + if (Box.isReference(box) && !box.params.generics) { + box.value = `Schemas.${box.value}`; + } + return box; + }) + : parameters.body, + )},` : "" } }` - : "parameters: never," - } + : "parameters: never," + } response: ${ - ctx.runtime === "none" - ? endpoint.response.recompute((box) => { - if (Box.isReference(box) && !box.params.generics && box.value !== "null") { - box.value = `Schemas.${box.value}`; - } - - return box; - }).value - : endpoint.response.value - }, + ctx.runtime === "none" + ? endpoint.response.recompute((box) => { + if (Box.isReference(box) && !box.params.generics && box.value !== "null") { + box.value = `Schemas.${box.value}`; + } + + return box; + }).value + : endpoint.response.value + }, + ${ + endpoint.responseHeaders ? `responseHeaders: ${responseHeadersObjectToString(endpoint.responseHeaders, ctx)},` : "" + } }\n`; }); @@ -199,14 +220,14 @@ const generateEndpointByMethod = (ctx: GeneratorContext) => { // export ${ctx.runtime === "none" ? "type" : "const"} EndpointByMethod = { ${Object.entries(byMethods) - .map(([method, list]) => { - return `${method}: { + .map(([method, list]) => { + return `${method}: { ${list.map( - (endpoint) => `"${endpoint.path}": ${ctx.runtime === "none" ? "Endpoints." : ""}${endpoint.meta.alias}`, - )} + (endpoint) => `"${endpoint.path}": ${ctx.runtime === "none" ? "Endpoints." : ""}${endpoint.meta.alias}`, + )} }`; - }) - .join(",\n")} + }) + .join(",\n")} } ${ctx.runtime === "none" ? "" : "export type EndpointByMethod = typeof EndpointByMethod;"} // @@ -216,8 +237,8 @@ const generateEndpointByMethod = (ctx: GeneratorContext) => { // ${Object.keys(byMethods) - .map((method) => `export type ${capitalize(method)}Endpoints = EndpointByMethod["${method}"]`) - .join("\n")} + .map((method) => `export type ${capitalize(method)}Endpoints = EndpointByMethod["${method}"]`) + .join("\n")} // `; @@ -246,6 +267,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -260,6 +282,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"] }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; @@ -303,18 +326,18 @@ export class ApiClient { ${method}( path: Path, ...params: MaybeOptionalArg<${match(ctx.runtime) - .with("zod", "yup", () => infer(`TEndpoint["parameters"]`)) - .with("arktype", "io-ts", "typebox", "valibot", () => infer(`TEndpoint`) + `["parameters"]`) - .otherwise(() => `TEndpoint["parameters"]`)}> + .with("zod", "yup", () => infer(`TEndpoint["parameters"]`)) + .with("arktype", "io-ts", "typebox", "valibot", () => infer(`TEndpoint`) + `["parameters"]`) + .otherwise(() => `TEndpoint["parameters"]`)}> ): Promise<${match(ctx.runtime) - .with("zod", "yup", () => infer(`TEndpoint["response"]`)) - .with("arktype", "io-ts", "typebox", "valibot", () => infer(`TEndpoint`) + `["response"]`) - .otherwise(() => `TEndpoint["response"]`)}> { + .with("zod", "yup", () => infer(`TEndpoint["response"]`)) + .with("arktype", "io-ts", "typebox", "valibot", () => infer(`TEndpoint`) + `["response"]`) + .otherwise(() => `TEndpoint["response"]`)}> { return this.fetcher("${method}", this.baseUrl + path, params[0]) .then(response => this.parseResponse(response))${match(ctx.runtime) - .with("zod", "yup", () => `as Promise<${infer(`TEndpoint["response"]`)}>`) - .with("arktype", "io-ts", "typebox", "valibot", () => `as Promise<${infer(`TEndpoint`) + `["response"]`}>`) - .otherwise(() => `as Promise`)}; + .with("zod", "yup", () => `as Promise<${infer(`TEndpoint["response"]`)}>`) + .with("arktype", "io-ts", "typebox", "valibot", () => `as Promise<${infer(`TEndpoint`) + `["response"]`}>`) + .otherwise(() => `as Promise`)}; } // ` @@ -334,17 +357,17 @@ export class ApiClient { method: TMethod, path: TPath, ...params: MaybeOptionalArg<${match(ctx.runtime) - .with("zod", "yup", () => - inferByRuntime[ctx.runtime](`TEndpoint extends { parameters: infer Params } ? Params : never`), - ) - .with( - "arktype", - "io-ts", - "typebox", - "valibot", - () => inferByRuntime[ctx.runtime](`TEndpoint`) + `["parameters"]`, - ) - .otherwise(() => `TEndpoint extends { parameters: infer Params } ? Params : never`)}>) + .with("zod", "yup", () => + inferByRuntime[ctx.runtime](`TEndpoint extends { parameters: infer Params } ? Params : never`), + ) + .with( + "arktype", + "io-ts", + "typebox", + "valibot", + () => inferByRuntime[ctx.runtime](`TEndpoint`) + `["parameters"]`, + ) + .otherwise(() => `TEndpoint extends { parameters: infer Params } ? Params : never`)}>) : Promise & { /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */ json: () => Promise; diff --git a/packages/typed-openapi/src/map-openapi-endpoints.ts b/packages/typed-openapi/src/map-openapi-endpoints.ts index 84e5384..addf4de 100644 --- a/packages/typed-openapi/src/map-openapi-endpoints.ts +++ b/packages/typed-openapi/src/map-openapi-endpoints.ts @@ -145,6 +145,16 @@ export const mapOpenApiEndpoints = (doc: OpenAPIObject) => { } } + // Map response headers + const headers = responseObject?.headers; + if (headers) { + endpoint.responseHeaders = Object.entries(headers).reduce((acc, [name, headerOrRef]) => { + const header = refs.unwrap(headerOrRef); + acc[name] = openApiSchemaToTs({ schema: header.schema ?? {}, ctx }); + return acc; + }, {} as Record); + } + endpointList.push(endpoint); }); }); @@ -184,6 +194,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: AnyBox; + responseHeaders?: Record }; export type Endpoint = { @@ -198,4 +209,5 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; -}; + responseHeaders?: TConfig["responseHeaders"]; +}; \ No newline at end of file diff --git a/packages/typed-openapi/tests/generate-runtime.test.ts b/packages/typed-openapi/tests/generate-runtime.test.ts index 8835edd..cd26bb6 100644 --- a/packages/typed-openapi/tests/generate-runtime.test.ts +++ b/packages/typed-openapi/tests/generate-runtime.test.ts @@ -10,7 +10,7 @@ const samples = ["petstore", "docker.openapi", "long-operation-id"]; const runtimes = allowedRuntimes.toJsonSchema().enum; samples.forEach((sample) => { - describe(`generate-rutime-${sample}`, async () => { + describe(`generate-runtime-${sample}`, async () => { const filePath = `${__dirname}/samples/${sample}.yaml`; const openApiDoc = (await SwaggerParser.parse(filePath)) as OpenAPIObject; const ctx = mapOpenApiEndpoints(openApiDoc); diff --git a/packages/typed-openapi/tests/generator.test.ts b/packages/typed-openapi/tests/generator.test.ts index 4368b30..f7d0645 100644 --- a/packages/typed-openapi/tests/generator.test.ts +++ b/packages/typed-openapi/tests/generator.test.ts @@ -186,6 +186,7 @@ describe("generator", () => { query: Partial<{ username: string; password: string }>; }; response: string; + responseHeaders: { "X-Rate-Limit": number; "X-Expires-After": string }; }; export type get_LogoutUser = { method: "GET"; @@ -283,6 +284,7 @@ describe("generator", () => { export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -297,6 +299,7 @@ describe("generator", () => { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; @@ -718,6 +721,7 @@ describe("generator", () => { export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -732,6 +736,7 @@ describe("generator", () => { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; @@ -928,6 +933,7 @@ describe("generator", () => { export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -942,6 +948,7 @@ describe("generator", () => { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/map-openapi-endpoints.test.ts b/packages/typed-openapi/tests/map-openapi-endpoints.test.ts index 2462c99..f3bf3b2 100644 --- a/packages/typed-openapi/tests/map-openapi-endpoints.test.ts +++ b/packages/typed-openapi/tests/map-openapi-endpoints.test.ts @@ -2297,6 +2297,16 @@ describe("map-openapi-endpoints", () => { "type": "keyword", "value": "string", }, + "responseHeaders": { + "X-Expires-After": { + "type": "keyword", + "value": "string", + }, + "X-Rate-Limit": { + "type": "keyword", + "value": "number", + }, + }, }, { "meta": { diff --git a/packages/typed-openapi/tests/snapshots/docker.openapi.client.ts b/packages/typed-openapi/tests/snapshots/docker.openapi.client.ts index b8f40f9..d094472 100644 --- a/packages/typed-openapi/tests/snapshots/docker.openapi.client.ts +++ b/packages/typed-openapi/tests/snapshots/docker.openapi.client.ts @@ -1152,6 +1152,7 @@ export namespace Endpoints { path: { id: string }; }; response: unknown; + responseHeaders: { "X-Docker-Container-Path-Stat": string }; }; export type post_ContainerPrune = { method: "POST"; @@ -1342,6 +1343,14 @@ export namespace Endpoints { requestFormat: "json"; parameters: never; response: unknown; + responseHeaders: { + Swarm: "inactive" | "pending" | "error" | "locked" | "active/worker" | "active/manager"; + "Docker-Experimental": boolean; + "Cache-Control": string; + Pragma: string; + "API-Version": string; + "Builder-Version": string; + }; }; export type head_SystemPingHead = { method: "HEAD"; @@ -1349,6 +1358,14 @@ export namespace Endpoints { requestFormat: "json"; parameters: never; response: unknown; + responseHeaders: { + Swarm: "inactive" | "pending" | "error" | "locked" | "active/worker" | "active/manager"; + "Docker-Experimental": boolean; + "Cache-Control": string; + Pragma: string; + "API-Version": string; + "Builder-Version": string; + }; }; export type post_ImageCommit = { method: "POST"; @@ -2224,6 +2241,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -2238,6 +2256,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/docker.openapi.io-ts.ts b/packages/typed-openapi/tests/snapshots/docker.openapi.io-ts.ts index 38a9bbc..9844dab 100644 --- a/packages/typed-openapi/tests/snapshots/docker.openapi.io-ts.ts +++ b/packages/typed-openapi/tests/snapshots/docker.openapi.io-ts.ts @@ -2166,6 +2166,9 @@ export const head_ContainerArchiveInfo = t.type({ }), }), response: t.unknown, + responseHeaders: t.type({ + "X-Docker-Container-Path-Stat": t.string, + }), }); export type post_ContainerPrune = t.TypeOf; @@ -2445,6 +2448,21 @@ export const get_SystemPing = t.type({ requestFormat: t.literal("json"), parameters: t.never, response: t.unknown, + responseHeaders: t.type({ + Swarm: t.union([ + t.literal("inactive"), + t.literal("pending"), + t.literal("error"), + t.literal("locked"), + t.literal("active/worker"), + t.literal("active/manager"), + ]), + "Docker-Experimental": t.boolean, + "Cache-Control": t.string, + Pragma: t.string, + "API-Version": t.string, + "Builder-Version": t.string, + }), }); export type head_SystemPingHead = t.TypeOf; @@ -2454,6 +2472,21 @@ export const head_SystemPingHead = t.type({ requestFormat: t.literal("json"), parameters: t.never, response: t.unknown, + responseHeaders: t.type({ + Swarm: t.union([ + t.literal("inactive"), + t.literal("pending"), + t.literal("error"), + t.literal("locked"), + t.literal("active/worker"), + t.literal("active/manager"), + ]), + "Docker-Experimental": t.boolean, + "Cache-Control": t.string, + Pragma: t.string, + "API-Version": t.string, + "Builder-Version": t.string, + }), }); export type post_ImageCommit = t.TypeOf; @@ -3628,6 +3661,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -3642,6 +3676,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/docker.openapi.typebox.ts b/packages/typed-openapi/tests/snapshots/docker.openapi.typebox.ts index 7c8fecf..d2bbf0c 100644 --- a/packages/typed-openapi/tests/snapshots/docker.openapi.typebox.ts +++ b/packages/typed-openapi/tests/snapshots/docker.openapi.typebox.ts @@ -2292,6 +2292,9 @@ export const head_ContainerArchiveInfo = Type.Object({ }), }), response: Type.Unknown(), + responseHeaders: Type.Object({ + "X-Docker-Container-Path-Stat": Type.String(), + }), }); export type post_ContainerPrune = Static; @@ -2601,6 +2604,21 @@ export const get_SystemPing = Type.Object({ requestFormat: Type.Literal("json"), parameters: Type.Never(), response: Type.Unknown(), + responseHeaders: Type.Object({ + Swarm: Type.Union([ + Type.Literal("inactive"), + Type.Literal("pending"), + Type.Literal("error"), + Type.Literal("locked"), + Type.Literal("active/worker"), + Type.Literal("active/manager"), + ]), + "Docker-Experimental": Type.Boolean(), + "Cache-Control": Type.String(), + Pragma: Type.String(), + "API-Version": Type.String(), + "Builder-Version": Type.String(), + }), }); export type head_SystemPingHead = Static; @@ -2610,6 +2628,21 @@ export const head_SystemPingHead = Type.Object({ requestFormat: Type.Literal("json"), parameters: Type.Never(), response: Type.Unknown(), + responseHeaders: Type.Object({ + Swarm: Type.Union([ + Type.Literal("inactive"), + Type.Literal("pending"), + Type.Literal("error"), + Type.Literal("locked"), + Type.Literal("active/worker"), + Type.Literal("active/manager"), + ]), + "Docker-Experimental": Type.Boolean(), + "Cache-Control": Type.String(), + Pragma: Type.String(), + "API-Version": Type.String(), + "Builder-Version": Type.String(), + }), }); export type post_ImageCommit = Static; @@ -3877,6 +3910,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -3891,6 +3925,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/docker.openapi.valibot.ts b/packages/typed-openapi/tests/snapshots/docker.openapi.valibot.ts index 1c453b6..4592810 100644 --- a/packages/typed-openapi/tests/snapshots/docker.openapi.valibot.ts +++ b/packages/typed-openapi/tests/snapshots/docker.openapi.valibot.ts @@ -2082,6 +2082,9 @@ export const head_ContainerArchiveInfo = v.object({ }), }), response: v.unknown(), + responseHeaders: v.object({ + "X-Docker-Container-Path-Stat": v.string(), + }), }); export type post_ContainerPrune = v.InferOutput; @@ -2361,6 +2364,21 @@ export const get_SystemPing = v.object({ requestFormat: v.literal("json"), parameters: v.never(), response: v.unknown(), + responseHeaders: v.object({ + Swarm: v.union([ + v.literal("inactive"), + v.literal("pending"), + v.literal("error"), + v.literal("locked"), + v.literal("active/worker"), + v.literal("active/manager"), + ]), + "Docker-Experimental": v.boolean(), + "Cache-Control": v.string(), + Pragma: v.string(), + "API-Version": v.string(), + "Builder-Version": v.string(), + }), }); export type head_SystemPingHead = v.InferOutput; @@ -2370,6 +2388,21 @@ export const head_SystemPingHead = v.object({ requestFormat: v.literal("json"), parameters: v.never(), response: v.unknown(), + responseHeaders: v.object({ + Swarm: v.union([ + v.literal("inactive"), + v.literal("pending"), + v.literal("error"), + v.literal("locked"), + v.literal("active/worker"), + v.literal("active/manager"), + ]), + "Docker-Experimental": v.boolean(), + "Cache-Control": v.string(), + Pragma: v.string(), + "API-Version": v.string(), + "Builder-Version": v.string(), + }), }); export type post_ImageCommit = v.InferOutput; @@ -3540,6 +3573,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -3554,6 +3588,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/docker.openapi.yup.ts b/packages/typed-openapi/tests/snapshots/docker.openapi.yup.ts index 734dc6a..0b49250 100644 --- a/packages/typed-openapi/tests/snapshots/docker.openapi.yup.ts +++ b/packages/typed-openapi/tests/snapshots/docker.openapi.yup.ts @@ -2474,6 +2474,9 @@ export const head_ContainerArchiveInfo = { }), }), response: y.mixed((value): value is any => true).required() as y.MixedSchema, + responseHeaders: y.object({ + "X-Docker-Container-Path-Stat": y.string().required(), + }), }; export type post_ContainerPrune = typeof post_ContainerPrune; @@ -2770,6 +2773,14 @@ export const get_SystemPing = { requestFormat: y.mixed((value): value is "json" => value === "json").required(), parameters: y.mixed((value): value is never => false).required(), response: y.mixed((value): value is any => true).required() as y.MixedSchema, + responseHeaders: y.object({ + Swarm: y.mixed().oneOf(["inactive", "pending", "error", "locked", "active/worker", "active/manager"]).required(), + "Docker-Experimental": y.boolean().required(), + "Cache-Control": y.string().required(), + Pragma: y.string().required(), + "API-Version": y.string().required(), + "Builder-Version": y.string().required(), + }), }; export type head_SystemPingHead = typeof head_SystemPingHead; @@ -2779,6 +2790,14 @@ export const head_SystemPingHead = { requestFormat: y.mixed((value): value is "json" => value === "json").required(), parameters: y.mixed((value): value is never => false).required(), response: y.mixed((value): value is any => true).required() as y.MixedSchema, + responseHeaders: y.object({ + Swarm: y.mixed().oneOf(["inactive", "pending", "error", "locked", "active/worker", "active/manager"]).required(), + "Docker-Experimental": y.boolean().required(), + "Cache-Control": y.string().required(), + Pragma: y.string().required(), + "API-Version": y.string().required(), + "Builder-Version": y.string().required(), + }), }; export type post_ImageCommit = typeof post_ImageCommit; @@ -4060,6 +4079,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -4074,6 +4094,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/docker.openapi.zod.ts b/packages/typed-openapi/tests/snapshots/docker.openapi.zod.ts index a8fe385..c73966b 100644 --- a/packages/typed-openapi/tests/snapshots/docker.openapi.zod.ts +++ b/packages/typed-openapi/tests/snapshots/docker.openapi.zod.ts @@ -2071,6 +2071,9 @@ export const head_ContainerArchiveInfo = { }), }), response: z.unknown(), + responseHeaders: z.object({ + "X-Docker-Container-Path-Stat": z.string(), + }), }; export type post_ContainerPrune = typeof post_ContainerPrune; @@ -2350,6 +2353,21 @@ export const get_SystemPing = { requestFormat: z.literal("json"), parameters: z.never(), response: z.unknown(), + responseHeaders: z.object({ + Swarm: z.union([ + z.literal("inactive"), + z.literal("pending"), + z.literal("error"), + z.literal("locked"), + z.literal("active/worker"), + z.literal("active/manager"), + ]), + "Docker-Experimental": z.boolean(), + "Cache-Control": z.string(), + Pragma: z.string(), + "API-Version": z.string(), + "Builder-Version": z.string(), + }), }; export type head_SystemPingHead = typeof head_SystemPingHead; @@ -2359,6 +2377,21 @@ export const head_SystemPingHead = { requestFormat: z.literal("json"), parameters: z.never(), response: z.unknown(), + responseHeaders: z.object({ + Swarm: z.union([ + z.literal("inactive"), + z.literal("pending"), + z.literal("error"), + z.literal("locked"), + z.literal("active/worker"), + z.literal("active/manager"), + ]), + "Docker-Experimental": z.boolean(), + "Cache-Control": z.string(), + Pragma: z.string(), + "API-Version": z.string(), + "Builder-Version": z.string(), + }), }; export type post_ImageCommit = typeof post_ImageCommit; @@ -3526,6 +3559,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -3540,6 +3574,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/long-operation-id.arktype.ts b/packages/typed-openapi/tests/snapshots/long-operation-id.arktype.ts index 77db548..368d8ce 100644 --- a/packages/typed-openapi/tests/snapshots/long-operation-id.arktype.ts +++ b/packages/typed-openapi/tests/snapshots/long-operation-id.arktype.ts @@ -67,6 +67,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -81,6 +82,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/long-operation-id.client.ts b/packages/typed-openapi/tests/snapshots/long-operation-id.client.ts index 4defa44..b9e82ed 100644 --- a/packages/typed-openapi/tests/snapshots/long-operation-id.client.ts +++ b/packages/typed-openapi/tests/snapshots/long-operation-id.client.ts @@ -59,6 +59,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -73,6 +74,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/long-operation-id.io-ts.ts b/packages/typed-openapi/tests/snapshots/long-operation-id.io-ts.ts index 4af0848..12dd39b 100644 --- a/packages/typed-openapi/tests/snapshots/long-operation-id.io-ts.ts +++ b/packages/typed-openapi/tests/snapshots/long-operation-id.io-ts.ts @@ -63,6 +63,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -77,6 +78,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/long-operation-id.typebox.ts b/packages/typed-openapi/tests/snapshots/long-operation-id.typebox.ts index 6767fdb..0f1a2f4 100644 --- a/packages/typed-openapi/tests/snapshots/long-operation-id.typebox.ts +++ b/packages/typed-openapi/tests/snapshots/long-operation-id.typebox.ts @@ -65,6 +65,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -79,6 +80,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/long-operation-id.valibot.ts b/packages/typed-openapi/tests/snapshots/long-operation-id.valibot.ts index 471f7c3..808676c 100644 --- a/packages/typed-openapi/tests/snapshots/long-operation-id.valibot.ts +++ b/packages/typed-openapi/tests/snapshots/long-operation-id.valibot.ts @@ -63,6 +63,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -77,6 +78,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/long-operation-id.yup.ts b/packages/typed-openapi/tests/snapshots/long-operation-id.yup.ts index 0d4a2c8..246555d 100644 --- a/packages/typed-openapi/tests/snapshots/long-operation-id.yup.ts +++ b/packages/typed-openapi/tests/snapshots/long-operation-id.yup.ts @@ -56,6 +56,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -70,6 +71,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/long-operation-id.zod.ts b/packages/typed-openapi/tests/snapshots/long-operation-id.zod.ts index 5751251..9addd68 100644 --- a/packages/typed-openapi/tests/snapshots/long-operation-id.zod.ts +++ b/packages/typed-openapi/tests/snapshots/long-operation-id.zod.ts @@ -56,6 +56,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -70,6 +71,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/petstore.arktype.ts b/packages/typed-openapi/tests/snapshots/petstore.arktype.ts index 5b75b3b..f240df4 100644 --- a/packages/typed-openapi/tests/snapshots/petstore.arktype.ts +++ b/packages/typed-openapi/tests/snapshots/petstore.arktype.ts @@ -214,6 +214,10 @@ export const types = scope({ }), }), response: "string", + responseHeaders: type({ + "X-Rate-Limit": "number", + "X-Expires-After": "string", + }), }), get_LogoutUser: type({ method: '"GET"', @@ -374,6 +378,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -388,6 +393,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/petstore.client.ts b/packages/typed-openapi/tests/snapshots/petstore.client.ts index 2b0c5f1..ceade18 100644 --- a/packages/typed-openapi/tests/snapshots/petstore.client.ts +++ b/packages/typed-openapi/tests/snapshots/petstore.client.ts @@ -175,6 +175,7 @@ export namespace Endpoints { query: Partial<{ username: string; password: string }>; }; response: string; + responseHeaders: { "X-Rate-Limit": number; "X-Expires-After": string }; }; export type get_LogoutUser = { method: "GET"; @@ -272,6 +273,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -286,6 +288,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/petstore.io-ts.ts b/packages/typed-openapi/tests/snapshots/petstore.io-ts.ts index 649c432..78c4d78 100644 --- a/packages/typed-openapi/tests/snapshots/petstore.io-ts.ts +++ b/packages/typed-openapi/tests/snapshots/petstore.io-ts.ts @@ -263,6 +263,10 @@ export const get_LoginUser = t.type({ }), }), response: t.string, + responseHeaders: t.type({ + "X-Rate-Limit": t.number, + "X-Expires-After": t.string, + }), }); export type get_LogoutUser = t.TypeOf; @@ -373,6 +377,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -387,6 +392,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/petstore.typebox.ts b/packages/typed-openapi/tests/snapshots/petstore.typebox.ts index 2d19f28..390de60 100644 --- a/packages/typed-openapi/tests/snapshots/petstore.typebox.ts +++ b/packages/typed-openapi/tests/snapshots/petstore.typebox.ts @@ -291,6 +291,10 @@ export const get_LoginUser = Type.Object({ ), }), response: Type.String(), + responseHeaders: Type.Object({ + "X-Rate-Limit": Type.Number(), + "X-Expires-After": Type.String(), + }), }); export type get_LogoutUser = Static; @@ -401,6 +405,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -415,6 +420,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/petstore.valibot.ts b/packages/typed-openapi/tests/snapshots/petstore.valibot.ts index aa10a96..5e2bfe6 100644 --- a/packages/typed-openapi/tests/snapshots/petstore.valibot.ts +++ b/packages/typed-openapi/tests/snapshots/petstore.valibot.ts @@ -262,6 +262,10 @@ export const get_LoginUser = v.object({ }), }), response: v.string(), + responseHeaders: v.object({ + "X-Rate-Limit": v.number(), + "X-Expires-After": v.string(), + }), }); export type get_LogoutUser = v.InferOutput; @@ -372,6 +376,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -386,6 +391,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/petstore.yup.ts b/packages/typed-openapi/tests/snapshots/petstore.yup.ts index fb190b4..5c29ecf 100644 --- a/packages/typed-openapi/tests/snapshots/petstore.yup.ts +++ b/packages/typed-openapi/tests/snapshots/petstore.yup.ts @@ -276,6 +276,10 @@ export const get_LoginUser = { }), }), response: y.string().required(), + responseHeaders: y.object({ + "X-Rate-Limit": y.number().required(), + "X-Expires-After": y.string().required(), + }), }; export type get_LogoutUser = typeof get_LogoutUser; @@ -383,6 +387,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -397,6 +402,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise; diff --git a/packages/typed-openapi/tests/snapshots/petstore.zod.ts b/packages/typed-openapi/tests/snapshots/petstore.zod.ts index b7961e7..7b93ade 100644 --- a/packages/typed-openapi/tests/snapshots/petstore.zod.ts +++ b/packages/typed-openapi/tests/snapshots/petstore.zod.ts @@ -259,6 +259,10 @@ export const get_LoginUser = { }), }), response: z.string(), + responseHeaders: z.object({ + "X-Rate-Limit": z.number(), + "X-Expires-After": z.string(), + }), }; export type get_LogoutUser = typeof get_LogoutUser; @@ -366,6 +370,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text"; export type DefaultEndpoint = { parameters?: EndpointParameters | undefined; response: unknown; + responseHeaders?: Record; }; export type Endpoint = { @@ -380,6 +385,7 @@ export type Endpoint = { areParametersRequired: boolean; }; response: TConfig["response"]; + responseHeaders?: TConfig["responseHeaders"]; }; export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise;