@bram-dc/fastify-type-provider-zod 5.0.3 → 7.0.1

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.
Files changed (41) hide show
  1. package/README.md +34 -4
  2. package/dist/cjs/core.cjs +33 -45
  3. package/dist/cjs/core.cjs.map +1 -1
  4. package/dist/cjs/core.d.cts +11 -12
  5. package/dist/cjs/errors.cjs +1 -1
  6. package/dist/cjs/errors.cjs.map +1 -1
  7. package/dist/cjs/registry.cjs +43 -0
  8. package/dist/cjs/registry.cjs.map +1 -0
  9. package/dist/cjs/registry.d.cts +9 -0
  10. package/dist/cjs/utils.cjs +21 -0
  11. package/dist/cjs/utils.cjs.map +1 -0
  12. package/dist/cjs/utils.d.cts +12 -0
  13. package/dist/cjs/zod-to-json.cjs +36 -41
  14. package/dist/cjs/zod-to-json.cjs.map +1 -1
  15. package/dist/cjs/zod-to-json.d.cts +7 -7
  16. package/dist/esm/core.d.ts +11 -12
  17. package/dist/esm/core.js +34 -46
  18. package/dist/esm/core.js.map +1 -1
  19. package/dist/esm/errors.js +1 -1
  20. package/dist/esm/errors.js.map +1 -1
  21. package/dist/esm/registry.d.ts +9 -0
  22. package/dist/esm/registry.js +43 -0
  23. package/dist/esm/registry.js.map +1 -0
  24. package/dist/esm/utils.d.ts +12 -0
  25. package/dist/esm/utils.js +21 -0
  26. package/dist/esm/utils.js.map +1 -0
  27. package/dist/esm/zod-to-json.d.ts +7 -7
  28. package/dist/esm/zod-to-json.js +36 -41
  29. package/dist/esm/zod-to-json.js.map +1 -1
  30. package/package.json +13 -11
  31. package/src/core.ts +44 -60
  32. package/src/registry.ts +64 -0
  33. package/src/utils.ts +33 -0
  34. package/src/zod-to-json.ts +62 -52
  35. package/dist/cjs/json-to-oas.cjs +0 -85
  36. package/dist/cjs/json-to-oas.cjs.map +0 -1
  37. package/dist/cjs/json-to-oas.d.cts +0 -9
  38. package/dist/esm/json-to-oas.d.ts +0 -9
  39. package/dist/esm/json-to-oas.js +0 -85
  40. package/dist/esm/json-to-oas.js.map +0 -1
  41. package/src/json-to-oas.ts +0 -109
package/src/core.ts CHANGED
@@ -5,16 +5,17 @@ import type {
5
5
  FastifyPluginOptions,
6
6
  FastifySchema,
7
7
  FastifySchemaCompiler,
8
+ FastifySerializerCompiler,
8
9
  FastifyTypeProvider,
9
10
  RawServerBase,
10
11
  RawServerDefault,
11
12
  } from 'fastify'
12
- import type { FastifySerializerCompiler } from 'fastify/types/schema'
13
- import type { $ZodRegistry, input, output } from 'zod/v4/core'
14
- import { $ZodType, globalRegistry, safeParse } from 'zod/v4/core'
13
+ import type { $ZodRegistry, output } from 'zod/v4/core'
14
+ import { $ZodType, globalRegistry, safeDecode, safeEncode } from 'zod/v4/core'
15
15
  import { createValidationError, InvalidSchemaError, ResponseSerializationError } from './errors'
16
- import { getOASVersion, jsonSchemaToOAS } from './json-to-oas'
17
- import { zodRegistryToJson, zodSchemaToJson } from './zod-to-json'
16
+ import { generateIORegistries, type SchemaRegistryMeta } from './registry'
17
+ import { assertIsOpenAPIObject, getJSONSchemaTarget } from './utils'
18
+ import { type ZodToJsonConfig, zodRegistryToJson, zodSchemaToJson } from './zod-to-json'
18
19
 
19
20
  type FreeformRecord = Record<string, any>
20
21
 
