• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SignpostMarv / JSON-Schema-TypeScript-CodeGen / 19434745916

17 Nov 2025 03:11PM UTC coverage: 99.427% (-0.6%) from 100.0%
19434745916

push

github

SignpostMarv
increasing coverage

639 of 649 branches covered (98.46%)

Branch coverage included in aggregate %.

9256 of 9303 relevant lines covered (99.49%)

210.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.45
/src/SchemaParser.ts
1
import type {
19✔
2
        Options,
19✔
3
} from 'ajv/dist/2020.js';
19✔
4

19✔
5
import {
19✔
6
        Ajv2020 as Ajv,
19✔
7
} from 'ajv/dist/2020.js';
19✔
8

19✔
9
import {
19✔
10
        Type,
19✔
11
} from './JSONSchema/Type.ts';
19✔
12

19✔
13
import {
19✔
14
        ConstString,
19✔
15
        EnumString,
19✔
16
        NonEmptyString,
19✔
17
        PatternString,
19✔
18
        String,
19✔
19
} from './JSONSchema/String.ts';
19✔
20

19✔
21
import type {
19✔
22
        needs_import_from_module,
19✔
23
} from './JSONSchema/Ref.ts';
19✔
24
import {
19✔
25
        $ref,
19✔
26
} from './JSONSchema/Ref.ts';
19✔
27

19✔
28
import {
19✔
29
        ObjectUnspecified,
19✔
30
} from './JSONSchema/Object.ts';
19✔
31

19✔
32
import {
19✔
33
        ArrayType,
19✔
34
} from './JSONSchema/Array.ts';
19✔
35

19✔
36
import {
19✔
37
        PositiveIntegerGuard,
19✔
38
} from './guarded.ts';
19✔
39

19✔
40
import type {
19✔
41
        SchemaObject,
19✔
42
        SchemaObjectWith$id,
19✔
43
} from './types.ts';
19✔
44

19✔
45
import {
19✔
46
        Unknown,
19✔
47
} from './JSONSchema/Unknown.ts';
19✔
48

19✔
49
import {
19✔
50
        OneOf,
19✔
51
} from './JSONSchema/OneOf.ts';
19✔
52

19✔
53
import {
19✔
54
        AllOf,
19✔
55
} from './JSONSchema/AllOf.ts';
19✔
56

19✔
57
import {
19✔
58
        AnyOf,
19✔
59
} from './JSONSchema/AnyOf.ts';
19✔
60

19✔
61
import {
19✔
62
        $defs,
19✔
63
} from './JSONSchema/$defs.ts';
19✔
64

19✔
65
type supported_type = (
19✔
66
        | Type<unknown>
19✔
67
);
19✔
68

19✔
69
type SchemaParserOptions = (
19✔
70
        & (
19✔
71
                | {
19✔
72
                        ajv: Ajv,
19✔
73
                }
19✔
74
                | {
19✔
75
                        ajv_options: Omit<
19✔
76
                                Options,
19✔
77
                                (
19✔
78
                                        | 'strict'
19✔
79
                                )
19✔
80
                        >,
19✔
81
                }
19✔
82
        )
19✔
83
        & {
19✔
84
                types?: [
19✔
85
                        Type<unknown>,
19✔
86
                        ...Type<unknown>[],
19✔
87
                ],
19✔
88
        }
19✔
89
);
19✔
90

19✔
91
type share_ajv_callback<T> = (ajv: Ajv) => T;
19✔
92

19✔
93
type SchemaParser_types_member = (
19✔
94
        | Type<unknown>
19✔
95
        | $defs
19✔
96
);
19✔
97

