@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,153 @@
|
|
|
1
|
+
// OpenApi version 3.1.0
|
|
2
|
+
// https://spec.openapis.org/oas/v3.1.0
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* OpenAPI version.
|
|
6
|
+
*/
|
|
7
|
+
export const OPENAPI_VERSION = '3.1.0';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Operation Method.
|
|
11
|
+
* https://spec.openapis.org/oas/v3.1.0#path-item-object
|
|
12
|
+
*/
|
|
13
|
+
export const OAOperationMethod = {
|
|
14
|
+
GET: 'get',
|
|
15
|
+
PUT: 'put',
|
|
16
|
+
POST: 'post',
|
|
17
|
+
DELETE: 'delete',
|
|
18
|
+
OPTIONS: 'options',
|
|
19
|
+
HEAD: 'head',
|
|
20
|
+
PATCH: 'patch',
|
|
21
|
+
TRACE: 'trace',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parameter Location.
|
|
26
|
+
* https://spec.openapis.org/oas/v3.1.0#parameter-locations
|
|
27
|
+
*/
|
|
28
|
+
export const OAParameterLocation = {
|
|
29
|
+
QUERY: 'query',
|
|
30
|
+
HEADER: 'header',
|
|
31
|
+
PATH: 'path',
|
|
32
|
+
COOKIE: 'cookie',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parameter Style.
|
|
37
|
+
* https://spec.openapis.org/oas/v3.1.0#style-values
|
|
38
|
+
*/
|
|
39
|
+
export const OAParameterStyle = {
|
|
40
|
+
MATRIX: 'matrix',
|
|
41
|
+
LABEL: 'label',
|
|
42
|
+
FORM: 'form',
|
|
43
|
+
SIMPLE: 'simple',
|
|
44
|
+
SPACE_DELIMITED: 'spaceDelimited',
|
|
45
|
+
PIPE_DELIMITED: 'pipeDelimited',
|
|
46
|
+
DEEP_OBJECT: 'deepObject',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Data type.
|
|
51
|
+
* https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-00#section-4.2.1
|
|
52
|
+
*/
|
|
53
|
+
export const OADataType = {
|
|
54
|
+
STRING: 'string',
|
|
55
|
+
NUMBER: 'number',
|
|
56
|
+
INTEGER: 'integer',
|
|
57
|
+
BOOLEAN: 'boolean',
|
|
58
|
+
OBJECT: 'object',
|
|
59
|
+
ARRAY: 'array',
|
|
60
|
+
NULL: 'null',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Data type list.
|
|
65
|
+
* https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-00#section-4.2.1
|
|
66
|
+
*/
|
|
67
|
+
export const OA_DATA_TYPE_LIST = Object.values(OADataType);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Data format.
|
|
71
|
+
* https://spec.openapis.org/oas/v3.1.0#dataTypeFormat
|
|
72
|
+
*/
|
|
73
|
+
export const OADataFormat = {
|
|
74
|
+
// number
|
|
75
|
+
INT32: 'int32',
|
|
76
|
+
INT64: 'int64',
|
|
77
|
+
FLOAT: 'float',
|
|
78
|
+
DOUBLE: 'double',
|
|
79
|
+
// date and time
|
|
80
|
+
DATE: 'date',
|
|
81
|
+
DATE_TIME: 'date-time',
|
|
82
|
+
TIME: 'time',
|
|
83
|
+
DURATION: 'duration',
|
|
84
|
+
// binary
|
|
85
|
+
BYTE: 'byte',
|
|
86
|
+
BINARY: 'binary',
|
|
87
|
+
// identifiers and network
|
|
88
|
+
EMAIL: 'email',
|
|
89
|
+
IDN_EMAIL: 'idn-email',
|
|
90
|
+
UUID: 'uuid',
|
|
91
|
+
HOSTNAME: 'hostname',
|
|
92
|
+
IDN_HOSTNAME: 'idn-hostname',
|
|
93
|
+
IPV4: 'ipv4',
|
|
94
|
+
IPV6: 'ipv6',
|
|
95
|
+
URI: 'uri',
|
|
96
|
+
URI_REFERENCE: 'uri-reference',
|
|
97
|
+
IRI: 'iri',
|
|
98
|
+
IRI_REFERENCE: 'iri-reference',
|
|
99
|
+
// extra
|
|
100
|
+
PASSWORD: 'password',
|
|
101
|
+
REGEX: 'regex',
|
|
102
|
+
JSON_POINTER: 'json-pointer',
|
|
103
|
+
RELATIVE_JSON_POINTER: 'relative-json-pointer',
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Media type.
|
|
108
|
+
* https://spec.openapis.org/oas/v3.1.0#media-types
|
|
109
|
+
*/
|
|
110
|
+
export const OAMediaType = {
|
|
111
|
+
TEXT_PLAIN: 'text/plain',
|
|
112
|
+
TEXT_HTML: 'text/html',
|
|
113
|
+
APPLICATION_XML: 'application/xml',
|
|
114
|
+
APPLICATION_JSON: 'application/json',
|
|
115
|
+
MULTIPART_FORM_DATA: 'multipart/form-data',
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Security Scheme Type.
|
|
120
|
+
* https://spec.openapis.org/oas/v3.1.0#security-scheme-object
|
|
121
|
+
*/
|
|
122
|
+
export const OASecuritySchemeType = {
|
|
123
|
+
API_KEY: 'apiKey',
|
|
124
|
+
HTTP: 'http',
|
|
125
|
+
MUTUAL_TLS: 'mutualTLS',
|
|
126
|
+
OAUTH_2: 'oauth2',
|
|
127
|
+
OPEN_ID_CONNECT: 'openIdConnect',
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Api Key Location.
|
|
132
|
+
* https://spec.openapis.org/oas/v3.1.0#security-scheme-object
|
|
133
|
+
*/
|
|
134
|
+
export const OAApiKeyLocation = {
|
|
135
|
+
QUERY: 'query',
|
|
136
|
+
HEADER: 'header',
|
|
137
|
+
COOKIE: 'cookie',
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Access mode.
|
|
142
|
+
* https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#name-readonly-and-writeonly
|
|
143
|
+
*/
|
|
144
|
+
export const OAAccessMode = {
|
|
145
|
+
READ: 'read',
|
|
146
|
+
WRITE: 'write',
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Access mode list.
|
|
151
|
+
* https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#name-readonly-and-writeonly
|
|
152
|
+
*/
|
|
153
|
+
export const ACCESS_MODE_LIST = Object.values(OAAccessMode);
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Count unicode.
|
|
3
|
+
*
|
|
4
|
+
* Подсчет длины строки выполняется с учетом символов, выходящих
|
|
5
|
+
* за пределы BMP (Basic Multilingual Plane) например, эмодзи
|
|
6
|
+
* или некоторые иероглифы в JS имеют `.length === 2`,
|
|
7
|
+
* но должны считаться за один символ.
|
|
8
|
+
*
|
|
9
|
+
* @param data
|
|
10
|
+
*/
|
|
11
|
+
export function countUnicode(data: string): number;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Count unicode.
|
|
3
|
+
*
|
|
4
|
+
* Подсчет длины строки выполняется с учетом символов, выходящих
|
|
5
|
+
* за пределы BMP (Basic Multilingual Plane) например, эмодзи
|
|
6
|
+
* или некоторые иероглифы в JS имеют `.length === 2`,
|
|
7
|
+
* но должны считаться за один символ.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} data
|
|
10
|
+
* @returns {number}
|
|
11
|
+
*/
|
|
12
|
+
export function countUnicode(data) {
|
|
13
|
+
const matchedChars = String(data).match(/[\s\S]/gu);
|
|
14
|
+
return Array.isArray(matchedChars) ? matchedChars.length : 0;
|
|
15
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Joins URL paths.
|
|
3
|
+
*
|
|
4
|
+
* @param {...string} segments
|
|
5
|
+
* @returns {string}
|
|
6
|
+
*/
|
|
7
|
+
export function joinPath(...segments) {
|
|
8
|
+
// фильтрация пустых значений (null, undefined, "")
|
|
9
|
+
const parts = segments.filter(seg => seg != undefined && String(seg) !== '');
|
|
10
|
+
if (parts.length === 0) {
|
|
11
|
+
return '/';
|
|
12
|
+
}
|
|
13
|
+
// простое объединение через разделитель
|
|
14
|
+
// и замена множественных слешей на один
|
|
15
|
+
let joined = parts.join('/').replace(/\/+/g, '/');
|
|
16
|
+
// проверка на наличие протокола в начале строки
|
|
17
|
+
// RFC 3986: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
|
18
|
+
const protocolRegex = /^([a-z][a-z0-9+.-]*):\//i;
|
|
19
|
+
const protocolMatch = joined.match(protocolRegex);
|
|
20
|
+
if (protocolMatch) {
|
|
21
|
+
// если это URL (есть схема), то восстанавливается
|
|
22
|
+
// двойной слеш "https:/" -> "https://"
|
|
23
|
+
joined = joined.replace(protocolRegex, '$1://');
|
|
24
|
+
} else {
|
|
25
|
+
// если это не URL, а путь файловой системы или API,
|
|
26
|
+
// то гарантируется наличие слеша в начале
|
|
27
|
+
if (!joined.startsWith('/')) {
|
|
28
|
+
joined = '/' + joined;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// удаление слеша в конце, если это не корень
|
|
32
|
+
if (joined.length > 1 && joined.endsWith('/')) {
|
|
33
|
+
joined = joined.slice(0, -1);
|
|
34
|
+
}
|
|
35
|
+
return joined;
|
|
36
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {joinPath} from './join-path.js';
|
|
3
|
+
|
|
4
|
+
describe('joinPath', function () {
|
|
5
|
+
describe('path joining', function () {
|
|
6
|
+
it('should join multiple segments with a forward slash', function () {
|
|
7
|
+
const result = joinPath('api', 'v1', 'users');
|
|
8
|
+
expect(result).to.be.eq('/api/v1/users');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should always return a path starting with a single forward slash', function () {
|
|
12
|
+
expect(joinPath('users')).to.be.eq('/users');
|
|
13
|
+
expect(joinPath('/users')).to.be.eq('/users');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should remove leading slashes from segments', function () {
|
|
17
|
+
const result = joinPath('/api', '/v1', '/users');
|
|
18
|
+
expect(result).to.be.eq('/api/v1/users');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should remove trailing slashes from segments', function () {
|
|
22
|
+
const result = joinPath('api/', 'v1/', 'users/');
|
|
23
|
+
expect(result).to.be.eq('/api/v1/users');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should handle segments with both leading and trailing slashes', function () {
|
|
27
|
+
const result = joinPath('/api/', '/v1/', '/users/');
|
|
28
|
+
expect(result).to.be.eq('/api/v1/users');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should ignore empty string segments', function () {
|
|
32
|
+
const result = joinPath('api', '', 'v1', '', 'users');
|
|
33
|
+
expect(result).to.be.eq('/api/v1/users');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should ignore null and undefined segments', function () {
|
|
37
|
+
const result = joinPath('api', null, 'v1', undefined, 'users');
|
|
38
|
+
expect(result).to.be.eq('/api/v1/users');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should handle a single segment correctly', function () {
|
|
42
|
+
expect(joinPath('users')).to.be.eq('/users');
|
|
43
|
+
expect(joinPath('/users/')).to.be.eq('/users');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return a single slash when no segments are provided', function () {
|
|
47
|
+
const result = joinPath();
|
|
48
|
+
expect(result).to.be.eq('/');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should return a single slash if all segments are empty or falsy', function () {
|
|
52
|
+
const result = joinPath('', null, '', undefined);
|
|
53
|
+
expect(result).to.be.eq('/');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should treat a single slash segment as an empty segment', function () {
|
|
57
|
+
const result = joinPath('api', '/', 'users');
|
|
58
|
+
expect(result).to.be.eq('/api/users');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should not add extra slashes if segments are already joined', function () {
|
|
62
|
+
const result = joinPath('api/v1', 'users');
|
|
63
|
+
expect(result).to.be.eq('/api/v1/users');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should convert number arguments to string segments', function () {
|
|
67
|
+
const result = joinPath(-10, 0, 10);
|
|
68
|
+
expect(result).to.be.eq('/-10/0/10');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should remove duplicate slashes', function () {
|
|
72
|
+
const result = joinPath('/', '//foo//', '///bar///');
|
|
73
|
+
expect(result).to.be.eq('/foo/bar');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('url joining', function () {
|
|
78
|
+
it('should preserve http:// protocol', function () {
|
|
79
|
+
const result = joinPath('http://example.com', 'api', 'v1');
|
|
80
|
+
expect(result).to.be.eq('http://example.com/api/v1');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should preserve https:// protocol', function () {
|
|
84
|
+
const result = joinPath('https://api.example.com/', '/users');
|
|
85
|
+
expect(result).to.be.eq('https://api.example.com/users');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should handle protocols with extra slashes in segments', function () {
|
|
89
|
+
const result = joinPath('https://site.com//', '//api//');
|
|
90
|
+
expect(result).to.be.eq('https://site.com/api');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should handle custom protocols (e.g. postgres://)', function () {
|
|
94
|
+
const result = joinPath('postgres://user:pass@localhost', 'db');
|
|
95
|
+
expect(result).to.be.eq('postgres://user:pass@localhost/db');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should not add a leading slash if a protocol is present', function () {
|
|
99
|
+
const result = joinPath('http://localhost');
|
|
100
|
+
expect(result).to.be.eq('http://localhost');
|
|
101
|
+
expect(result.startsWith('/')).to.be.false;
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize path.
|
|
3
|
+
*
|
|
4
|
+
* Заменяет любые повторяющиеся слеши на один.
|
|
5
|
+
* Удаляет пробельные символы в начале и конце.
|
|
6
|
+
* Удаляет слеш в конце строки.
|
|
7
|
+
* Гарантирует слеш в начале строки (по умолчанию).
|
|
8
|
+
*
|
|
9
|
+
* @param value
|
|
10
|
+
* @param noStartingSlash
|
|
11
|
+
*/
|
|
12
|
+
export function normalizePath(value: string, noStartingSlash?: boolean): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize path.
|
|
3
|
+
*
|
|
4
|
+
* Заменяет любые повторяющиеся слеши на один.
|
|
5
|
+
* Удаляет пробельные символы в начале и конце.
|
|
6
|
+
* Удаляет слеш в конце строки.
|
|
7
|
+
* Гарантирует слеш в начале строки (по умолчанию).
|
|
8
|
+
*
|
|
9
|
+
* @param {string} value
|
|
10
|
+
* @param {boolean} [noStartingSlash]
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
export function normalizePath(value, noStartingSlash = false) {
|
|
14
|
+
if (typeof value !== 'string') {
|
|
15
|
+
return '/';
|
|
16
|
+
}
|
|
17
|
+
const res = value
|
|
18
|
+
.trim()
|
|
19
|
+
.replace(/\/+/g, '/')
|
|
20
|
+
.replace(/(^\/|\/$)/g, '');
|
|
21
|
+
return noStartingSlash ? res : '/' + res;
|
|
22
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {normalizePath} from './normalize-path.js';
|
|
3
|
+
|
|
4
|
+
describe('normalizePath', function () {
|
|
5
|
+
describe('input validation', function () {
|
|
6
|
+
it('should return a root path "/" if value is null', function () {
|
|
7
|
+
expect(normalizePath(null)).to.equal('/');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should return a root path "/" if value is undefined', function () {
|
|
11
|
+
expect(normalizePath(undefined)).to.equal('/');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should return a root path "/" if value is a number', function () {
|
|
15
|
+
expect(normalizePath(123)).to.equal('/');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should return a root path "/" if value is an object', function () {
|
|
19
|
+
expect(normalizePath({})).to.equal('/');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('path normalization', function () {
|
|
24
|
+
it('should replace multiple slashes with a single slash', function () {
|
|
25
|
+
expect(normalizePath('//api///users//')).to.equal('/api/users');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should trim a given string but preserve whitespace characters', function () {
|
|
29
|
+
expect(normalizePath(' /my folder/ ')).to.equal('/my folder');
|
|
30
|
+
expect(normalizePath('path\twith\ntabs')).to.equal('/path\twith\ntabs');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should remove leading and trailing slashes before applying the final format', function () {
|
|
34
|
+
expect(normalizePath('/foo/bar/')).to.equal('/foo/bar');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should handle an empty string by returning "/" by default', function () {
|
|
38
|
+
expect(normalizePath('')).to.equal('/');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('the "noStartingSlash" option', function () {
|
|
43
|
+
it('should always prepend a leading slash when the option is false', function () {
|
|
44
|
+
expect(normalizePath('foo/bar', false)).to.equal('/foo/bar');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should not prepend a leading slash when the option is true', function () {
|
|
48
|
+
expect(normalizePath('/foo/bar/', true)).to.equal('foo/bar');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should return an empty string if the input results in an empty path', function () {
|
|
52
|
+
expect(normalizePath('', true)).to.equal('');
|
|
53
|
+
expect(normalizePath('///', true)).to.equal('');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* To pascal case.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} input
|
|
5
|
+
* @returns {string}
|
|
6
|
+
*/
|
|
7
|
+
export function toPascalCase(input) {
|
|
8
|
+
if (!input) {
|
|
9
|
+
return '';
|
|
10
|
+
}
|
|
11
|
+
return (
|
|
12
|
+
input
|
|
13
|
+
// splits camelCase words into separate words
|
|
14
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
15
|
+
// splits numbers and words
|
|
16
|
+
.replace(/([0-9])([a-zA-Z])/g, '$1 $2')
|
|
17
|
+
// replaces dashes, underscores, and special characters with spaces
|
|
18
|
+
.replace(/[-_]+|[^\p{L}\p{N}]/gu, ' ')
|
|
19
|
+
// converts the entire string to lowercase
|
|
20
|
+
.toLowerCase()
|
|
21
|
+
// capitalizes the first letter of each word
|
|
22
|
+
.replace(/(?:^|\s)(\p{L})/gu, (_, letter) => letter.toUpperCase())
|
|
23
|
+
// removes all spaces
|
|
24
|
+
.replace(/\s+/g, '')
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {toPascalCase} from './to-pascal-case.js';
|
|
3
|
+
|
|
4
|
+
describe('toPascalCase', function () {
|
|
5
|
+
it('returns a PascalCase string', function () {
|
|
6
|
+
expect(toPascalCase('hello world')).to.be.eq('HelloWorld');
|
|
7
|
+
expect(toPascalCase('snake_case')).to.be.eq('SnakeCase');
|
|
8
|
+
expect(toPascalCase('kebab-case')).to.be.eq('KebabCase');
|
|
9
|
+
expect(toPascalCase('alreadyCamel')).to.be.eq('AlreadyCamel');
|
|
10
|
+
expect(toPascalCase('AlreadyPascal')).to.be.eq('AlreadyPascal');
|
|
11
|
+
expect(toPascalCase(' single word ')).to.be.eq('SingleWord');
|
|
12
|
+
expect(toPascalCase('')).to.be.eq('');
|
|
13
|
+
expect(toPascalCase('1number')).to.be.eq('1Number');
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* To spaced JSON options.
|
|
3
|
+
*/
|
|
4
|
+
export type ToSpacedJsonOptions = {
|
|
5
|
+
truncate?: number;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Функция превращает объект в JSON с пробелами,
|
|
10
|
+
* но в одну строку.
|
|
11
|
+
*
|
|
12
|
+
* @param value
|
|
13
|
+
*/
|
|
14
|
+
export function toSpacedJson(
|
|
15
|
+
value: unknown,
|
|
16
|
+
options?: ToSpacedJsonOptions,
|
|
17
|
+
): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Функция превращает объект в JSON с пробелами,
|
|
3
|
+
* но в одну строку.
|
|
4
|
+
*
|
|
5
|
+
* @param {*} value
|
|
6
|
+
* @param {*} [options]
|
|
7
|
+
* @returns {string}
|
|
8
|
+
*/
|
|
9
|
+
export function toSpacedJson(value, options = {}) {
|
|
10
|
+
const stringified = JSON.stringify(value, null, 1);
|
|
11
|
+
const res = stringified
|
|
12
|
+
? stringified
|
|
13
|
+
.replace(/\n\s*/g, ' ')
|
|
14
|
+
.replace(/\[\s/g, '[')
|
|
15
|
+
.replace(/\s\]/g, ']')
|
|
16
|
+
.replace(/\{\s/g, '{')
|
|
17
|
+
.replace(/\s\}/g, '}')
|
|
18
|
+
: 'undefined';
|
|
19
|
+
if (
|
|
20
|
+
options.truncate &&
|
|
21
|
+
typeof options.truncate === 'number' &&
|
|
22
|
+
res.length > options.truncate
|
|
23
|
+
) {
|
|
24
|
+
return res.slice(0, options.truncate) + '...';
|
|
25
|
+
}
|
|
26
|
+
return res;
|
|
27
|
+
}
|
package/tsconfig.json
ADDED