@@ -30,7 +31,7 @@ const defaultSkipList = [
30
31
 
31
32
  export interface ZodTypeProvider extends FastifyTypeProvider {
32
33
  validator: this['schema'] extends $ZodType ? output<this['schema']> : unknown
33
- serializer: this['schema'] extends $ZodType ? input<this['schema']> : unknown
34
+ serializer: this['schema'] extends $ZodType ? output<this['schema']> : unknown
34
35
  }
35
36
 
36
37
  interface Schema extends FastifySchema {
@@ -39,19 +40,19 @@ interface Schema extends FastifySchema {
39
40
 
40
41
  type CreateJsonSchemaTransformOptions = {
41
42
  skipList?: readonly string[]
42
- schemaRegistry?: $ZodRegistry<{ id?: string | undefined }>
43
+ schemaRegistry?: $ZodRegistry<SchemaRegistryMeta>
44
+ zodToJsonConfig?: ZodToJsonConfig
43
45
  }
44
46
 
45
47
  export const createJsonSchemaTransform = ({
46
48
  skipList = defaultSkipList,
47
49
  schemaRegistry = globalRegistry,
50
+ zodToJsonConfig = {},
48
51
  }: CreateJsonSchemaTransformOptions): SwaggerTransform<Schema> => {
49
- return (input) => {
50
- if ('swaggerObject' in input) {
51
- throw new Error('OpenAPI 2.0 is not supported')
52
- }
52
+ return (document) => {
53
+ assertIsOpenAPIObject(document)
53
54
 
54
- const { schema, url } = input
55
+ const { schema, url } = document
55
56
 
56
57
  if (!schema) {
57
58
  return {
@@ -60,6 +61,14 @@ export const createJsonSchemaTransform = ({
60
61
  }
61
62
  }
62
63
 
64
+ const target = getJSONSchemaTarget(document.openapiObject.openapi)
65
+ const config = {
66
+ target,
67
+ ...zodToJsonConfig,
68
+ }
69
+
70
+ const { inputRegistry, outputRegistry } = generateIORegistries(schemaRegistry)
71
+
63
72
  const { response, headers, querystring, body, params, hide, ...rest } = schema
64
73
 
65
74
  const transformed: FreeformRecord = {}
@@ -71,15 +80,10 @@ export const createJsonSchemaTransform = ({
71
80
 
72
81
  const zodSchemas: FreeformRecord = { headers, querystring, body, params }
73
82
 
74
- const oasVersion = getOASVersion(input)
75
-
76
83
  for (const prop in zodSchemas) {
77
84
  const zodSchema = zodSchemas[prop]
78
85
  if (zodSchema) {
79
- const jsonSchema = zodSchemaToJson(zodSchema, schemaRegistry, 'input')
80
- const oasSchema = jsonSchemaToOAS(jsonSchema, oasVersion)
81
-
82
- transformed[prop] = oasSchema
86
+ transformed[prop] = zodSchemaToJson(zodSchema, inputRegistry, 'input', config)
83
87
  }
84
88
  }
85
89
 
@@ -88,17 +92,8 @@ export const createJsonSchemaTransform = ({
88
92
 
89
93
  for (const prop in response as any) {
90
94
  const zodSchema = resolveSchema((response as any)[prop])
91
- const jsonSchema = zodSchemaToJson(zodSchema, schemaRegistry, 'output')
92
-
93
- // Check is the JSON schema is null then return as it is since fastify-swagger will handle it
94
- if (jsonSchema.type === 'null') {
95
- transformed.response[prop] = jsonSchema
96
- continue
97
- }
98
95
 
99
- const oasSchema = jsonSchemaToOAS(jsonSchema, oasVersion)
100
-
101
- transformed.response[prop] = oasSchema
96
+ transformed.response[prop] = zodSchemaToJson(zodSchema, outputRegistry, 'output', config)
102
97
  }
103
98
  }
104
99
 
@@ -116,47 +111,36 @@ export const createJsonSchemaTransform = ({
116
111
  export const jsonSchemaTransform: SwaggerTransform<Schema> = createJsonSchemaTransform({})
117
112
 
118
113
  type CreateJsonSchemaTransformObjectOptions = {
119
- schemaRegistry?: $ZodRegistry<{ id?: string | undefined }>
114
+ schemaRegistry?: $ZodRegistry<SchemaRegistryMeta>
115
+ zodToJsonConfig?: ZodToJsonConfig
120
116
  }
121
117
 
122
118
  export const createJsonSchemaTransformObject =
123
119
  ({
124
120
  schemaRegistry = globalRegistry,
121
+ zodToJsonConfig = {},
125
122
  }: CreateJsonSchemaTransformObjectOptions): SwaggerTransformObject =>
126
- (input) => {
127
- if ('swaggerObject' in input) {
128
- throw new Error('OpenAPI 2.0 is not supported')
129
- }
130
-
131
- const oasVersion = getOASVersion(input)
132
-
133
- const inputSchemas = zodRegistryToJson(schemaRegistry, 'input')
134
- const outputSchemas = zodRegistryToJson(schemaRegistry, 'output')
135
-
136
- for (const key in outputSchemas) {
137
- if (inputSchemas[key]) {
138
- throw new Error(
139
- `Collision detected for schema "${key}". The is already an input schema with the same name.`,
140
- )
141
- }
142
- }
123
+ (document) => {
124
+ assertIsOpenAPIObject(document)
143
125
 
144
- const jsonSchemas = {
145
- ...inputSchemas,
146
- ...outputSchemas,
126
+ const target = getJSONSchemaTarget(document.openapiObject.openapi)
127
+ const config = {
128
+ target,
129
+ ...zodToJsonConfig,
147
130
  }
148
131
 
149
- const oasSchemas = Object.fromEntries(
150
- Object.entries(jsonSchemas).map(([key, value]) => [key, jsonSchemaToOAS(value, oasVersion)]),
151
- )
132
+ const { inputRegistry, outputRegistry } = generateIORegistries(schemaRegistry)
133
+ const inputSchemas = zodRegistryToJson(inputRegistry, 'input', config)
134
+ const outputSchemas = zodRegistryToJson(outputRegistry, 'output', config)
152
135
 
153
136
  return {
154
- ...input.openapiObject,
137
+ ...document.openapiObject,
155
138
  components: {
156
- ...input.openapiObject.components,
139
+ ...document.openapiObject.components,
157
140
  schemas: {
158
- ...input.openapiObject.components?.schemas,
159
- ...oasSchemas,
141
+ ...document.openapiObject.components?.schemas,
142
+ ...inputSchemas,
143
+ ...outputSchemas,
160
144
  },
161
145
  },
162
146
  } as ReturnType<SwaggerTransformObject>
@@ -167,7 +151,7 @@ export const jsonSchemaTransformObject: SwaggerTransformObject = createJsonSchem
167
151
  export const validatorCompiler: FastifySchemaCompiler<$ZodType> =
168
152
  ({ schema }) =>
169
153
  (data) => {
170
- const result = safeParse(schema, data)
154
+ const result = safeDecode(schema, data)
171
155
  if (result.error) {
172
156
  return { error: createValidationError(result.error) as unknown as Error }
173
157
  }
@@ -177,10 +161,10 @@ export const validatorCompiler: FastifySchemaCompiler<$ZodType> =
177
161
 
178
162
  function resolveSchema(maybeSchema: $ZodType | { properties: $ZodType }): $ZodType {
179
163
  if (maybeSchema instanceof $ZodType) {
180
- return maybeSchema
164
+ return maybeSchema as $ZodType
181
165
  }
182
166
  if ('properties' in maybeSchema && maybeSchema.properties instanceof $ZodType) {
183
- return maybeSchema.properties
167
+ return maybeSchema.properties as $ZodType
184
168
  }
185
169
  throw new InvalidSchemaError(JSON.stringify(maybeSchema))
186
170
  }
@@ -199,7 +183,7 @@ export const createSerializerCompiler =
199
183
  (data) => {
200
184
  const schema = resolveSchema(maybeSchema)
201
185
 
202
- const result = safeParse(schema, data)
186
+ const result = safeEncode(schema, data)
203
187
  if (result.error) {
204
188
  throw new ResponseSerializationError(method, url, { cause: result.error })
205
189
  }
@@ -0,0 +1,64 @@
1
+ import { $ZodRegistry, type $ZodType } from 'zod/v4/core'
2
+
3
+ export type SchemaRegistryMeta = {
4
+ id?: string | undefined
5
+ [key: string]: unknown
6
+ }
7
+
8
+ const getSchemaId = (id: string, io: 'input' | 'output'): string => {
9
+ return io === 'input' ? `${id}Input` : id
10
+ }
11
+
12
+ // A WeakMap that falls back to another WeakMap when a key is not found, this is to ensure nested metadata is properly resolved
13
+ class WeakMapWithFallback extends WeakMap<$ZodType, SchemaRegistryMeta> {
14
+ constructor(private fallback: WeakMap<$ZodType, SchemaRegistryMeta>) {
15
+ super()
16
+ }
17
+
18
+ get(key: $ZodType): SchemaRegistryMeta | undefined {
19
+ return super.get(key) ?? this.fallback.get(key)
20
+ }
21
+
22
+ has(key: $ZodType): boolean {
23
+ return super.has(key) || this.fallback.has(key)
24
+ }
25
+ }
26
+
27
+ const copyRegistry = (
28
+ inputRegistry: $ZodRegistry<SchemaRegistryMeta>,
29
+ idReplaceFn: (id: string) => string,
30
+ ): $ZodRegistry<SchemaRegistryMeta> => {
31
+ const outputRegistry = new $ZodRegistry<SchemaRegistryMeta>()
32
+
33
+ outputRegistry._map = new WeakMapWithFallback(inputRegistry._map)
34
+
35
+ inputRegistry._idmap.forEach((schema, id) => {
36
+ outputRegistry.add(schema, {
37
+ ...inputRegistry._map.get(schema),
38
+ id: idReplaceFn(id),
39
+ })
40
+ })
41
+
42
+ return outputRegistry
43
+ }
44
+
45
+ export const generateIORegistries = (
46
+ baseRegistry: $ZodRegistry<SchemaRegistryMeta>,
47
+ ): {
48
+ inputRegistry: $ZodRegistry<SchemaRegistryMeta>
49
+ outputRegistry: $ZodRegistry<SchemaRegistryMeta>
50
+ } => {
51
+ const inputRegistry = copyRegistry(baseRegistry, (id) => getSchemaId(id, 'input'))
52
+ const outputRegistry = copyRegistry(baseRegistry, (id) => getSchemaId(id, 'output'))
53
+
54
+ // Detect colliding schemas
55
+ inputRegistry._idmap.forEach((_, id) => {
56
+ if (outputRegistry._idmap.has(id)) {
57
+ throw new Error(
58
+ `Collision detected for schema "${id}". There is already an input schema with the same name.`,
59
+ )
60
+ }
61
+ })
62
+
63
+ return { inputRegistry, outputRegistry }
64
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'
2
+
3
+ type SwaggerObject = {
4
+ swaggerObject: Partial<OpenAPIV2.Document>
5
+ }
6
+
7
+ type OpenAPIObject = {
8
+ openapiObject: Partial<OpenAPIV3.Document | OpenAPIV3_1.Document>
9
+ }
10
+
11
+ export const assertIsOpenAPIObject: (
12
+ obj: SwaggerObject | OpenAPIObject,
13
+ ) => asserts obj is OpenAPIObject = (obj) => {
14
+ if ('swaggerObject' in obj) {
15
+ throw new Error('This package currently does not support component references for Swagger 2.0')
16
+ }
17
+ }
18
+
19
+ export type JSONSchemaTarget = 'draft-2020-12' | 'openapi-3.0'
20
+
21
+ export const getReferenceUri = (input: string): string => {
22
+ const id = input.replace(/^#\/(?:\$defs|definitions|components\/schemas)\//, '')
23
+
24
+ return `#/components/schemas/${id}`
25
+ }
26
+
27
+ export const getJSONSchemaTarget = (version = '3.0.0'): JSONSchemaTarget => {
28
+ if (version.startsWith('3.0')) {
29
+ return 'openapi-3.0'
30
+ }
31
+
32
+ return 'draft-2020-12'
33
+ }
@@ -1,13 +1,16 @@
1
- import type { $ZodDate, $ZodUndefined, $ZodUnion, JSONSchema } from 'zod/v4/core'
1
+ import type {
2
+ $ZodDate,
3
+ $ZodUndefined,
4
+ $ZodUnion,
5
+ JSONSchema,
6
+ RegistryToJSONSchemaParams,
7
+ } from 'zod/v4/core'
2
8
  import { $ZodRegistry, $ZodType, toJSONSchema } from 'zod/v4/core'
9
+ import type { SchemaRegistryMeta } from './registry'
10
+ import { getReferenceUri } from './utils'
3
11
 
4
- const getSchemaId = (id: string, io: 'input' | 'output') => {
5
- return io === 'input' ? `${id}Input` : id
6
- }
7
-
8
- const getReferenceUri = (id: string, io: 'input' | 'output') => {
9
- return `#/components/schemas/${getSchemaId(id, io)}`
10
- }
12
+ const SCHEMA_REGISTRY_ID_PLACEHOLDER = '__SCHEMA__ID__PLACEHOLDER__'
13
+ const SCHEMA_URI_PLACEHOLDER = '__SCHEMA__PLACEHOLDER__'
11
14
 
12
15
  function isZodDate(entity: unknown): entity is $ZodDate {
13
16
  return entity instanceof $ZodType && entity._zod.def.type === 'date'
@@ -51,23 +54,40 @@ const getOverride = (
51
54
  }
52
55
  }
53
56
 
57
+ export type ZodToJsonConfig = {} & Omit<
58
+ RegistryToJSONSchemaParams,
59
+ 'io' | 'metadata' | 'cycles' | 'reused' | 'uri'
60
+ >
61
+
62
+ const deleteInvalidProperties: (
63
+ schema: JSONSchema.BaseSchema,
64
+ ) => Omit<JSONSchema.BaseSchema, 'id' | '$schema'> = (schema) => {
65
+ const object = { ...schema }
66
+
67
+ delete object.id
68
+ delete object.$schema
69
+
70
+ // ToDo added in newer zod
71
+ delete object.$id
72
+
73
+ return object
74
+ }
75
+
54
76
  export const zodSchemaToJson: (
55
77
  zodSchema: $ZodType,
56
- registry: $ZodRegistry<{ id?: string }>,
78
+ registry: $ZodRegistry<SchemaRegistryMeta>,
57
79
  io: 'input' | 'output',
58
- ) => JSONSchema.BaseSchema = (zodSchema, registry, io) => {
59
- const schemaRegistryEntry = registry.get(zodSchema)
60
-
80
+ config: ZodToJsonConfig,
81
+ ) => ReturnType<typeof deleteInvalidProperties> = (zodSchema, registry, io, config) => {
61
82
  /**
62
83
  * Checks whether the provided schema is registered in the given registry.
63
84
  * If it is present and has an `id`, it can be referenced as component.
64
85
  *
65
86
  * @see https://github.com/turkerdev/fastify-type-provider-zod/issues/173
66
87
  */
88
+ const schemaRegistryEntry = registry.get(zodSchema)
67
89
  if (schemaRegistryEntry?.id) {
68
- return {
69
- $ref: getReferenceUri(schemaRegistryEntry.id, io),
70
- }
90
+ return { $ref: getReferenceUri(schemaRegistryEntry.id) }
71
91
  }
72
92
 
73
93
  /**
@@ -78,72 +98,62 @@ export const zodSchemaToJson: (
78
98
  *
79
99
  * @see https://github.com/colinhacks/zod/issues/4281
80
100
  */
81
- const tempID = 'GEN'
82
- const tempRegistry = new $ZodRegistry<{ id?: string }>()
83
- tempRegistry.add(zodSchema, { id: tempID })
101
+ const tempRegistry = new $ZodRegistry<SchemaRegistryMeta>()
102
+ tempRegistry.add(zodSchema, { id: SCHEMA_REGISTRY_ID_PLACEHOLDER })
84
103
 
85
104
  const {
86
- schemas: { [tempID]: result },
105
+ schemas: { [SCHEMA_REGISTRY_ID_PLACEHOLDER]: result },
87
106
  } = toJSONSchema(tempRegistry, {
88
- target: 'draft-2020-12',
89
- metadata: registry,
107
+ ...config,
90
108
  io,
91
- unrepresentable: 'any',
109
+ target: config.target,
110
+ metadata: registry,
111
+ unrepresentable: config.unrepresentable ?? 'any',
92
112
  cycles: 'ref',
93
113
  reused: 'inline',
94
-
95
114
  /**
96
115
  * The uri option only allows customizing the base path of the `$ref`, and it automatically appends a path to it.
97
- * As a workaround, we set a placeholder that looks something like this:
98
- *
99
- * | marker | always added by zod | meta.id |
100
- * |__SCHEMA__PLACEHOLDER__| #/$defs/ | User |
101
- *
102
- * @example `__SCHEMA__PLACEHOLDER__#/$defs/User"`
103
- * @example `__SCHEMA__PLACEHOLDER__#/$defs/Group"`
104
- *
116
+ * As a workaround, we set a placeholder that looks something like this.
105
117
  * @see jsonSchemaReplaceRef
106
118
  * @see https://github.com/colinhacks/zod/issues/4750
107
119
  */
108
- uri: () => `__SCHEMA__PLACEHOLDER__`,
109
- override: (ctx) => getOverride(ctx, io),
120
+ uri: () => SCHEMA_URI_PLACEHOLDER,
121
+ override: config.override ?? ((ctx) => getOverride(ctx, io)),
110
122
  })
111
123
 
112
- const jsonSchema = { ...result }
113
- delete jsonSchema.id
124
+ const jsonSchema = deleteInvalidProperties(result)
114
125
 
115
126
  /**
116
127
  * Replace the previous generated placeholders with the final `$ref` value
117
128
  */
118
- const jsonSchemaReplaceRef = JSON.stringify(jsonSchema).replaceAll(
119
- /"__SCHEMA__PLACEHOLDER__#\/\$defs\/(.+?)"/g,
120
- (_, id) => `"${getReferenceUri(id, io)}"`,
121
- )
122
-
123
- return JSON.parse(jsonSchemaReplaceRef) as typeof result
129
+ return JSON.parse(JSON.stringify(jsonSchema), (__key, value) => {
130
+ if (typeof value === 'string' && value.startsWith(SCHEMA_URI_PLACEHOLDER)) {
131
+ return getReferenceUri(value.slice(SCHEMA_URI_PLACEHOLDER.length))
132
+ }
133
+ return value
134
+ }) as typeof result
124
135
  }
125
136
 
126
137
  export const zodRegistryToJson: (
127
- registry: $ZodRegistry<{ id?: string }>,
138
+ registry: $ZodRegistry<SchemaRegistryMeta>,
128
139
  io: 'input' | 'output',
129
- ) => Record<string, JSONSchema.BaseSchema> = (registry, io) => {
140
+ config: ZodToJsonConfig,
141
+ ) => Record<string, JSONSchema.BaseSchema> = (registry, io, config) => {
130
142
  const result = toJSONSchema(registry, {
131
- target: 'draft-2020-12',
143
+ ...config,
132
144
  io,
133
- unrepresentable: 'any',
145
+ target: config.target,
146
+ metadata: registry,
147
+ unrepresentable: config.unrepresentable ?? 'any',
134
148
  cycles: 'ref',
135
149
  reused: 'inline',
136
- uri: (id) => getReferenceUri(id, io),
137
- override: (ctx) => getOverride(ctx, io),
150
+ uri: (id) => getReferenceUri(id),
151
+ override: config.override ?? ((ctx) => getOverride(ctx, io)),
138
152
  }).schemas
139
153
 
140
154
  const jsonSchemas: Record<string, JSONSchema.BaseSchema> = {}
141
155
  for (const id in result) {
142
- const jsonSchema = { ...result[id] }
143
-
144
- delete jsonSchema.id
145
-
146
- jsonSchemas[getSchemaId(id, io)] = jsonSchema
156
+ jsonSchemas[id] = deleteInvalidProperties(result[id])
147
157
  }
148
158
 
149
159
  return jsonSchemas
@@ -1,85 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const getOASVersion = (documentObject) => {
4
- const openapiVersion = documentObject.openapiObject.openapi || "3.0.3";
5
- if (openapiVersion.startsWith("3.1")) {
6
- return "3.1";
7
- }
8
- if (openapiVersion.startsWith("3.0")) {
9
- return "3.0";
10
- }
11
- throw new Error("Unsupported OpenAPI document object");
12
- };
13
- const jsonSchemaToOAS_3_0 = (jsonSchema) => {
14
- const clone = { ...jsonSchema };
15
- if (clone.type === "null") {
16
- clone.nullable = true;
17
- delete clone.type;
18
- clone.enum = [null];
19
- }
20
- if (Array.isArray(clone.prefixItems)) {
21
- const tuple = clone.prefixItems;
22
- clone.minItems ??= tuple.length;
23
- clone.maxItems ??= tuple.length;
24
- clone.items = {
25
- oneOf: tuple.map(jsonSchemaToOAS_3_0)
26
- };
27
- delete clone.prefixItems;
28
- }
29
- if ("const" in clone && clone.const !== void 0) {
30
- clone.enum = [clone.const];
31
- delete clone.const;
32
- }
33
- if (typeof clone.exclusiveMinimum === "number") {
34
- clone.minimum = clone.exclusiveMinimum;
35
- clone.exclusiveMinimum = true;
36
- }
37
- if (typeof clone.exclusiveMaximum === "number") {
38
- clone.maximum = clone.exclusiveMaximum;
39
- clone.exclusiveMaximum = true;
40
- }
41
- for (const key of [
42
- "$schema",
43
- "$id",
44
- "unevaluatedProperties",
45
- "dependentSchemas",
46
- "patternProperties",
47
- "propertyNames",
48
- "contentEncoding",
49
- "contentMediaType"
50
- ]) {
51
- delete clone[key];
52
- }
53
- const recursive = (v) => Array.isArray(v) ? v.map(jsonSchemaToOAS_3_0) : jsonSchemaToOAS_3_0(v);
54
- if (clone.properties) {
55
- for (const [k, v] of Object.entries(clone.properties)) {
56
- clone.properties[k] = jsonSchemaToOAS_3_0(v);
57
- }
58
- }
59
- if (clone.items && !Array.isArray(clone.items)) {
60
- clone.items = recursive(clone.items);
61
- }
62
- for (const key of ["allOf", "anyOf", "oneOf", "not", "then", "else", "if", "contains"]) {
63
- if (clone[key]) {
64
- clone[key] = recursive(clone[key]);
65
- }
66
- }
67
- return clone;
68
- };
69
- const jsonSchemaToOAS_3_1 = (jsonSchema) => {
70
- return jsonSchema;
71
- };
72
- const jsonSchemaToOAS = (jsonSchema, oasVersion) => {
73
- switch (oasVersion) {
74
- case "3.0":
75
- return jsonSchemaToOAS_3_0(jsonSchema);
76
- case "3.1":
77
- return jsonSchemaToOAS_3_1(jsonSchema);
78
- default:
79
- throw new Error(`Unsupported OpenAPI version: ${oasVersion}`);
80
- }
81
- };
82
- exports.getOASVersion = getOASVersion;
83
- exports.jsonSchemaToOAS = jsonSchemaToOAS;
84
- exports.jsonSchemaToOAS_3_0 = jsonSchemaToOAS_3_0;
85
- //# sourceMappingURL=json-to-oas.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"json-to-oas.cjs","sources":["../../src/json-to-oas.ts"],"sourcesContent":["import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'\nimport type { JSONSchema } from 'zod/v4/core'\n\ntype OASVersion = '3.0' | '3.1'\n\nexport const getOASVersion = (documentObject: {\n openapiObject: Partial<OpenAPIV3.Document | OpenAPIV3_1.Document>\n}): OASVersion => {\n const openapiVersion = documentObject.openapiObject.openapi || '3.0.3'\n\n if (openapiVersion.startsWith('3.1')) {\n return '3.1'\n }\n\n if (openapiVersion.startsWith('3.0')) {\n return '3.0'\n }\n\n throw new Error('Unsupported OpenAPI document object')\n}\n\nexport const jsonSchemaToOAS_3_0 = (jsonSchema: JSONSchema.BaseSchema): OpenAPIV3.SchemaObject => {\n const clone: any = { ...jsonSchema }\n\n if (clone.type === 'null') {\n clone.nullable = true\n delete clone.type\n clone.enum = [null]\n }\n\n if (Array.isArray(clone.prefixItems)) {\n const tuple = clone.prefixItems as JSONSchema.BaseSchema[]\n\n clone.minItems ??= tuple.length\n clone.maxItems ??= tuple.length\n\n clone.items = {\n oneOf: tuple.map(jsonSchemaToOAS_3_0),\n }\n\n delete clone.prefixItems\n }\n\n if ('const' in clone && clone.const !== undefined) {\n clone.enum = [clone.const]\n delete clone.const\n }\n\n if (typeof clone.exclusiveMinimum === 'number') {\n clone.minimum = clone.exclusiveMinimum\n clone.exclusiveMinimum = true\n }\n if (typeof clone.exclusiveMaximum === 'number') {\n clone.maximum = clone.exclusiveMaximum\n clone.exclusiveMaximum = true\n }\n\n for (const key of [\n '$schema',\n '$id',\n 'unevaluatedProperties',\n 'dependentSchemas',\n 'patternProperties',\n 'propertyNames',\n 'contentEncoding',\n 'contentMediaType',\n ]) {\n delete clone[key]\n }\n\n const recursive = (v: any): any =>\n Array.isArray(v) ? v.map(jsonSchemaToOAS_3_0) : jsonSchemaToOAS_3_0(v)\n\n if (clone.properties) {\n for (const [k, v] of Object.entries(clone.properties)) {\n clone.properties![k] = jsonSchemaToOAS_3_0(v as any)\n }\n }\n\n if (clone.items && !Array.isArray(clone.items)) {\n clone.items = recursive(clone.items)\n }\n\n for (const key of ['allOf', 'anyOf', 'oneOf', 'not', 'then', 'else', 'if', 'contains']) {\n if (clone[key]) {\n clone[key] = recursive(clone[key])\n }\n }\n\n return clone as OpenAPIV3.SchemaObject\n}\n\nconst jsonSchemaToOAS_3_1 = (jsonSchema: JSONSchema.BaseSchema): OpenAPIV3_1.SchemaObject => {\n return jsonSchema as OpenAPIV3_1.SchemaObject\n}\n\nexport const jsonSchemaToOAS = (\n jsonSchema: JSONSchema.BaseSchema,\n oasVersion: OASVersion,\n): OpenAPIV3.SchemaObject | OpenAPIV3_1.SchemaObject => {\n switch (oasVersion) {\n case '3.0':\n return jsonSchemaToOAS_3_0(jsonSchema)\n case '3.1':\n return jsonSchemaToOAS_3_1(jsonSchema)\n default:\n throw new Error(`Unsupported OpenAPI version: ${oasVersion}`)\n }\n}\n"],"names":[],"mappings":";;AAKO,MAAM,gBAAgB,CAAC,mBAEZ;AAChB,QAAM,iBAAiB,eAAe,cAAc,WAAW;AAE/D,MAAI,eAAe,WAAW,KAAK,GAAG;AACpC,WAAO;AAAA,EACT;AAEA,MAAI,eAAe,WAAW,KAAK,GAAG;AACpC,WAAO;AAAA,EACT;AAEA,QAAM,IAAI,MAAM,qCAAqC;AACvD;AAEO,MAAM,sBAAsB,CAAC,eAA8D;AAChG,QAAM,QAAa,EAAE,GAAG,WAAA;AAExB,MAAI,MAAM,SAAS,QAAQ;AACzB,UAAM,WAAW;AACjB,WAAO,MAAM;AACb,UAAM,OAAO,CAAC,IAAI;AAAA,EACpB;AAEA,MAAI,MAAM,QAAQ,MAAM,WAAW,GAAG;AACpC,UAAM,QAAQ,MAAM;AAEpB,UAAM,aAAa,MAAM;AACzB,UAAM,aAAa,MAAM;AAEzB,UAAM,QAAQ;AAAA,MACZ,OAAO,MAAM,IAAI,mBAAmB;AAAA,IAAA;AAGtC,WAAO,MAAM;AAAA,EACf;AAEA,MAAI,WAAW,SAAS,MAAM,UAAU,QAAW;AACjD,UAAM,OAAO,CAAC,MAAM,KAAK;AACzB,WAAO,MAAM;AAAA,EACf;AAEA,MAAI,OAAO,MAAM,qBAAqB,UAAU;AAC9C,UAAM,UAAU,MAAM;AACtB,UAAM,mBAAmB;AAAA,EAC3B;AACA,MAAI,OAAO,MAAM,qBAAqB,UAAU;AAC9C,UAAM,UAAU,MAAM;AACtB,UAAM,mBAAmB;AAAA,EAC3B;AAEA,aAAW,OAAO;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,GACC;AACD,WAAO,MAAM,GAAG;AAAA,EAClB;AAEA,QAAM,YAAY,CAAC,MACjB,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,mBAAmB,IAAI,oBAAoB,CAAC;AAEvE,MAAI,MAAM,YAAY;AACpB,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,UAAU,GAAG;AACrD,YAAM,WAAY,CAAC,IAAI,oBAAoB,CAAQ;AAAA,IACrD;AAAA,EACF;AAEA,MAAI,MAAM,SAAS,CAAC,MAAM,QAAQ,MAAM,KAAK,GAAG;AAC9C,UAAM,QAAQ,UAAU,MAAM,KAAK;AAAA,EACrC;AAEA,aAAW,OAAO,CAAC,SAAS,SAAS,SAAS,OAAO,QAAQ,QAAQ,MAAM,UAAU,GAAG;AACtF,QAAI,MAAM,GAAG,GAAG;AACd,YAAM,GAAG,IAAI,UAAU,MAAM,GAAG,CAAC;AAAA,IACnC;AAAA,EACF;AAEA,SAAO;AACT;AAEA,MAAM,sBAAsB,CAAC,eAAgE;AAC3F,SAAO;AACT;AAEO,MAAM,kBAAkB,CAC7B,YACA,eACsD;AACtD,UAAQ,YAAA;AAAA,IACN,KAAK;AACH,aAAO,oBAAoB,UAAU;AAAA,IACvC,KAAK;AACH,aAAO,oBAAoB,UAAU;AAAA,IACvC;AACE,YAAM,IAAI,MAAM,gCAAgC,UAAU,EAAE;AAAA,EAAA;AAElE;;;;"}
@@ -1,9 +0,0 @@
1
- import type { OpenAPIV3, OpenAPIV3_1 } from "openapi-types";
2
- import type { JSONSchema } from "zod/v4/core";
3
- type OASVersion = "3.0" | "3.1";
4
- export declare const getOASVersion: (documentObject: {
5
- openapiObject: Partial<OpenAPIV3.Document | OpenAPIV3_1.Document>;
6
- }) => OASVersion;
7
- export declare const jsonSchemaToOAS_3_0: (jsonSchema: JSONSchema.BaseSchema) => OpenAPIV3.SchemaObject;
8
- export declare const jsonSchemaToOAS: (jsonSchema: JSONSchema.BaseSchema, oasVersion: OASVersion) => OpenAPIV3.SchemaObject | OpenAPIV3_1.SchemaObject;
9
- export {};
@@ -1,9 +0,0 @@
1
- import type { OpenAPIV3, OpenAPIV3_1 } from "openapi-types";
2
- import type { JSONSchema } from "zod/v4/core";
3
- type OASVersion = "3.0" | "3.1";
4
- export declare const getOASVersion: (documentObject: {
5
- openapiObject: Partial<OpenAPIV3.Document | OpenAPIV3_1.Document>;
6
- }) => OASVersion;
7
- export declare const jsonSchemaToOAS_3_0: (jsonSchema: JSONSchema.BaseSchema) => OpenAPIV3.SchemaObject;
8
- export declare const jsonSchemaToOAS: (jsonSchema: JSONSchema.BaseSchema, oasVersion: OASVersion) => OpenAPIV3.SchemaObject | OpenAPIV3_1.SchemaObject;
9
- export {};
@@ -1,85 +0,0 @@
1
- const getOASVersion = (documentObject) => {
2
- const openapiVersion = documentObject.openapiObject.openapi || "3.0.3";
3
- if (openapiVersion.startsWith("3.1")) {
4
- return "3.1";
5
- }
6
- if (openapiVersion.startsWith("3.0")) {
7
- return "3.0";
8
- }
9
- throw new Error("Unsupported OpenAPI document object");
10
- };
11
- const jsonSchemaToOAS_3_0 = (jsonSchema) => {
12
- const clone = { ...jsonSchema };
13
- if (clone.type === "null") {
14
- clone.nullable = true;
15
- delete clone.type;
16
- clone.enum = [null];
17
- }
18
- if (Array.isArray(clone.prefixItems)) {
19
- const tuple = clone.prefixItems;
20
- clone.minItems ??= tuple.length;
21
- clone.maxItems ??= tuple.length;
22
- clone.items = {
23
- oneOf: tuple.map(jsonSchemaToOAS_3_0)
24
- };
25
- delete clone.prefixItems;
26
- }
27
- if ("const" in clone && clone.const !== void 0) {
28
- clone.enum = [clone.const];
29
- delete clone.const;
30
- }
31
- if (typeof clone.exclusiveMinimum === "number") {
32
- clone.minimum = clone.exclusiveMinimum;
33
- clone.exclusiveMinimum = true;
34
- }
35
- if (typeof clone.exclusiveMaximum === "number") {
36
- clone.maximum = clone.exclusiveMaximum;
37
- clone.exclusiveMaximum = true;
38
- }
39
- for (const key of [
40
- "$schema",
41
- "$id",
42
- "unevaluatedProperties",
43
- "dependentSchemas",
44
- "patternProperties",
45
- "propertyNames",
46
- "contentEncoding",
47
- "contentMediaType"
48
- ]) {
49
- delete clone[key];
50
- }
51
- const recursive = (v) => Array.isArray(v) ? v.map(jsonSchemaToOAS_3_0) : jsonSchemaToOAS_3_0(v);
52
- if (clone.properties) {
53
- for (const [k, v] of Object.entries(clone.properties)) {
54
- clone.properties[k] = jsonSchemaToOAS_3_0(v);
55
- }
56
- }
57
- if (clone.items && !Array.isArray(clone.items)) {
58
- clone.items = recursive(clone.items);
59
- }
60
- for (const key of ["allOf", "anyOf", "oneOf", "not", "then", "else", "if", "contains"]) {
61
- if (clone[key]) {
62
- clone[key] = recursive(clone[key]);
63
- }
64
- }
65
- return clone;
66
- };
67
- const jsonSchemaToOAS_3_1 = (jsonSchema) => {
68
- return jsonSchema;
69
- };
70
- const jsonSchemaToOAS = (jsonSchema, oasVersion) => {
71
- switch (oasVersion) {
72
- case "3.0":
73
- return jsonSchemaToOAS_3_0(jsonSchema);
74
- case "3.1":
75
- return jsonSchemaToOAS_3_1(jsonSchema);
76
- default:
77
- throw new Error(`Unsupported OpenAPI version: ${oasVersion}`);
78
- }
79
- };
80
- export {
81
- getOASVersion,
82
- jsonSchemaToOAS,
83
- jsonSchemaToOAS_3_0
84
- };
85
- //# sourceMappingURL=json-to-oas.js.map