typed-openapi 1.4.5 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -104,15 +104,17 @@ var openApiSchemaToTs = ({ schema, meta: _inheritedMeta, ctx }) => {
104
104
  if (schemaType === "null") return t.literal("null");
105
105
  }
106
106
  if (!schemaType && schema.enum) {
107
- return t.union(schema.enum.map((value) => {
108
- if (typeof value === "string") {
109
- return t.literal(`"${value}"`);
110
- }
111
- if (value === null) {
112
- return t.literal("null");
113
- }
114
- return t.literal(value);
115
- }));
107
+ return t.union(
108
+ schema.enum.map((value) => {
109
+ if (typeof value === "string") {
110
+ return t.literal(`"${value}"`);
111
+ }
112
+ if (value === null) {
113
+ return t.literal("null");
114
+ }
115
+ return t.literal(value);
116
+ })
117
+ );
116
118
  }
117
119
  if (schemaType === "array") {
118
120
  if (schema.items) {
@@ -360,6 +362,20 @@ var parameterObjectToString = (parameters) => {
360
362
  }
361
363
  return str + "}";
362
364
  };
365
+ var responseHeadersObjectToString = (responseHeaders, ctx) => {
366
+ let str = "{";
367
+ for (const [key, responseHeader] of Object.entries(responseHeaders)) {
368
+ const value = ctx.runtime === "none" ? responseHeader.recompute((box) => {
369
+ if (Box.isReference(box) && !box.params.generics && box.value !== "null") {
370
+ box.value = `Schemas.${box.value}`;
371
+ }
372
+ return box;
373
+ }).value : responseHeader.value;
374
+ str += `${wrapWithQuotesIfNeeded(key.toLowerCase())}: ${value},
375
+ `;
376
+ }
377
+ return str + "}";
378
+ };
363
379
  var generateEndpointSchemaList = (ctx) => {
364
380
  let file = `
365
381
  ${ctx.runtime === "none" ? "export namespace Endpoints {" : ""}
@@ -391,6 +407,7 @@ var generateEndpointSchemaList = (ctx) => {
391
407
  }
392
408
  return box;
393
409
  }).value : endpoint.response.value},
410
+ ${endpoint.responseHeaders ? `responseHeaders: ${responseHeadersObjectToString(endpoint.responseHeaders, ctx)},` : ""}
394
411
  }
395
412
  `;
396
413
  });
@@ -446,6 +463,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text";
446
463
  export type DefaultEndpoint = {
447
464
  parameters?: EndpointParameters | undefined;
448
465
  response: unknown;
466
+ responseHeaders?: Record<string, unknown>;
449
467
  };
450
468
 
451
469
  export type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
@@ -460,6 +478,7 @@ export type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
460
478
  areParametersRequired: boolean;
461
479
  };
462
480
  response: TConfig["response"];
481
+ responseHeaders?: TConfig["responseHeaders"]
463
482
  };
464
483
 
465
484
  export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise<Response>;
@@ -852,6 +871,17 @@ var mapOpenApiEndpoints = (doc) => {
852
871
  });
853
872
  }
854
873
  }
874
+ const headers = responseObject?.headers;
875
+ if (headers) {
876
+ endpoint.responseHeaders = Object.entries(headers).reduce(
877
+ (acc, [name, headerOrRef]) => {
878
+ const header = refs.unwrap(headerOrRef);
879
+ acc[name] = openApiSchemaToTs({ schema: header.schema ?? {}, ctx });
880
+ return acc;
881
+ },
882
+ {}
883
+ );
884
+ }
855
885
  endpointList.push(endpoint);
856
886
  });
857
887
  });
@@ -4,7 +4,7 @@ import {
4
4
  generateTanstackQueryFile,
5
5
  mapOpenApiEndpoints,
6
6
  prettify
7
- } from "./chunk-52X6V4N5.js";
7
+ } from "./chunk-N6BWPZUB.js";
8
8
 
9
9
  // src/generate-client-files.ts
10
10
  import SwaggerParser from "@apidevtools/swagger-parser";
@@ -30,11 +30,13 @@ async function generateClientFiles(input, options) {
30
30
  const openApiDoc = await SwaggerParser.bundle(input);
31
31
  const ctx = mapOpenApiEndpoints(openApiDoc);
32
32
  console.log(`Found ${ctx.endpointList.length} endpoints`);
33
- const content = await prettify(generateFile({
34
- ...ctx,
35
- runtime: options.runtime,
36
- schemasOnly: options.schemasOnly
37
- }));
33
+ const content = await prettify(
34
+ generateFile({
35
+ ...ctx,
36
+ runtime: options.runtime,
37
+ schemasOnly: options.schemasOnly
38
+ })
39
+ );
38
40
  const outputPath = join(
39
41
  cwd,
40
42
  options.output ?? input + `.${options.runtime === "none" ? "client" : options.runtime}.ts`
@@ -47,7 +49,10 @@ async function generateClientFiles(input, options) {
47
49
  ...ctx,
48
50
  relativeApiClientPath: "./" + basename(outputPath)
49
51
  });
50
- const tanstackOutputPath = join(dirname(outputPath), typeof options.tanstack === "string" ? options.tanstack : `tanstack.client.ts`);
52
+ const tanstackOutputPath = join(
53
+ dirname(outputPath),
54
+ typeof options.tanstack === "string" ? options.tanstack : `tanstack.client.ts`
55
+ );
51
56
  console.log("Generating tanstack client...", tanstackOutputPath);
52
57
  await ensureDir(dirname(tanstackOutputPath));
53
58
  await writeFile(tanstackOutputPath, tanstackContent);
package/dist/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  generateClientFiles
3
- } from "./chunk-F4UA5MLD.js";
3
+ } from "./chunk-RGFFCU3R.js";
4
4
  import {
5
5
  allowedRuntimes
6
- } from "./chunk-52X6V4N5.js";
6
+ } from "./chunk-N6BWPZUB.js";
7
7
 
8
8
  // src/cli.ts
9
9
  import { cac } from "cac";
@@ -14,11 +14,7 @@ cli.command("<input>", "Generate").option("-o, --output <path>", "Output path fo
14
14
  "-r, --runtime <name>",
15
15
  `Runtime to use for validation; defaults to \`none\`; available: ${allowedRuntimes.toString()}`,
16
16
  { default: "none" }
17
- ).option(
18
- "--schemas-only",
19
- "Only generate schemas, skipping client generation (defaults to false)",
20
- { default: false }
21
- ).option(
17
+ ).option("--schemas-only", "Only generate schemas, skipping client generation (defaults to false)", { default: false }).option(
22
18
  "--tanstack [name]",
23
19
  "Generate tanstack client, defaults to false, can optionally specify a name for the generated file"
24
20
  ).action(async (input, _options) => {
package/dist/index.d.ts CHANGED
@@ -219,6 +219,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text";
219
219
  type DefaultEndpoint = {
220
220
  parameters?: EndpointParameters | undefined;
221
221
  response: AnyBox;
222
+ responseHeaders?: Record<string, AnyBox>;
222
223
  };
223
224
  type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
224
225
  operation: OperationObject;
@@ -232,6 +233,7 @@ type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
232
233
  areParametersRequired: boolean;
233
234
  };
234
235
  response: TConfig["response"];
236
+ responseHeaders?: TConfig["responseHeaders"];
235
237
  };
236
238
 
