@takeshape/json-schema 11.52.0 → 11.55.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/__tests__/schema-validator.test.d.ts +1 -0
- package/dist/__tests__/schema-validator.test.js +295 -0
- package/dist/converters/__tests__/schema-converter.test.d.ts +1 -0
- package/dist/converters/__tests__/schema-converter.test.js +1134 -0
- package/dist/converters/__tests__/search-shape-schema.json +495 -0
- package/dist/converters/index.d.ts +1 -2
- package/dist/converters/index.js +1 -16
- package/dist/converters/schema-converter.d.ts +0 -1
- package/dist/converters/schema-converter.js +540 -643
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -16
- package/dist/schema-validator.d.ts +1 -2
- package/dist/schema-validator.js +163 -189
- package/dist/utils/__tests__/references.test.d.ts +1 -0
- package/dist/utils/__tests__/references.test.js +121 -0
- package/dist/utils/__tests__/type-utils.test.d.ts +1 -0
- package/dist/utils/__tests__/type-utils.test.js +143 -0
- package/dist/utils/constants.d.ts +0 -1
- package/dist/utils/constants.js +49 -7
- package/dist/utils/index.d.ts +4 -5
- package/dist/utils/index.js +4 -49
- package/dist/utils/keys.d.ts +0 -1
- package/dist/utils/keys.js +5 -12
- package/dist/utils/references.d.ts +0 -1
- package/dist/utils/references.js +56 -57
- package/dist/utils/type-utils.d.ts +1 -2
- package/dist/utils/type-utils.js +36 -53
- package/dist/utils/types.d.ts +0 -1
- package/dist/utils/types.js +1 -5
- package/package.json +18 -25
- package/dist/converters/index.d.ts.map +0 -1
- package/dist/converters/schema-converter.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/schema-validator.d.ts.map +0 -1
- package/dist/utils/constants.d.ts.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/keys.d.ts.map +0 -1
- package/dist/utils/references.d.ts.map +0 -1
- package/dist/utils/type-utils.d.ts.map +0 -1
- package/dist/utils/types.d.ts.map +0 -1
- package/es/converters/index.js +0 -1
- package/es/converters/schema-converter.js +0 -654
- package/es/index.js +0 -1
- package/es/schema-validator.js +0 -207
- package/es/utils/constants.js +0 -1
- package/es/utils/index.js +0 -4
- package/es/utils/keys.js +0 -9
- package/es/utils/references.js +0 -66
- package/es/utils/type-utils.js +0 -36
- package/es/utils/types.js +0 -1
package/es/schema-validator.js
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { visit } from '@takeshape/util';
|
|
2
|
-
import Ajv from 'ajv';
|
|
3
|
-
import addFormats from 'ajv-formats';
|
|
4
|
-
import get from 'lodash/get';
|
|
5
|
-
import isString from 'lodash/isString';
|
|
6
|
-
import unset from 'lodash/unset';
|
|
7
|
-
export function parseDataPath(instancePath) {
|
|
8
|
-
return instancePath.substr(1).split('/');
|
|
9
|
-
}
|
|
10
|
-
function getData(error, data) {
|
|
11
|
-
return get(data, parseDataPath(error.instancePath));
|
|
12
|
-
}
|
|
13
|
-
function ignoreMissing(error) {
|
|
14
|
-
// ignore top level missing errors
|
|
15
|
-
return !(error.instancePath === '' && error.keyword === 'required');
|
|
16
|
-
}
|
|
17
|
-
function ignoreNull(error, data, topLevelSchema) {
|
|
18
|
-
return !((error.keyword === 'type' || error.keyword === 'oneOf') && getData(error, data) === null && !isInvalidPropertyRequired(topLevelSchema, error));
|
|
19
|
-
}
|
|
20
|
-
function getErrorFilter(params, data, schema) {
|
|
21
|
-
return error => (!params.ignoreMissing || ignoreMissing(error)) && (!params.ignoreNulls || ignoreNull(error, data, schema));
|
|
22
|
-
}
|
|
23
|
-
export function isInvalidPropertyRequired(topLevelSchema, error) {
|
|
24
|
-
const instancePath = parseDataPath(error.instancePath);
|
|
25
|
-
const parentDataPath = instancePath.slice(0, instancePath.length - 1);
|
|
26
|
-
const parentSchema = followSchemaPath(topLevelSchema, parentDataPath);
|
|
27
|
-
const name = getName(error.instancePath);
|
|
28
|
-
if (!parentSchema) {
|
|
29
|
-
throw new Error('Unexpected error cannot find parent schema');
|
|
30
|
-
}
|
|
31
|
-
return isRequired(topLevelSchema, parentSchema, name);
|
|
32
|
-
}
|
|
33
|
-
function isRequired(topLevelSchema, schema, name) {
|
|
34
|
-
const schemas = schema.allOf || schema.anyOf || schema.oneOf || [schema];
|
|
35
|
-
return schemas.some(childSchema => followRef(topLevelSchema, childSchema)?.required?.includes(name));
|
|
36
|
-
}
|
|
37
|
-
function getName(path) {
|
|
38
|
-
const parts = parseDataPath(path);
|
|
39
|
-
return parts[parts.length - 1];
|
|
40
|
-
}
|
|
41
|
-
export function refToPath(ref) {
|
|
42
|
-
return ref.substring(2).split('/');
|
|
43
|
-
}
|
|
44
|
-
function followRef(topLevelSchema, schema) {
|
|
45
|
-
if (schema.$ref) {
|
|
46
|
-
const referencedSchema = get(topLevelSchema, refToPath(schema.$ref));
|
|
47
|
-
if (!referencedSchema) {
|
|
48
|
-
throw new Error(`Could not resolve ${schema.$ref}`);
|
|
49
|
-
}
|
|
50
|
-
return referencedSchema;
|
|
51
|
-
}
|
|
52
|
-
return schema;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Given a schema object traverse it using a "instancePath" and return the schema at that path
|
|
57
|
-
*/
|
|
58
|
-
export function followSchemaPath(topLevelSchema, instancePath) {
|
|
59
|
-
const followPath = (schemaObject, path) => {
|
|
60
|
-
const schema = followRef(topLevelSchema, schemaObject);
|
|
61
|
-
if (path.length === 0) {
|
|
62
|
-
return schema;
|
|
63
|
-
}
|
|
64
|
-
const combinedSchemas = schema.allOf || schema.anyOf || schema.oneOf;
|
|
65
|
-
if (combinedSchemas) {
|
|
66
|
-
for (const childSchema of combinedSchemas) {
|
|
67
|
-
const result = followPath(childSchema, path);
|
|
68
|
-
if (result) {
|
|
69
|
-
return result;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const [first, ...rest] = path;
|
|
74
|
-
if (schema.items && /^\d+$/.exec(first)) {
|
|
75
|
-
return followPath(schema.items, rest);
|
|
76
|
-
}
|
|
77
|
-
const prop = schema?.properties?.[first];
|
|
78
|
-
if (prop) {
|
|
79
|
-
return rest.length ? followPath(prop, rest) : prop;
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
return followPath(topLevelSchema, instancePath);
|
|
83
|
-
}
|
|
84
|
-
function isSchemaObject(schema) {
|
|
85
|
-
return typeof schema === 'object';
|
|
86
|
-
}
|
|
87
|
-
function getSchemaWithDefinitions(ajv, id) {
|
|
88
|
-
if (id.startsWith('#')) {
|
|
89
|
-
const rootSchema = ajv.getSchema('#')?.schema;
|
|
90
|
-
if (isSchemaObject(rootSchema)) {
|
|
91
|
-
const path = refToPath(id);
|
|
92
|
-
const {
|
|
93
|
-
definitions
|
|
94
|
-
} = rootSchema;
|
|
95
|
-
return {
|
|
96
|
-
...get(rootSchema, path),
|
|
97
|
-
definitions
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return ajv.getSchema(id)?.schema;
|
|
102
|
-
}
|
|
103
|
-
export function validate(ajv, id, data, options) {
|
|
104
|
-
const params = {
|
|
105
|
-
ignoreMissing: false,
|
|
106
|
-
ignoreNulls: true,
|
|
107
|
-
errorsText: false,
|
|
108
|
-
...options
|
|
109
|
-
};
|
|
110
|
-
let valid = ajv.validate(id, data);
|
|
111
|
-
let errors;
|
|
112
|
-
if (valid) {
|
|
113
|
-
errors = [];
|
|
114
|
-
} else {
|
|
115
|
-
errors = ajv.errors;
|
|
116
|
-
if (params.ignoreNulls || params.ignoreMissing) {
|
|
117
|
-
const schema = getSchemaWithDefinitions(ajv, id);
|
|
118
|
-
if (isSchemaObject(schema)) {
|
|
119
|
-
errors = errors.filter(getErrorFilter(params, data, schema));
|
|
120
|
-
valid = errors.length === 0;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
const errorsText = params.errorsText && !valid ? ajv.errorsText(errors) : '';
|
|
125
|
-
return {
|
|
126
|
-
valid,
|
|
127
|
-
errors,
|
|
128
|
-
errorsText
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
export { Ajv };
|
|
132
|
-
export function createAjv(options) {
|
|
133
|
-
const ajv = new Ajv({
|
|
134
|
-
discriminator: true,
|
|
135
|
-
allErrors: true,
|
|
136
|
-
unevaluated: true,
|
|
137
|
-
strict: false,
|
|
138
|
-
...options
|
|
139
|
-
});
|
|
140
|
-
addFormats(ajv);
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Formats ingested by Stripe OpenAPI and possibly other — trigger warnings
|
|
144
|
-
* in the client when Stripe types are loaded.
|
|
145
|
-
*/
|
|
146
|
-
ajv.addFormat('unix-time', /^[0-9]{10}$/);
|
|
147
|
-
ajv.addFormat('decimal', /^[0-9]+\.[0-9]{1,12}$/);
|
|
148
|
-
return ajv;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Determine whether a string is a valid regular expression
|
|
153
|
-
*/
|
|
154
|
-
function isValidRegex(str) {
|
|
155
|
-
try {
|
|
156
|
-
new RegExp(str, 'u');
|
|
157
|
-
return true;
|
|
158
|
-
} catch {
|
|
159
|
-
return false;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Apply various fixes to the schema to work around AJV issues and bugs.
|
|
165
|
-
* See inline comments for more.
|
|
166
|
-
*/
|
|
167
|
-
export function fixSchema(schema) {
|
|
168
|
-
visit(schema, ['pattern', 'oneOf'], (value, path) => {
|
|
169
|
-
const key = path.slice(-1)[0];
|
|
170
|
-
const parent = get(schema, path.slice(0, -1));
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Fix schema by removing any broken regexes that will cause ajv to throw a compile error.
|
|
174
|
-
* It is better to remove the invalid regex rather than forgo validation altogether
|
|
175
|
-
* This should be fixed in ajv see the comment here
|
|
176
|
-
* https://github.com/ajv-validator/ajv/blob/9f1c3eaa4b91ca17b72b122cdac9b108d1ac30cb/lib/vocabularies/validation/pattern.ts#L21
|
|
177
|
-
*/
|
|
178
|
-
if (key === 'pattern') {
|
|
179
|
-
if (parent.type === 'string' && (!isString(value) || !isValidRegex(value))) {
|
|
180
|
-
unset(schema, path);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
return schema;
|
|
185
|
-
}
|
|
186
|
-
export function createSchemaValidator(schema, metaSchemas = [], options = {}) {
|
|
187
|
-
const schemas = Array.isArray(schema) ? schema : [schema];
|
|
188
|
-
const ajv = createAjv({
|
|
189
|
-
removeAdditional: true,
|
|
190
|
-
...options
|
|
191
|
-
});
|
|
192
|
-
if (metaSchemas) {
|
|
193
|
-
for (const metaSchema of metaSchemas) {
|
|
194
|
-
ajv.addMetaSchema(metaSchema);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
for (const schema of schemas) {
|
|
198
|
-
ajv.addSchema(fixSchema(schema));
|
|
199
|
-
}
|
|
200
|
-
const defaultRef = schemas[0].$id;
|
|
201
|
-
if (!defaultRef) {
|
|
202
|
-
throw Error('Failed to create schema validator: schema is missing $id');
|
|
203
|
-
}
|
|
204
|
-
return (data, options) => {
|
|
205
|
-
return validate(ajv, defaultRef, data, options);
|
|
206
|
-
};
|
|
207
|
-
}
|
package/es/utils/constants.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const jsonSchema7Keys = ['$id', '$schema', '$ref', '$comment', 'title', 'description', 'default', 'readOnly', 'writeOnly', 'examples', 'multipleOf', 'maximum', 'exclusiveMaximum', 'minimum', 'exclusiveMinimum', 'maxLength', 'minLength', 'pattern', 'additionalItems', 'items', 'maxItems', 'minItems', 'uniqueItems', 'contains', 'maxProperties', 'minProperties', 'required', 'properties', 'patternProperties', 'additionalProperties', 'dependencies', 'propertyNames', 'const', 'enum', 'type', 'format', 'contentMediaType', 'contentEncoding', 'if', 'then', 'else', 'allOf', 'anyOf', 'oneOf', 'not', 'definitions', '$defs'];
|
package/es/utils/index.js
DELETED
package/es/utils/keys.js
DELETED
package/es/utils/references.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { isAllOfSchema, isAnyOfSchema, isOneOfSchema, isPropertySchema, isRefSchema, isSchema } from './type-utils';
|
|
2
|
-
const SCHEMA_DEFINITION_PREFIX = ['#/definitions/', '#/$defs/'];
|
|
3
|
-
function collectRef(def, key, collect) {
|
|
4
|
-
if (isRefSchema(def)) {
|
|
5
|
-
collect(def.$ref, key);
|
|
6
|
-
} else if (isSchema(def)) {
|
|
7
|
-
collectRefs(def, key, collect);
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
function collectRefs(def, key, collect) {
|
|
11
|
-
if (isPropertySchema(def)) {
|
|
12
|
-
for (const d of Object.values(def.properties)) {
|
|
13
|
-
collectRef(d, key, collect);
|
|
14
|
-
}
|
|
15
|
-
} else if (isAllOfSchema(def)) {
|
|
16
|
-
for (const d of def.allOf) {
|
|
17
|
-
collectRef(d, key, collect);
|
|
18
|
-
}
|
|
19
|
-
} else if (isAnyOfSchema(def)) {
|
|
20
|
-
for (const d of def.anyOf) {
|
|
21
|
-
collectRef(d, key, collect);
|
|
22
|
-
}
|
|
23
|
-
} else if (isOneOfSchema(def)) {
|
|
24
|
-
for (const d of def.oneOf) {
|
|
25
|
-
collectRef(d, key, collect);
|
|
26
|
-
}
|
|
27
|
-
} else if (Array.isArray(def.items)) {
|
|
28
|
-
for (const d of def.items) {
|
|
29
|
-
collectRef(d, key, collect);
|
|
30
|
-
}
|
|
31
|
-
} else if (def.items) {
|
|
32
|
-
if (isSchema(def.items) && isRefSchema(def.items)) {
|
|
33
|
-
collect(def.items.$ref, key);
|
|
34
|
-
}
|
|
35
|
-
} else if (isRefSchema(def)) {
|
|
36
|
-
collect(def.$ref, key);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Get all the definitions that are referenced by other definitions.
|
|
42
|
-
*/
|
|
43
|
-
export function getReferenceMap(definitions) {
|
|
44
|
-
const references = new Map();
|
|
45
|
-
const addToReferenceMap = (ref, key) => {
|
|
46
|
-
const refKey = ref.replace(SCHEMA_DEFINITION_PREFIX[0], '').replace(SCHEMA_DEFINITION_PREFIX[1], '');
|
|
47
|
-
const refSet = references.get(key) ?? new Set();
|
|
48
|
-
|
|
49
|
-
// Circular reference
|
|
50
|
-
if (key === refKey) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
refSet.add(refKey);
|
|
54
|
-
references.set(key, refSet);
|
|
55
|
-
const refDef = definitions[refKey];
|
|
56
|
-
if (refDef && isSchema(refDef)) {
|
|
57
|
-
collectRefs(refDef, key, addToReferenceMap);
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
for (const [key, def] of Object.entries(definitions)) {
|
|
61
|
-
if (isSchema(def)) {
|
|
62
|
-
collectRefs(def, key, addToReferenceMap);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return references;
|
|
66
|
-
}
|
package/es/utils/type-utils.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export function isSchema(schema) {
|
|
2
|
-
return typeof schema !== 'boolean';
|
|
3
|
-
}
|
|
4
|
-
export function isOneOfSchema(schema) {
|
|
5
|
-
return isSchema(schema) && Array.isArray(schema.oneOf);
|
|
6
|
-
}
|
|
7
|
-
export function isAnyOfSchema(schema) {
|
|
8
|
-
return isSchema(schema) && Array.isArray(schema.anyOf);
|
|
9
|
-
}
|
|
10
|
-
export function isAllOfSchema(schema) {
|
|
11
|
-
return isSchema(schema) && Array.isArray(schema.allOf);
|
|
12
|
-
}
|
|
13
|
-
export function isUnionSchema(schema) {
|
|
14
|
-
return isOneOfSchema(schema) || isAnyOfSchema(schema) || isAllOfSchema(schema);
|
|
15
|
-
}
|
|
16
|
-
export function isObjectSchema(schema) {
|
|
17
|
-
return isSchema(schema) && schema.type === 'object';
|
|
18
|
-
}
|
|
19
|
-
export function isPropertySchema(schema) {
|
|
20
|
-
return isObjectSchema(schema) && typeof schema.properties === 'object';
|
|
21
|
-
}
|
|
22
|
-
export function isArraySchema(schema) {
|
|
23
|
-
return isSchema(schema) && schema.type === 'array';
|
|
24
|
-
}
|
|
25
|
-
export function isListSchema(schema) {
|
|
26
|
-
return isArraySchema(schema) && typeof schema.items === 'object' && !Array.isArray(schema.items);
|
|
27
|
-
}
|
|
28
|
-
export function isTupleSchema(schema) {
|
|
29
|
-
return isArraySchema(schema) && Array.isArray(schema.items);
|
|
30
|
-
}
|
|
31
|
-
export function isEnumSchema(schema) {
|
|
32
|
-
return Boolean(isSchema(schema) && schema.enum);
|
|
33
|
-
}
|
|
34
|
-
export function isRefSchema(schema) {
|
|
35
|
-
return Boolean(isSchema(schema) && schema.$ref);
|
|
36
|
-
}
|
package/es/utils/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|