@e22m4u/js-openapi 0.0.5
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/.c8rc +9 -0
- package/.commitlintrc +5 -0
- package/.editorconfig +13 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +6 -0
- package/.mocharc.json +4 -0
- package/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/README.md +510 -0
- package/build-cjs.js +16 -0
- package/dist/cjs/index.cjs +2695 -0
- package/eslint.config.js +41 -0
- package/package.json +64 -0
- package/src/data-type/index.d.ts +1 -0
- package/src/data-type/index.js +1 -0
- package/src/data-type/infer-openapi-data-type.d.ts +30 -0
- package/src/data-type/infer-openapi-data-type.js +38 -0
- package/src/data-validation/data-format-validator-map.d.ts +13 -0
- package/src/data-validation/data-format-validator-map.js +36 -0
- package/src/data-validation/data-format-validator-map.spec.js +39 -0
- package/src/data-validation/data-format-validators.d.ts +84 -0
- package/src/data-validation/data-format-validators.js +217 -0
- package/src/data-validation/index.d.ts +3 -0
- package/src/data-validation/index.js +3 -0
- package/src/data-validation/validate-data-with-openapi-schema.d.ts +46 -0
- package/src/data-validation/validate-data-with-openapi-schema.js +1913 -0
- package/src/data-validation/validate-data-with-openapi-schema.spec.js +6953 -0
- package/src/errors/index.d.ts +1 -0
- package/src/errors/index.js +1 -0
- package/src/errors/oa-data-validation-error.d.ts +6 -0
- package/src/errors/oa-data-validation-error.js +6 -0
- package/src/errors/oa-data-validation-error.spec.js +17 -0
- package/src/index.d.ts +9 -0
- package/src/index.js +9 -0
- package/src/json-pointer/escape-json-pointer.d.ts +7 -0
- package/src/json-pointer/escape-json-pointer.js +18 -0
- package/src/json-pointer/escape-json-pointer.spec.js +36 -0
- package/src/json-pointer/index.d.ts +3 -0
- package/src/json-pointer/index.js +3 -0
- package/src/json-pointer/resolve-json-pointer.d.ts +10 -0
- package/src/json-pointer/resolve-json-pointer.js +83 -0
- package/src/json-pointer/resolve-json-pointer.spec.js +103 -0
- package/src/json-pointer/unescape-json-pointer.d.ts +8 -0
- package/src/json-pointer/unescape-json-pointer.js +18 -0
- package/src/json-pointer/unescape-json-pointer.spec.js +32 -0
- package/src/oa-document-builder.d.ts +312 -0
- package/src/oa-document-builder.js +450 -0
- package/src/oa-document-object/index.d.ts +1 -0
- package/src/oa-document-object/index.js +1 -0
- package/src/oa-document-object/validate-shallow-oa-document.d.ts +10 -0
- package/src/oa-document-object/validate-shallow-oa-document.js +209 -0
- package/src/oa-document-object/validate-shallow-oa-document.spec.js +362 -0
- package/src/oa-document-scope.d.ts +52 -0
- package/src/oa-document-scope.js +228 -0
- package/src/oa-reference-object/index.d.ts +3 -0
- package/src/oa-reference-object/index.js +3 -0
- package/src/oa-reference-object/is-oa-reference-object.d.ts +9 -0
- package/src/oa-reference-object/is-oa-reference-object.js +14 -0
- package/src/oa-reference-object/is-oa-reference-object.spec.js +19 -0
- package/src/oa-reference-object/oa-ref.d.ts +11 -0
- package/src/oa-reference-object/oa-ref.js +31 -0
- package/src/oa-reference-object/oa-ref.spec.js +56 -0
- package/src/oa-reference-object/resolve-oa-reference-object.d.ts +18 -0
- package/src/oa-reference-object/resolve-oa-reference-object.js +113 -0
- package/src/oa-reference-object/resolve-oa-reference-object.spec.js +233 -0
- package/src/oa-specification.d.ts +767 -0
- package/src/oa-specification.js +153 -0
- package/src/types.d.ts +4 -0
- package/src/utils/count-unicode.d.ts +11 -0
- package/src/utils/count-unicode.js +15 -0
- package/src/utils/index.d.ts +5 -0
- package/src/utils/index.js +5 -0
- package/src/utils/join-path.d.ts +6 -0
- package/src/utils/join-path.js +36 -0
- package/src/utils/join-path.spec.js +104 -0
- package/src/utils/normalize-path.d.ts +12 -0
- package/src/utils/normalize-path.js +22 -0
- package/src/utils/normalize-path.spec.js +56 -0
- package/src/utils/to-pascal-case.d.ts +6 -0
- package/src/utils/to-pascal-case.js +26 -0
- package/src/utils/to-pascal-case.spec.js +15 -0
- package/src/utils/to-spaced-json.d.ts +17 -0
- package/src/utils/to-spaced-json.js +27 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './oa-data-validation-error.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './oa-data-validation-error.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {InvalidArgumentError} from '@e22m4u/js-format';
|
|
3
|
+
import {OADataValidationError} from './oa-data-validation-error.js';
|
|
4
|
+
|
|
5
|
+
describe('OADataValidationError', function () {
|
|
6
|
+
describe('constructor', function () {
|
|
7
|
+
it('should interpolate an error message with given arguments', function () {
|
|
8
|
+
const res = new OADataValidationError('message %v %s', 'str', 10);
|
|
9
|
+
expect(res.message).to.be.eql('message "str" 10');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should not extend the InvalidArgumentError class', function () {
|
|
13
|
+
const res = new OADataValidationError('message');
|
|
14
|
+
expect(res).to.be.not.instanceOf(InvalidArgumentError);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
});
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './errors/index.js';
|
|
2
|
+
export * from './data-type/index.js';
|
|
3
|
+
export * from './oa-specification.js';
|
|
4
|
+
export * from './oa-document-scope.js';
|
|
5
|
+
export * from './json-pointer/index.js';
|
|
6
|
+
export * from './oa-document-builder.js';
|
|
7
|
+
export * from './data-validation/index.js';
|
|
8
|
+
export * from './oa-document-object/index.js';
|
|
9
|
+
export * from './oa-reference-object/index.js';
|
package/src/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './errors/index.js';
|
|
2
|
+
export * from './data-type/index.js';
|
|
3
|
+
export * from './oa-specification.js';
|
|
4
|
+
export * from './oa-document-scope.js';
|
|
5
|
+
export * from './json-pointer/index.js';
|
|
6
|
+
export * from './oa-document-builder.js';
|
|
7
|
+
export * from './data-validation/index.js';
|
|
8
|
+
export * from './oa-document-object/index.js';
|
|
9
|
+
export * from './oa-reference-object/index.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {InvalidArgumentError} from '@e22m4u/js-format';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Escape JSON Pointer.
|
|
5
|
+
* Replaces "~" with "~0" and "/" with "~1".
|
|
6
|
+
*
|
|
7
|
+
* @param {string} jsonPointer
|
|
8
|
+
* @returns {string}
|
|
9
|
+
*/
|
|
10
|
+
export function escapeJsonPointer(jsonPointer) {
|
|
11
|
+
if (typeof jsonPointer !== 'string') {
|
|
12
|
+
throw new InvalidArgumentError(
|
|
13
|
+
'Parameter "jsonPointer" must be a String, but %v was given.',
|
|
14
|
+
jsonPointer,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
return jsonPointer.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
18
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {format} from '@e22m4u/js-format';
|
|
3
|
+
import {escapeJsonPointer} from './escape-json-pointer.js';
|
|
4
|
+
|
|
5
|
+
describe('escapeJsonPointer', function () {
|
|
6
|
+
it('should require the "jsonPointer" parameter to be a String', function () {
|
|
7
|
+
const throwable = v => () => escapeJsonPointer(v);
|
|
8
|
+
const error = s =>
|
|
9
|
+
format('Parameter "jsonPointer" must be a String, but %s was given.', s);
|
|
10
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
11
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
12
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
13
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
14
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
15
|
+
expect(throwable(undefined)).to.throw(error('undefined'));
|
|
16
|
+
expect(throwable(null)).to.throw(error('null'));
|
|
17
|
+
throwable('str')();
|
|
18
|
+
throwable('')();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should replace "~" with "~0"', function () {
|
|
22
|
+
expect(escapeJsonPointer('example~test')).to.eq('example~0test');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should replace "/" with "~1"', function () {
|
|
26
|
+
expect(escapeJsonPointer('models/User')).to.eq('models~1User');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should handle complex strings', function () {
|
|
30
|
+
expect(escapeJsonPointer('a/b~c')).to.eq('a~1b~0c');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should do nothing if no special chars are present', function () {
|
|
34
|
+
expect(escapeJsonPointer('simple')).to.eq('simple');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {InvalidArgumentError} from '@e22m4u/js-format';
|
|
2
|
+
import {unescapeJsonPointer} from '../json-pointer/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Регулярное выражение для индексов массива.
|
|
6
|
+
* RFC 6901: A token that is a decimal number (0, 1, 2...)
|
|
7
|
+
*/
|
|
8
|
+
const ARRAY_INDEX_PATTERN = /^(0|[1-9][0-9]*)$/;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolve json pointer.
|
|
12
|
+
*
|
|
13
|
+
* @param {object|*[]} document
|
|
14
|
+
* @param {object} pointer
|
|
15
|
+
* @returns {*}
|
|
16
|
+
*/
|
|
17
|
+
export function resolveJsonPointer(document, pointer) {
|
|
18
|
+
if (!document || typeof document !== 'object') {
|
|
19
|
+
throw new InvalidArgumentError(
|
|
20
|
+
'Parameter "document" must be an Object or an Array, but %v was given.',
|
|
21
|
+
document,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
if (typeof pointer !== 'string') {
|
|
25
|
+
throw new InvalidArgumentError(
|
|
26
|
+
'JSON pointer must be a String, but %v was given.',
|
|
27
|
+
pointer,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
// так как пустая строка является указателем
|
|
31
|
+
// корня документа, то в этом случае возвращается
|
|
32
|
+
// сам документ без поиска сегмента
|
|
33
|
+
if (pointer === '') {
|
|
34
|
+
return document;
|
|
35
|
+
}
|
|
36
|
+
// указатель должен начинаться с символа "/",
|
|
37
|
+
// в противном случае выбрасывается ошибка
|
|
38
|
+
if (!pointer.startsWith('/')) {
|
|
39
|
+
throw new InvalidArgumentError(
|
|
40
|
+
'JSON pointer must start with a forward slash "/", but %v was given.',
|
|
41
|
+
pointer,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
// если указатель содержит сегменты,
|
|
45
|
+
// то выполняется их извлечение
|
|
46
|
+
const pathParts = pointer.substring(1).split('/');
|
|
47
|
+
let current = document;
|
|
48
|
+
for (let i = 0, l = pathParts.length; i < l; i++) {
|
|
49
|
+
const part = pathParts[i];
|
|
50
|
+
const segment = unescapeJsonPointer(part);
|
|
51
|
+
// если предыдущий узел является объектом,
|
|
52
|
+
// то проверяется наличие данного ключа
|
|
53
|
+
if (
|
|
54
|
+
current &&
|
|
55
|
+
typeof current === 'object' &&
|
|
56
|
+
!Array.isArray(current) &&
|
|
57
|
+
Object.hasOwn(current, segment) &&
|
|
58
|
+
current[segment] !== undefined
|
|
59
|
+
) {
|
|
60
|
+
current = current[segment];
|
|
61
|
+
}
|
|
62
|
+
// элементы массива должны извлекаться только
|
|
63
|
+
// по числовым индексам, но исключая индексы
|
|
64
|
+
// начинающиеся с нуля (но сам ноль допустим)
|
|
65
|
+
else if (
|
|
66
|
+
Array.isArray(current) &&
|
|
67
|
+
ARRAY_INDEX_PATTERN.test(segment) &&
|
|
68
|
+
current[segment] !== undefined
|
|
69
|
+
) {
|
|
70
|
+
current = current[segment];
|
|
71
|
+
}
|
|
72
|
+
// если узел не содержит значения,
|
|
73
|
+
// то выбрасывается ошибка
|
|
74
|
+
else {
|
|
75
|
+
const currentPointer = '/' + pathParts.slice(0, i + 1).join('/');
|
|
76
|
+
throw new InvalidArgumentError(
|
|
77
|
+
'Unable to resolve %v in the given document.',
|
|
78
|
+
currentPointer,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return current;
|
|
83
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {format} from '@e22m4u/js-format';
|
|
3
|
+
import {resolveJsonPointer} from './resolve-json-pointer.js';
|
|
4
|
+
|
|
5
|
+
describe('resolveJsonPointer', function () {
|
|
6
|
+
it('should require the parameter "document" to be an Object or an Array', function () {
|
|
7
|
+
const throwable = v => () => resolveJsonPointer(v, '/0');
|
|
8
|
+
const error = s =>
|
|
9
|
+
format(
|
|
10
|
+
'Parameter "document" must be an Object or an Array, ' +
|
|
11
|
+
'but %s was given.',
|
|
12
|
+
s,
|
|
13
|
+
);
|
|
14
|
+
expect(throwable('str')).to.throw(error('"str"'));
|
|
15
|
+
expect(throwable('')).to.throw(error('""'));
|
|
16
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
17
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
18
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
19
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
20
|
+
expect(throwable(undefined)).to.throw(error('undefined'));
|
|
21
|
+
expect(throwable(null)).to.throw(error('null'));
|
|
22
|
+
throwable({0: 'bar'})();
|
|
23
|
+
throwable(['bar'])();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should require the parameter "pointer" to be a String', function () {
|
|
27
|
+
const throwable = v => () => resolveJsonPointer({foo: 'bar'}, v);
|
|
28
|
+
const error = s =>
|
|
29
|
+
format('JSON pointer must be a String, but %s was given.', s);
|
|
30
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
31
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
32
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
33
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
34
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
35
|
+
expect(throwable(undefined)).to.throw(error('undefined'));
|
|
36
|
+
expect(throwable(null)).to.throw(error('null'));
|
|
37
|
+
throwable('/foo')();
|
|
38
|
+
throwable('')();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should return the document when an empty string is given', function () {
|
|
42
|
+
const document = {};
|
|
43
|
+
const res = resolveJsonPointer(document, '');
|
|
44
|
+
expect(res).to.be.eq(document);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should be able to find a node with an empty string key', function () {
|
|
48
|
+
const node = {foo: 'bar'};
|
|
49
|
+
const res1 = resolveJsonPointer({'': node}, '/');
|
|
50
|
+
expect(res1).to.be.eq(node);
|
|
51
|
+
const res2 = resolveJsonPointer({path: {'': node}}, '/path/');
|
|
52
|
+
expect(res2).to.be.eq(node);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should unescape segments of the json pointer', function () {
|
|
56
|
+
const node = {foo: 'bar'};
|
|
57
|
+
const res = resolveJsonPointer({'/path': node}, '/~1path');
|
|
58
|
+
expect(res).to.be.eq(node);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should return a document node for the segment chain', function () {
|
|
62
|
+
const node = {foo: 'bar'};
|
|
63
|
+
const res = resolveJsonPointer({a: {b: {c: node}}}, '/a/b/c');
|
|
64
|
+
expect(res).to.be.eq(node);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should resolve an array element when the document is an array', function () {
|
|
68
|
+
const res = resolveJsonPointer(['foo', 'bar', 'baz'], '/1');
|
|
69
|
+
expect(res).to.be.eq('bar');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should be able to walk through an array element', function () {
|
|
73
|
+
const res = resolveJsonPointer(
|
|
74
|
+
[{prop: 'foo'}, {prop: 'bar'}, {prop: 'baz'}],
|
|
75
|
+
'/1/prop',
|
|
76
|
+
);
|
|
77
|
+
expect(res).to.be.eq('bar');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should throw an error when an array index has a zero prefix', function () {
|
|
81
|
+
const throwable = () => resolveJsonPointer(['foo', 'bar', 'baz'], '/01');
|
|
82
|
+
expect(throwable).to.throw(
|
|
83
|
+
'Unable to resolve "/01" in the given document.',
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should throw an error if a property value is undefined', function () {
|
|
88
|
+
const node = {foo: undefined};
|
|
89
|
+
const throwable = () => resolveJsonPointer(node, '/foo');
|
|
90
|
+
expect(throwable).to.throw(
|
|
91
|
+
'Unable to resolve "/foo" in the given document.',
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should return the pointer target when its value is not undefined', function () {
|
|
96
|
+
const values = ['str', '', 10, 0, true, false, {}, [], null];
|
|
97
|
+
values.forEach(value => {
|
|
98
|
+
const document = {foo: value};
|
|
99
|
+
const res = resolveJsonPointer(document, '/foo');
|
|
100
|
+
expect(res).to.be.eq(value);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {InvalidArgumentError} from '@e22m4u/js-format';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Unescape JSON Pointer.
|
|
5
|
+
* Replaces "~1" with "/" and "~0" with "~".
|
|
6
|
+
*
|
|
7
|
+
* @param {string} jsonPointer
|
|
8
|
+
* @returns {string}
|
|
9
|
+
*/
|
|
10
|
+
export function unescapeJsonPointer(jsonPointer) {
|
|
11
|
+
if (typeof jsonPointer !== 'string') {
|
|
12
|
+
throw new InvalidArgumentError(
|
|
13
|
+
'Parameter "jsonPointer" must be a String, but %v was given.',
|
|
14
|
+
jsonPointer,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
return jsonPointer.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
18
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {format} from '@e22m4u/js-format';
|
|
3
|
+
import {unescapeJsonPointer} from './unescape-json-pointer.js';
|
|
4
|
+
|
|
5
|
+
describe('unescapeJsonPointer', function () {
|
|
6
|
+
it('should require the "jsonPointer" parameter to be a String', function () {
|
|
7
|
+
const throwable = v => () => unescapeJsonPointer(v);
|
|
8
|
+
const error = s =>
|
|
9
|
+
format('Parameter "jsonPointer" must be a String, but %s was given.', s);
|
|
10
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
11
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
12
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
13
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
14
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
15
|
+
expect(throwable(undefined)).to.throw(error('undefined'));
|
|
16
|
+
expect(throwable(null)).to.throw(error('null'));
|
|
17
|
+
throwable('str')();
|
|
18
|
+
throwable('')();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should replace "~0" with "~"', function () {
|
|
22
|
+
expect(unescapeJsonPointer('example~0test')).to.eq('example~test');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should replace "~1" with "/"', function () {
|
|
26
|
+
expect(unescapeJsonPointer('models~1User')).to.eq('models/User');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should handle complex strings', function () {
|
|
30
|
+
expect(unescapeJsonPointer('a~1b~0c')).to.eq('a/b~c');
|
|
31
|
+
});
|
|
32
|
+
});
|