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

SignpostMarv / JSON-Schema-TypeScript-CodeGen / 18644002448

20 Oct 2025 06:21AM UTC coverage: 99.06% (-0.9%) from 100.0%
18644002448

push

github

SignpostMarv
negotiate external schemas

516 of 518 branches covered (99.61%)

Branch coverage included in aggregate %.

16 of 96 new or added lines in 2 files covered. (16.67%)

1 existing line in 1 file now uncovered.

8228 of 8309 relevant lines covered (99.03%)

599.12 hits per line

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

98.78
/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 {
19✔
22
        $ref,
19✔
23
} from './JSONSchema/Ref.ts';
19✔
24

19✔
25
import {
19✔
26
        ObjectUnspecified,
19✔
27
} from './JSONSchema/Object.ts';
19✔
28

19✔
29
import {
19✔
30
        ArrayType,
19✔
31
} from './JSONSchema/Array.ts';
19✔
32

19✔
33
import {
19✔
34
        PositiveIntegerGuard,
19✔
35
} from './guarded.ts';
19✔
36

19✔
37
import type {
19✔
38
        SchemaObject,
19✔
39
        SchemaObjectWith$id,
19✔
40
} from './types.ts';
19✔
41

19✔
42
import {
19✔
43
        Unknown,
19✔
44
} from './JSONSchema/Unknown.ts';
19✔
45

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

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

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

19✔
58
import {
19✔
59
        $defs,
19✔
60
} from './JSONSchema/$defs.ts';
19✔
61

19✔
62
type supported_type = (
19✔
63
        | Type<unknown>
19✔
64
);
19✔
65

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

19✔
88
type share_ajv_callback<T> = (ajv: Ajv) => T;
19✔
89