19✔
98
class SchemaParser {
19✔
99
        #ajv: Ajv;
19✔
100

304✔
101
        #schemas: {[key in SchemaObjectWith$id['$id']]: SchemaObjectWith$id} = {};
304✔
102

304✔
103
        types: [SchemaParser_types_member, ...SchemaParser_types_member[]];
19✔
104

19✔
105
        constructor(options: SchemaParserOptions = {
19✔
106
                ajv_options: {
304✔
107
                },
304✔
108
        }) {
304✔
109
                this.#ajv = SchemaParser.#AjvFactory(options);
304✔
110
                const {types} = options;
304✔
111
                this.types = (
304✔
112
                        types
304✔
113
                        || SchemaParser.#default_types(this.#ajv)
304✔
114
                );
304✔
115
        }
304✔
116

19✔
117
        get imports(): Set<string> {
19✔
118
                return new Set<string>(this.types.filter(
21✔
119
                        (maybe) => maybe instanceof $ref,
21✔
120
                ).flatMap((
21✔
121
                        instance: $ref,
21✔
122
                ) => [...instance.needs_import.values()]));
21✔
123
        }
21✔
124

19✔
125
        get imports_from_module(): $ref['needs_import_from_module'] {
19✔
126
                const result: $ref['needs_import_from_module'] = new Set(
38✔
127
                        this.types.filter(
38✔
128
                                (maybe) => maybe instanceof $ref,
38✔
129
                        ).flatMap((
38✔
130
                                instance: $ref,
38✔
131
                        ) => [...instance.needs_import_from_module.values()]),
38✔
132
                );
38✔
133

38✔
134
                return result;
38✔
135
        }
38✔
136

19✔
137
        add_schema(
19✔
138
                schema: SchemaObjectWith$id,
7✔
139
        ) {
7✔
140
                this.#ajv.addSchema(schema);
7✔
141
                this.types.filter(
7✔
142
                        (maybe): maybe is $ref => maybe instanceof $ref,
7✔
143
                ).forEach((inform_this: $ref) => {
7✔
144
                        inform_this.remote_defs[schema.$id] = schema.$defs || {};
7✔
145
                });
7✔
146
                this.#schemas[schema.$id] = schema;
7✔
147
        }
7✔
148

19✔
149
        clear_imports() {
19✔
150
                this.types.filter(
1✔
151
                        (maybe) => maybe instanceof $ref,
1✔
152
                ).forEach(($ref) => {
1✔
153
                        $ref.needs_import.clear();
1✔
154
                        $ref.needs_import_from_module.clear();
1✔
155
                });
1✔
156
        }
1✔
157

19✔
158
        get_schema(id: SchemaObjectWith$id['$id']): SchemaObjectWith$id|undefined {
19✔
159
                if (id in this.#schemas) {
15✔
160
                        return this.#schemas[id];
13✔
161
                }
13✔
162

2✔
163
                return undefined;
2✔
164
        }
15✔
165

19✔
166
        maybe_parse<
19✔
167
                T extends Type<unknown>,
654✔
168
        >(
654✔
169
                schema: SchemaObject,
654✔
170
                must_be_of_type: typeof Type<unknown>|typeof $defs,
654✔
171
        ): T|undefined {
654✔
172
                let result: T|undefined = undefined;
654✔
173
                for (const type of this.types) {
654✔
174
                        const maybe: (
5,250✔
175
                                | undefined
5,250✔
176
                                | SchemaParser_types_member
5,250✔
177
                        ) = type.can_handle_schema(schema);
5,250✔
178

5,250✔
179
                        if (maybe) {
5,250✔
180
                                if (!must_be_of_type.is_a(maybe)) {
505✔
181
                                        continue;
1✔
182
                                }
1✔
183

504✔
184
                                result = maybe as T;
504✔
185
                                break;
504✔
186
                        }
504✔
187
                }
5,250✔
188

654✔
189
                return result;
654✔
190
        }
654✔
191

19✔
192
        maybe_parse_by_type<
19✔
193
                T extends Type<unknown>,
19✔
194
        >(
19✔
195
                value: unknown,
19✔
196
                must_be_of_type: (maybe: unknown) => maybe is T,
19✔
197
        ): T|undefined;
19✔
198
        maybe_parse_by_type(
19✔
199
                value: unknown,
19✔
200
                must_be_of_type?: undefined,
19✔
201
        ): Type<unknown>|undefined;
19✔
202
        maybe_parse_by_type<
19✔
203
                T extends Type<unknown>,
133✔
204
        >(
133✔
205
                value: unknown,
133✔
206
                must_be_of_type?: (maybe: unknown) => maybe is T,
133✔
207
        ): SchemaParser_types_member|T|undefined {
133✔
208
                let result: T|undefined = undefined;
133✔
209
                for (const type of this.types) {
133✔
210
                        if (type.check_type(value)) {
856✔
211
                                if (!must_be_of_type) {
289✔
212
                                        return type;
62✔
213
                                }
62✔
214

227✔
215
                                if (!must_be_of_type(type)) {
238✔
216
                                        continue;
181✔
217
                                }
181✔
218

46✔
219
                                result = type;
46✔
220
                                break;
46✔
221
                        }
46✔
222
                }
856✔
223

71✔
224
                return result;
71✔
225
        }
133✔
226

19✔
227
        needs_import_from_module(value: needs_import_from_module) {
19✔
228
                this.types.filter(
8✔
229
                        (maybe) => maybe instanceof $ref,
8✔
230
                ).forEach(($ref) => {
8✔
231
                        $ref.needs_import_from_module.add(value);
8✔
232
                });
8✔
233
        }
8✔
234

19✔
235
        parse(
19✔
236
                schema: SchemaObject,
641✔
237
        ): Type<unknown> {
641✔
238
                if (0 === Object.keys(schema).length) {
641✔
239
                        const result = this.types.find<Unknown>(
1✔
240
                                (maybe) => maybe instanceof Unknown,
1✔
241
                        );
1✔
242

1✔
243
                        if (result) {
1!
244
                                return result;
×
245
                        }
×
246
                }
1✔
247

641✔
248
                const result = this.maybe_parse<
641✔
249
                        Type<unknown>
641✔
250
                >(
641✔
251
                        schema,
641✔
252
                        Type,
641✔
253
                );
641✔
254

641✔
255
                if (undefined === result && '$ref' in schema) {
641✔
256
                        const maybe_$ref = this.types.find(
131✔
257
                                (maybe) => maybe instanceof $ref,
131✔
258
                        );
131✔
259

131✔
260
                        if (maybe_$ref && maybe_$ref.check_type(schema)) {
131✔
261
                                return maybe_$ref;
131✔
262
                        }
131✔
263
                }
131✔
264

510✔
265
                if (result) {
510✔
266
                        return result;
500✔
267
                }
500✔
268

10✔
269
                throw new TypeError('Could not determine type for schema!');
10✔
270
        }
641✔
271

19✔
272
        parse_by_type<
19✔
273
                T extends Type<unknown>,
19✔
274
        >(
19✔
275
                value: unknown,
19✔
276
                must_be_of_type: (maybe: unknown) => maybe is T,
19✔
277
        ): T;
19✔
278
        parse_by_type(
19✔
279
                value: unknown,
19✔
280
                must_be_of_type?: undefined,
19✔
281
        ): Type<unknown>;
19✔
282
        parse_by_type<
19✔
283
                T extends Type<unknown>,
98✔
284
        >(
98✔
285
                value: unknown,
98✔
286
                must_be_of_type?: (maybe: unknown) => maybe is T,
98✔
287
        ): Type<unknown>|T {
98✔
288
                const result = must_be_of_type
98✔
289
                        ? this.maybe_parse_by_type(value, must_be_of_type)
98✔
290
                        : this.maybe_parse_by_type(value);
98✔
291

98✔
292
                if (undefined === result) {
98✔
293
                        throw new TypeError(
6✔
294
                                'Could not determine type for schema!',
6✔
295
                        );
6✔
296
                }
6✔
297

92✔
298
                return result;
92✔
299
        }
98✔
300

19✔
301
        share_ajv<T>(
19✔
302
                callback: share_ajv_callback<T>,
599✔
303
        ): T {
599✔
304
                return callback(this.#ajv);
599✔
305
        }
599✔
306

19✔
307
        static #AjvFactory(options: SchemaParserOptions): Ajv {
19✔
308
                if ('ajv' in options) {
304✔
309
                        return options.ajv;
181✔
310
                }
181✔
311

123✔
312
                return new Ajv({
123✔
313
                        ...options.ajv_options,
123✔
314
                        strict: true,
123✔
315
                });
123✔
316
        }
304✔
317

19✔
318
        static #default_types(ajv: Ajv): [
19✔
319
                String<string>,
296✔
320
                EnumString<string, never[]>,
296✔
321
                PatternString<string, undefined>,
296✔
322
                ConstString,
296✔
323
                NonEmptyString,
296✔
324
                $ref,
296✔
325
                ObjectUnspecified<{[key: string]: unknown}, 'properties'>,
296✔
326
                ObjectUnspecified<{[key: string]: unknown}, 'pattern'>,
296✔
327
                ObjectUnspecified<{[key: string]: unknown}, 'both'>,
296✔
328
                ObjectUnspecified<{[key: string]: unknown}, 'neither'>,
296✔
329
                ArrayType<
296✔
330
                        'items',
296✔
331
                        'unspecified',
296✔
332
                        'no',
296✔
333
                        'optional'
296✔
334
                >,
296✔
335
                ArrayType<
296✔
336
                        'prefixItems',
296✔
337
                        'unspecified',
296✔
338
                        'no',
296✔
339
                        'optional'
296✔
340
                >,
296✔
341
                ArrayType<
296✔
342
                        'items',
296✔
343
                        'unspecified',
296✔
344
                        'yes',
296✔
345
                        'optional'
296✔
346
                >,
296✔
347
                ArrayType<
296✔
348
                        'prefixItems',
296✔
349
                        'unspecified',
296✔
350
                        'yes',
296✔
351
                        'optional'
296✔
352
                >,
296✔
353
                OneOf<unknown, 'unspecified'>,
296✔
354
                AllOf<unknown, 'unspecified'>,
296✔
355
                AnyOf<unknown, 'unspecified'>,
296✔
356
                $defs,
296✔
357

296✔
358
                /*
296✔
359
                Unknown,
296✔
360
                */
296✔
361
        ] {
296✔
362
                return [
296✔
363
                        new String({
296✔
364
                                ajv,
296✔
365
                        }),
296✔
366
                        new EnumString([], {ajv}),
296✔
367
                        new PatternString(undefined, {ajv}),
296✔
368
                        new ConstString(undefined, {ajv}),
296✔
369
                        new NonEmptyString({ajv}),
296✔
370
                        new $ref(
296✔
371
                                {},
296✔
372
                                {
296✔
373
                                        ajv,
296✔
374
                                },
296✔
375
                        ),
296✔
376
                        new ObjectUnspecified(
296✔
377
                                {properties_mode: 'properties'},
296✔
378
                                {ajv},
296✔
379
                        ),
296✔
380
                        new ObjectUnspecified(
296✔
381
                                {properties_mode: 'pattern'},
296✔
382
                                {ajv},
296✔
383
                        ),
296✔
384
                        new ObjectUnspecified(
296✔
385
                                {properties_mode: 'both'},
296✔
386
                                {ajv},
296✔
387
                        ),
296✔
388
                        new ObjectUnspecified(
296✔
389
                                {properties_mode: 'neither'},
296✔
390
                                {ajv},
296✔
391
                        ),
296✔
392
                        new ArrayType(
296✔
393
                                {ajv},
296✔
394
                                {
296✔
395
                                        array_options: {
296✔
396
                                                array_mode: 'items',
296✔
397
                                                specified_mode: 'unspecified',
296✔
398
                                                unique_items_mode: 'no',
296✔
399
                                                min_items_mode: 'optional',
296✔
400
                                        },
296✔
401
                                },
296✔
402
                        ),
296✔
403
                        new ArrayType(
296✔
404
                                {ajv},
296✔
405
                                {
296✔
406
                                        array_options: {
296✔
407
                                                array_mode: 'prefixItems',
296✔
408
                                                specified_mode: 'unspecified',
296✔
409
                                                unique_items_mode: 'no',
296✔
410
                                                min_items_mode: 'optional',
296✔
411
                                                minItems: PositiveIntegerGuard(1),
296✔
412
                                        },
296✔
413
                                },
296✔
414
                        ),
296✔
415
                        new ArrayType(
296✔
416
                                {ajv},
296✔
417
                                {
296✔
418
                                        array_options: {
296✔
419
                                                array_mode: 'items',
296✔
420
                                                specified_mode: 'unspecified',
296✔
421
                                                unique_items_mode: 'yes',
296✔
422
                                                min_items_mode: 'optional',
296✔
423
                                        },
296✔
424
                                },
296✔
425
                        ),
296✔
426
                        new ArrayType(
296✔
427
                                {ajv},
296✔
428
                                {
296✔
429
                                        array_options: {
296✔
430
                                                array_mode: 'prefixItems',
296✔
431
                                                specified_mode: 'unspecified',
296✔
432
                                                unique_items_mode: 'yes',
296✔
433
                                                min_items_mode: 'optional',
296✔
434
                                                minItems: PositiveIntegerGuard(1),
296✔
435
                                        },
296✔
436
                                },
296✔
437
                        ),
296✔
438
                        new OneOf<unknown, 'unspecified'>({
296✔
439
                                ajv,
296✔
440
                                type_definition: {
296✔
441
                                        kind: 'oneOf',
296✔
442
                                        mode: 'unspecified',
296✔
443
                                },
296✔
444
                                schema_definition: {
296✔
445
                                        kind: 'oneOf',
296✔
446
                                        mode: 'unspecified',
296✔
447
                                },
296✔
448
                        }),
296✔
449
                        new AllOf<unknown, 'unspecified'>({
296✔
450
                                ajv,
296✔
451
                                type_definition: {
296✔
452
                                        kind: 'allOf',
296✔
453
                                        mode: 'unspecified',
296✔
454
                                },
296✔
455
                                schema_definition: {
296✔
456
                                        kind: 'allOf',
296✔
457
                                        mode: 'unspecified',
296✔
458
                                },
296✔
459
                        }),
296✔
460
                        new AnyOf<unknown, 'unspecified'>({
296✔
461
                                ajv,
296✔
462
                                type_definition: {
296✔
463
                                        kind: 'anyOf',
296✔
464
                                        mode: 'unspecified',
296✔
465
                                },
296✔
466
                                schema_definition: {
296✔
467
                                        kind: 'anyOf',
296✔
468
                                        mode: 'unspecified',
296✔
469
                                },
296✔
470
                        }),
296✔
471
                        new $defs({ajv}, {}, {}),
296✔
472

296✔
473
                        /*
296✔
474
                        new Unknown({ajv}),
296✔
475
                        */
296✔
476
                ];
296✔
477
        }
296✔
478
}
19✔
479

19✔
480
export type {
19✔
481
        supported_type,
19✔
482
        SchemaParserOptions,
19✔
483
        share_ajv_callback,
19✔
484
};
19✔
485

19✔
486
export {
19✔
487
        SchemaParser,
19✔
488
};
19✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc