typed-openapi 1.4.4 → 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.
@@ -66,10 +66,11 @@ var openApiSchemaToTs = ({ schema, meta: _inheritedMeta, ctx }) => {
66
66
  return t.union(schema.anyOf.map((prop) => openApiSchemaToTs({ schema: prop, ctx, meta })));
67
67
  }
68
68
  if (schema.allOf) {
69
- if (schema.allOf.length === 1) {
70
- return openApiSchemaToTs({ schema: schema.allOf[0], ctx, meta });
71
- }
72
69
  const types = schema.allOf.map((prop) => openApiSchemaToTs({ schema: prop, ctx, meta }));
70
+ const { allOf, externalDocs, example, examples, description, title, ...rest } = schema;
71
+ if (Object.keys(rest).length > 0) {
72
+ types.push(openApiSchemaToTs({ schema: rest, ctx, meta }));
73
+ }
73
74
  return t.intersection(types);
74
75
  }
75
76
  const schemaType = schema.type ? schema.type.toLowerCase() : void 0;
@@ -103,15 +104,17 @@ var openApiSchemaToTs = ({ schema, meta: _inheritedMeta, ctx }) => {
103
104
  if (schemaType === "null") return t.literal("null");
104
105
  }
105
106
  if (!schemaType && schema.enum) {
106
- return t.union(schema.enum.map((value) => {
107
- if (typeof value === "string") {
108
- return t.literal(`"${value}"`);
109
- }
110
- if (value === null) {
111
- return t.literal("null");
112
- }
113
- return t.literal(value);
114
- }));
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
+ );
115
118
  }
116
119
  if (schemaType === "array") {
117
120
  if (schema.items) {
@@ -359,6 +362,20 @@ var parameterObjectToString = (parameters) => {
359
362
  }
360
363
  return str + "}";
361
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
+ };
362
379
  var generateEndpointSchemaList = (ctx) => {
363
380
  let file = `
364
381
  ${ctx.runtime === "none" ? "export namespace Endpoints {" : ""}
@@ -390,6 +407,7 @@ var generateEndpointSchemaList = (ctx) => {
390
407
  }
391
408
  return box;
392
409
  }).value : endpoint.response.value},
410
+ ${endpoint.responseHeaders ? `responseHeaders: ${responseHeadersObjectToString(endpoint.responseHeaders, ctx)},` : ""}
393
411
  }
394
412
  `;
395
413
  });
@@ -445,6 +463,7 @@ type RequestFormat = "json" | "form-data" | "form-url" | "binary" | "text";
445
463
  export type DefaultEndpoint = {
446
464
  parameters?: EndpointParameters | undefined;
447
465
  response: unknown;
466
+ responseHeaders?: Record<string, unknown>;
448
467
  };
449
468
 
450
469
  export type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
@@ -459,6 +478,7 @@ export type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
459
478
  areParametersRequired: boolean;
460
479
  };
461
480
  response: TConfig["response"];
481
+ responseHeaders?: TConfig["responseHeaders"]
462
482
  };
463
483
 
464
484
  export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise<Response>;
@@ -851,6 +871,17 @@ var mapOpenApiEndpoints = (doc) => {
851
871
  });
852
872
  }
853
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
+ }
854
885
  endpointList.push(endpoint);
855
886
  });
856
887
  });
@@ -4,7 +4,7 @@ import {
4
4
  generateTanstackQueryFile,
5
5
  mapOpenApiEndpoints,
6
6
  prettify
7
- } from "./chunk-SWKVSGL4.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-2KFUYPFA.js";
3
+ } from "./chunk-RGFFCU3R.js";
4
4
  import {
5
5
  allowedRuntimes
6
- } from "./chunk-SWKVSGL4.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-SWKVSGL4.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-2KFUYPFA.js";
3
+ } from "./chunk-RGFFCU3R.js";
4
4
  import {
5
5
  prettify
6
- } from "./chunk-SWKVSGL4.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.4",
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
  };
@@ -52,11 +52,11 @@ export const openApiSchemaToTs = ({ schema, meta: _inheritedMeta, ctx }: Openapi
52
52
  }
53
53
 
54
54
  if (schema.allOf) {
55
- if (schema.allOf.length === 1) {
56
- return openApiSchemaToTs({ schema: schema.allOf[0]!, ctx, meta });
57
- }
58
-
59
55
  const types = schema.allOf.map((prop) => openApiSchemaToTs({ schema: prop, ctx, meta }));
56
+ const { allOf, externalDocs, example, examples, description, title, ...rest } = schema;
57
+ if (Object.keys(rest).length > 0) {
58
+ types.push(openApiSchemaToTs({ schema: rest, ctx, meta }));
59
+ }
60
60
  return t.intersection(types);
61
61
  }
62
62
 
@@ -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") {