19✔
90
class SchemaParser {
19✔
91
        #ajv: Ajv;
19✔
92

201✔
93
        #schemas: {[key in SchemaObjectWith$id['$id']]: SchemaObjectWith$id} = {};
201✔
94

201✔
95
        types: [Type<unknown>, ...Type<unknown>[]];
19✔
96

19✔
97
        constructor(options: SchemaParserOptions = {
19✔
98
                ajv_options: {
201✔
99
                },
201✔
100
        }) {
201✔
101
                this.#ajv = SchemaParser.#AjvFactory(options);
201✔
102
                const {types} = options;
201✔
103
                this.types = (
201✔
104
                        types
201✔
105
                        || SchemaParser.#default_types(this.#ajv)
201✔
106
                );
201✔
107
        }
201✔
108

19✔
109
        get imports(): Set<string> {
19✔
110
                return new Set<string>(this.types.filter(
15✔
111
                        (maybe) => maybe instanceof $ref,
15✔
112
                ).flatMap((
15✔
113
                        instance: $ref,
15✔
114
                ) => [...instance.needs_import.values()]));
15✔
115
        }
15✔
116

19✔
117
        add_schema(
19✔
118
                schema: SchemaObjectWith$id,
2✔
119
        ) {
2✔
120
                this.#ajv.addSchema(schema);
2✔
121
                this.types.filter(
2✔
122
                        (maybe): maybe is $ref => maybe instanceof $ref,
2✔
123
                ).forEach((inform_this: $ref) => {
2✔
124
                        inform_this.remote_defs[schema.$id] = schema.$defs || {};
2✔
125
                });
2✔
126
                this.#schemas[schema.$id] = schema;
2✔
127
        }
2✔
128

19✔
129
        get_schema(id: SchemaObjectWith$id['$id']): SchemaObjectWith$id|undefined {
19✔
NEW
130
                if (id in this.#schemas) {
×
NEW
131
                        return this.#schemas[id];
×
NEW
132
                }
×
NEW
133

×
NEW
134
                return undefined;
×
UNCOV
135
        }
×
136

19✔
137
        maybe_parse<
19✔
138
                T extends Type<unknown>,
333✔
139
        >(
333✔
140
                schema: SchemaObject,
333✔
141
                must_be_of_type: typeof Type<unknown>,
333✔
142
        ): T|undefined {
333✔
143
                let result: T|undefined = undefined;
333✔
144
                for (const type of this.types) {
333✔
145
                        const maybe: (
3,455✔
146
                                | undefined
3,455✔
147
                                | Type<unknown>
3,455✔
148
                        ) = type.can_handle_schema(schema);
3,455✔
149

3,455✔
150
                        if (maybe) {
3,455✔
151
                                if (!must_be_of_type.is_a(maybe)) {
218✔
152
                                        continue;
1✔
153
                                }
1✔
154

217✔
155
                                result = maybe as T;
217✔
156
                                break;
217✔
157
                        }
217✔
158
                }
3,455✔
159

333✔
160
                return result;
333✔
161
        }
333✔
162

19✔
163
        maybe_parse_by_type<
19✔
164
                T extends Type<unknown>,
19✔
165
        >(
19✔
166
                value: unknown,
19✔
167
                must_be_of_type: (maybe: unknown) => maybe is T,
19✔
168
        ): T|undefined;
19✔
169
        maybe_parse_by_type(
19✔
170
                value: unknown,
19✔
171
                must_be_of_type?: undefined,
19✔
172
        ): Type<unknown>|undefined;
19✔
173
        maybe_parse_by_type<
19✔
174
                T extends Type<unknown>,
20,994✔
175
        >(
20,994✔
176
                value: unknown,
20,994✔
177
                must_be_of_type?: (maybe: unknown) => maybe is T,
20,994✔
178
        ): Type<unknown>|T|undefined {
20,994✔
179
                let result: T|undefined = undefined;
20,994✔
180
                for (const type of this.types) {
20,994✔
181
                        if (type.check_type(value)) {
313,407✔
182
                                if (!must_be_of_type) {
21,087✔
183
                                        return type;
11✔
184
                                }
11✔
185

21,076✔
186
                                if (!must_be_of_type(type)) {
21,081✔
187
                                        continue;
105✔
188
                                }
105✔
189

20,971✔
190
                                result = type;
20,971✔
191
                                break;
20,971✔
192
                        }
20,971✔
193
                }
313,407✔
194

20,983✔
195
                return result;
20,983✔
196
        }
20,994✔
197

19✔
198
        parse(
19✔
199
                schema: SchemaObject,
347✔
200
        ): Type<unknown> {
347✔
201
                if (0 === Object.keys(schema).length) {
347✔
202
                        const result = this.types.find<Unknown>(
30✔
203
                                (maybe) => maybe instanceof Unknown,
30✔
204
                        );
30✔
205

30✔
206
                        if (result) {
30✔
207
                                return result;
29✔
208
                        }
29✔
209
                }
30✔
210

318✔
211
                const result = this.maybe_parse<
318✔
212
                        Type<unknown>
318✔
213
                >(
318✔
214
                        schema,
318✔
215
                        Type,
318✔
216
                );
318✔
217

318✔
218
                if (undefined === result && '$ref' in schema) {
347✔
219
                        const maybe_$ref = this.types.find(
97✔
220
                                (maybe) => maybe instanceof $ref,
97✔
221
                        );
97✔
222

97✔
223
                        if (maybe_$ref && maybe_$ref.check_type(schema)) {
97✔
224
                                return maybe_$ref;
97✔
225
                        }
97✔
226
                }
97✔
227

221✔
228
                if (result) {
221✔
229
                        return result;
211✔
230
                }
211✔
231

10✔
232
                throw new TypeError('Could not determine type for schema!');
10✔
233
        }
347✔
234

19✔
235
        parse_by_type<
19✔
236
                T extends Type<unknown>,
19✔
237
        >(
19✔
238
                value: unknown,
19✔
239
                must_be_of_type: (maybe: unknown) => maybe is T,
19✔
240
        ): T;
19✔
241
        parse_by_type(
19✔
242
                value: unknown,
19✔
243
                must_be_of_type?: undefined,
19✔
244
        ): Type<unknown>;
19✔
245
        parse_by_type<
19✔
246
                T extends Type<unknown>,
20,975✔
247
        >(
20,975✔
248
                value: unknown,
20,975✔
249
                must_be_of_type?: (maybe: unknown) => maybe is T,
20,975✔
250
        ): Type<unknown>|T {
20,975✔
251
                const result = must_be_of_type
20,975✔
252
                        ? this.maybe_parse_by_type(value, must_be_of_type)
20,975✔
253
                        : this.maybe_parse_by_type(value);
20,975✔
254

20,975✔
255
                if (undefined === result) {
20,975✔
256
                        throw new TypeError(
1✔
257
                                'Could not determine type for schema!',
1✔
258
                        );
1✔
259
                }
1✔
260

20,971✔
261
                return result;
20,971✔
262
        }
20,975✔
263

19✔
264
        share_ajv<T>(
19✔
265
                callback: share_ajv_callback<T>,
21,120✔
266
        ): T {
21,120✔
267
                return callback(this.#ajv);
21,120✔
268
        }
21,120✔
269

19✔
270
        static #AjvFactory(options: SchemaParserOptions): Ajv {
19✔
271
                if ('ajv' in options) {
201✔
272
                        return options.ajv;
111✔
273
                }
111✔
274

90✔
275
                return new Ajv({
90✔
276
                        ...options.ajv_options,
90✔
277
                        strict: true,
90✔
278
                });
90✔
279
        }
201✔
280

19✔
281
        static #default_types(ajv: Ajv): [
19✔
282
                String<string>,
193✔
283
                EnumString<string, never[]>,
193✔
284
                PatternString<string, undefined>,
193✔
285
                ConstString,
193✔
286
                NonEmptyString,
193✔
287
                $ref,
193✔
288
                ObjectUnspecified<{[key: string]: unknown}, 'properties'>,
193✔
289
                ObjectUnspecified<{[key: string]: unknown}, 'pattern'>,
193✔
290
                ObjectUnspecified<{[key: string]: unknown}, 'both'>,
193✔
291
                ObjectUnspecified<{[key: string]: unknown}, 'neither'>,
193✔
292
                ArrayType<
193✔
293
                        'items',
193✔
294
                        'unspecified',
193✔
295
                        'no',
193✔
296
                        'optional'
193✔
297
                >,
193✔
298
                ArrayType<
193✔
299
                        'prefixItems',
193✔
300
                        'unspecified',
193✔
301
                        'no',
193✔
302
                        'optional'
193✔
303
                >,
193✔
304
                ArrayType<
193✔
305
                        'items',
193✔
306
                        'unspecified',
193✔
307
                        'yes',
193✔
308
                        'optional'
193✔
309
                >,
193✔
310
                ArrayType<
193✔
311
                        'prefixItems',
193✔
312
                        'unspecified',
193✔
313
                        'yes',
193✔
314
                        'optional'
193✔
315
                >,
193✔
316
                OneOf<unknown, 'unspecified'>,
193✔
317
                AllOf<unknown, 'unspecified'>,
193✔
318
                AnyOf<unknown, 'unspecified'>,
193✔
319
                $defs,
193✔
320
                Unknown,
193✔
321
        ] {
193✔
322
                return [
193✔
323
                        new String({
193✔
324
                                ajv,
193✔
325
                        }),
193✔
326
                        new EnumString([], {ajv}),
193✔
327
                        new PatternString(undefined, {ajv}),
193✔
328
                        new ConstString(undefined, {ajv}),
193✔
329
                        new NonEmptyString({ajv}),
193✔
330
                        new $ref(
193✔
331
                                {},
193✔
332
                                {
193✔
333
                                        ajv,
193✔
334
                                },
193✔
335
                        ),
193✔
336
                        new ObjectUnspecified(
193✔
337
                                {properties_mode: 'properties'},
193✔
338
                                {ajv},
193✔
339
                        ),
193✔
340
                        new ObjectUnspecified(
193✔
341
                                {properties_mode: 'pattern'},
193✔
342
                                {ajv},
193✔
343
                        ),
193✔
344
                        new ObjectUnspecified(
193✔
345
                                {properties_mode: 'both'},
193✔
346
                                {ajv},
193✔
347
                        ),
193✔
348
                        new ObjectUnspecified(
193✔
349
                                {properties_mode: 'neither'},
193✔
350
                                {ajv},
193✔
351
                        ),
193✔
352
                        new ArrayType(
193✔
353
                                {ajv},
193✔
354
                                {
193✔
355
                                        array_options: {
193✔
356
                                                array_mode: 'items',
193✔
357
                                                specified_mode: 'unspecified',
193✔
358
                                                unique_items_mode: 'no',
193✔
359
                                                min_items_mode: 'optional',
193✔
360
                                        },
193✔
361
                                },
193✔
362
                        ),
193✔
363
                        new ArrayType(
193✔
364
                                {ajv},
193✔
365
                                {
193✔
366
                                        array_options: {
193✔
367
                                                array_mode: 'prefixItems',
193✔
368
                                                specified_mode: 'unspecified',
193✔
369
                                                unique_items_mode: 'no',
193✔
370
                                                min_items_mode: 'optional',
193✔
371
                                                minItems: PositiveIntegerGuard(1),
193✔
372
                                        },
193✔
373
                                },
193✔
374
                        ),
193✔
375
                        new ArrayType(
193✔
376
                                {ajv},
193✔
377
                                {
193✔
378
                                        array_options: {
193✔
379
                                                array_mode: 'items',
193✔
380
                                                specified_mode: 'unspecified',
193✔
381
                                                unique_items_mode: 'yes',
193✔
382
                                                min_items_mode: 'optional',
193✔
383
                                        },
193✔
384
                                },
193✔
385
                        ),
193✔
386
                        new ArrayType(
193✔
387
                                {ajv},
193✔
388
                                {
193✔
389
                                        array_options: {
193✔
390
                                                array_mode: 'prefixItems',
193✔
391
                                                specified_mode: 'unspecified',
193✔
392
                                                unique_items_mode: 'yes',
193✔
393
                                                min_items_mode: 'optional',
193✔
394
                                                minItems: PositiveIntegerGuard(1),
193✔
395
                                        },
193✔
396
                                },
193✔
397
                        ),
193✔
398
                        new OneOf<unknown, 'unspecified'>({
193✔
399
                                ajv,
193✔
400
                                type_definition: {
193✔
401
                                        kind: 'oneOf',
193✔
402
                                        mode: 'unspecified',
193✔
403
                                },
193✔
404
                                schema_definition: {
193✔
405
                                        kind: 'oneOf',
193✔
406
                                        mode: 'unspecified',
193✔
407
                                },
193✔
408
                        }),
193✔
409
                        new AllOf<unknown, 'unspecified'>({
193✔
410
                                ajv,
193✔
411
                                type_definition: {
193✔
412
                                        kind: 'allOf',
193✔
413
                                        mode: 'unspecified',
193✔
414
                                },
193✔
415
                                schema_definition: {
193✔
416
                                        kind: 'allOf',
193✔
417
                                        mode: 'unspecified',
193✔
418
                                },
193✔
419
                        }),
193✔
420
                        new AnyOf<unknown, 'unspecified'>({
193✔
421
                                ajv,
193✔
422
                                type_definition: {
193✔
423
                                        kind: 'anyOf',
193✔
424
                                        mode: 'unspecified',
193✔
425
                                },
193✔
426
                                schema_definition: {
193✔
427
                                        kind: 'anyOf',
193✔
428
                                        mode: 'unspecified',
193✔
429
                                },
193✔
430
                        }),
193✔
431
                        new $defs({ajv}, {}),
193✔
432
                        new Unknown({ajv}),
193✔
433
                ];
193✔
434
        }
193✔
435
}
19✔
436

19✔
437
export type {
19✔
438
        supported_type,
19✔
439
        SchemaParserOptions,
19✔
440
        share_ajv_callback,
19✔
441
};
19✔
442

19✔
443
export {
19✔
444
        SchemaParser,
19✔
445
};
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