237
239
  type GeneratorOptions$1 = ReturnType<typeof mapOpenApiEndpoints> & {
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  openApiSchemaToTs,
9
9
  tsFactory,
10
10
  unwrap
11
- } from "./chunk-52X6V4N5.js";
11
+ } from "./chunk-N6BWPZUB.js";
12
12
  export {
13
13
  createBoxFactory,
14
14
  createFactory,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  generateClientFiles
3
- } from "./chunk-F4UA5MLD.js";
3
+ } from "./chunk-RGFFCU3R.js";
4
4
  import {
5
5
  prettify
6
- } from "./chunk-52X6V4N5.js";
6
+ } from "./chunk-N6BWPZUB.js";
7
7
  export {
8
8
  generateClientFiles,
9
9
  prettify
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "typed-openapi",
3
3
  "type": "module",
4
- "version": "1.4.5",
4
+ "version": "1.5.0",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
7
7
  "exports": {
package/src/cli.ts CHANGED
@@ -15,11 +15,7 @@ cli
15
15
  `Runtime to use for validation; defaults to \`none\`; available: ${allowedRuntimes.toString()}`,
16
16
  { default: "none" },
17
17
  )
18
- .option(
19
- "--schemas-only",
20
- "Only generate schemas, skipping client generation (defaults to false)",
21
- { default: false },
22
- )
18
+ .option("--schemas-only", "Only generate schemas, skipping client generation (defaults to false)", { default: false })
23
19
  .option(
24
20
  "--tanstack [name]",
25
21
  "Generate tanstack client, defaults to false, can optionally specify a name for the generated file",
@@ -32,11 +32,13 @@ export async function generateClientFiles(input: string, options: typeof options
32
32
  const ctx = mapOpenApiEndpoints(openApiDoc);
33
33
  console.log(`Found ${ctx.endpointList.length} endpoints`);
34
34
 
35
- const content = await prettify(generateFile({
36
- ...ctx,
37
- runtime: options.runtime,
38
- schemasOnly: options.schemasOnly,
39
- }));
35
+ const content = await prettify(
36
+ generateFile({
37
+ ...ctx,
38
+ runtime: options.runtime,
39
+ schemasOnly: options.schemasOnly,
40
+ }),
41
+ );
40
42
  const outputPath = join(
41
43
  cwd,
42
44
  options.output ?? input + `.${options.runtime === "none" ? "client" : options.runtime}.ts`,
@@ -49,9 +51,12 @@ export async function generateClientFiles(input: string, options: typeof options
49
51
  if (options.tanstack) {
50
52
  const tanstackContent = await generateTanstackQueryFile({
51
53
  ...ctx,
52
- relativeApiClientPath: './' + basename(outputPath),
54
+ relativeApiClientPath: "./" + basename(outputPath),
53
55
  });
54
- const tanstackOutputPath = join(dirname(outputPath), typeof options.tanstack === "string" ? options.tanstack : `tanstack.client.ts`);
56
+ const tanstackOutputPath = join(
57
+ dirname(outputPath),
58
+ typeof options.tanstack === "string" ? options.tanstack : `tanstack.client.ts`,
59
+ );
55
60
  console.log("Generating tanstack client...", tanstackOutputPath);
56
61
  await ensureDir(dirname(tanstackOutputPath));
57
62
  await writeFile(tanstackOutputPath, tanstackContent);
package/src/generator.ts CHANGED
@@ -132,6 +132,25 @@ const parameterObjectToString = (parameters: Box<AnyBoxDef> | Record<string, Any
132
132
  }
133
133
  return str + "}";
134
134
  };
135
+
136
+ const responseHeadersObjectToString = (responseHeaders: Record<string, AnyBox>, ctx: GeneratorContext) => {
137
+ let str = "{";
138
+ for (const [key, responseHeader] of Object.entries(responseHeaders)) {
139
+ const value =
140
+ ctx.runtime === "none"
141
+ ? responseHeader.recompute((box) => {
142
+ if (Box.isReference(box) && !box.params.generics && box.value !== "null") {
143
+ box.value = `Schemas.${box.value}`;
144
+ }
145
+
146
+ return box;
147
+ }).value
148
+ : responseHeader.value;
149
+ str += `${wrapWithQuotesIfNeeded(key.toLowerCase())}: ${value},\n`;
150
+ }
151
+ return str + "}";
152
+ };
153
+
135
154
  const generateEndpointSchemaList = (ctx: GeneratorContext) => {
136
155
  let file = `
137
156
  ${ctx.runtime === "none" ? "export namespace Endpoints {" : ""}
@@ -178,6 +197,11 @@ const generateEndpointSchemaList = (ctx: GeneratorContext) => {
178
197
  }).value
179
198
  : endpoint.response.value
180
199
  },
200
+ ${
201
+ endpoint.responseHeaders
202
+ ? `responseHeaders: ${responseHeadersObjectToString(endpoint.responseHeaders, ctx)},`
203
+ : ""
204
+ }
181
205
  }\n`;
182
206
  });
183
207
 
@@ -246,6 +270,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text";
246
270
  export type DefaultEndpoint = {
247
271
  parameters?: EndpointParameters | undefined;
248
272
  response: unknown;
273
+ responseHeaders?: Record<string, unknown>;
249
274
  };
250
275
 
251
276
  export type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
@@ -260,6 +285,7 @@ export type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
260
285
  areParametersRequired: boolean;
261
286
  };
262
287
  response: TConfig["response"];
288
+ responseHeaders?: TConfig["responseHeaders"]
263
289
  };
264
290
 
265
291
  export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise<Response>;
@@ -145,6 +145,19 @@ export const mapOpenApiEndpoints = (doc: OpenAPIObject) => {
145
145
  }
146
146
  }
147
147
 
148
+ // Map response headers
149
+ const headers = responseObject?.headers;
150
+ if (headers) {
151
+ endpoint.responseHeaders = Object.entries(headers).reduce(
152
+ (acc, [name, headerOrRef]) => {
153
+ const header = refs.unwrap(headerOrRef);
154
+ acc[name] = openApiSchemaToTs({ schema: header.schema ?? {}, ctx });
155
+ return acc;
156
+ },
157
+ {} as Record<string, Box>,
158
+ );
159
+ }
160
+
148
161
  endpointList.push(endpoint);
149
162
  });
