@tsed/schema 8.44.5 → 8.44.7
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/lib/esm/fn/compile.js +70 -25
- package/lib/esm/utils/matchGroups.js +26 -5
- package/package.json +7 -7
package/lib/esm/fn/compile.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../components/index.js";
|
|
2
|
-
import { DecoratorTypes, getValue, isClass, isPlainObject, nameOf } from "@tsed/core";
|
|
2
|
+
import { DecoratorTypes, getValue, isArray, isClass, isPlainObject, nameOf } from "@tsed/core";
|
|
3
3
|
import { JsonSchema, SpecTypes } from "../domain/index.js";
|
|
4
4
|
import { execMapper } from "../registries/JsonSchemaMapperContainer.js";
|
|
5
5
|
import { getJsonEntityStore } from "../registries/JsonEntitiesContainer.js";
|
|
@@ -7,11 +7,49 @@ import { inlineRefs } from "../utils/inlineRefs.js";
|
|
|
7
7
|
/**
|
|
8
8
|
* @ignore
|
|
9
9
|
*/
|
|
10
|
-
const CACHES = new
|
|
10
|
+
const CACHES = new WeakMap();
|
|
11
|
+
/**
|
|
12
|
+
* Inline typeof on purpose: this runs for each option on every compile() call, and the
|
|
13
|
+
* `isPrimitive()`/`isNil()` helpers of @tsed/core made the cache hit ~50% slower.
|
|
14
|
+
* @ignore
|
|
15
|
+
*/
|
|
16
|
+
function isPrimitiveValue(value) {
|
|
17
|
+
return value === null || (typeof value !== "object" && typeof value !== "function");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Options holding only primitives and arrays of primitives (the usual `groups`, `customKeys`, `specType`...)
|
|
21
|
+
* give the same key with the native `JSON.stringify`, because the replacer below returns them unchanged.
|
|
22
|
+
* @ignore
|
|
23
|
+
*/
|
|
24
|
+
function hasOnlyPrimitiveValues(options) {
|
|
25
|
+
for (const key in options) {
|
|
26
|
+
// always set by compile() to an empty object
|
|
27
|
+
if (key === "components") {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const value = options[key];
|
|
31
|
+
if (isPrimitiveValue(value)) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
// an array with its own toJSON can return a class, that only the replacer turns into a name
|
|
35
|
+
if (!isArray(value) || value.toJSON !== undefined) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
for (let i = 0; i < value.length; i++) {
|
|
39
|
+
if (!isPrimitiveValue(value[i])) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
11
46
|
/**
|
|
12
47
|
* @ignore
|
|
13
48
|
*/
|
|
14
49
|
function getKey(options) {
|
|
50
|
+
if (hasOnlyPrimitiveValues(options)) {
|
|
51
|
+
return JSON.stringify(options);
|
|
52
|
+
}
|
|
15
53
|
return JSON.stringify(options, (key, value) => {
|
|
16
54
|
if (value && !isPlainObject(value) && isClass(value)) {
|
|
17
55
|
return nameOf(value);
|
|
@@ -23,32 +61,39 @@ function getKey(options) {
|
|
|
23
61
|
* @ignore
|
|
24
62
|
*/
|
|
25
63
|
function get(model, options) {
|
|
26
|
-
|
|
27
|
-
CACHES.set(model, cache);
|
|
64
|
+
let cache = CACHES.get(model);
|
|
28
65
|
const key = getKey(options);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
schema.definitions = options.components.schemas;
|
|
66
|
+
// compiled schemas are objects, never undefined
|
|
67
|
+
const cached = cache?.get(key);
|
|
68
|
+
if (cached !== undefined) {
|
|
69
|
+
return cached;
|
|
70
|
+
}
|
|
71
|
+
let schema;
|
|
72
|
+
if (model instanceof JsonSchema) {
|
|
73
|
+
schema = model.toJSON(options);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const entity = getJsonEntityStore(model);
|
|
77
|
+
let mapper = "schema";
|
|
78
|
+
if (entity.is(DecoratorTypes.PARAM)) {
|
|
79
|
+
options = {
|
|
80
|
+
...options,
|
|
81
|
+
root: true,
|
|
82
|
+
groups: entity.schema.getGroups()
|
|
83
|
+
};
|
|
84
|
+
mapper = "item";
|
|
49
85
|
}
|
|
50
|
-
|
|
86
|
+
schema = execMapper(mapper, [entity.schema], options);
|
|
87
|
+
}
|
|
88
|
+
if (Object.keys(getValue(options, "components.schemas", {})).length) {
|
|
89
|
+
schema.definitions = options.components.schemas;
|
|
90
|
+
}
|
|
91
|
+
if (!cache) {
|
|
92
|
+
// a nested compile() of the same model may have created it in the meantime
|
|
93
|
+
cache = CACHES.get(model) ?? new Map();
|
|
94
|
+
CACHES.set(model, cache);
|
|
51
95
|
}
|
|
96
|
+
cache.set(key, options.inlineRefs ? inlineRefs(schema) : schema);
|
|
52
97
|
return cache.get(key);
|
|
53
98
|
}
|
|
54
99
|
/**
|
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import picomatch from "picomatch";
|
|
2
|
+
/**
|
|
3
|
+
* Compiled matchers per pattern. Patterns can come from the request (`includes`), so the cache is bounded
|
|
4
|
+
* in number of entries, and oversized patterns are compiled on each call instead of being kept.
|
|
5
|
+
* @ignore
|
|
6
|
+
*/
|
|
7
|
+
const MATCHERS = new Map();
|
|
8
|
+
const MAX_MATCHERS = 1000;
|
|
9
|
+
const MAX_CACHED_PATTERN_LENGTH = 256;
|
|
10
|
+
function getMatcher(pattern) {
|
|
11
|
+
if (pattern.length > MAX_CACHED_PATTERN_LENGTH) {
|
|
12
|
+
return picomatch(pattern, {}, true);
|
|
13
|
+
}
|
|
14
|
+
let isMatch = MATCHERS.get(pattern);
|
|
15
|
+
if (!isMatch) {
|
|
16
|
+
isMatch = picomatch(pattern, {}, true);
|
|
17
|
+
if (MATCHERS.size >= MAX_MATCHERS) {
|
|
18
|
+
MATCHERS.clear();
|
|
19
|
+
}
|
|
20
|
+
MATCHERS.set(pattern, isMatch);
|
|
21
|
+
}
|
|
22
|
+
return isMatch;
|
|
23
|
+
}
|
|
2
24
|
function micromatch(list, patterns) {
|
|
3
25
|
patterns = [].concat(patterns);
|
|
4
26
|
list = [].concat(list);
|
|
@@ -6,17 +28,16 @@ function micromatch(list, patterns) {
|
|
|
6
28
|
let keep = new Set();
|
|
7
29
|
let items = new Set();
|
|
8
30
|
let negatives = 0;
|
|
9
|
-
let onResult = (state) => {
|
|
10
|
-
items.add(state.output);
|
|
11
|
-
};
|
|
12
31
|
for (let i = 0; i < patterns.length; i++) {
|
|
13
|
-
|
|
32
|
+
const isMatch = getMatcher(String(patterns[i]));
|
|
14
33
|
let negated = isMatch.state.negated || isMatch.state.negatedExtglob;
|
|
15
34
|
if (negated) {
|
|
16
35
|
negatives++;
|
|
17
36
|
}
|
|
18
37
|
for (let item of list) {
|
|
19
38
|
let matched = isMatch(item, true);
|
|
39
|
+
// same as the picomatch onResult option: collect the output of every tested item
|
|
40
|
+
items.add(matched.output);
|
|
20
41
|
let match = negated ? !matched.isMatch : matched.isMatch;
|
|
21
42
|
if (!match)
|
|
22
43
|
continue;
|
|
@@ -44,7 +65,7 @@ export function matchGroups(groups, compareWith = []) {
|
|
|
44
65
|
}
|
|
45
66
|
}
|
|
46
67
|
if (groupsIncludes.length) {
|
|
47
|
-
return !micromatch(
|
|
68
|
+
return !micromatch(groupsIncludes, compareWith);
|
|
48
69
|
}
|
|
49
70
|
return false;
|
|
50
71
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsed/schema",
|
|
3
|
-
"version": "8.44.
|
|
3
|
+
"version": "8.44.7",
|
|
4
4
|
"description": "JsonSchema module for Ts.ED Framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"JsonSchema",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"test:ci": "vitest run --coverage.thresholds.autoUpdate=true"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@tsed/openspec": "8.44.
|
|
37
|
+
"@tsed/openspec": "8.44.7",
|
|
38
38
|
"change-case": "^5.4.4",
|
|
39
39
|
"json-schema": "0.4.0",
|
|
40
40
|
"picomatch": "4.0.4",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@apidevtools/swagger-parser": "10.1.0",
|
|
46
|
-
"@tsed/barrels": "8.44.
|
|
47
|
-
"@tsed/core": "8.44.
|
|
48
|
-
"@tsed/hooks": "8.44.
|
|
49
|
-
"@tsed/openspec": "8.44.
|
|
50
|
-
"@tsed/typescript": "8.44.
|
|
46
|
+
"@tsed/barrels": "8.44.7",
|
|
47
|
+
"@tsed/core": "8.44.7",
|
|
48
|
+
"@tsed/hooks": "8.44.7",
|
|
49
|
+
"@tsed/openspec": "8.44.7",
|
|
50
|
+
"@tsed/typescript": "8.44.7",
|
|
51
51
|
"@types/fs-extra": "11.0.4",
|
|
52
52
|
"@types/json-schema": "7.0.15",
|
|
53
53
|
"@types/picomatch": "3.0.2",
|