@geekmidas/schema 0.0.3 → 1.0.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/CHANGELOG.md +7 -0
- package/dist/{conversion-Civd6bIU.mjs → conversion-BJGLAqtd.mjs} +2 -3
- package/dist/conversion-BJGLAqtd.mjs.map +1 -0
- package/dist/{conversion-CrSNoSRa.cjs → conversion-CFlFrSru.cjs} +2 -3
- package/dist/conversion-CFlFrSru.cjs.map +1 -0
- package/dist/conversion-CH_EmflL.d.cts.map +1 -0
- package/dist/conversion-DUyZYTWO.d.mts.map +1 -0
- package/dist/conversion.cjs +1 -1
- package/dist/conversion.mjs +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{openapi-DR4_PG-e.d.cts → openapi-Aeb_1e9T.d.cts} +3 -1
- package/dist/openapi-Aeb_1e9T.d.cts.map +1 -0
- package/dist/openapi-DH5yCqKh.mjs.map +1 -1
- package/dist/openapi-DVvLYx-8.cjs.map +1 -1
- package/dist/{openapi-j01kFjpI.d.mts → openapi-Dxc4FuMP.d.mts} +3 -1
- package/dist/openapi-Dxc4FuMP.d.mts.map +1 -0
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/parser.cjs.map +1 -1
- package/dist/parser.d.cts +3 -1
- package/dist/parser.d.cts.map +1 -0
- package/dist/parser.d.mts +3 -1
- package/dist/parser.d.mts.map +1 -0
- package/dist/parser.mjs.map +1 -1
- package/dist/{types-ByLHeRGs.d.mts → types-D7ZRea_7.d.cts} +3 -1
- package/dist/types-D7ZRea_7.d.cts.map +1 -0
- package/dist/{types-DfcgE7cO.d.cts → types-o4dOkvCd.d.mts} +3 -1
- package/dist/types-o4dOkvCd.d.mts.map +1 -0
- package/dist/types.d.cts +1 -1
- package/dist/types.d.mts +1 -1
- package/package.json +1 -1
- package/src/__benchmarks__/conversion.bench.ts +88 -0
- package/src/__tests__/conversion.spec.ts +440 -257
- package/src/__tests__/openapi.spec.ts +390 -390
- package/src/__tests__/parser.spec.ts +202 -202
- package/src/conversion.ts +154 -158
- package/src/index.ts +8 -9
- package/src/openapi.ts +49 -49
- package/src/parser.ts +11 -11
- package/src/types.ts +16 -16
- package/tsconfig.json +9 -0
- package/tsdown.config.ts +1 -1
- package/dist/conversion-Civd6bIU.mjs.map +0 -1
- package/dist/conversion-CrSNoSRa.cjs.map +0 -1
|
@@ -1,319 +1,502 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import { z } from 'zod/v4';
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
convertSchemaWithComponents,
|
|
5
|
+
convertStandardSchemaToJsonSchema,
|
|
6
|
+
getSchemaMetadata,
|
|
7
|
+
getZodMetadata,
|
|
8
|
+
SchemaVendor,
|
|
9
9
|
} from '../conversion';
|
|
10
10
|
import { createComponentCollector } from '../openapi';
|
|
11
11
|
|
|
12
12
|
describe('Schema Conversion', () => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
describe('convertStandardSchemaToJsonSchema', () => {
|
|
14
|
+
it('should convert Zod object schema to JSON Schema', async () => {
|
|
15
|
+
const schema = z.object({
|
|
16
|
+
name: z.string(),
|
|
17
|
+
age: z.number(),
|
|
18
|
+
});
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
expect(jsonSchema).toHaveProperty('type', 'object');
|
|
23
|
+
expect(jsonSchema).toHaveProperty('properties');
|
|
24
|
+
expect(jsonSchema.properties).toHaveProperty('name');
|
|
25
|
+
expect(jsonSchema.properties).toHaveProperty('age');
|
|
26
|
+
});
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
it('should return undefined for undefined schema', async () => {
|
|
29
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(undefined);
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
31
|
+
expect(jsonSchema).toBeUndefined();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should convert Zod string schema', async () => {
|
|
35
|
+
const schema = z.string();
|
|
36
|
+
|
|
37
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
|
|
38
|
+
|
|
39
|
+
expect(jsonSchema).toHaveProperty('type', 'string');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should convert Zod number schema', async () => {
|
|
43
|
+
const schema = z.number();
|
|
44
|
+
|
|
45
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
|
|
46
|
+
|
|
47
|
+
expect(jsonSchema).toHaveProperty('type', 'number');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should convert Zod array schema', async () => {
|
|
51
|
+
const schema = z.array(z.string());
|
|
52
|
+
|
|
53
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
|
|
54
|
+
|
|
55
|
+
expect(jsonSchema).toHaveProperty('type', 'array');
|
|
56
|
+
expect(jsonSchema).toHaveProperty('items');
|
|
57
|
+
});
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
it('should convert Zod enum schema', async () => {
|
|
60
|
+
const schema = z.enum(['active', 'inactive', 'pending']);
|
|
61
|
+
|
|
62
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
expect(jsonSchema).toHaveProperty('enum');
|
|
65
|
+
expect(jsonSchema.enum).toEqual(['active', 'inactive', 'pending']);
|
|
66
|
+
});
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
it('should convert nested object schema', async () => {
|
|
69
|
+
const schema = z.object({
|
|
70
|
+
user: z.object({
|
|
71
|
+
name: z.string(),
|
|
72
|
+
age: z.number(),
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
expect(jsonSchema.type).toBe('object');
|
|
79
|
+
expect(jsonSchema.properties).toHaveProperty('user');
|
|
80
|
+
expect(jsonSchema.properties.user).toHaveProperty('type', 'object');
|
|
81
|
+
});
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
it('should handle optional fields', async () => {
|
|
84
|
+
const schema = z.object({
|
|
85
|
+
required: z.string(),
|
|
86
|
+
optional: z.string().optional(),
|
|
87
|
+
});
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
expect(jsonSchema.required).toContain('required');
|
|
92
|
+
expect(jsonSchema.required).not.toContain('optional');
|
|
93
|
+
});
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
it('should extract and convert $defs with component collector', async () => {
|
|
96
|
+
const userSchema = z.object({
|
|
97
|
+
name: z.string(),
|
|
98
|
+
age: z.number(),
|
|
99
|
+
});
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
const schema = z.object({
|
|
102
|
+
user: userSchema,
|
|
103
|
+
});
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
const collector = createComponentCollector();
|
|
106
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(
|
|
107
|
+
schema,
|
|
108
|
+
collector,
|
|
109
|
+
);
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
expect(jsonSchema).toBeDefined();
|
|
112
|
+
// $defs should be removed from main schema
|
|
113
|
+
expect(jsonSchema).not.toHaveProperty('$defs');
|
|
114
|
+
});
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
it('should throw error for unsupported vendor', async () => {
|
|
117
|
+
const invalidSchema = {
|
|
118
|
+
'~standard': {
|
|
119
|
+
vendor: 'unsupported' as any,
|
|
120
|
+
validate: vi.fn(),
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
await expect(
|
|
125
|
+
convertStandardSchemaToJsonSchema(invalidSchema as any),
|
|
126
|
+
).rejects.toThrow(/Unsupported or missing vendor/);
|
|
127
|
+
});
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
129
|
+
it('should throw error for missing vendor', async () => {
|
|
130
|
+
const invalidSchema = {
|
|
131
|
+
'~standard': {
|
|
132
|
+
vendor: undefined,
|
|
133
|
+
validate: vi.fn(),
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
await expect(
|
|
138
|
+
convertStandardSchemaToJsonSchema(invalidSchema as any),
|
|
139
|
+
).rejects.toThrow(/Unsupported or missing vendor/);
|
|
140
|
+
});
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
it('should handle schema with descriptions', async () => {
|
|
143
|
+
const schema = z
|
|
144
|
+
.object({
|
|
145
|
+
name: z.string(),
|
|
146
|
+
})
|
|
147
|
+
.describe('User information');
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
expect(jsonSchema.description).toBe('User information');
|
|
152
|
+
});
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
it('should handle union types', async () => {
|
|
155
|
+
const schema = z.union([z.string(), z.number()]);
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
expect(jsonSchema).toHaveProperty('anyOf');
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
describe('getZodMetadata', () => {
|
|
164
|
+
it('should return undefined for non-Zod objects', async () => {
|
|
165
|
+
const schema = z.string();
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
const metadata = await getZodMetadata(schema);
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
expect(metadata).toBeUndefined();
|
|
170
|
+
});
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
it('should get metadata from Zod object with meta', async () => {
|
|
173
|
+
const schema = z.object({ name: z.string() }).meta({ id: 'User' });
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
const metadata = await getZodMetadata(schema);
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
expect(metadata).toEqual({ id: 'User' });
|
|
178
|
+
});
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
180
|
+
it('should return undefined for Zod object without meta', async () => {
|
|
181
|
+
const schema = z.object({ name: z.string() });
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
const metadata = await getZodMetadata(schema);
|
|
184
184
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
// Returns undefined when no meta is set
|
|
186
|
+
expect(metadata).toBeUndefined();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
189
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
190
|
+
describe('getSchemaMetadata', () => {
|
|
191
|
+
it('should get metadata for Zod schema', async () => {
|
|
192
|
+
const schema = z.object({ name: z.string() }).meta({ id: 'UserMeta' });
|
|
193
193
|
|
|
194
|
-
|
|
194
|
+
const metadata = await getSchemaMetadata(schema);
|
|
195
195
|
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
expect(metadata).toEqual({ id: 'UserMeta' });
|
|
197
|
+
});
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
199
|
+
it('should return undefined for non-Zod vendor', async () => {
|
|
200
|
+
const schema = {
|
|
201
|
+
'~standard': {
|
|
202
|
+
vendor: 'valibot',
|
|
203
|
+
validate: vi.fn(),
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
206
|
|
|
207
|
-
|
|
207
|
+
const metadata = await getSchemaMetadata(schema as any);
|
|
208
208
|
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
expect(metadata).toBeUndefined();
|
|
210
|
+
});
|
|
211
211
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
212
|
+
it('should return undefined for schema without vendor', async () => {
|
|
213
|
+
const schema = {
|
|
214
|
+
'~standard': {
|
|
215
|
+
vendor: undefined,
|
|
216
|
+
validate: vi.fn(),
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
const metadata = await getSchemaMetadata(schema as any);
|
|
221
221
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
222
|
+
expect(metadata).toBeUndefined();
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
225
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
226
|
+
describe('convertSchemaWithComponents', () => {
|
|
227
|
+
it('should convert schema without component collector', async () => {
|
|
228
|
+
const schema = z.object({
|
|
229
|
+
name: z.string(),
|
|
230
|
+
age: z.number(),
|
|
231
|
+
});
|
|
232
232
|
|
|
233
|
-
|
|
233
|
+
const jsonSchema = await convertSchemaWithComponents(schema);
|
|
234
234
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
235
|
+
expect(jsonSchema).toHaveProperty('type', 'object');
|
|
236
|
+
expect(jsonSchema).toHaveProperty('properties');
|
|
237
|
+
});
|
|
238
238
|
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
it('should return undefined for undefined schema', async () => {
|
|
240
|
+
const jsonSchema = await convertSchemaWithComponents(undefined);
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
|
|
242
|
+
expect(jsonSchema).toBeUndefined();
|
|
243
|
+
});
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
it('should add schema with ID to component collector', async () => {
|
|
246
|
+
const schema = z.object({ name: z.string() }).meta({ id: 'UserComp' });
|
|
247
247
|
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
const collector = createComponentCollector();
|
|
249
|
+
const result = await convertSchemaWithComponents(schema, collector);
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
expect(result).toEqual({ $ref: '#/components/schemas/UserComp' });
|
|
252
|
+
expect(collector.schemas).toHaveProperty('UserComp');
|
|
253
|
+
expect(collector.schemas.UserComp).not.toHaveProperty('id');
|
|
254
|
+
});
|
|
255
255
|
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
it('should not add schema without ID to component collector', async () => {
|
|
257
|
+
const schema = z.object({ name: z.string() });
|
|
258
258
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
259
|
+
const collector = createComponentCollector();
|
|
260
|
+
const result = await convertSchemaWithComponents(schema, collector);
|
|
261
|
+
|
|
262
|
+
expect(result).not.toHaveProperty('$ref');
|
|
263
|
+
expect(Object.keys(collector.schemas)).toHaveLength(0);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should handle schema with id in JSON Schema', async () => {
|
|
267
|
+
const schema = z.object({ name: z.string() }).meta({ id: 'Person' });
|
|
268
|
+
|
|
269
|
+
const collector = createComponentCollector();
|
|
270
|
+
const result = await convertSchemaWithComponents(schema, collector);
|
|
271
|
+
|
|
272
|
+
expect(result).toEqual({ $ref: '#/components/schemas/Person' });
|
|
273
|
+
expect(collector.schemas).toHaveProperty('Person');
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('should convert multiple schemas with collector', async () => {
|
|
277
|
+
const schema1 = z.object({ name: z.string() }).meta({ id: 'UserMulti' });
|
|
278
|
+
const schema2 = z.object({ title: z.string() }).meta({ id: 'PostMulti' });
|
|
279
|
+
|
|
280
|
+
const collector = createComponentCollector();
|
|
281
|
+
|
|
282
|
+
await convertSchemaWithComponents(schema1, collector);
|
|
283
|
+
await convertSchemaWithComponents(schema2, collector);
|
|
284
|
+
|
|
285
|
+
expect(Object.keys(collector.schemas)).toHaveLength(2);
|
|
286
|
+
expect(collector.schemas).toHaveProperty('UserMulti');
|
|
287
|
+
expect(collector.schemas).toHaveProperty('PostMulti');
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should handle nested objects with collector', async () => {
|
|
291
|
+
const schema = z
|
|
292
|
+
.object({
|
|
293
|
+
user: z.object({
|
|
294
|
+
name: z.string(),
|
|
295
|
+
profile: z.object({
|
|
296
|
+
bio: z.string(),
|
|
297
|
+
}),
|
|
298
|
+
}),
|
|
299
|
+
})
|
|
300
|
+
.meta({ id: 'UserData' });
|
|
301
|
+
|
|
302
|
+
const collector = createComponentCollector();
|
|
303
|
+
const result = await convertSchemaWithComponents(schema, collector);
|
|
304
|
+
|
|
305
|
+
expect(result).toEqual({ $ref: '#/components/schemas/UserData' });
|
|
306
|
+
expect(collector.schemas).toHaveProperty('UserData');
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
describe('SchemaVendor enum', () => {
|
|
311
|
+
it('should have zod vendor', () => {
|
|
312
|
+
expect(SchemaVendor.zod).toBe('zod');
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('should have valibot vendor', () => {
|
|
316
|
+
expect(SchemaVendor.valibot).toBe('valibot');
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('extractAndConvertDefs edge cases', () => {
|
|
321
|
+
it('should handle $ref that does not start with #/$defs/', async () => {
|
|
322
|
+
// Create a schema with a $ref that doesn't use $defs pattern
|
|
323
|
+
const schema = z.object({
|
|
324
|
+
name: z.string(),
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const collector = createComponentCollector();
|
|
328
|
+
// Add a schema first
|
|
329
|
+
collector.addSchema('ExternalRef', { type: 'string' });
|
|
330
|
+
|
|
331
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(
|
|
332
|
+
schema,
|
|
333
|
+
collector,
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
// Basic test - the schema should still convert properly
|
|
337
|
+
expect(jsonSchema).toHaveProperty('type', 'object');
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('should process arrays in schema correctly', async () => {
|
|
341
|
+
const schema = z.object({
|
|
342
|
+
tags: z.array(z.string()),
|
|
343
|
+
numbers: z.array(z.number()),
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
const collector = createComponentCollector();
|
|
347
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(
|
|
348
|
+
schema,
|
|
349
|
+
collector,
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
expect(jsonSchema.properties.tags).toHaveProperty('type', 'array');
|
|
353
|
+
expect(jsonSchema.properties.numbers).toHaveProperty('type', 'array');
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('should skip $defs key during processing and extract to components', async () => {
|
|
357
|
+
// Create a schema that produces $defs in JSON Schema output
|
|
358
|
+
const addressSchema = z.object({
|
|
359
|
+
street: z.string(),
|
|
360
|
+
city: z.string(),
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const personSchema = z.object({
|
|
364
|
+
name: z.string(),
|
|
365
|
+
address: addressSchema,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
const collector = createComponentCollector();
|
|
369
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(
|
|
370
|
+
personSchema,
|
|
371
|
+
collector,
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
// $defs should be removed from the output
|
|
375
|
+
expect(jsonSchema).not.toHaveProperty('$defs');
|
|
376
|
+
expect(jsonSchema).toHaveProperty('type', 'object');
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('should handle null/undefined values in schema processing', async () => {
|
|
380
|
+
const schema = z.object({
|
|
381
|
+
nullableField: z.string().nullable(),
|
|
382
|
+
optionalField: z.string().optional(),
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
const collector = createComponentCollector();
|
|
386
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(
|
|
387
|
+
schema,
|
|
388
|
+
collector,
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
expect(jsonSchema).toHaveProperty('properties');
|
|
392
|
+
expect(jsonSchema.properties).toHaveProperty('nullableField');
|
|
393
|
+
expect(jsonSchema.properties).toHaveProperty('optionalField');
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('should handle deeply nested objects with arrays', async () => {
|
|
397
|
+
const schema = z.object({
|
|
398
|
+
users: z.array(
|
|
399
|
+
z.object({
|
|
400
|
+
name: z.string(),
|
|
401
|
+
addresses: z.array(
|
|
402
|
+
z.object({
|
|
403
|
+
street: z.string(),
|
|
404
|
+
}),
|
|
405
|
+
),
|
|
406
|
+
}),
|
|
407
|
+
),
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const collector = createComponentCollector();
|
|
411
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(
|
|
412
|
+
schema,
|
|
413
|
+
collector,
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
expect(jsonSchema).toHaveProperty('type', 'object');
|
|
417
|
+
expect(jsonSchema.properties.users).toHaveProperty('type', 'array');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('should handle primitive non-object schema types', async () => {
|
|
421
|
+
const stringSchema = z.string();
|
|
422
|
+
const numberSchema = z.number();
|
|
423
|
+
const booleanSchema = z.boolean();
|
|
424
|
+
|
|
425
|
+
const collector = createComponentCollector();
|
|
426
|
+
|
|
427
|
+
const stringJson = await convertStandardSchemaToJsonSchema(
|
|
428
|
+
stringSchema,
|
|
429
|
+
collector,
|
|
430
|
+
);
|
|
431
|
+
const numberJson = await convertStandardSchemaToJsonSchema(
|
|
432
|
+
numberSchema,
|
|
433
|
+
collector,
|
|
434
|
+
);
|
|
435
|
+
const booleanJson = await convertStandardSchemaToJsonSchema(
|
|
436
|
+
booleanSchema,
|
|
437
|
+
collector,
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
expect(stringJson).toMatchObject({ type: 'string' });
|
|
441
|
+
expect(numberJson).toMatchObject({ type: 'number' });
|
|
442
|
+
expect(booleanJson).toMatchObject({ type: 'boolean' });
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('should handle schema with discriminated unions', async () => {
|
|
446
|
+
const catSchema = z.object({
|
|
447
|
+
type: z.literal('cat'),
|
|
448
|
+
meows: z.boolean(),
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
const dogSchema = z.object({
|
|
452
|
+
type: z.literal('dog'),
|
|
453
|
+
barks: z.boolean(),
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
const petSchema = z.discriminatedUnion('type', [catSchema, dogSchema]);
|
|
457
|
+
|
|
458
|
+
const collector = createComponentCollector();
|
|
459
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(
|
|
460
|
+
petSchema,
|
|
461
|
+
collector,
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
// Should have oneOf or anyOf for discriminated union
|
|
465
|
+
expect(
|
|
466
|
+
jsonSchema.anyOf || jsonSchema.oneOf || jsonSchema.discriminator,
|
|
467
|
+
).toBeDefined();
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('should preserve $ref as-is when not matching $defs pattern', async () => {
|
|
471
|
+
// Test the extractAndConvertDefs function with a schema that has
|
|
472
|
+
// $ref not starting with #/$defs/
|
|
473
|
+
const schema = z.object({
|
|
474
|
+
name: z.string(),
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// Without collector, refs should be preserved as-is
|
|
478
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
|
|
479
|
+
|
|
480
|
+
expect(jsonSchema).toHaveProperty('type', 'object');
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('should convert $defs references to component references with collector', async () => {
|
|
484
|
+
// Use lazy schema to create a recursive structure that produces $defs
|
|
485
|
+
const nodeSchema: z.ZodTypeAny = z.lazy(() =>
|
|
486
|
+
z.object({
|
|
487
|
+
value: z.string(),
|
|
488
|
+
children: z.array(nodeSchema).optional(),
|
|
489
|
+
}),
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
const collector = createComponentCollector();
|
|
493
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(
|
|
494
|
+
nodeSchema,
|
|
495
|
+
collector,
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
// $defs should be processed and potentially converted
|
|
499
|
+
expect(jsonSchema).not.toHaveProperty('$defs');
|
|
500
|
+
});
|
|
501
|
+
});
|
|
319
502
|
});
|