150
163
  });
@@ -184,6 +197,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text";
184
197
  type DefaultEndpoint = {
185
198
  parameters?: EndpointParameters | undefined;
186
199
  response: AnyBox;
200
+ responseHeaders?: Record<string, AnyBox>;
187
201
  };
188
202
 
189
203
  export type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
@@ -198,4 +212,5 @@ export type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
198
212
  areParametersRequired: boolean;
199
213
  };
200
214
  response: TConfig["response"];
215
+ responseHeaders?: TConfig["responseHeaders"];
201
216
  };
@@ -53,9 +53,9 @@ export const openApiSchemaToTs = ({ schema, meta: _inheritedMeta, ctx }: Openapi
53
53
 
54
54
  if (schema.allOf) {
55
55
  const types = schema.allOf.map((prop) => openApiSchemaToTs({ schema: prop, ctx, meta }));
56
- const {allOf, externalDocs, example, examples, description, title, ...rest} = schema
56
+ const { allOf, externalDocs, example, examples, description, title, ...rest } = schema;
57
57
  if (Object.keys(rest).length > 0) {
58
- types.push(openApiSchemaToTs({schema: rest, ctx, meta}))
58
+ types.push(openApiSchemaToTs({ schema: rest, ctx, meta }));
59
59
  }
60
60
  return t.intersection(types);
61
61
  }
@@ -72,7 +72,7 @@ export const openApiSchemaToTs = ({ schema, meta: _inheritedMeta, ctx }: Openapi
72
72
  } else if (value === false) {
73
73
  return t.literal("false");
74
74
  } else if (typeof value === "number") {
75
- return t.literal(`${value}`)
75
+ return t.literal(`${value}`);
76
76
  } else {
77
77
  return t.literal(`"${value}"`);
78
78
  }
@@ -95,16 +95,18 @@ export const openApiSchemaToTs = ({ schema, meta: _inheritedMeta, ctx }: Openapi
95
95
  if (schemaType === "null") return t.literal("null");
96
96
  }
97
97
  if (!schemaType && schema.enum) {
98
- return t.union(schema.enum.map((value) => {
99
- if (typeof value === "string") {
100
- return t.literal(`"${value}"`)
101
- }
102
- if (value === null) {
103
- return t.literal("null")
104
- }
105
- // handle boolean and number literals
106
- return t.literal(value)
107
- }));
98
+ return t.union(
99
+ schema.enum.map((value) => {
100
+ if (typeof value === "string") {
101
+ return t.literal(`"${value}"`);
102
+ }
103
+ if (value === null) {
104
+ return t.literal("null");
105
+ }
106
+ // handle boolean and number literals
107
+ return t.literal(value);
108
+ }),
109
+ );
108
110
  }
109
111
 
110
112
  if (schemaType === "array") {