jsonschema-key-compression 1.6.1 → 2.0.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/CHANGELOG.md +13 -0
- package/README.md +65 -5
- package/dist/es/compress.d.ts +3 -1
- package/dist/es/compress.js +164 -26
- package/dist/es/compress.js.map +1 -1
- package/dist/es/create-compressed-json-schema.d.ts +9 -0
- package/dist/es/create-compressed-json-schema.js +136 -26
- package/dist/es/create-compressed-json-schema.js.map +1 -1
- package/dist/es/create-compression-table.d.ts +7 -1
- package/dist/es/create-compression-table.js +56 -30
- package/dist/es/create-compression-table.js.map +1 -1
- package/dist/es/decompress.js +41 -21
- package/dist/es/decompress.js.map +1 -1
- package/dist/es/enum-compression.d.ts +35 -0
- package/dist/es/enum-compression.js +217 -0
- package/dist/es/enum-compression.js.map +1 -0
- package/dist/es/index.d.ts +1 -1
- package/dist/es/index.js.map +1 -1
- package/dist/es/types.d.ts +11 -0
- package/dist/es/util.d.ts +1 -1
- package/dist/es/util.js.map +1 -1
- package/dist/lib/compress.d.ts +3 -1
- package/dist/lib/compress.js +170 -33
- package/dist/lib/compress.js.map +1 -1
- package/dist/lib/create-compressed-json-schema.d.ts +9 -0
- package/dist/lib/create-compressed-json-schema.js +136 -27
- package/dist/lib/create-compressed-json-schema.js.map +1 -1
- package/dist/lib/create-compression-table.d.ts +7 -1
- package/dist/lib/create-compression-table.js +61 -35
- package/dist/lib/create-compression-table.js.map +1 -1
- package/dist/lib/decompress.js +44 -25
- package/dist/lib/decompress.js.map +1 -1
- package/dist/lib/enum-compression.d.ts +35 -0
- package/dist/lib/enum-compression.js +223 -0
- package/dist/lib/enum-compression.js.map +1 -0
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/types.d.ts +11 -0
- package/dist/lib/util.d.ts +1 -1
- package/dist/lib/util.js +3 -3
- package/dist/lib/util.js.map +1 -1
- package/package.json +14 -12
- package/src/compress.ts +184 -34
- package/src/create-compressed-json-schema.ts +158 -35
- package/src/create-compression-table.ts +59 -31
- package/src/decompress.ts +42 -27
- package/src/enum-compression.ts +240 -0
- package/src/index.ts +1 -0
- package/src/types.d.ts +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsonschema-key-compression",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Compress json-data based on its json-schema",
|
|
5
5
|
"author": "pubkey",
|
|
6
6
|
"keywords": [
|
|
@@ -29,26 +29,28 @@
|
|
|
29
29
|
"lint:fix": "tslint --project . --fix",
|
|
30
30
|
"transpile": "tsc -p ./ && copyfiles -u 1 \"src/**/*.d.ts\" dist/lib && echo '# transpile es5 (require) sucess!'",
|
|
31
31
|
"transpile:es": "tsc -p ./ --target ES5 --module ES6 --outDir ./dist/es && copyfiles -u 1 \"src/**/*.d.ts\" dist/es && echo '# transpile es6 (modules) sucess!'",
|
|
32
|
-
"build": "rimraf -
|
|
33
|
-
"test": "mocha -r ts-node/register test/unit/index.test.ts",
|
|
32
|
+
"build": "rimraf -fr ./dist && npm run transpile && npm run transpile:es",
|
|
33
|
+
"test": "mocha -r ts-node/register test/unit/index.test.ts --bail",
|
|
34
34
|
"test:performance": "mocha -r ts-node/register --bail --exit ./test/performance.test.ts",
|
|
35
35
|
"test:efficiency": "mocha -r ts-node/register --bail --exit ./test/efficiency.test.ts",
|
|
36
36
|
"dev": "mocha -r ts-node/register test/unit/index.test.ts --watch --watch-extensions ts"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/faker": "5.5.9",
|
|
40
|
-
"@types/mocha": "
|
|
41
|
-
"@types/node": "
|
|
42
|
-
"@types/node-gzip": "1.1.
|
|
43
|
-
"
|
|
44
|
-
"
|
|
40
|
+
"@types/mocha": "10.0.10",
|
|
41
|
+
"@types/node": "26.6.6",
|
|
42
|
+
"@types/node-gzip": "1.1.3",
|
|
43
|
+
"ajv": "8.20.0",
|
|
44
|
+
"assert": "2.1.0",
|
|
45
|
+
"async-test-util": "2.5.0",
|
|
45
46
|
"copyfiles": "2.4.1",
|
|
46
47
|
"faker": "5.5.3",
|
|
47
|
-
"
|
|
48
|
+
"mingo": "7.2.7",
|
|
49
|
+
"mocha": "12.0.3",
|
|
48
50
|
"node-gzip": "1.1.2",
|
|
49
|
-
"rimraf": "
|
|
50
|
-
"ts-node": "10.
|
|
51
|
+
"rimraf": "6.1.4",
|
|
52
|
+
"ts-node": "10.9.2",
|
|
51
53
|
"tslint": "6.1.3",
|
|
52
|
-
"typescript": "
|
|
54
|
+
"typescript": "5.9.3"
|
|
53
55
|
}
|
|
54
56
|
}
|
package/src/compress.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
PlainJsonObject,
|
|
3
|
+
PlainJsonObjectNotArray,
|
|
3
4
|
CompressionTable,
|
|
4
5
|
MangoQuery
|
|
5
6
|
} from './types';
|
|
7
|
+
import { compressEnumValue } from './enum-compression';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* compress the keys of an object via the compression-table
|
|
@@ -13,26 +15,34 @@ export function compressObject(
|
|
|
13
15
|
obj: PlainJsonObject
|
|
14
16
|
): PlainJsonObject {
|
|
15
17
|
if (typeof obj !== 'object' || obj === null) return obj;
|
|
16
|
-
|
|
18
|
+
if (Array.isArray(obj)) {
|
|
17
19
|
// array
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
const retArray: PlainJsonObjectNotArray[] = new Array(obj.length);
|
|
21
|
+
for (let index = 0; index < obj.length; index++) {
|
|
22
|
+
const item = obj[index];
|
|
23
|
+
// primitives do not need a recursive call
|
|
24
|
+
retArray[index] = (typeof item === 'object' && item !== null) ? compressObject(table, item) as any : item;
|
|
25
|
+
}
|
|
26
|
+
return retArray;
|
|
27
|
+
}
|
|
28
|
+
// object
|
|
29
|
+
const ret: PlainJsonObjectNotArray = {};
|
|
30
|
+
const keys = Object.keys(obj);
|
|
31
|
+
const enumCompression = table.enumCompression;
|
|
32
|
+
for (let index = 0; index < keys.length; index++) {
|
|
33
|
+
const key = keys[index] as string;
|
|
34
|
+
const value = (obj as PlainJsonObjectNotArray)[key];
|
|
35
|
+
const enumValues = enumCompression && enumCompression.get(key);
|
|
36
|
+
if (enumValues && (typeof value === 'string' || Array.isArray(value))) {
|
|
37
|
+
ret[compressedAndFlaggedKey(table, key)] = compressEnumValue(enumValues, value);
|
|
38
|
+
} else {
|
|
39
|
+
// primitives do not need a recursive call
|
|
40
|
+
ret[compressedAndFlaggedKey(table, key)] = (typeof value === 'object' && value !== null) ?
|
|
41
|
+
compressObject(table, value) :
|
|
42
|
+
value;
|
|
43
|
+
}
|
|
35
44
|
}
|
|
45
|
+
return ret;
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
/**
|
|
@@ -69,6 +79,26 @@ export function throwErrorIfCompressionFlagUsed(
|
|
|
69
79
|
}
|
|
70
80
|
}
|
|
71
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Cache of the flagged compressed keys per table,
|
|
84
|
+
* so that the flag does not have to be concatenated on each use.
|
|
85
|
+
* The cache is keyed by the table object, so a table must not be
|
|
86
|
+
* mutated after it has been used for compression.
|
|
87
|
+
*/
|
|
88
|
+
const flaggedKeysCache: WeakMap<CompressionTable, Map<string, string>> = new WeakMap();
|
|
89
|
+
function getFlaggedKeys(table: CompressionTable): Map<string, string> {
|
|
90
|
+
let flaggedKeys = flaggedKeysCache.get(table);
|
|
91
|
+
if (!flaggedKeys) {
|
|
92
|
+
const newFlaggedKeys: Map<string, string> = new Map();
|
|
93
|
+
table.compressedToUncompressed.forEach((compressedKey, key) => {
|
|
94
|
+
newFlaggedKeys.set(key, table.compressionFlag + compressedKey);
|
|
95
|
+
});
|
|
96
|
+
flaggedKeysCache.set(table, newFlaggedKeys);
|
|
97
|
+
flaggedKeys = newFlaggedKeys;
|
|
98
|
+
}
|
|
99
|
+
return flaggedKeys;
|
|
100
|
+
}
|
|
101
|
+
|
|
72
102
|
export function compressedAndFlaggedKey(
|
|
73
103
|
table: CompressionTable,
|
|
74
104
|
key: string
|
|
@@ -77,21 +107,24 @@ export function compressedAndFlaggedKey(
|
|
|
77
107
|
table,
|
|
78
108
|
key
|
|
79
109
|
);
|
|
80
|
-
|
|
81
110
|
/**
|
|
82
111
|
* keys could be array-accessors like myArray[4]
|
|
83
|
-
*
|
|
112
|
+
* so the part before the squared bracket is looked up
|
|
113
|
+
* and the bracket part is re-added.
|
|
114
|
+
* Most keys have no bracket, so the plain lookup is the fast path.
|
|
84
115
|
*/
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
const flaggedKeys = getFlaggedKeys(table);
|
|
117
|
+
const bracketIndex = key.indexOf('[');
|
|
118
|
+
if (bracketIndex === -1) {
|
|
119
|
+
const directFlaggedKey = flaggedKeys.get(key);
|
|
120
|
+
return directFlaggedKey ? directFlaggedKey : key;
|
|
121
|
+
}
|
|
122
|
+
const plainKey = key.slice(0, bracketIndex);
|
|
123
|
+
const flaggedKey = flaggedKeys.get(plainKey);
|
|
124
|
+
if (!flaggedKey) {
|
|
90
125
|
return key;
|
|
91
|
-
} else {
|
|
92
|
-
const readdSquared = splitSquaredBrackets.length ? '[' + splitSquaredBrackets.join('[') : '';
|
|
93
|
-
return table.compressionFlag + compressedKey + readdSquared;
|
|
94
126
|
}
|
|
127
|
+
return flaggedKey + key.slice(bracketIndex);
|
|
95
128
|
}
|
|
96
129
|
|
|
97
130
|
|
|
@@ -161,38 +194,155 @@ export function compressQuery(
|
|
|
161
194
|
return ret;
|
|
162
195
|
}
|
|
163
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Operators whose value is a document-value and therefore
|
|
199
|
+
* has to be enum-compressed like the documents themselves.
|
|
200
|
+
* Any other operator, like $type or $mod, gets its value unchanged.
|
|
201
|
+
*/
|
|
202
|
+
const ENUM_VALUE_OPERATORS: string[] = [
|
|
203
|
+
'$eq',
|
|
204
|
+
'$ne',
|
|
205
|
+
'$gt',
|
|
206
|
+
'$gte',
|
|
207
|
+
'$lt',
|
|
208
|
+
'$lte',
|
|
209
|
+
'$in',
|
|
210
|
+
'$nin',
|
|
211
|
+
'$all',
|
|
212
|
+
'$not',
|
|
213
|
+
'$elemMatch'
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Returns the enum of the property that the path points to,
|
|
218
|
+
* or undefined when that property is not enum-compressed.
|
|
219
|
+
*/
|
|
220
|
+
function enumValuesOfPath(
|
|
221
|
+
table: CompressionTable,
|
|
222
|
+
path: string
|
|
223
|
+
): string[] | undefined {
|
|
224
|
+
const enumCompression = table.enumCompression;
|
|
225
|
+
if (!enumCompression) {
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
const splitted = path.split('.');
|
|
229
|
+
let lastKey = splitted[splitted.length - 1] as string;
|
|
230
|
+
const bracketIndex = lastKey.indexOf('[');
|
|
231
|
+
if (bracketIndex !== -1) {
|
|
232
|
+
lastKey = lastKey.slice(0, bracketIndex);
|
|
233
|
+
}
|
|
234
|
+
return enumCompression.get(lastKey);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* A regular expression cannot run on the compressed number of an enum-value,
|
|
239
|
+
* so it is resolved up front into the indexes of the matching enum-values.
|
|
240
|
+
*/
|
|
241
|
+
function enumIndexesOfRegex(
|
|
242
|
+
enumValues: string[],
|
|
243
|
+
pattern: any,
|
|
244
|
+
options: any
|
|
245
|
+
): number[] {
|
|
246
|
+
let source: string;
|
|
247
|
+
let flags: string;
|
|
248
|
+
if (pattern instanceof RegExp) {
|
|
249
|
+
source = pattern.source;
|
|
250
|
+
flags = (pattern.ignoreCase ? 'i' : '') + (pattern.multiline ? 'm' : '');
|
|
251
|
+
} else {
|
|
252
|
+
source = String(pattern);
|
|
253
|
+
flags = '';
|
|
254
|
+
}
|
|
255
|
+
if (typeof options === 'string') {
|
|
256
|
+
flags = options;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* The global flags make .test() stateful
|
|
260
|
+
* and are meaningless for a full check of a single value.
|
|
261
|
+
*/
|
|
262
|
+
flags = flags.replace(/[gy]/g, '');
|
|
263
|
+
const regex = new RegExp(source, flags);
|
|
264
|
+
const indexes: number[] = [];
|
|
265
|
+
for (let i = 0; i < enumValues.length; i++) {
|
|
266
|
+
if (regex.test(enumValues[i] as string)) {
|
|
267
|
+
indexes.push(i);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return indexes;
|
|
271
|
+
}
|
|
272
|
+
|
|
164
273
|
/**
|
|
165
274
|
* @recursive
|
|
275
|
+
* @param enumValues The enum of the property that the selector belongs to,
|
|
276
|
+
* so that the compared values can be enum-compressed.
|
|
166
277
|
*/
|
|
167
278
|
export function compressQuerySelector(
|
|
168
279
|
table: CompressionTable,
|
|
169
|
-
selector: any
|
|
280
|
+
selector: any,
|
|
281
|
+
enumValues?: string[]
|
|
170
282
|
): any {
|
|
171
283
|
if (Array.isArray(selector)) {
|
|
172
|
-
return selector.map(item => compressQuerySelector(table, item));
|
|
284
|
+
return selector.map(item => compressQuerySelector(table, item, enumValues));
|
|
173
285
|
} else if (selector instanceof RegExp) {
|
|
174
286
|
return selector;
|
|
175
287
|
} else if (typeof selector === 'object' && selector !== null) {
|
|
176
288
|
const ret: any = {};
|
|
289
|
+
/**
|
|
290
|
+
* $regex and its $options are replaced by a single $in
|
|
291
|
+
* with the indexes of the matching enum-values.
|
|
292
|
+
*/
|
|
293
|
+
let regexIndexes: number[] | undefined;
|
|
294
|
+
if (enumValues && typeof selector['$regex'] !== 'undefined') {
|
|
295
|
+
regexIndexes = enumIndexesOfRegex(
|
|
296
|
+
enumValues,
|
|
297
|
+
selector['$regex'],
|
|
298
|
+
selector['$options']
|
|
299
|
+
);
|
|
300
|
+
}
|
|
177
301
|
Object.keys(selector).forEach(key => {
|
|
302
|
+
if (regexIndexes && (key === '$regex' || key === '$options')) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
178
305
|
let useKey;
|
|
306
|
+
let useEnumValues;
|
|
179
307
|
if (key.startsWith('$')) {
|
|
180
308
|
// operator
|
|
181
309
|
useKey = key;
|
|
310
|
+
useEnumValues = ENUM_VALUE_OPERATORS.indexOf(key) === -1 ? undefined : enumValues;
|
|
182
311
|
} else {
|
|
183
312
|
// property path
|
|
184
313
|
useKey = compressedPath(
|
|
185
314
|
table,
|
|
186
315
|
key
|
|
187
316
|
);
|
|
317
|
+
useEnumValues = enumValuesOfPath(
|
|
318
|
+
table,
|
|
319
|
+
key
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const value = selector[key];
|
|
323
|
+
if (useEnumValues && value instanceof RegExp) {
|
|
324
|
+
ret[useKey] = {
|
|
325
|
+
$in: enumIndexesOfRegex(useEnumValues, value, undefined)
|
|
326
|
+
};
|
|
327
|
+
} else {
|
|
328
|
+
ret[useKey] = compressQuerySelector(
|
|
329
|
+
table,
|
|
330
|
+
value,
|
|
331
|
+
useEnumValues
|
|
332
|
+
);
|
|
188
333
|
}
|
|
189
|
-
ret[useKey] = compressQuerySelector(
|
|
190
|
-
table,
|
|
191
|
-
selector[key]
|
|
192
|
-
);
|
|
193
334
|
});
|
|
335
|
+
if (regexIndexes) {
|
|
336
|
+
const alreadyIn = ret['$in'];
|
|
337
|
+
ret['$in'] = Array.isArray(alreadyIn) ?
|
|
338
|
+
regexIndexes.filter(index => alreadyIn.includes(index)) :
|
|
339
|
+
regexIndexes;
|
|
340
|
+
}
|
|
194
341
|
return ret;
|
|
195
342
|
} else {
|
|
343
|
+
if (enumValues) {
|
|
344
|
+
return compressEnumValue(enumValues, selector);
|
|
345
|
+
}
|
|
196
346
|
return selector;
|
|
197
347
|
}
|
|
198
348
|
}
|
|
@@ -1,53 +1,176 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
JsonSchema,
|
|
3
|
+
JsonSchemaTypes,
|
|
3
4
|
CompressionTable
|
|
4
5
|
} from './types';
|
|
5
6
|
import { flatClone } from './util';
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
compressedPath,
|
|
9
|
+
compressObject
|
|
10
|
+
} from './compress';
|
|
7
11
|
|
|
12
|
+
/**
|
|
13
|
+
* A value that is enum-compressed is stored as the index of the value,
|
|
14
|
+
* so the type of the property changes from string to number.
|
|
15
|
+
*/
|
|
16
|
+
function toNumberType(
|
|
17
|
+
type: JsonSchemaTypes | JsonSchemaTypes[] | undefined
|
|
18
|
+
): JsonSchemaTypes | JsonSchemaTypes[] | undefined {
|
|
19
|
+
if (!type) {
|
|
20
|
+
return type;
|
|
21
|
+
}
|
|
22
|
+
if (Array.isArray(type)) {
|
|
23
|
+
return type.map(single => single === 'string' ? 'number' : single);
|
|
24
|
+
}
|
|
25
|
+
return type === 'string' ? 'number' : type;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Replaces the enum-values of an enum-compressed property
|
|
30
|
+
* by the indexes that the compressed documents contain.
|
|
31
|
+
* The enum can sit at the property itself or at the items of an array-property.
|
|
32
|
+
* @recursive
|
|
33
|
+
*/
|
|
34
|
+
function compressEnumOfSchema(
|
|
35
|
+
schema: JsonSchema,
|
|
36
|
+
enumValues: string[]
|
|
37
|
+
): JsonSchema {
|
|
38
|
+
let ret = schema;
|
|
39
|
+
if (Array.isArray(schema.enum)) {
|
|
40
|
+
ret = flatClone(schema);
|
|
41
|
+
ret.enum = enumValues.map((_value, index) => index);
|
|
42
|
+
ret.type = toNumberType(schema.type);
|
|
43
|
+
}
|
|
44
|
+
const items = ret.items;
|
|
45
|
+
if (items && !Array.isArray(items) && Array.isArray(items.enum)) {
|
|
46
|
+
if (ret === schema) {
|
|
47
|
+
ret = flatClone(schema);
|
|
48
|
+
}
|
|
49
|
+
ret.items = compressEnumOfSchema(items, enumValues);
|
|
50
|
+
}
|
|
51
|
+
return ret;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Transforms the schema so that it describes the compressed objects.
|
|
56
|
+
* It recurses into all keywords that can contain sub-schemas,
|
|
57
|
+
* independent of the 'type' field. This mirrors how compressObject() works,
|
|
58
|
+
* which compresses every key that is known to the compression-table,
|
|
59
|
+
* wherever the key occurs in the document. For example a nullable nested object
|
|
60
|
+
* has type: ['object', 'null'] and its properties must still be compressed,
|
|
61
|
+
* and properties inside of oneOf/anyOf/allOf must be compressed as well.
|
|
62
|
+
*/
|
|
8
63
|
export function createCompressedJsonSchema(
|
|
9
64
|
compressionTable: CompressionTable,
|
|
10
65
|
schema: JsonSchema
|
|
11
66
|
): JsonSchema {
|
|
12
|
-
|
|
13
|
-
|
|
67
|
+
const hasNested = schema.items ||
|
|
68
|
+
schema.additionalItems ||
|
|
69
|
+
schema.properties ||
|
|
70
|
+
schema.patternProperties ||
|
|
71
|
+
schema.required ||
|
|
72
|
+
schema.allOf ||
|
|
73
|
+
schema.anyOf ||
|
|
74
|
+
schema.oneOf ||
|
|
75
|
+
schema.not ||
|
|
76
|
+
schema.dependencies ||
|
|
77
|
+
schema.definitions ||
|
|
78
|
+
schema.enum;
|
|
79
|
+
if (!hasNested) {
|
|
80
|
+
// no deeper fields in the schema
|
|
81
|
+
return schema;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const cloned = flatClone(schema);
|
|
85
|
+
const enumCompression = compressionTable.enumCompression;
|
|
86
|
+
const compressSchema = (subSchema: JsonSchema) => createCompressedJsonSchema(compressionTable, subSchema);
|
|
87
|
+
const compressKey = (key: string) => compressedPath(compressionTable, key);
|
|
88
|
+
const compressSchemaMap = (
|
|
89
|
+
schemaMap: { [k: string]: JsonSchema },
|
|
90
|
+
compressKeys: boolean
|
|
91
|
+
): { [k: string]: JsonSchema } => {
|
|
92
|
+
const ret: { [k: string]: JsonSchema } = {};
|
|
93
|
+
// do not use Object.entries, it is transpiled shitty
|
|
94
|
+
Object.keys(schemaMap).forEach(key => {
|
|
95
|
+
const useKey = compressKeys ? compressKey(key) : key;
|
|
96
|
+
const subSchema = compressSchema(schemaMap[key] as JsonSchema);
|
|
97
|
+
/**
|
|
98
|
+
* Only real property names can be enum-compressed,
|
|
99
|
+
* the keys of patternProperties and definitions are no property names.
|
|
100
|
+
*/
|
|
101
|
+
const enumValues = (compressKeys && enumCompression) ? enumCompression.get(key) : undefined;
|
|
102
|
+
ret[useKey] = enumValues ? compressEnumOfSchema(subSchema, enumValues) : subSchema;
|
|
103
|
+
});
|
|
104
|
+
return ret;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
if (schema.items) {
|
|
14
108
|
if (Array.isArray(schema.items)) {
|
|
15
|
-
|
|
16
|
-
compressionTable,
|
|
17
|
-
item
|
|
18
|
-
));
|
|
19
|
-
cloned.items = newItems;
|
|
109
|
+
cloned.items = schema.items.map(compressSchema);
|
|
20
110
|
} else {
|
|
21
|
-
|
|
22
|
-
compressionTable,
|
|
23
|
-
schema.items
|
|
24
|
-
);
|
|
25
|
-
cloned.items = newItems;
|
|
111
|
+
cloned.items = compressSchema(schema.items);
|
|
26
112
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
113
|
+
}
|
|
114
|
+
if (schema.additionalItems && typeof schema.additionalItems === 'object') {
|
|
115
|
+
cloned.additionalItems = compressSchema(schema.additionalItems);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// compress all property names
|
|
119
|
+
if (schema.properties) {
|
|
120
|
+
cloned.properties = compressSchemaMap(schema.properties, true);
|
|
121
|
+
}
|
|
122
|
+
// the patterns are not property names, only the sub-schemas are compressed
|
|
123
|
+
if (schema.patternProperties) {
|
|
124
|
+
cloned.patternProperties = compressSchemaMap(schema.patternProperties, false);
|
|
125
|
+
}
|
|
126
|
+
// also compress the required array
|
|
127
|
+
if (schema.required) {
|
|
128
|
+
cloned.required = schema.required.map(compressKey);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (schema.allOf) {
|
|
132
|
+
cloned.allOf = schema.allOf.map(compressSchema);
|
|
133
|
+
}
|
|
134
|
+
if (schema.anyOf) {
|
|
135
|
+
cloned.anyOf = schema.anyOf.map(compressSchema);
|
|
136
|
+
}
|
|
137
|
+
if (schema.oneOf) {
|
|
138
|
+
cloned.oneOf = schema.oneOf.map(compressSchema);
|
|
139
|
+
}
|
|
140
|
+
if (schema.not) {
|
|
141
|
+
cloned.not = compressSchema(schema.not);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* dependencies are keyed by property name
|
|
146
|
+
* and contain either a list of property names
|
|
147
|
+
* or a sub-schema
|
|
148
|
+
*/
|
|
149
|
+
if (schema.dependencies) {
|
|
150
|
+
const dependencies = schema.dependencies;
|
|
151
|
+
const newDependencies: { [k: string]: JsonSchema | string[] } = {};
|
|
152
|
+
Object.keys(dependencies).forEach(key => {
|
|
153
|
+
const dependency = dependencies[key] as JsonSchema | string[];
|
|
154
|
+
newDependencies[compressKey(key)] = Array.isArray(dependency) ?
|
|
155
|
+
dependency.map(compressKey) :
|
|
156
|
+
compressSchema(dependency);
|
|
40
157
|
});
|
|
41
|
-
cloned.
|
|
158
|
+
cloned.dependencies = newDependencies;
|
|
159
|
+
}
|
|
42
160
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
161
|
+
// definition names are not property names, only the sub-schemas are compressed
|
|
162
|
+
if (schema.definitions) {
|
|
163
|
+
cloned.definitions = compressSchemaMap(schema.definitions, false);
|
|
164
|
+
}
|
|
47
165
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
166
|
+
/**
|
|
167
|
+
* enum values are document fragments,
|
|
168
|
+
* so object values contain property names that must be compressed.
|
|
169
|
+
* Primitive values are returned unchanged by compressObject().
|
|
170
|
+
*/
|
|
171
|
+
if (schema.enum) {
|
|
172
|
+
cloned.enum = schema.enum.map(value => compressObject(compressionTable, value));
|
|
52
173
|
}
|
|
174
|
+
|
|
175
|
+
return cloned;
|
|
53
176
|
}
|