@takeshape/json-schema 11.72.0 → 11.74.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.
@@ -1,1134 +0,0 @@
1
- import { describe, expect, test } from 'vitest';
2
- import { SchemaConversionTarget, SchemaConverter } from "../schema-converter.js";
3
- import searchShapeSchemaJson from './search-shape-schema.json' with { type: 'json' };
4
- describe('SchemaConverter', () => {
5
- describe('target = json-schema', () => {
6
- test('handles nested object schemas', () => {
7
- const exampleSchema = {
8
- type: 'object',
9
- properties: {
10
- user: {
11
- type: 'object',
12
- properties: {
13
- name: { type: 'string' },
14
- address: {
15
- type: 'object',
16
- properties: {
17
- street: { type: 'string' },
18
- city: { type: 'string' }
19
- },
20
- required: ['street', 'city']
21
- }
22
- },
23
- required: ['name', 'address']
24
- }
25
- }
26
- };
27
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.JSONSchema });
28
- expect(result.schema).toEqual({
29
- properties: {
30
- user: {
31
- properties: {
32
- name: {
33
- type: 'string'
34
- },
35
- address: {
36
- properties: {
37
- street: {
38
- type: 'string'
39
- },
40
- city: {
41
- type: 'string'
42
- }
43
- },
44
- required: ['street', 'city'],
45
- type: 'object'
46
- }
47
- },
48
- required: ['name', 'address'],
49
- type: 'object'
50
- }
51
- },
52
- type: 'object'
53
- });
54
- });
55
- test('handles schemas with $ref', () => {
56
- const exampleSchema = {
57
- type: 'object',
58
- properties: {
59
- user: {
60
- $ref: '#/definitions/User'
61
- }
62
- },
63
- definitions: {
64
- NotUsed: {
65
- type: 'object',
66
- properties: {
67
- foo: { type: 'string' }
68
- }
69
- },
70
- User: {
71
- type: 'object',
72
- properties: {
73
- name: { type: 'string' },
74
- age: { type: 'integer' }
75
- },
76
- required: ['name']
77
- }
78
- }
79
- };
80
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.JSONSchema });
81
- expect(result.schema).toEqual({
82
- $defs: {
83
- User: {
84
- type: 'object',
85
- properties: {
86
- name: { type: 'string' },
87
- age: { type: 'integer' }
88
- },
89
- required: ['name']
90
- }
91
- },
92
- properties: {
93
- user: {
94
- $ref: '#/$defs/User'
95
- }
96
- },
97
- type: 'object'
98
- });
99
- });
100
- test('handles schemas with oneOf', () => {
101
- const exampleSchema = {
102
- type: 'object',
103
- properties: {
104
- user: {
105
- discriminator: {
106
- propertyName: 'type'
107
- },
108
- oneOf: [
109
- {
110
- $ref: '#/definitions/User'
111
- },
112
- {
113
- $ref: '#/definitions/Dog'
114
- }
115
- ]
116
- }
117
- },
118
- definitions: {
119
- Dog: {
120
- type: 'object',
121
- properties: {
122
- type: { type: 'string', const: 'dog' },
123
- barks: { type: 'boolean' }
124
- },
125
- required: ['type']
126
- },
127
- User: {
128
- type: 'object',
129
- properties: {
130
- type: { type: 'string', const: 'user' },
131
- name: { type: 'string' },
132
- age: { type: 'integer' }
133
- },
134
- required: ['type', 'name']
135
- }
136
- }
137
- };
138
- const result = SchemaConverter.convert(exampleSchema, {
139
- target: SchemaConversionTarget.JSONSchema,
140
- inlineDefinitions: true
141
- });
142
- expect(result.schema).toEqual({
143
- properties: {
144
- user: {
145
- oneOf: [
146
- {
147
- type: 'object',
148
- properties: {
149
- type: { type: 'string', const: 'user' },
150
- name: { type: 'string' },
151
- age: { type: 'integer' }
152
- },
153
- required: ['type', 'name']
154
- },
155
- {
156
- type: 'object',
157
- properties: {
158
- type: { type: 'string', const: 'dog' },
159
- barks: { type: 'boolean' }
160
- },
161
- required: ['type']
162
- }
163
- ]
164
- }
165
- },
166
- type: 'object'
167
- });
168
- });
169
- test('preserves unknown property discriminator when allowUnknownKeys is enabled', () => {
170
- const exampleSchema = {
171
- type: 'object',
172
- properties: {
173
- user: {
174
- discriminator: {
175
- propertyName: 'type'
176
- },
177
- oneOf: [
178
- {
179
- $ref: '#/definitions/User'
180
- },
181
- {
182
- $ref: '#/definitions/Dog'
183
- }
184
- ]
185
- }
186
- },
187
- definitions: {
188
- Dog: {
189
- type: 'object',
190
- properties: {
191
- type: { type: 'string', const: 'dog' },
192
- barks: { type: 'boolean' }
193
- },
194
- required: ['type']
195
- },
196
- User: {
197
- type: 'object',
198
- properties: {
199
- type: { type: 'string', const: 'user' },
200
- name: { type: 'string' },
201
- age: { type: 'integer' }
202
- },
203
- required: ['type', 'name']
204
- }
205
- }
206
- };
207
- const result = SchemaConverter.convert(exampleSchema, {
208
- target: SchemaConversionTarget.JSONSchema,
209
- inlineDefinitions: true,
210
- allowUnknownKeys: true
211
- });
212
- expect(result.schema).toEqual({
213
- properties: {
214
- user: {
215
- discriminator: {
216
- propertyName: 'type'
217
- },
218
- oneOf: [
219
- {
220
- type: 'object',
221
- properties: {
222
- type: { type: 'string', const: 'user' },
223
- name: { type: 'string' },
224
- age: { type: 'integer' }
225
- },
226
- required: ['type', 'name']
227
- },
228
- {
229
- type: 'object',
230
- properties: {
231
- type: { type: 'string', const: 'dog' },
232
- barks: { type: 'boolean' }
233
- },
234
- required: ['type']
235
- }
236
- ]
237
- }
238
- },
239
- type: 'object'
240
- });
241
- });
242
- test('does not trigger warnings on schemas with enums exceeding openai limits', () => {
243
- const exampleSchema = {
244
- type: 'object',
245
- properties: {
246
- color: {
247
- type: 'string',
248
- enum: Array.from({ length: 600 }, (_, i) => `color${i}`)
249
- }
250
- }
251
- };
252
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.JSONSchema });
253
- expect(result.schema).toEqual({
254
- properties: {
255
- color: {
256
- enum: Array.from({ length: 600 }, (_, i) => `color${i}`),
257
- type: 'string'
258
- }
259
- },
260
- type: 'object'
261
- });
262
- expect(result.stats).toEqual({ propertiesCount: 1, stringLength: 4695, enumValuesCount: 600 });
263
- expect(result.warnings.length).toEqual(0);
264
- });
265
- test('removes keys not included in propertyKeys', () => {
266
- const exampleSchema = {
267
- type: 'object',
268
- properties: {
269
- id: { type: 'string' },
270
- name: { type: 'string' },
271
- _secret: { type: 'string' }
272
- },
273
- required: ['id', 'name', 'secret']
274
- };
275
- const result = SchemaConverter.convert(exampleSchema, {
276
- target: SchemaConversionTarget.JSONSchema,
277
- propertyFilter: (key) => key === 'id' || key === 'name'
278
- });
279
- expect(result.schema).toEqual({
280
- properties: {
281
- id: {
282
- type: 'string'
283
- },
284
- name: {
285
- type: 'string'
286
- }
287
- },
288
- required: ['id', 'name'],
289
- type: 'object'
290
- });
291
- });
292
- test('inlines definitions', () => {
293
- const exampleSchema = {
294
- type: 'object',
295
- properties: {
296
- user: {
297
- $ref: '#/definitions/User'
298
- }
299
- },
300
- definitions: {
301
- User: {
302
- type: 'object',
303
- properties: {
304
- name: { type: 'string' },
305
- age: { type: 'integer' }
306
- },
307
- required: ['name']
308
- }
309
- }
310
- };
311
- const result = SchemaConverter.convert(exampleSchema, {
312
- target: SchemaConversionTarget.JSONSchema,
313
- inlineDefinitions: true
314
- });
315
- expect(result.schema).toEqual({
316
- properties: {
317
- user: {
318
- properties: {
319
- name: {
320
- type: 'string'
321
- },
322
- age: {
323
- type: 'integer'
324
- }
325
- },
326
- required: ['name'],
327
- type: 'object'
328
- }
329
- },
330
- type: 'object'
331
- });
332
- });
333
- test('only includes defs in the referenced graph, pruning other defs', () => {
334
- const exampleSchema = {
335
- type: 'object',
336
- properties: {
337
- user: {
338
- $ref: '#/definitions/User'
339
- }
340
- },
341
- $defs: {
342
- NotUsed: {
343
- type: 'object',
344
- properties: {
345
- foo: { type: 'string' }
346
- },
347
- required: ['foo']
348
- },
349
- Used: {
350
- type: 'object',
351
- properties: {
352
- foo: { type: 'string' }
353
- },
354
- required: ['foo']
355
- },
356
- User: {
357
- type: 'object',
358
- properties: {
359
- name: { type: 'string' },
360
- age: { type: 'integer' },
361
- used: {
362
- $ref: '#/$defs/Used'
363
- }
364
- },
365
- required: ['name', 'age']
366
- }
367
- }
368
- };
369
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.JSONSchema });
370
- expect(result.stats).toEqual({ propertiesCount: 5, stringLength: 26, enumValuesCount: 0 });
371
- expect(result.warnings).toEqual([]);
372
- expect(result.schema).toEqual({
373
- type: 'object',
374
- properties: {
375
- user: {
376
- $ref: '#/$defs/User'
377
- }
378
- },
379
- $defs: {
380
- User: {
381
- type: 'object',
382
- properties: {
383
- name: {
384
- type: 'string'
385
- },
386
- age: {
387
- type: 'integer'
388
- },
389
- used: {
390
- $ref: '#/$defs/Used'
391
- }
392
- },
393
- required: ['name', 'age']
394
- },
395
- Used: {
396
- type: 'object',
397
- properties: {
398
- foo: {
399
- type: 'string'
400
- }
401
- },
402
- required: ['foo']
403
- }
404
- }
405
- });
406
- });
407
- test('real world example', async () => {
408
- const result = SchemaConverter.convert(searchShapeSchemaJson, {
409
- target: SchemaConversionTarget.JSONSchema,
410
- inlineDefinitions: false,
411
- propertyFilter: (key) => !key.startsWith('_') && key !== 'where'
412
- });
413
- expect(result.stats).toEqual({ propertiesCount: 10, stringLength: 87, enumValuesCount: 0 });
414
- expect(result.warnings).toEqual([]);
415
- expect(result.schema).toEqual({
416
- type: 'object',
417
- title: 'Args',
418
- properties: {
419
- terms: {
420
- type: 'string'
421
- },
422
- from: {
423
- type: 'integer',
424
- description: 'The offset from the first result you want to fetch.'
425
- },
426
- size: {
427
- type: 'integer',
428
- description: 'The maximum number of items to return.'
429
- },
430
- filter: {
431
- type: 'object',
432
- description: 'An elasticsearch style filter. Overrides onlyEnabled.',
433
- properties: {}
434
- },
435
- sort: {
436
- type: 'array',
437
- description: 'An list of fields to sort by.',
438
- items: {
439
- $ref: '#/$defs/TSSearchSortInput'
440
- }
441
- },
442
- locale: {
443
- type: 'string'
444
- },
445
- enableLocaleFallback: {
446
- type: 'boolean',
447
- default: true
448
- },
449
- onlyEnabled: {
450
- type: 'boolean',
451
- description: 'Filter out content that is not enabled. Defaults to true.',
452
- default: true
453
- }
454
- },
455
- $defs: {
456
- TSSearchSortInput: {
457
- type: 'object',
458
- title: 'TSSearchSortInput',
459
- properties: {
460
- field: {
461
- type: 'string'
462
- },
463
- order: {
464
- type: 'string',
465
- description: 'asc for ascending or desc for descending'
466
- }
467
- },
468
- required: ['field', 'order']
469
- }
470
- }
471
- });
472
- });
473
- });
474
- describe('target = openai', () => {
475
- test('adds nullable to simple schemas', () => {
476
- const exampleSchema = {
477
- type: 'object',
478
- properties: {
479
- location: {
480
- type: 'string',
481
- description: 'The location to get the weather for'
482
- },
483
- unit: {
484
- type: 'string',
485
- description: 'The unit to return the temperature in',
486
- enum: ['F', 'C']
487
- }
488
- }
489
- };
490
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.OpenAI });
491
- expect(result.schema).toEqual({
492
- additionalProperties: false,
493
- properties: {
494
- location: {
495
- description: 'The location to get the weather for',
496
- type: ['string', 'null']
497
- },
498
- unit: {
499
- description: 'The unit to return the temperature in',
500
- enum: ['F', 'C'],
501
- type: ['string', 'null']
502
- }
503
- },
504
- required: ['location', 'unit'],
505
- type: 'object'
506
- });
507
- });
508
- test('adds nullable to object schemas using anyOf', () => {
509
- const exampleSchema = {
510
- type: 'object',
511
- properties: {
512
- location: {
513
- type: 'object',
514
- properties: {
515
- lat: { type: 'number' },
516
- lon: { type: 'number' }
517
- },
518
- required: ['lat', 'lon'],
519
- description: 'The location to get the weather for'
520
- },
521
- unit: {
522
- type: 'string',
523
- description: 'The unit to return the temperature in',
524
- enum: ['F', 'C']
525
- }
526
- }
527
- };
528
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.OpenAI });
529
- expect(result.schema).toEqual({
530
- additionalProperties: false,
531
- properties: {
532
- location: {
533
- anyOf: [
534
- {
535
- additionalProperties: false,
536
- description: 'The location to get the weather for',
537
- properties: {
538
- lat: {
539
- type: 'number'
540
- },
541
- lon: {
542
- type: 'number'
543
- }
544
- },
545
- required: ['lat', 'lon'],
546
- type: 'object'
547
- },
548
- {
549
- type: 'null'
550
- }
551
- ]
552
- },
553
- unit: {
554
- description: 'The unit to return the temperature in',
555
- enum: ['F', 'C'],
556
- type: ['string', 'null']
557
- }
558
- },
559
- required: ['location', 'unit'],
560
- type: 'object'
561
- });
562
- });
563
- test('handles schemas with oneOf', () => {
564
- const exampleSchema = {
565
- type: 'object',
566
- properties: {
567
- user: {
568
- discriminator: {
569
- propertyName: 'type'
570
- },
571
- oneOf: [
572
- {
573
- $ref: '#/definitions/User'
574
- },
575
- {
576
- $ref: '#/definitions/Dog'
577
- }
578
- ]
579
- }
580
- },
581
- required: ['user'],
582
- definitions: {
583
- Dog: {
584
- type: 'object',
585
- properties: {
586
- type: { type: 'string', const: 'dog' },
587
- barks: { type: 'boolean' }
588
- },
589
- required: ['type']
590
- },
591
- User: {
592
- type: 'object',
593
- properties: {
594
- type: { type: 'string', const: 'user' },
595
- name: { type: 'string' },
596
- age: { type: 'integer' }
597
- },
598
- required: ['type', 'name']
599
- }
600
- }
601
- };
602
- const result = SchemaConverter.convert(exampleSchema, {
603
- target: SchemaConversionTarget.OpenAI,
604
- inlineDefinitions: true
605
- });
606
- expect(result.schema).toEqual({
607
- required: ['user'],
608
- additionalProperties: false,
609
- type: 'object',
610
- properties: {
611
- user: {
612
- anyOf: [
613
- {
614
- required: ['type', 'name', 'age'],
615
- additionalProperties: false,
616
- type: 'object',
617
- properties: {
618
- type: {
619
- const: 'user',
620
- type: 'string'
621
- },
622
- name: {
623
- type: 'string'
624
- },
625
- age: {
626
- type: ['integer', 'null']
627
- }
628
- }
629
- },
630
- {
631
- required: ['type', 'barks'],
632
- additionalProperties: false,
633
- type: 'object',
634
- properties: {
635
- type: {
636
- const: 'dog',
637
- type: 'string'
638
- },
639
- barks: {
640
- type: ['boolean', 'null']
641
- }
642
- }
643
- }
644
- ]
645
- }
646
- }
647
- });
648
- });
649
- test('allOf schemas at root are merged with warnings', () => {
650
- const exampleSchema = {
651
- allOf: [
652
- {
653
- type: 'object',
654
- properties: {
655
- unit: {
656
- type: 'string',
657
- description: 'The unit to return the temperature in',
658
- enum: ['F', 'C']
659
- }
660
- }
661
- },
662
- {
663
- type: 'object',
664
- properties: {
665
- location: {
666
- type: 'object',
667
- properties: {
668
- lat: { type: 'number' },
669
- lon: { type: 'number' }
670
- },
671
- required: ['lat', 'lon'],
672
- description: 'The location to get the weather for'
673
- }
674
- }
675
- }
676
- ]
677
- };
678
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.OpenAI });
679
- expect(result.schema).toEqual({
680
- additionalProperties: false,
681
- properties: {
682
- location: {
683
- anyOf: [
684
- {
685
- additionalProperties: false,
686
- description: 'The location to get the weather for',
687
- properties: {
688
- lat: {
689
- type: 'number'
690
- },
691
- lon: {
692
- type: 'number'
693
- }
694
- },
695
- required: ['lat', 'lon'],
696
- type: 'object'
697
- },
698
- {
699
- type: 'null'
700
- }
701
- ]
702
- },
703
- unit: {
704
- description: 'The unit to return the temperature in',
705
- enum: ['F', 'C'],
706
- type: ['string', 'null']
707
- }
708
- },
709
- required: ['unit', 'location'],
710
- type: 'object'
711
- });
712
- expect(result.warnings).toContain('allOf at root is not directly supported, attempting to merge schemas');
713
- });
714
- test('non object root schemas are nested in an object schema', () => {
715
- const exampleSchema = {
716
- type: 'string',
717
- enum: ['F', 'C']
718
- };
719
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.OpenAI });
720
- expect(result.schema).toEqual({
721
- additionalProperties: false,
722
- properties: {
723
- root: {
724
- type: 'string',
725
- enum: ['F', 'C']
726
- }
727
- },
728
- required: ['root'],
729
- type: 'object'
730
- });
731
- expect(result.warnings).toContain('Root level must be an object type, wrapping in an object');
732
- });
733
- test('handles nested object schemas', () => {
734
- const exampleSchema = {
735
- type: 'object',
736
- properties: {
737
- user: {
738
- type: 'object',
739
- properties: {
740
- name: { type: 'string' },
741
- address: {
742
- type: 'object',
743
- properties: {
744
- street: { type: 'string' },
745
- city: { type: 'string' }
746
- },
747
- required: ['street', 'city']
748
- }
749
- },
750
- required: ['name', 'address']
751
- }
752
- }
753
- };
754
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.OpenAI });
755
- expect(result.schema).toEqual({
756
- additionalProperties: false,
757
- properties: {
758
- user: {
759
- anyOf: [
760
- {
761
- additionalProperties: false,
762
- properties: {
763
- name: {
764
- type: 'string'
765
- },
766
- address: {
767
- additionalProperties: false,
768
- properties: {
769
- street: {
770
- type: 'string'
771
- },
772
- city: {
773
- type: 'string'
774
- }
775
- },
776
- required: ['street', 'city'],
777
- type: 'object'
778
- }
779
- },
780
- required: ['name', 'address'],
781
- type: 'object'
782
- },
783
- {
784
- type: 'null'
785
- }
786
- ]
787
- }
788
- },
789
- required: ['user'],
790
- type: 'object'
791
- });
792
- });
793
- test('handles schemas with $ref', () => {
794
- const exampleSchema = {
795
- type: 'object',
796
- properties: {
797
- user: {
798
- $ref: '#/definitions/User'
799
- }
800
- },
801
- definitions: {
802
- NotUsed: {
803
- type: 'object',
804
- properties: {
805
- foo: { type: 'string' }
806
- }
807
- },
808
- User: {
809
- type: 'object',
810
- properties: {
811
- name: { type: 'string' },
812
- age: { type: 'integer' }
813
- },
814
- required: ['name']
815
- }
816
- }
817
- };
818
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.OpenAI });
819
- expect(result.schema).toEqual({
820
- $defs: {
821
- User: {
822
- additionalProperties: false,
823
- type: 'object',
824
- properties: {
825
- name: { type: 'string' },
826
- age: { type: ['integer', 'null'] }
827
- },
828
- required: ['name', 'age']
829
- }
830
- },
831
- additionalProperties: false,
832
- properties: {
833
- user: {
834
- anyOf: [
835
- {
836
- $ref: '#/$defs/User'
837
- },
838
- {
839
- type: 'null'
840
- }
841
- ]
842
- }
843
- },
844
- required: ['user'],
845
- type: 'object'
846
- });
847
- });
848
- test('handles schemas with enums exceeding limits', () => {
849
- const exampleSchema = {
850
- type: 'object',
851
- properties: {
852
- color: {
853
- type: 'string',
854
- enum: Array.from({ length: 600 }, (_, i) => `color${i}`)
855
- }
856
- }
857
- };
858
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.OpenAI });
859
- expect(result.schema).toEqual({
860
- additionalProperties: false,
861
- properties: {
862
- color: {
863
- enum: Array.from({ length: 500 }, (_, i) => `color${i}`),
864
- type: ['string', 'null']
865
- }
866
- },
867
- required: ['color'],
868
- type: 'object'
869
- });
870
- expect(result.stats).toEqual({ propertiesCount: 1, stringLength: 3895, enumValuesCount: 600 });
871
- expect(result.warnings).toContain('Schema has 600 enum values across all properties, exceeding the 500 limit');
872
- });
873
- test('removes keys matching removePropertyKeyPatterns', () => {
874
- const exampleSchema = {
875
- type: 'object',
876
- properties: {
877
- id: { type: 'string' },
878
- name: { type: 'string' },
879
- _secret: { type: 'string' }
880
- },
881
- required: ['id', 'name', 'secret']
882
- };
883
- const result = SchemaConverter.convert(exampleSchema, {
884
- target: SchemaConversionTarget.OpenAI,
885
- propertyFilter: (key) => key === 'id' || key === 'name'
886
- });
887
- expect(result.schema).toEqual({
888
- additionalProperties: false,
889
- properties: {
890
- id: {
891
- type: 'string'
892
- },
893
- name: {
894
- type: 'string'
895
- }
896
- },
897
- required: ['id', 'name'],
898
- type: 'object'
899
- });
900
- });
901
- test('inlines definitions', () => {
902
- const exampleSchema = {
903
- type: 'object',
904
- properties: {
905
- user: {
906
- $ref: '#/definitions/User'
907
- }
908
- },
909
- definitions: {
910
- User: {
911
- type: 'object',
912
- properties: {
913
- name: { type: 'string' },
914
- age: { type: 'integer' }
915
- },
916
- required: ['name']
917
- }
918
- }
919
- };
920
- const result = SchemaConverter.convert(exampleSchema, {
921
- target: SchemaConversionTarget.OpenAI,
922
- inlineDefinitions: true
923
- });
924
- expect(result.schema).toEqual({
925
- additionalProperties: false,
926
- properties: {
927
- user: {
928
- anyOf: [
929
- {
930
- additionalProperties: false,
931
- properties: {
932
- name: {
933
- type: 'string'
934
- },
935
- age: {
936
- type: ['integer', 'null']
937
- }
938
- },
939
- required: ['name', 'age'],
940
- type: 'object'
941
- },
942
- { type: 'null' }
943
- ]
944
- }
945
- },
946
- required: ['user'],
947
- type: 'object'
948
- });
949
- });
950
- test('only includes defs in the referenced graph, pruning other defs', () => {
951
- const exampleSchema = {
952
- type: 'object',
953
- properties: {
954
- user: {
955
- $ref: '#/definitions/User'
956
- }
957
- },
958
- $defs: {
959
- NotUsed: {
960
- type: 'object',
961
- properties: {
962
- foo: { type: 'string' }
963
- },
964
- required: ['foo']
965
- },
966
- Used: {
967
- type: 'object',
968
- properties: {
969
- foo: { type: 'string' }
970
- },
971
- required: ['foo']
972
- },
973
- User: {
974
- type: 'object',
975
- properties: {
976
- name: { type: 'string' },
977
- age: { type: 'integer' },
978
- used: {
979
- $ref: '#/$defs/Used'
980
- }
981
- },
982
- required: ['name', 'age']
983
- }
984
- }
985
- };
986
- const result = SchemaConverter.convert(exampleSchema, { target: SchemaConversionTarget.OpenAI });
987
- expect(result.stats).toEqual({ propertiesCount: 5, stringLength: 26, enumValuesCount: 0 });
988
- expect(result.warnings).toEqual([]);
989
- expect(result.schema).toEqual({
990
- type: 'object',
991
- additionalProperties: false,
992
- properties: {
993
- user: {
994
- anyOf: [
995
- {
996
- $ref: '#/$defs/User'
997
- },
998
- {
999
- type: 'null'
1000
- }
1001
- ]
1002
- }
1003
- },
1004
- required: ['user'],
1005
- $defs: {
1006
- User: {
1007
- type: 'object',
1008
- additionalProperties: false,
1009
- properties: {
1010
- name: {
1011
- type: 'string'
1012
- },
1013
- age: {
1014
- type: 'integer'
1015
- },
1016
- used: {
1017
- anyOf: [
1018
- {
1019
- $ref: '#/$defs/Used'
1020
- },
1021
- {
1022
- type: 'null'
1023
- }
1024
- ]
1025
- }
1026
- },
1027
- required: ['name', 'age', 'used']
1028
- },
1029
- Used: {
1030
- type: 'object',
1031
- additionalProperties: false,
1032
- properties: {
1033
- foo: {
1034
- type: 'string'
1035
- }
1036
- },
1037
- required: ['foo']
1038
- }
1039
- }
1040
- });
1041
- });
1042
- test('real world example', async () => {
1043
- const result = SchemaConverter.convert(searchShapeSchemaJson, {
1044
- target: SchemaConversionTarget.OpenAI,
1045
- inlineDefinitions: false,
1046
- propertyFilter: (key) => !key.startsWith('_') && key !== 'where'
1047
- });
1048
- expect(result.stats).toEqual({ propertiesCount: 10, stringLength: 87, enumValuesCount: 0 });
1049
- expect(result.warnings).toEqual([]);
1050
- expect(result.schema).toEqual({
1051
- type: 'object',
1052
- additionalProperties: false,
1053
- title: 'Args',
1054
- properties: {
1055
- terms: {
1056
- type: ['string', 'null']
1057
- },
1058
- from: {
1059
- type: ['integer', 'null'],
1060
- description: 'The offset from the first result you want to fetch.'
1061
- },
1062
- size: {
1063
- type: ['integer', 'null'],
1064
- description: 'The maximum number of items to return.'
1065
- },
1066
- filter: {
1067
- anyOf: [
1068
- {
1069
- type: 'object',
1070
- additionalProperties: false,
1071
- description: 'An elasticsearch style filter. Overrides onlyEnabled.',
1072
- properties: {},
1073
- required: []
1074
- },
1075
- {
1076
- type: 'null'
1077
- }
1078
- ]
1079
- },
1080
- sort: {
1081
- anyOf: [
1082
- {
1083
- type: 'array',
1084
- description: 'An list of fields to sort by.',
1085
- items: {
1086
- anyOf: [
1087
- {
1088
- $ref: '#/$defs/TSSearchSortInput'
1089
- },
1090
- {
1091
- type: 'null'
1092
- }
1093
- ]
1094
- }
1095
- },
1096
- {
1097
- type: 'null'
1098
- }
1099
- ]
1100
- },
1101
- locale: {
1102
- type: ['string', 'null']
1103
- },
1104
- enableLocaleFallback: {
1105
- type: ['boolean', 'null'],
1106
- description: 'Default value: true'
1107
- },
1108
- onlyEnabled: {
1109
- type: ['boolean', 'null'],
1110
- description: 'Filter out content that is not enabled. Defaults to true. Default value: true'
1111
- }
1112
- },
1113
- required: ['terms', 'from', 'size', 'filter', 'sort', 'locale', 'enableLocaleFallback', 'onlyEnabled'],
1114
- $defs: {
1115
- TSSearchSortInput: {
1116
- type: 'object',
1117
- additionalProperties: false,
1118
- title: 'TSSearchSortInput',
1119
- properties: {
1120
- field: {
1121
- type: 'string'
1122
- },
1123
- order: {
1124
- type: 'string',
1125
- description: 'asc for ascending or desc for descending'
1126
- }
1127
- },
1128
- required: ['field', 'order']
1129
- }
1130
- }
1131
- });
1132
- });
1133
- });
1134
- });