@specbook/codegen 0.10.0 → 0.11.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specbook/codegen",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Turns an OpenAPI or Swagger document into types, examples, schemas and metadata, in Node or in a browser.",
5
5
  "keywords": [
6
6
  "specbook",
@@ -44,7 +44,7 @@
44
44
  "swagger2openapi": "7.0.8"
45
45
  },
46
46
  "devDependencies": {
47
- "@specbook/core": "0.10.0",
47
+ "@specbook/core": "0.11.0",
48
48
  "@types/node": "24.19.2",
49
49
  "@types/swagger2openapi": "7.0.4",
50
50
  "typescript": "7.0.2"
package/src/browser.ts CHANGED
@@ -15,6 +15,7 @@ export type {
15
15
  ManifestOperation,
16
16
  ManifestSecurityScheme,
17
17
  OperationMeta,
18
+ ResponseDoc,
18
19
  } from './build'
19
20
  export { generateFromDoc, logoOf } from './build'
20
21
 
package/src/build.ts CHANGED
@@ -73,6 +73,13 @@ interface ExampleDoc {
73
73
  body?: unknown
74
74
  }
75
75
 
76
+ /** What `docs/<id>.json` says about one response, for the Docs tab. */
77
+ export interface ResponseDoc {
78
+ description?: string
79
+ /** the media type the body is written in, as the spec names it */
80
+ contentType?: string
81
+ }
82
+
76
83
  export interface ManifestOperation {
77
84
  id: string
78
85
  method: string
@@ -174,6 +181,7 @@ function findJsonLike(content: Record<string, any> | undefined) {
174
181
  schema: content[mime].schema as Schema | undefined,
175
182
  encoding: enc,
176
183
  media: content[mime],
184
+ mime,
177
185
  }
178
186
  }
179
187
  const json = Object.keys(content).find((m) => /json/.test(m))
@@ -182,6 +190,7 @@ function findJsonLike(content: Record<string, any> | undefined) {
182
190
  schema: content[json].schema as Schema | undefined,
183
191
  encoding: 'json',
184
192
  media: content[json],
193
+ mime: json,
185
194
  }
186
195
  const binary = Object.keys(content).find((m) => BINARY_MIME.test(m))
187
196
  if (binary)
@@ -189,6 +198,7 @@ function findJsonLike(content: Record<string, any> | undefined) {
189
198
  schema: content[binary].schema as Schema | undefined,
190
199
  encoding: 'binary',
191
200
  media: content[binary],
201
+ mime: binary,
192
202
  mediaType: binary,
193
203
  }
194
204
  // A wildcard stands for whatever the server sends, and a framework that is not told what it produces
@@ -201,6 +211,7 @@ function findJsonLike(content: Record<string, any> | undefined) {
201
211
  schema,
202
212
  encoding: file ? 'binary' : 'json',
203
213
  media: content[any],
214
+ mime: any,
204
215
  ...(file ? { mediaType: any } : {}),
205
216
  }
206
217
  }
@@ -544,6 +555,18 @@ function generateAll(
544
555
  let defaultSchema: Schema | undefined
545
556
  const responseSchemas: Record<string, Schema> = {}
546
557
  const mockVariants: NonNullable<ManifestOperation['mockVariants']> = {}
558
+ const responseDocs: Record<string, ResponseDoc> = {}
559
+ let defaultDoc: ResponseDoc | undefined
560
+ const docOf = (res: any, found: { mime?: string } | undefined): ResponseDoc | undefined => {
561
+ const description =
562
+ typeof res?.description === 'string' && res.description.trim()
563
+ ? res.description.trim()
564
+ : undefined
565
+ const contentType = found?.mime ?? Object.keys(res?.content ?? {})[0]
566
+ return description || contentType
567
+ ? { ...(description ? { description } : {}), ...(contentType ? { contentType } : {}) }
568
+ : undefined
569
+ }
547
570
  let defaultVariants: ManifestOperation['mockVariants'] extends infer M
548
571
  ? M extends Record<string, infer V>
549
572
  ? V | undefined
@@ -551,7 +574,9 @@ function generateAll(
551
574
  : never
552
575
  for (const [code, rawRes] of Object.entries<any>(op.responses ?? {})) {
553
576
  if (code === 'default') {
554
- const found = findJsonLike(resolveAny<any>(rawRes)?.content)
577
+ const defaultRes = resolveAny<any>(rawRes)
578
+ const found = findJsonLike(defaultRes?.content)
579
+ defaultDoc = docOf(defaultRes, found)
555
580
  if (found?.schema) {
556
581
  hasDefault = true
557
582
  if (ts) defaultType = tsType(found.schema, ' ', 'T.')
@@ -569,6 +594,8 @@ function generateAll(
569
594
  const res = resolveAny<any>(rawRes)
570
595
  const found = findJsonLike(res?.content)
571
596
  statuses.push(Number(code))
597
+ const responseDoc = docOf(res, found)
598
+ if (responseDoc) responseDocs[code] = responseDoc
572
599
  if (found?.schema) responseSchemas[code] = toJsonSchema(found.schema, `${defsId}#/$defs/`)
573
600
  if (ts)
574
601
  responseTypes.push(
@@ -587,6 +614,7 @@ function generateAll(
587
614
  for (const st of DEFAULT_ERROR_STATUSES) {
588
615
  if (statuses.includes(st)) continue
589
616
  statuses.push(st)
617
+ if (defaultDoc) responseDocs[String(st)] = defaultDoc
590
618
  if (defaultSchema) responseSchemas[String(st)] = defaultSchema
591
619
  if (defaultVariants) mockVariants[String(st)] = defaultVariants
592
620
  if (defaultType) responseTypes.push(`${st}: ${defaultType}`)
@@ -746,6 +774,7 @@ ${jsdoc({ description: [...new Set([op.summary, op.description].filter(Boolean))
746
774
  content: `${JSON.stringify({
747
775
  ...(op.description && op.description !== summary ? { description: op.description } : {}),
748
776
  examples: docs.map((d) => ({ name: d.name, title: d.title, source: d.source })),
777
+ ...(Object.keys(responseDocs).length ? { responses: responseDocs } : {}),
749
778
  })}\n`,
750
779
  })
751
780
  operations.push({