@naturalcycles/nodejs-lib 15.49.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/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.js +2 -2
- package/dist/yargs/yargs.util.js +1 -1
- package/package.json +1 -1
- package/src/validation/ajv/ajvSchema.ts +1 -1
- package/src/validation/ajv/from-data/generateJsonSchemaFromData.ts +112 -0
- package/src/validation/ajv/index.ts +1 -0
- package/src/validation/ajv/jsonSchemaBuilder.ts +2 -2
- package/src/yargs/yargs.util.ts +1 -1
|
@@ -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
|
+
}
|
|
@@ -496,7 +496,7 @@ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
|
|
|
496
496
|
/**
|
|
497
497
|
* Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
|
|
498
498
|
*/
|
|
499
|
-
//
|
|
499
|
+
// oxlint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
|
|
500
500
|
dbEntity() {
|
|
501
501
|
return this.extend({
|
|
502
502
|
id: j.string(),
|
|
@@ -558,7 +558,7 @@ export class JsonSchemaObjectInferringBuilder extends JsonSchemaAnyBuilder {
|
|
|
558
558
|
/**
|
|
559
559
|
* Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
|
|
560
560
|
*/
|
|
561
|
-
//
|
|
561
|
+
// oxlint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
|
|
562
562
|
dbEntity() {
|
|
563
563
|
return this.extend({
|
|
564
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
|
@@ -73,7 +73,7 @@ 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
79
|
jsonSchema = schema
|
|
@@ -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
|
+
}
|
|
@@ -677,7 +677,7 @@ export class JsonSchemaObjectBuilder<
|
|
|
677
677
|
/**
|
|
678
678
|
* Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
|
|
679
679
|
*/
|
|
680
|
-
//
|
|
680
|
+
// oxlint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
|
|
681
681
|
dbEntity() {
|
|
682
682
|
return this.extend({
|
|
683
683
|
id: j.string(),
|
|
@@ -805,7 +805,7 @@ export class JsonSchemaObjectInferringBuilder<
|
|
|
805
805
|
/**
|
|
806
806
|
* Extends the current schema with `id`, `created` and `updated` according to NC DB conventions.
|
|
807
807
|
*/
|
|
808
|
-
//
|
|
808
|
+
// oxlint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types
|
|
809
809
|
dbEntity() {
|
|
810
810
|
return this.extend({
|
|
811
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
|
}
|