@naturalcycles/nodejs-lib 15.48.0 → 15.50.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/dist/validation/ajv/ajvSchema.d.ts +3 -3
- package/dist/validation/ajv/ajvSchema.js +2 -2
- package/dist/validation/ajv/from-data/generateJsonSchemaFromData.d.ts +8 -0
- package/dist/validation/ajv/from-data/generateJsonSchemaFromData.js +87 -0
- package/dist/validation/ajv/index.d.ts +1 -0
- package/dist/validation/ajv/index.js +1 -0
- package/dist/validation/ajv/jsonSchemaBuilder.d.ts +17 -11
- package/dist/validation/ajv/jsonSchemaBuilder.js +27 -19
- package/dist/yargs/yargs.util.js +1 -1
- package/package.json +1 -1
- package/src/validation/ajv/ajvSchema.ts +6 -8
- package/src/validation/ajv/from-data/generateJsonSchemaFromData.ts +112 -0
- package/src/validation/ajv/index.ts +1 -0
- package/src/validation/ajv/jsonSchemaBuilder.ts +36 -23
- package/src/yargs/yargs.util.ts +1 -1
|
@@ -3,7 +3,7 @@ import { type AnyObject } from '@naturalcycles/js-lib/types';
|
|
|
3
3
|
import type { ZodType } from '@naturalcycles/js-lib/zod';
|
|
4
4
|
import type { Ajv } from 'ajv';
|
|
5
5
|
import { AjvValidationError } from './ajvValidationError.js';
|
|
6
|
-
import { type JsonSchema,
|
|
6
|
+
import { type JsonSchema, JsonSchemaTerminal } from './jsonSchemaBuilder.js';
|
|
7
7
|
/**
|
|
8
8
|
* On creation - compiles ajv validation function.
|
|
9
9
|
* Provides convenient methods, error reporting, etc.
|
|
@@ -29,7 +29,7 @@ export declare class AjvSchema<IN = unknown, OUT = IN> {
|
|
|
29
29
|
* @deprecated Use `j` to build schemas, not `z` or `zod`.
|
|
30
30
|
*/
|
|
31
31
|
static createFromZod<T extends ZodType<any, any, any>>(schema: T): AjvSchema<T['_input'], T['_output']>;
|
|
32
|
-
static isJsonSchemaBuilder<IN, OUT>(schema: unknown): schema is
|
|
32
|
+
static isJsonSchemaBuilder<IN, OUT>(schema: unknown): schema is JsonSchemaTerminal<IN, OUT, any>;
|
|
33
33
|
readonly cfg: AjvSchemaCfg;
|
|
34
34
|
/**
|
|
35
35
|
* It returns the original object just for convenience.
|
|
@@ -94,4 +94,4 @@ export interface AjvSchemaCfg {
|
|
|
94
94
|
*/
|
|
95
95
|
lazy?: boolean;
|
|
96
96
|
}
|
|
97
|
-
export type SchemaHandledByAjv<IN, OUT = IN> =
|
|
97
|
+
export type SchemaHandledByAjv<IN, OUT = IN> = JsonSchemaTerminal<IN, OUT, any> | JsonSchema<IN, OUT> | AjvSchema<IN, OUT>;
|
|
@@ -7,7 +7,7 @@ import { z } from '@naturalcycles/js-lib/zod';
|
|
|
7
7
|
import { _inspect } from '../../string/inspect.js';
|
|
8
8
|
import { AjvValidationError } from './ajvValidationError.js';
|
|
9
9
|
import { getAjv } from './getAjv.js';
|
|
10
|
-
import {
|
|
10
|
+
import { JsonSchemaTerminal } from './jsonSchemaBuilder.js';
|
|
11
11
|
/**
|
|
12
12
|
* On creation - compiles ajv validation function.
|
|
13
13
|
* Provides convenient methods, error reporting, etc.
|
|
@@ -73,7 +73,7 @@ export class AjvSchema {
|
|
|
73
73
|
return AjvSchema.create(jsonSchema);
|
|
74
74
|
}
|
|
75
75
|
static isJsonSchemaBuilder(schema) {
|
|
76
|
-
return schema instanceof
|
|
76
|
+
return schema instanceof JsonSchemaTerminal;
|
|
77
77
|
}
|
|
78
78
|
cfg;
|
|
79
79
|
/**
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type AnyObject } from '@naturalcycles/js-lib/types';
|
|
2
|
+
import type { JsonSchema } from '../jsonSchemaBuilder.js';
|
|
3
|
+
/**
|
|
4
|
+
* Each row must be an object (current limitation).
|
|
5
|
+
*
|
|
6
|
+
* `additionalProperties` is set to `true`, cause it's safer.
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateJsonSchemaFromData<T extends AnyObject = AnyObject>(rows: AnyObject[]): JsonSchema<T>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { _uniq } from '@naturalcycles/js-lib/array';
|
|
2
|
+
import { _stringMapEntries } from '@naturalcycles/js-lib/types';
|
|
3
|
+
/**
|
|
4
|
+
* Each row must be an object (current limitation).
|
|
5
|
+
*
|
|
6
|
+
* `additionalProperties` is set to `true`, cause it's safer.
|
|
7
|
+
*/
|
|
8
|
+
export function generateJsonSchemaFromData(rows) {
|
|
9
|
+
return objectToJsonSchema(rows);
|
|
10
|
+
}
|
|
11
|
+
function objectToJsonSchema(rows) {
|
|
12
|
+
const typesByKey = {};
|
|
13
|
+
rows.forEach(r => {
|
|
14
|
+
Object.keys(r).forEach(key => {
|
|
15
|
+
typesByKey[key] ||= new Set();
|
|
16
|
+
typesByKey[key].add(getTypeOfValue(r[key]));
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
const s = {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {},
|
|
22
|
+
required: [],
|
|
23
|
+
additionalProperties: true,
|
|
24
|
+
};
|
|
25
|
+
_stringMapEntries(typesByKey).forEach(([key, types]) => {
|
|
26
|
+
const schema = mergeTypes([...types], rows.map(r => r[key]));
|
|
27
|
+
if (!schema)
|
|
28
|
+
return;
|
|
29
|
+
s.properties[key] = schema;
|
|
30
|
+
});
|
|
31
|
+
// console.log(typesByKey)
|
|
32
|
+
return s;
|
|
33
|
+
}
|
|
34
|
+
function mergeTypes(types, samples) {
|
|
35
|
+
// skip "undefined" types
|
|
36
|
+
types = types.filter(t => t !== 'undefined');
|
|
37
|
+
if (!types.length)
|
|
38
|
+
return undefined;
|
|
39
|
+
if (types.length > 1) {
|
|
40
|
+
// oneOf
|
|
41
|
+
const s = {
|
|
42
|
+
oneOf: types.map(type => mergeTypes([type], samples)),
|
|
43
|
+
};
|
|
44
|
+
return s;
|
|
45
|
+
}
|
|
46
|
+
const type = types[0];
|
|
47
|
+
if (type === 'null') {
|
|
48
|
+
return {
|
|
49
|
+
type: 'null',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (type === 'boolean') {
|
|
53
|
+
return {
|
|
54
|
+
type: 'boolean',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (type === 'string') {
|
|
58
|
+
return {
|
|
59
|
+
type: 'string',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (type === 'number') {
|
|
63
|
+
return {
|
|
64
|
+
type: 'number',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (type === 'object') {
|
|
68
|
+
return objectToJsonSchema(samples.filter((r) => r && typeof r === 'object'));
|
|
69
|
+
}
|
|
70
|
+
if (type === 'array') {
|
|
71
|
+
// possible feature: detect if it's a tuple
|
|
72
|
+
// currently assume no-tuple
|
|
73
|
+
const items = samples.filter(r => Array.isArray(r)).flat();
|
|
74
|
+
const itemTypes = _uniq(items.map(i => getTypeOfValue(i)));
|
|
75
|
+
return {
|
|
76
|
+
type: 'array',
|
|
77
|
+
items: mergeTypes(itemTypes, items),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function getTypeOfValue(v) {
|
|
82
|
+
if (v === null)
|
|
83
|
+
return 'null';
|
|
84
|
+
if (Array.isArray(v))
|
|
85
|
+
return 'array';
|
|
86
|
+
return typeof v;
|
|
87
|
+
}
|
|
@@ -15,9 +15,23 @@ export declare const j: {
|
|
|
15
15
|
enum<const T extends readonly (string | number | boolean | null)[] | StringEnum | NumberEnum>(input: T, opt?: JsonBuilderRuleOpt): JsonSchemaEnumBuilder<T extends readonly (infer U)[] ? U : T extends StringEnum ? T[keyof T] : T extends NumberEnum ? T[keyof T] : never>;
|
|
16
16
|
oneOf<B extends readonly JsonSchemaAnyBuilder<any, any, boolean>[], IN = BuilderInUnion<B>, OUT = BuilderOutUnion<B>>(items: [...B]): JsonSchemaAnyBuilder<IN, OUT, false>;
|
|
17
17
|
};
|
|
18
|
-
export declare class
|
|
18
|
+
export declare class JsonSchemaTerminal<IN, OUT, Opt> {
|
|
19
19
|
protected schema: JsonSchema;
|
|
20
20
|
constructor(schema: JsonSchema);
|
|
21
|
+
getSchema(): JsonSchema;
|
|
22
|
+
/**
|
|
23
|
+
* Produces a "clean schema object" without methods.
|
|
24
|
+
* Same as if it would be JSON.stringified.
|
|
25
|
+
*/
|
|
26
|
+
build(): JsonSchema<IN, OUT>;
|
|
27
|
+
clone(): JsonSchemaAnyBuilder<IN, OUT, Opt>;
|
|
28
|
+
/**
|
|
29
|
+
* @experimental
|
|
30
|
+
*/
|
|
31
|
+
in: IN;
|
|
32
|
+
out: OUT;
|
|
33
|
+
}
|
|
34
|
+
export declare class JsonSchemaAnyBuilder<IN, OUT, Opt> extends JsonSchemaTerminal<IN, OUT, Opt> {
|
|
21
35
|
protected setErrorMessage(ruleName: string, errorMessage: string | undefined): void;
|
|
22
36
|
/**
|
|
23
37
|
* A helper function that takes a type parameter and compares it with the type inferred from the schema.
|
|
@@ -33,7 +47,6 @@ export declare class JsonSchemaAnyBuilder<IN, OUT, Opt> {
|
|
|
33
47
|
* ```
|
|
34
48
|
*/
|
|
35
49
|
isOfType<ExpectedType>(): ExactMatch<ExpectedType, OUT> extends true ? this : never;
|
|
36
|
-
getSchema(): JsonSchema;
|
|
37
50
|
$schema($schema: string): this;
|
|
38
51
|
$schemaDraft7(): this;
|
|
39
52
|
$id($id: string): this;
|
|
@@ -45,22 +58,15 @@ export declare class JsonSchemaAnyBuilder<IN, OUT, Opt> {
|
|
|
45
58
|
instanceof(of: string): this;
|
|
46
59
|
optional(): JsonSchemaAnyBuilder<IN | undefined, OUT | undefined, true>;
|
|
47
60
|
nullable(): JsonSchemaAnyBuilder<IN | null, OUT | null, Opt>;
|
|
48
|
-
/**
|
|
49
|
-
* Produces a "clean schema object" without methods.
|
|
50
|
-
* Same as if it would be JSON.stringified.
|
|
51
|
-
*/
|
|
52
|
-
build(): JsonSchema<IN, OUT>;
|
|
53
|
-
clone(): JsonSchemaAnyBuilder<IN, OUT, Opt>;
|
|
54
61
|
/**
|
|
55
62
|
* @deprecated
|
|
56
63
|
* The usage of this function is discouraged as it defeats the purpose of having type-safe validation.
|
|
57
64
|
*/
|
|
58
65
|
castAs<T>(): JsonSchemaAnyBuilder<T, T, Opt>;
|
|
59
66
|
/**
|
|
60
|
-
*
|
|
67
|
+
* Locks the given schema chain and no other modification can be done to it.
|
|
61
68
|
*/
|
|
62
|
-
|
|
63
|
-
out: OUT;
|
|
69
|
+
final(): JsonSchemaTerminal<IN, OUT, Opt>;
|
|
64
70
|
}
|
|
65
71
|
export declare class JsonSchemaStringBuilder<IN extends string = string, OUT = IN, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
|
|
66
72
|
constructor();
|
|
@@ -78,11 +78,31 @@ const TS_2000_MILLIS = TS_2000 * 1000;
|
|
|
78
78
|
which means that the `foo` property would be mandatory, it's just that its value can be `undefined` as well.
|
|
79
79
|
With `Opt`, we can infer it as `{ foo?: string | undefined }`.
|
|
80
80
|
*/
|
|
81
|
-
export class
|
|
81
|
+
export class JsonSchemaTerminal {
|
|
82
82
|
schema;
|
|
83
83
|
constructor(schema) {
|
|
84
84
|
this.schema = schema;
|
|
85
85
|
}
|
|
86
|
+
getSchema() {
|
|
87
|
+
return this.schema;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Produces a "clean schema object" without methods.
|
|
91
|
+
* Same as if it would be JSON.stringified.
|
|
92
|
+
*/
|
|
93
|
+
build() {
|
|
94
|
+
return _sortObject(JSON.parse(JSON.stringify(this.schema)), JSON_SCHEMA_ORDER);
|
|
95
|
+
}
|
|
96
|
+
clone() {
|
|
97
|
+
return new JsonSchemaAnyBuilder(_deepCopy(this.schema));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* @experimental
|
|
101
|
+
*/
|
|
102
|
+
in;
|
|
103
|
+
out;
|
|
104
|
+
}
|
|
105
|
+
export class JsonSchemaAnyBuilder extends JsonSchemaTerminal {
|
|
86
106
|
setErrorMessage(ruleName, errorMessage) {
|
|
87
107
|
if (_isUndefined(errorMessage))
|
|
88
108
|
return;
|
|
@@ -106,9 +126,6 @@ export class JsonSchemaAnyBuilder {
|
|
|
106
126
|
_objectAssign(this.schema, { hasIsOfTypeCheck: true });
|
|
107
127
|
return this;
|
|
108
128
|
}
|
|
109
|
-
getSchema() {
|
|
110
|
-
return this.schema;
|
|
111
|
-
}
|
|
112
129
|
$schema($schema) {
|
|
113
130
|
_objectAssign(this.schema, { $schema });
|
|
114
131
|
return this;
|
|
@@ -154,16 +171,6 @@ export class JsonSchemaAnyBuilder {
|
|
|
154
171
|
anyOf: [this.build(), { type: 'null' }],
|
|
155
172
|
});
|
|
156
173
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Produces a "clean schema object" without methods.
|
|
159
|
-
* Same as if it would be JSON.stringified.
|
|
160
|
-
*/
|
|
161
|
-
build() {
|
|
162
|
-
return _sortObject(JSON.parse(JSON.stringify(this.schema)), JSON_SCHEMA_ORDER);
|
|
163
|
-
}
|
|
164
|
-
clone() {
|
|
165
|
-
return new JsonSchemaAnyBuilder(_deepCopy(this.schema));
|
|
166
|
-
}
|
|
167
174
|
/**
|
|
168
175
|
* @deprecated
|
|
169
176
|
* The usage of this function is discouraged as it defeats the purpose of having type-safe validation.
|
|
@@ -172,10 +179,11 @@ export class JsonSchemaAnyBuilder {
|
|
|
172
179
|
return this;
|
|
173
180
|
}
|
|
174
181
|
/**
|
|
175
|
-
*
|
|
182
|
+
* Locks the given schema chain and no other modification can be done to it.
|
|
176
183
|
*/
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
final() {
|
|
185
|
+
return new JsonSchemaTerminal(this.schema);
|
|
186
|
+
}
|
|
179
187
|
}
|
|
180
188
|
export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
|
|
181
189
|
constructor() {
|
|
@@ -488,7 +496,7 @@ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
|
|
|
488
496
|
/**
|
|
489
497
|
* Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
|
|
490
498
|
*/
|
|
491
|
-
//
|
|
499
|
+
// oxlint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
|
|
492
500
|
dbEntity() {
|
|
493
501
|
return this.extend({
|
|
494
502
|
id: j.string(),
|
|
@@ -550,7 +558,7 @@ export class JsonSchemaObjectInferringBuilder extends JsonSchemaAnyBuilder {
|
|
|
550
558
|
/**
|
|
551
559
|
* Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
|
|
552
560
|
*/
|
|
553
|
-
//
|
|
561
|
+
// oxlint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
|
|
554
562
|
dbEntity() {
|
|
555
563
|
return this.extend({
|
|
556
564
|
id: j.string(),
|
package/dist/yargs/yargs.util.js
CHANGED
|
@@ -5,7 +5,7 @@ import { hideBin } from 'yargs/helpers';
|
|
|
5
5
|
* Quick yargs helper to make it work in esm.
|
|
6
6
|
* It also allows to not have yargs and `@types/yargs` to be declared as dependencies.
|
|
7
7
|
*/
|
|
8
|
-
//
|
|
8
|
+
// oxlint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
|
|
9
9
|
export function _yargs() {
|
|
10
10
|
return yargs(hideBin(process.argv));
|
|
11
11
|
}
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@ import type { Ajv, ErrorObject } from 'ajv'
|
|
|
14
14
|
import { _inspect } from '../../string/inspect.js'
|
|
15
15
|
import { AjvValidationError } from './ajvValidationError.js'
|
|
16
16
|
import { getAjv } from './getAjv.js'
|
|
17
|
-
import { type JsonSchema,
|
|
17
|
+
import { type JsonSchema, JsonSchemaTerminal } from './jsonSchemaBuilder.js'
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* On creation - compiles ajv validation function.
|
|
@@ -73,10 +73,10 @@ export class AjvSchema<IN = unknown, OUT = IN> {
|
|
|
73
73
|
let jsonSchema: JsonSchema<IN, OUT>
|
|
74
74
|
|
|
75
75
|
if (AjvSchema.isJsonSchemaBuilder(schema)) {
|
|
76
|
-
jsonSchema =
|
|
76
|
+
jsonSchema = schema.build()
|
|
77
77
|
AjvSchema.requireValidJsonSchema(jsonSchema)
|
|
78
78
|
} else {
|
|
79
|
-
jsonSchema = schema
|
|
79
|
+
jsonSchema = schema
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const ajvSchema = new AjvSchema<IN, OUT>(jsonSchema, cfg)
|
|
@@ -98,10 +98,8 @@ export class AjvSchema<IN = unknown, OUT = IN> {
|
|
|
98
98
|
return AjvSchema.create(jsonSchema)
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
static isJsonSchemaBuilder<IN, OUT>(
|
|
102
|
-
schema
|
|
103
|
-
): schema is JsonSchemaAnyBuilder<IN, OUT, any> {
|
|
104
|
-
return schema instanceof JsonSchemaAnyBuilder
|
|
101
|
+
static isJsonSchemaBuilder<IN, OUT>(schema: unknown): schema is JsonSchemaTerminal<IN, OUT, any> {
|
|
102
|
+
return schema instanceof JsonSchemaTerminal
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
readonly cfg: AjvSchemaCfg
|
|
@@ -286,6 +284,6 @@ export interface AjvSchemaCfg {
|
|
|
286
284
|
}
|
|
287
285
|
|
|
288
286
|
export type SchemaHandledByAjv<IN, OUT = IN> =
|
|
289
|
-
|
|
|
287
|
+
| JsonSchemaTerminal<IN, OUT, any>
|
|
290
288
|
| JsonSchema<IN, OUT>
|
|
291
289
|
| AjvSchema<IN, OUT>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { _uniq } from '@naturalcycles/js-lib/array'
|
|
2
|
+
import { _stringMapEntries, type AnyObject, type StringMap } from '@naturalcycles/js-lib/types'
|
|
3
|
+
import type { JsonSchema } from '../jsonSchemaBuilder.js'
|
|
4
|
+
|
|
5
|
+
type PrimitiveType = 'undefined' | 'null' | 'boolean' | 'string' | 'number'
|
|
6
|
+
type Type = PrimitiveType | 'array' | 'object'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Each row must be an object (current limitation).
|
|
10
|
+
*
|
|
11
|
+
* `additionalProperties` is set to `true`, cause it's safer.
|
|
12
|
+
*/
|
|
13
|
+
export function generateJsonSchemaFromData<T extends AnyObject = AnyObject>(
|
|
14
|
+
rows: AnyObject[],
|
|
15
|
+
): JsonSchema<T> {
|
|
16
|
+
return objectToJsonSchema<T>(rows as any)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function objectToJsonSchema<T extends AnyObject>(rows: AnyObject[]): JsonSchema<T> {
|
|
20
|
+
const typesByKey: StringMap<Set<Type>> = {}
|
|
21
|
+
|
|
22
|
+
rows.forEach(r => {
|
|
23
|
+
Object.keys(r).forEach(key => {
|
|
24
|
+
typesByKey[key] ||= new Set<Type>()
|
|
25
|
+
typesByKey[key].add(getTypeOfValue(r[key]))
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const s: JsonSchema<T> = {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {} as any,
|
|
32
|
+
required: [],
|
|
33
|
+
additionalProperties: true,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_stringMapEntries(typesByKey).forEach(([key, types]) => {
|
|
37
|
+
const schema = mergeTypes(
|
|
38
|
+
[...types],
|
|
39
|
+
rows.map(r => r[key]),
|
|
40
|
+
)
|
|
41
|
+
if (!schema) return
|
|
42
|
+
s.properties![key as keyof T] = schema as any
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// console.log(typesByKey)
|
|
46
|
+
|
|
47
|
+
return s
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function mergeTypes(types: Type[], samples: any[]): JsonSchema | undefined {
|
|
51
|
+
// skip "undefined" types
|
|
52
|
+
types = types.filter(t => t !== 'undefined')
|
|
53
|
+
|
|
54
|
+
if (!types.length) return undefined
|
|
55
|
+
|
|
56
|
+
if (types.length > 1) {
|
|
57
|
+
// oneOf
|
|
58
|
+
const s: JsonSchema = {
|
|
59
|
+
oneOf: types.map(type => mergeTypes([type], samples)!),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return s
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const type = types[0]!
|
|
66
|
+
|
|
67
|
+
if (type === 'null') {
|
|
68
|
+
return {
|
|
69
|
+
type: 'null',
|
|
70
|
+
} as JsonSchema
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (type === 'boolean') {
|
|
74
|
+
return {
|
|
75
|
+
type: 'boolean',
|
|
76
|
+
} as JsonSchema
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (type === 'string') {
|
|
80
|
+
return {
|
|
81
|
+
type: 'string',
|
|
82
|
+
} as JsonSchema
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (type === 'number') {
|
|
86
|
+
return {
|
|
87
|
+
type: 'number',
|
|
88
|
+
} as JsonSchema
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (type === 'object') {
|
|
92
|
+
return objectToJsonSchema(samples.filter((r: any) => r && typeof r === 'object'))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (type === 'array') {
|
|
96
|
+
// possible feature: detect if it's a tuple
|
|
97
|
+
// currently assume no-tuple
|
|
98
|
+
const items = samples.filter(r => Array.isArray(r)).flat()
|
|
99
|
+
const itemTypes = _uniq(items.map(i => getTypeOfValue(i)))
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
type: 'array',
|
|
103
|
+
items: mergeTypes(itemTypes, items),
|
|
104
|
+
} as JsonSchema
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getTypeOfValue(v: any): Type {
|
|
109
|
+
if (v === null) return 'null'
|
|
110
|
+
if (Array.isArray(v)) return 'array'
|
|
111
|
+
return typeof v as Type
|
|
112
|
+
}
|
|
@@ -134,9 +134,37 @@ const TS_2000_MILLIS = TS_2000 * 1000
|
|
|
134
134
|
With `Opt`, we can infer it as `{ foo?: string | undefined }`.
|
|
135
135
|
*/
|
|
136
136
|
|
|
137
|
-
export class
|
|
138
|
-
|
|
137
|
+
export class JsonSchemaTerminal<IN, OUT, Opt> {
|
|
138
|
+
protected schema: JsonSchema
|
|
139
139
|
|
|
140
|
+
constructor(schema: JsonSchema) {
|
|
141
|
+
this.schema = schema
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getSchema(): JsonSchema {
|
|
145
|
+
return this.schema
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Produces a "clean schema object" without methods.
|
|
150
|
+
* Same as if it would be JSON.stringified.
|
|
151
|
+
*/
|
|
152
|
+
build(): JsonSchema<IN, OUT> {
|
|
153
|
+
return _sortObject(JSON.parse(JSON.stringify(this.schema)), JSON_SCHEMA_ORDER)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
clone(): JsonSchemaAnyBuilder<IN, OUT, Opt> {
|
|
157
|
+
return new JsonSchemaAnyBuilder<IN, OUT, Opt>(_deepCopy(this.schema))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @experimental
|
|
162
|
+
*/
|
|
163
|
+
in!: IN
|
|
164
|
+
out!: OUT
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export class JsonSchemaAnyBuilder<IN, OUT, Opt> extends JsonSchemaTerminal<IN, OUT, Opt> {
|
|
140
168
|
protected setErrorMessage(ruleName: string, errorMessage: string | undefined): void {
|
|
141
169
|
if (_isUndefined(errorMessage)) return
|
|
142
170
|
|
|
@@ -162,10 +190,6 @@ export class JsonSchemaAnyBuilder<IN, OUT, Opt> {
|
|
|
162
190
|
return this as any
|
|
163
191
|
}
|
|
164
192
|
|
|
165
|
-
getSchema(): JsonSchema {
|
|
166
|
-
return this.schema
|
|
167
|
-
}
|
|
168
|
-
|
|
169
193
|
$schema($schema: string): this {
|
|
170
194
|
_objectAssign(this.schema, { $schema })
|
|
171
195
|
return this
|
|
@@ -222,18 +246,6 @@ export class JsonSchemaAnyBuilder<IN, OUT, Opt> {
|
|
|
222
246
|
})
|
|
223
247
|
}
|
|
224
248
|
|
|
225
|
-
/**
|
|
226
|
-
* Produces a "clean schema object" without methods.
|
|
227
|
-
* Same as if it would be JSON.stringified.
|
|
228
|
-
*/
|
|
229
|
-
build(): JsonSchema<IN, OUT> {
|
|
230
|
-
return _sortObject(JSON.parse(JSON.stringify(this.schema)), JSON_SCHEMA_ORDER)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
clone(): JsonSchemaAnyBuilder<IN, OUT, Opt> {
|
|
234
|
-
return new JsonSchemaAnyBuilder<IN, OUT, Opt>(_deepCopy(this.schema))
|
|
235
|
-
}
|
|
236
|
-
|
|
237
249
|
/**
|
|
238
250
|
* @deprecated
|
|
239
251
|
* The usage of this function is discouraged as it defeats the purpose of having type-safe validation.
|
|
@@ -243,10 +255,11 @@ export class JsonSchemaAnyBuilder<IN, OUT, Opt> {
|
|
|
243
255
|
}
|
|
244
256
|
|
|
245
257
|
/**
|
|
246
|
-
*
|
|
258
|
+
* Locks the given schema chain and no other modification can be done to it.
|
|
247
259
|
*/
|
|
248
|
-
|
|
249
|
-
|
|
260
|
+
final(): JsonSchemaTerminal<IN, OUT, Opt> {
|
|
261
|
+
return new JsonSchemaTerminal<IN, OUT, Opt>(this.schema)
|
|
262
|
+
}
|
|
250
263
|
}
|
|
251
264
|
|
|
252
265
|
export class JsonSchemaStringBuilder<
|
|
@@ -664,7 +677,7 @@ export class JsonSchemaObjectBuilder<
|
|
|
664
677
|
/**
|
|
665
678
|
* Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
|
|
666
679
|
*/
|
|
667
|
-
//
|
|
680
|
+
// oxlint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
|
|
668
681
|
dbEntity() {
|
|
669
682
|
return this.extend({
|
|
670
683
|
id: j.string(),
|
|
@@ -792,7 +805,7 @@ export class JsonSchemaObjectInferringBuilder<
|
|
|
792
805
|
/**
|
|
793
806
|
* Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
|
|
794
807
|
*/
|
|
795
|
-
//
|
|
808
|
+
// oxlint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
|
|
796
809
|
dbEntity() {
|
|
797
810
|
return this.extend({
|
|
798
811
|
id: j.string(),
|
package/src/yargs/yargs.util.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { hideBin } from 'yargs/helpers'
|
|
|
6
6
|
* Quick yargs helper to make it work in esm.
|
|
7
7
|
* It also allows to not have yargs and `@types/yargs` to be declared as dependencies.
|
|
8
8
|
*/
|
|
9
|
-
//
|
|
9
|
+
// oxlint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
|
|
10
10
|
export function _yargs() {
|
|
11
11
|
return yargs(hideBin(process.argv))
|
|
12
12
|
}
|