@specbook/codegen 0.9.0 → 0.10.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.9.0",
3
+ "version": "0.10.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.9.0",
47
+ "@specbook/core": "0.10.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
@@ -13,6 +13,7 @@ export type {
13
13
  GenerateFlags,
14
14
  GenerateOptions,
15
15
  ManifestOperation,
16
+ ManifestSecurityScheme,
16
17
  OperationMeta,
17
18
  } from './build'
18
19
  export { generateFromDoc, logoOf } from './build'
package/src/build.ts CHANGED
@@ -89,9 +89,55 @@ export interface ManifestOperation {
89
89
  examples: ManifestExample[]
90
90
  /** the spec requires credentials for this operation */
91
91
  secured?: boolean
92
+ /** any one of these is enough; every scheme in one entry must apply together, with the scopes it needs */
93
+ security?: Record<string, string[]>[]
92
94
  file: string
93
95
  }
94
96
 
97
+ /** What a security scheme is, without addresses or descriptions: enough to say how an operation is secured. */
98
+ export interface ManifestSecurityScheme {
99
+ type: string
100
+ in?: string
101
+ name?: string
102
+ scheme?: string
103
+ openIdConnectUrl?: string
104
+ /** flow name -> its scope names */
105
+ flows?: Record<string, { scopes: string[] }>
106
+ }
107
+
108
+ /** The document's security schemes in the manifest's compact form. */
109
+ export function securitySchemesOf(doc: any): Record<string, ManifestSecurityScheme> {
110
+ const out: Record<string, ManifestSecurityScheme> = {}
111
+ for (const [name, s] of Object.entries<any>(doc.components?.securitySchemes ?? {})) {
112
+ if (!s || typeof s.type !== 'string') continue
113
+ const flows = Object.fromEntries(
114
+ Object.entries<any>(s.flows ?? {}).map(([f, v]) => [
115
+ f,
116
+ { scopes: Object.keys(v?.scopes ?? {}) },
117
+ ]),
118
+ )
119
+ out[name] = {
120
+ type: s.type,
121
+ ...(typeof s.in === 'string' ? { in: s.in } : {}),
122
+ ...(typeof s.name === 'string' ? { name: s.name } : {}),
123
+ ...(typeof s.scheme === 'string' ? { scheme: s.scheme } : {}),
124
+ ...(typeof s.openIdConnectUrl === 'string' ? { openIdConnectUrl: s.openIdConnectUrl } : {}),
125
+ ...(Object.keys(flows).length ? { flows } : {}),
126
+ }
127
+ }
128
+ return out
129
+ }
130
+
131
+ /** An operation's requirements as a plain list of `{ scheme: scopes }`. */
132
+ function requirementsFrom(effective: unknown): Record<string, string[]>[] {
133
+ if (!Array.isArray(effective)) return []
134
+ return effective.map((r: any) =>
135
+ Object.fromEntries(
136
+ Object.entries(r ?? {}).map(([k, v]) => [k, Array.isArray(v) ? v.map(String) : []]),
137
+ ),
138
+ )
139
+ }
140
+
95
141
  /** `info.x-logo` (a Redoc convention): only absolute http(s) or data-image URLs are trusted. */
96
142
  export function logoOf(
97
143
  info: any,
@@ -245,7 +291,8 @@ export interface OperationMeta {
245
291
  mediaType?: string
246
292
  files?: string[]
247
293
  formArrays?: Record<string, string>
248
- security?: boolean
294
+ /** `false` = none required; otherwise any one of the requirements (see `ManifestOperation.security`) */
295
+ security?: false | Record<string, string[]>[]
249
296
  }
250
297
 
251
298
  /** The generator proper, from an OpenAPI 3 document that is already parsed and bundled. */
@@ -643,9 +690,10 @@ function generateAll(
643
690
 
644
691
  const effective = op.security ?? doc.security
645
692
  const secured = hasSchemes && Array.isArray(effective) && effective.length > 0
693
+ const requirements = requirementsFrom(effective)
646
694
  const securityLine = !hasSchemes
647
695
  ? ''
648
- : `, security: ${Array.isArray(effective) && effective.length > 0 ? 'true' : 'false'}`
696
+ : `, security: ${requirements.length > 0 ? JSON.stringify(requirements) : 'false'}`
649
697
  // a binary body's declared type (`application/octet-stream`) is what the server expects as Content-Type
650
698
  const mediaType =
651
699
  body &&
@@ -673,7 +721,9 @@ function generateAll(
673
721
  ...(mediaType ? { mediaType } : {}),
674
722
  ...(fileFieldPaths.length ? { files: fileFieldPaths } : {}),
675
723
  ...(Object.keys(arrayStyles).length ? { formArrays: arrayStyles } : {}),
676
- ...(hasSchemes ? { security: Array.isArray(effective) && effective.length > 0 } : {}),
724
+ ...(hasSchemes
725
+ ? { security: requirements.length > 0 ? requirements : (false as const) }
726
+ : {}),
677
727
  }
678
728
  if (ts)
679
729
  files.push({
@@ -708,7 +758,7 @@ ${jsdoc({ description: [...new Set([op.summary, op.description].filter(Boolean))
708
758
  statuses,
709
759
  ...(Object.keys(mockVariants).length ? { mockVariants } : {}),
710
760
  examples: docs.map((d) => ({ name: d.name, title: d.title, source: d.source })),
711
- ...(secured ? { secured: true } : {}),
761
+ ...(secured ? { secured: true, security: requirements } : {}),
712
762
  file: `operations/${id}.ts`,
713
763
  })
714
764
  }
@@ -742,6 +792,7 @@ ${jsdoc({ description: [...new Set([op.summary, op.description].filter(Boolean))
742
792
  version: doc.info?.version,
743
793
  ...(logoOf(doc.info) ? { logo: logoOf(doc.info) } : {}),
744
794
  servers: (doc.servers ?? []).map((s: any) => s.url),
795
+ ...(hasSchemes ? { securitySchemes: securitySchemesOf(doc) } : {}),
745
796
  tags: [...byTag.keys()],
746
797
  operations,
747
798
  },
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ export type {
3
3
  GenerateFlags,
4
4
  GenerateOptions,
5
5
  ManifestOperation,
6
+ ManifestSecurityScheme,
6
7
  OperationMeta,
7
8
  } from './build'
8
9
  export { generate, generateFromDoc, withPlainSpec } from './generate'