Skip to content

Commit 589302d

Browse files
fix(zui): json-schemas should be strict by default (#600)
1 parent 5fbea00 commit 589302d

File tree

4 files changed

+53
-7
lines changed

4 files changed

+53
-7
lines changed

zui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@bpinternal/zui",
3-
"version": "0.22.5",
3+
"version": "0.22.6",
44
"description": "A fork of Zod with additional features",
55
"type": "module",
66
"source": "./src/index.ts",

zui/src/transforms/transform-pipeline.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,11 +368,11 @@ describe.concurrent('transformPipeline', () => {
368368
assert(srcSchema).toTransformBackToItself()
369369
})
370370
it('should map ZodObject to itself', async () => {
371-
const srcSchema = z.object({ foo: z.string() })
371+
const srcSchema = z.object({ foo: z.string() }).strict()
372372
assert(srcSchema).toTransformBackToItself()
373373
})
374374
it('should map empty ZodObject to itself', async () => {
375-
const srcSchema = z.object({})
375+
const srcSchema = z.object({}).strict()
376376
assert(srcSchema).toTransformBackToItself()
377377
})
378378
it('should map ZodUnion to itself', async () => {

zui/src/transforms/zui-to-json-schema-next/index.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ describe('zuiToJsonSchemaNext', () => {
7171
type: 'object',
7272
properties: { name: { type: 'string' } },
7373
required: ['name'],
74+
additionalProperties: false,
7475
})
7576
})
7677

@@ -94,6 +95,7 @@ describe('zuiToJsonSchemaNext', () => {
9495
email: { type: 'string', readOnly: true },
9596
},
9697
required: ['name'],
98+
additionalProperties: false,
9799
})
98100
})
99101

@@ -156,6 +158,7 @@ describe('zuiToJsonSchemaNext', () => {
156158
},
157159
},
158160
required: ['tableIdOrName'],
161+
additionalProperties: false,
159162
})
160163
})
161164

@@ -179,11 +182,13 @@ describe('zuiToJsonSchemaNext', () => {
179182
type: 'object',
180183
properties: { type: { type: 'string', const: 'A' }, a: { type: 'string' } },
181184
required: ['type', 'a'],
185+
additionalProperties: false,
182186
},
183187
{
184188
type: 'object',
185189
properties: { type: { type: 'string', const: 'B' }, b: { type: 'number' } },
186190
required: ['type', 'b'],
191+
additionalProperties: false,
187192
},
188193
],
189194
})
@@ -207,6 +212,30 @@ describe('zuiToJsonSchemaNext', () => {
207212
})
208213
})
209214

215+
test('should map ZodIntersection of strict schemas to IntersectionSchema removing additional properties', () => {
216+
const schema = toJsonSchema(
217+
z.intersection(
218+
z.object({ a: z.string() }).strict(), //
219+
z.object({ b: z.number() }).strict(),
220+
),
221+
)
222+
223+
expect(schema).toEqual({
224+
allOf: [
225+
{
226+
type: 'object',
227+
properties: { a: { type: 'string' } },
228+
required: ['a'],
229+
},
230+
{
231+
type: 'object',
232+
properties: { b: { type: 'number' } },
233+
required: ['b'],
234+
},
235+
],
236+
})
237+
})
238+
210239
test('should map ZodTuple to TupleSchema', () => {
211240
const schema = toJsonSchema(z.tuple([z.string(), z.number()]))
212241
expect(schema).toEqual({

zui/src/transforms/zui-to-json-schema-next/index.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,11 @@ export function toJsonSchema(schema: z.Schema): json.ZuiJsonSchema {
8484
.map(([key, value]) => [key, value.mandatory()] satisfies [string, z.ZodType])
8585
.map(([key, value]) => [key, toJsonSchema(value)] satisfies [string, json.ZuiJsonSchema])
8686

87-
let additionalProperties: json.ObjectSchema['additionalProperties'] = undefined
87+
let additionalProperties: json.ObjectSchema['additionalProperties'] = false
8888
if (def.unknownKeys instanceof z.ZodType) {
8989
additionalProperties = toJsonSchema(def.unknownKeys)
9090
} else if (def.unknownKeys === 'passthrough') {
9191
additionalProperties = true
92-
} else if (def.unknownKeys === 'strict') {
93-
additionalProperties = false
9492
}
9593

9694
return {
@@ -117,9 +115,28 @@ export function toJsonSchema(schema: z.Schema): json.ZuiJsonSchema {
117115
} satisfies json.UnionSchema
118116

119117
case z.ZodFirstPartyTypeKind.ZodIntersection:
118+
const left = toJsonSchema(def.left)
119+
const right = toJsonSchema(def.right)
120+
121+
/**
122+
* TODO: Potential conflict between `additionalProperties` in the left and right schemas.
123+
* To avoid this, we currently strip `additionalProperties` from both sides.
124+
* This is a workaround and results in lost schema information.
125+
* A proper fix would involve using `unevaluatedProperties`.
126+
* See: https://json-schema.org/understanding-json-schema/reference/object#unevaluatedproperties
127+
*
128+
* – fleur
129+
*/
130+
if ('additionalProperties' in left) {
131+
delete left.additionalProperties
132+
}
133+
if ('additionalProperties' in right) {
134+
delete right.additionalProperties
135+
}
136+
120137
return {
121138
description: def.description,
122-
allOf: [toJsonSchema(def.left), toJsonSchema(def.right)],
139+
allOf: [left, right],
123140
'x-zui': def['x-zui'],
124141
} satisfies json.IntersectionSchema
125142

0 commit comments

Comments
 (0)