@strapi/utils 5.51.1 → 5.52.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/content-types.d.ts.map +1 -1
- package/dist/content-types.js +39 -2
- package/dist/content-types.js.map +1 -1
- package/dist/content-types.mjs +39 -2
- package/dist/content-types.mjs.map +1 -1
- package/dist/convert-query-params.js +4 -4
- package/dist/convert-query-params.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/errors.mjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/install-id.js +1 -1
- package/dist/install-id.js.map +1 -1
- package/dist/install-id.mjs +1 -1
- package/dist/install-id.mjs.map +1 -1
- package/dist/pagination.d.ts +1 -1
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +12 -5
- package/dist/pagination.js.map +1 -1
- package/dist/pagination.mjs +12 -5
- package/dist/pagination.mjs.map +1 -1
- package/dist/parse-type.d.ts +13 -0
- package/dist/parse-type.d.ts.map +1 -1
- package/dist/parse-type.js +44 -15
- package/dist/parse-type.js.map +1 -1
- package/dist/parse-type.mjs +41 -15
- package/dist/parse-type.mjs.map +1 -1
- package/dist/policy.js +1 -1
- package/dist/policy.js.map +1 -1
- package/dist/policy.mjs +1 -1
- package/dist/policy.mjs.map +1 -1
- package/dist/route-serialization.d.ts +1 -1
- package/dist/route-serialization.d.ts.map +1 -1
- package/dist/route-serialization.js +1 -1
- package/dist/route-serialization.js.map +1 -1
- package/dist/route-serialization.mjs +1 -1
- package/dist/route-serialization.mjs.map +1 -1
- package/dist/sanitize/index.js +3 -1
- package/dist/sanitize/index.js.map +1 -1
- package/dist/sanitize/index.mjs +3 -1
- package/dist/sanitize/index.mjs.map +1 -1
- package/dist/sanitize/sanitizers.d.ts.map +1 -1
- package/dist/sanitize/sanitizers.js +5 -3
- package/dist/sanitize/sanitizers.js.map +1 -1
- package/dist/sanitize/sanitizers.mjs +5 -3
- package/dist/sanitize/sanitizers.mjs.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-relations.d.ts.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-relations.js +128 -86
- package/dist/sanitize/visitors/remove-restricted-relations.js.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-relations.mjs +128 -86
- package/dist/sanitize/visitors/remove-restricted-relations.mjs.map +1 -1
- package/dist/traverse-entity.d.ts.map +1 -1
- package/dist/traverse-entity.js +9 -1
- package/dist/traverse-entity.js.map +1 -1
- package/dist/traverse-entity.mjs +9 -1
- package/dist/traverse-entity.mjs.map +1 -1
- package/dist/validate/validators.js +12 -19
- package/dist/validate/validators.js.map +1 -1
- package/dist/validate/validators.mjs +13 -20
- package/dist/validate/validators.mjs.map +1 -1
- package/package.json +4 -4
package/dist/parse-type.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
3
5
|
var _ = require('lodash');
|
|
4
6
|
var dates = require('date-fns');
|
|
5
7
|
|
|
@@ -54,7 +56,7 @@ const parseDate = (value)=>{
|
|
|
54
56
|
const date = dates__namespace.parseISO(value);
|
|
55
57
|
if (dates__namespace.isValid(date)) return dates__namespace.format(date, 'yyyy-MM-dd');
|
|
56
58
|
throw new Error(`Invalid format, expected an ISO compatible date`);
|
|
57
|
-
} catch
|
|
59
|
+
} catch {
|
|
58
60
|
throw new Error(`Invalid format, expected an ISO compatible date`);
|
|
59
61
|
}
|
|
60
62
|
};
|
|
@@ -71,30 +73,56 @@ const parseDateTimeOrTimestamp = (value)=>{
|
|
|
71
73
|
const milliUnixDate = dates__namespace.parse(value, 'T', new Date());
|
|
72
74
|
if (dates__namespace.isValid(milliUnixDate)) return milliUnixDate;
|
|
73
75
|
throw new Error(`Invalid format, expected a timestamp or an ISO date`);
|
|
74
|
-
} catch
|
|
76
|
+
} catch {
|
|
75
77
|
throw new Error(`Invalid format, expected a timestamp or an ISO date`);
|
|
76
78
|
}
|
|
77
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Hoisted out of `parseBoolean`, which allocated both of these arrays on every call.
|
|
82
|
+
* Query validation calls it once per node of the populate tree of every request, which
|
|
83
|
+
* made those two throwaway allocations one of the largest single sources of GC pressure
|
|
84
|
+
* in a read: 4.3% of on-CPU time in a profiled LaunchPad run.
|
|
85
|
+
*/ const TRUTHY_INPUTS = [
|
|
86
|
+
'true',
|
|
87
|
+
't',
|
|
88
|
+
'1',
|
|
89
|
+
1
|
|
90
|
+
];
|
|
91
|
+
const FALSY_INPUTS = [
|
|
92
|
+
'false',
|
|
93
|
+
'f',
|
|
94
|
+
'0',
|
|
95
|
+
0
|
|
96
|
+
];
|
|
97
|
+
/**
|
|
98
|
+
* Whether `parseBoolean` would accept this value without `forceCast`.
|
|
99
|
+
*
|
|
100
|
+
* Exists so callers can ask the question without using an exception as control flow.
|
|
101
|
+
* Query validation asked it once per populate key by calling `parseType` inside a
|
|
102
|
+
* `try`/`catch`, so every key that is not a boolean keyword — which is nearly all of
|
|
103
|
+
* them — built an Error and captured a stack trace only to discard it. Stack capture is
|
|
104
|
+
* the expensive part, and it made this the second hottest function in a read.
|
|
105
|
+
*
|
|
106
|
+
* Kept in lockstep with `parseBoolean`'s accept conditions below.
|
|
107
|
+
*/ const isBooleanLike = (value)=>{
|
|
108
|
+
if (typeof value === 'boolean') {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
112
|
+
return TRUTHY_INPUTS.includes(value) || FALSY_INPUTS.includes(value);
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
};
|
|
78
116
|
const parseBoolean = (value, options)=>{
|
|
79
117
|
const { forceCast = false } = options;
|
|
80
118
|
if (typeof value === 'boolean') {
|
|
81
119
|
return value;
|
|
82
120
|
}
|
|
83
121
|
if (typeof value === 'string' || typeof value === 'number') {
|
|
84
|
-
if (
|
|
85
|
-
'true',
|
|
86
|
-
't',
|
|
87
|
-
'1',
|
|
88
|
-
1
|
|
89
|
-
].includes(value)) {
|
|
122
|
+
if (TRUTHY_INPUTS.includes(value)) {
|
|
90
123
|
return true;
|
|
91
124
|
}
|
|
92
|
-
if (
|
|
93
|
-
'false',
|
|
94
|
-
'f',
|
|
95
|
-
'0',
|
|
96
|
-
0
|
|
97
|
-
].includes(value)) {
|
|
125
|
+
if (FALSY_INPUTS.includes(value)) {
|
|
98
126
|
return false;
|
|
99
127
|
}
|
|
100
128
|
}
|
|
@@ -137,5 +165,6 @@ const parseBoolean = (value, options)=>{
|
|
|
137
165
|
}
|
|
138
166
|
};
|
|
139
167
|
|
|
140
|
-
|
|
168
|
+
exports.default = parseType;
|
|
169
|
+
exports.isBooleanLike = isBooleanLike;
|
|
141
170
|
//# sourceMappingURL=parse-type.js.map
|
package/dist/parse-type.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-type.js","sources":["../src/parse-type.ts"],"sourcesContent":["import * as _ from 'lodash';\nimport * as dates from 'date-fns';\n\nconst timeRegex = /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]{1,3})?$/;\n\nconst isDate = (v: unknown): v is Date => {\n return dates.isDate(v);\n};\n\nconst parseTime = (value: unknown): string => {\n if (isDate(value)) {\n return dates.format(value, 'HH:mm:ss.SSS');\n }\n\n if (typeof value !== 'string') {\n throw new Error(`Expected a string, got a ${typeof value}`);\n }\n const result = value.match(timeRegex);\n\n if (result === null) {\n throw new Error('Invalid time format, expected HH:mm:ss.SSS');\n }\n\n const [, hours, minutes, seconds, fraction = '.000'] = result;\n const fractionPart = _.padEnd(fraction.slice(1), 3, '0');\n\n return `${hours}:${minutes}:${seconds}.${fractionPart}`;\n};\n\nconst parseDate = (value: unknown) => {\n if (isDate(value)) {\n return dates.format(value, 'yyyy-MM-dd');\n }\n\n if (typeof value !== 'string') {\n throw new Error(`Expected a string, got a ${typeof value}`);\n }\n\n try {\n const date = dates.parseISO(value);\n\n if (dates.isValid(date)) return dates.format(date, 'yyyy-MM-dd');\n\n throw new Error(`Invalid format, expected an ISO compatible date`);\n } catch
|
|
1
|
+
{"version":3,"file":"parse-type.js","sources":["../src/parse-type.ts"],"sourcesContent":["import * as _ from 'lodash';\nimport * as dates from 'date-fns';\n\nconst timeRegex = /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]{1,3})?$/;\n\nconst isDate = (v: unknown): v is Date => {\n return dates.isDate(v);\n};\n\nconst parseTime = (value: unknown): string => {\n if (isDate(value)) {\n return dates.format(value, 'HH:mm:ss.SSS');\n }\n\n if (typeof value !== 'string') {\n throw new Error(`Expected a string, got a ${typeof value}`);\n }\n const result = value.match(timeRegex);\n\n if (result === null) {\n throw new Error('Invalid time format, expected HH:mm:ss.SSS');\n }\n\n const [, hours, minutes, seconds, fraction = '.000'] = result;\n const fractionPart = _.padEnd(fraction.slice(1), 3, '0');\n\n return `${hours}:${minutes}:${seconds}.${fractionPart}`;\n};\n\nconst parseDate = (value: unknown) => {\n if (isDate(value)) {\n return dates.format(value, 'yyyy-MM-dd');\n }\n\n if (typeof value !== 'string') {\n throw new Error(`Expected a string, got a ${typeof value}`);\n }\n\n try {\n const date = dates.parseISO(value);\n\n if (dates.isValid(date)) return dates.format(date, 'yyyy-MM-dd');\n\n throw new Error(`Invalid format, expected an ISO compatible date`);\n } catch {\n throw new Error(`Invalid format, expected an ISO compatible date`);\n }\n};\n\nconst parseDateTimeOrTimestamp = (value: unknown) => {\n if (isDate(value)) {\n return value;\n }\n\n if (typeof value !== 'string') {\n throw new Error(`Expected a string, got a ${typeof value}`);\n }\n\n try {\n const date = dates.parseISO(value);\n if (dates.isValid(date)) return date;\n\n const milliUnixDate = dates.parse(value, 'T', new Date());\n if (dates.isValid(milliUnixDate)) return milliUnixDate;\n\n throw new Error(`Invalid format, expected a timestamp or an ISO date`);\n } catch {\n throw new Error(`Invalid format, expected a timestamp or an ISO date`);\n }\n};\n\ntype TypeMap = {\n boolean: boolean;\n integer: number;\n biginteger: number;\n float: number;\n decimal: number;\n time: string;\n date: string;\n timestamp: Date;\n datetime: Date;\n};\n\nexport interface ParseTypeOptions<T extends keyof TypeMap> {\n type: T;\n value: unknown;\n forceCast?: boolean;\n}\n\n/**\n * Hoisted out of `parseBoolean`, which allocated both of these arrays on every call.\n * Query validation calls it once per node of the populate tree of every request, which\n * made those two throwaway allocations one of the largest single sources of GC pressure\n * in a read: 4.3% of on-CPU time in a profiled LaunchPad run.\n */\nconst TRUTHY_INPUTS: ReadonlyArray<string | number> = ['true', 't', '1', 1];\nconst FALSY_INPUTS: ReadonlyArray<string | number> = ['false', 'f', '0', 0];\n\n/**\n * Whether `parseBoolean` would accept this value without `forceCast`.\n *\n * Exists so callers can ask the question without using an exception as control flow.\n * Query validation asked it once per populate key by calling `parseType` inside a\n * `try`/`catch`, so every key that is not a boolean keyword — which is nearly all of\n * them — built an Error and captured a stack trace only to discard it. Stack capture is\n * the expensive part, and it made this the second hottest function in a read.\n *\n * Kept in lockstep with `parseBoolean`'s accept conditions below.\n */\nconst isBooleanLike = (value: unknown): boolean => {\n if (typeof value === 'boolean') {\n return true;\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n return TRUTHY_INPUTS.includes(value) || FALSY_INPUTS.includes(value);\n }\n\n return false;\n};\n\nconst parseBoolean = (value: unknown, options: { forceCast?: boolean }): boolean => {\n const { forceCast = false } = options;\n\n if (typeof value === 'boolean') {\n return value;\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n if (TRUTHY_INPUTS.includes(value)) {\n return true;\n }\n\n if (FALSY_INPUTS.includes(value)) {\n return false;\n }\n }\n\n if (forceCast) {\n return Boolean(value);\n }\n\n throw new Error('Invalid boolean input. Expected \"t\",\"1\",\"true\",\"false\",\"0\",\"f\"');\n};\n\n/**\n * Cast basic values based on attribute type\n */\nconst parseType = <Type extends keyof TypeMap>(options: ParseTypeOptions<Type>): TypeMap[Type] => {\n const { type, value, forceCast } = options;\n\n switch (type) {\n case 'boolean':\n return parseBoolean(value, { forceCast }) as TypeMap[Type];\n case 'integer':\n case 'biginteger':\n case 'float':\n case 'decimal': {\n return _.toNumber(value) as TypeMap[Type];\n }\n case 'time': {\n return parseTime(value) as TypeMap[Type];\n }\n case 'date': {\n return parseDate(value) as TypeMap[Type];\n }\n case 'timestamp':\n case 'datetime': {\n return parseDateTimeOrTimestamp(value) as TypeMap[Type];\n }\n default:\n return value as TypeMap[Type];\n }\n};\n\nexport { isBooleanLike };\nexport default parseType;\n"],"names":["timeRegex","isDate","v","dates","parseTime","value","format","Error","result","match","hours","minutes","seconds","fraction","fractionPart","_","padEnd","slice","parseDate","date","parseISO","isValid","parseDateTimeOrTimestamp","milliUnixDate","parse","Date","TRUTHY_INPUTS","FALSY_INPUTS","isBooleanLike","includes","parseBoolean","options","forceCast","Boolean","parseType","type","toNumber"],"mappings":"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"}
|
package/dist/parse-type.mjs
CHANGED
|
@@ -31,7 +31,7 @@ const parseDate = (value)=>{
|
|
|
31
31
|
const date = dates.parseISO(value);
|
|
32
32
|
if (dates.isValid(date)) return dates.format(date, 'yyyy-MM-dd');
|
|
33
33
|
throw new Error(`Invalid format, expected an ISO compatible date`);
|
|
34
|
-
} catch
|
|
34
|
+
} catch {
|
|
35
35
|
throw new Error(`Invalid format, expected an ISO compatible date`);
|
|
36
36
|
}
|
|
37
37
|
};
|
|
@@ -48,30 +48,56 @@ const parseDateTimeOrTimestamp = (value)=>{
|
|
|
48
48
|
const milliUnixDate = dates.parse(value, 'T', new Date());
|
|
49
49
|
if (dates.isValid(milliUnixDate)) return milliUnixDate;
|
|
50
50
|
throw new Error(`Invalid format, expected a timestamp or an ISO date`);
|
|
51
|
-
} catch
|
|
51
|
+
} catch {
|
|
52
52
|
throw new Error(`Invalid format, expected a timestamp or an ISO date`);
|
|
53
53
|
}
|
|
54
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* Hoisted out of `parseBoolean`, which allocated both of these arrays on every call.
|
|
57
|
+
* Query validation calls it once per node of the populate tree of every request, which
|
|
58
|
+
* made those two throwaway allocations one of the largest single sources of GC pressure
|
|
59
|
+
* in a read: 4.3% of on-CPU time in a profiled LaunchPad run.
|
|
60
|
+
*/ const TRUTHY_INPUTS = [
|
|
61
|
+
'true',
|
|
62
|
+
't',
|
|
63
|
+
'1',
|
|
64
|
+
1
|
|
65
|
+
];
|
|
66
|
+
const FALSY_INPUTS = [
|
|
67
|
+
'false',
|
|
68
|
+
'f',
|
|
69
|
+
'0',
|
|
70
|
+
0
|
|
71
|
+
];
|
|
72
|
+
/**
|
|
73
|
+
* Whether `parseBoolean` would accept this value without `forceCast`.
|
|
74
|
+
*
|
|
75
|
+
* Exists so callers can ask the question without using an exception as control flow.
|
|
76
|
+
* Query validation asked it once per populate key by calling `parseType` inside a
|
|
77
|
+
* `try`/`catch`, so every key that is not a boolean keyword — which is nearly all of
|
|
78
|
+
* them — built an Error and captured a stack trace only to discard it. Stack capture is
|
|
79
|
+
* the expensive part, and it made this the second hottest function in a read.
|
|
80
|
+
*
|
|
81
|
+
* Kept in lockstep with `parseBoolean`'s accept conditions below.
|
|
82
|
+
*/ const isBooleanLike = (value)=>{
|
|
83
|
+
if (typeof value === 'boolean') {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
87
|
+
return TRUTHY_INPUTS.includes(value) || FALSY_INPUTS.includes(value);
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
};
|
|
55
91
|
const parseBoolean = (value, options)=>{
|
|
56
92
|
const { forceCast = false } = options;
|
|
57
93
|
if (typeof value === 'boolean') {
|
|
58
94
|
return value;
|
|
59
95
|
}
|
|
60
96
|
if (typeof value === 'string' || typeof value === 'number') {
|
|
61
|
-
if (
|
|
62
|
-
'true',
|
|
63
|
-
't',
|
|
64
|
-
'1',
|
|
65
|
-
1
|
|
66
|
-
].includes(value)) {
|
|
97
|
+
if (TRUTHY_INPUTS.includes(value)) {
|
|
67
98
|
return true;
|
|
68
99
|
}
|
|
69
|
-
if (
|
|
70
|
-
'false',
|
|
71
|
-
'f',
|
|
72
|
-
'0',
|
|
73
|
-
0
|
|
74
|
-
].includes(value)) {
|
|
100
|
+
if (FALSY_INPUTS.includes(value)) {
|
|
75
101
|
return false;
|
|
76
102
|
}
|
|
77
103
|
}
|
|
@@ -114,5 +140,5 @@ const parseBoolean = (value, options)=>{
|
|
|
114
140
|
}
|
|
115
141
|
};
|
|
116
142
|
|
|
117
|
-
export { parseType as default };
|
|
143
|
+
export { parseType as default, isBooleanLike };
|
|
118
144
|
//# sourceMappingURL=parse-type.mjs.map
|
package/dist/parse-type.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-type.mjs","sources":["../src/parse-type.ts"],"sourcesContent":["import * as _ from 'lodash';\nimport * as dates from 'date-fns';\n\nconst timeRegex = /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]{1,3})?$/;\n\nconst isDate = (v: unknown): v is Date => {\n return dates.isDate(v);\n};\n\nconst parseTime = (value: unknown): string => {\n if (isDate(value)) {\n return dates.format(value, 'HH:mm:ss.SSS');\n }\n\n if (typeof value !== 'string') {\n throw new Error(`Expected a string, got a ${typeof value}`);\n }\n const result = value.match(timeRegex);\n\n if (result === null) {\n throw new Error('Invalid time format, expected HH:mm:ss.SSS');\n }\n\n const [, hours, minutes, seconds, fraction = '.000'] = result;\n const fractionPart = _.padEnd(fraction.slice(1), 3, '0');\n\n return `${hours}:${minutes}:${seconds}.${fractionPart}`;\n};\n\nconst parseDate = (value: unknown) => {\n if (isDate(value)) {\n return dates.format(value, 'yyyy-MM-dd');\n }\n\n if (typeof value !== 'string') {\n throw new Error(`Expected a string, got a ${typeof value}`);\n }\n\n try {\n const date = dates.parseISO(value);\n\n if (dates.isValid(date)) return dates.format(date, 'yyyy-MM-dd');\n\n throw new Error(`Invalid format, expected an ISO compatible date`);\n } catch
|
|
1
|
+
{"version":3,"file":"parse-type.mjs","sources":["../src/parse-type.ts"],"sourcesContent":["import * as _ from 'lodash';\nimport * as dates from 'date-fns';\n\nconst timeRegex = /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]{1,3})?$/;\n\nconst isDate = (v: unknown): v is Date => {\n return dates.isDate(v);\n};\n\nconst parseTime = (value: unknown): string => {\n if (isDate(value)) {\n return dates.format(value, 'HH:mm:ss.SSS');\n }\n\n if (typeof value !== 'string') {\n throw new Error(`Expected a string, got a ${typeof value}`);\n }\n const result = value.match(timeRegex);\n\n if (result === null) {\n throw new Error('Invalid time format, expected HH:mm:ss.SSS');\n }\n\n const [, hours, minutes, seconds, fraction = '.000'] = result;\n const fractionPart = _.padEnd(fraction.slice(1), 3, '0');\n\n return `${hours}:${minutes}:${seconds}.${fractionPart}`;\n};\n\nconst parseDate = (value: unknown) => {\n if (isDate(value)) {\n return dates.format(value, 'yyyy-MM-dd');\n }\n\n if (typeof value !== 'string') {\n throw new Error(`Expected a string, got a ${typeof value}`);\n }\n\n try {\n const date = dates.parseISO(value);\n\n if (dates.isValid(date)) return dates.format(date, 'yyyy-MM-dd');\n\n throw new Error(`Invalid format, expected an ISO compatible date`);\n } catch {\n throw new Error(`Invalid format, expected an ISO compatible date`);\n }\n};\n\nconst parseDateTimeOrTimestamp = (value: unknown) => {\n if (isDate(value)) {\n return value;\n }\n\n if (typeof value !== 'string') {\n throw new Error(`Expected a string, got a ${typeof value}`);\n }\n\n try {\n const date = dates.parseISO(value);\n if (dates.isValid(date)) return date;\n\n const milliUnixDate = dates.parse(value, 'T', new Date());\n if (dates.isValid(milliUnixDate)) return milliUnixDate;\n\n throw new Error(`Invalid format, expected a timestamp or an ISO date`);\n } catch {\n throw new Error(`Invalid format, expected a timestamp or an ISO date`);\n }\n};\n\ntype TypeMap = {\n boolean: boolean;\n integer: number;\n biginteger: number;\n float: number;\n decimal: number;\n time: string;\n date: string;\n timestamp: Date;\n datetime: Date;\n};\n\nexport interface ParseTypeOptions<T extends keyof TypeMap> {\n type: T;\n value: unknown;\n forceCast?: boolean;\n}\n\n/**\n * Hoisted out of `parseBoolean`, which allocated both of these arrays on every call.\n * Query validation calls it once per node of the populate tree of every request, which\n * made those two throwaway allocations one of the largest single sources of GC pressure\n * in a read: 4.3% of on-CPU time in a profiled LaunchPad run.\n */\nconst TRUTHY_INPUTS: ReadonlyArray<string | number> = ['true', 't', '1', 1];\nconst FALSY_INPUTS: ReadonlyArray<string | number> = ['false', 'f', '0', 0];\n\n/**\n * Whether `parseBoolean` would accept this value without `forceCast`.\n *\n * Exists so callers can ask the question without using an exception as control flow.\n * Query validation asked it once per populate key by calling `parseType` inside a\n * `try`/`catch`, so every key that is not a boolean keyword — which is nearly all of\n * them — built an Error and captured a stack trace only to discard it. Stack capture is\n * the expensive part, and it made this the second hottest function in a read.\n *\n * Kept in lockstep with `parseBoolean`'s accept conditions below.\n */\nconst isBooleanLike = (value: unknown): boolean => {\n if (typeof value === 'boolean') {\n return true;\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n return TRUTHY_INPUTS.includes(value) || FALSY_INPUTS.includes(value);\n }\n\n return false;\n};\n\nconst parseBoolean = (value: unknown, options: { forceCast?: boolean }): boolean => {\n const { forceCast = false } = options;\n\n if (typeof value === 'boolean') {\n return value;\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n if (TRUTHY_INPUTS.includes(value)) {\n return true;\n }\n\n if (FALSY_INPUTS.includes(value)) {\n return false;\n }\n }\n\n if (forceCast) {\n return Boolean(value);\n }\n\n throw new Error('Invalid boolean input. Expected \"t\",\"1\",\"true\",\"false\",\"0\",\"f\"');\n};\n\n/**\n * Cast basic values based on attribute type\n */\nconst parseType = <Type extends keyof TypeMap>(options: ParseTypeOptions<Type>): TypeMap[Type] => {\n const { type, value, forceCast } = options;\n\n switch (type) {\n case 'boolean':\n return parseBoolean(value, { forceCast }) as TypeMap[Type];\n case 'integer':\n case 'biginteger':\n case 'float':\n case 'decimal': {\n return _.toNumber(value) as TypeMap[Type];\n }\n case 'time': {\n return parseTime(value) as TypeMap[Type];\n }\n case 'date': {\n return parseDate(value) as TypeMap[Type];\n }\n case 'timestamp':\n case 'datetime': {\n return parseDateTimeOrTimestamp(value) as TypeMap[Type];\n }\n default:\n return value as TypeMap[Type];\n }\n};\n\nexport { isBooleanLike };\nexport default parseType;\n"],"names":["timeRegex","isDate","v","dates","parseTime","value","format","Error","result","match","hours","minutes","seconds","fraction","fractionPart","_","padEnd","slice","parseDate","date","parseISO","isValid","parseDateTimeOrTimestamp","milliUnixDate","parse","Date","TRUTHY_INPUTS","FALSY_INPUTS","isBooleanLike","includes","parseBoolean","options","forceCast","Boolean","parseType","type","toNumber"],"mappings":"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"}
|
package/dist/policy.js
CHANGED
package/dist/policy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sources":["../src/policy.ts"],"sourcesContent":["import { eq } from 'lodash/fp';\n\ninterface Options {\n name: string;\n validator?(config: unknown): void;\n handler(...args: any[]): any;\n}\n\nconst createPolicy = (options: Options) => {\n const { name = 'unnamed', validator, handler } = options;\n\n const wrappedValidator = (config: unknown) => {\n if (validator) {\n try {\n validator(config);\n } catch
|
|
1
|
+
{"version":3,"file":"policy.js","sources":["../src/policy.ts"],"sourcesContent":["import { eq } from 'lodash/fp';\n\ninterface Options {\n name: string;\n validator?(config: unknown): void;\n handler(...args: any[]): any;\n}\n\nconst createPolicy = (options: Options) => {\n const { name = 'unnamed', validator, handler } = options;\n\n const wrappedValidator = (config: unknown) => {\n if (validator) {\n try {\n validator(config);\n } catch {\n throw new Error(`Invalid config passed to \"${name}\" policy.`);\n }\n }\n };\n\n return {\n name,\n validator: wrappedValidator,\n handler,\n };\n};\n\nconst createPolicyContext = (type: string, ctx: object) => {\n return Object.assign(\n {\n is: eq(type),\n get type() {\n return type;\n },\n },\n ctx\n );\n};\n\nexport { createPolicy, createPolicyContext };\n"],"names":["createPolicy","options","name","validator","handler","wrappedValidator","config","Error","createPolicyContext","type","ctx","Object","assign","is","eq"],"mappings":";;;;AAQA,MAAMA,eAAe,CAACC,OAAAA,GAAAA;IACpB,MAAM,EAAEC,OAAO,SAAS,EAAEC,SAAS,EAAEC,OAAO,EAAE,GAAGH,OAAAA;AAEjD,IAAA,MAAMI,mBAAmB,CAACC,MAAAA,GAAAA;AACxB,QAAA,IAAIH,SAAAA,EAAW;YACb,IAAI;gBACFA,SAAAA,CAAUG,MAAAA,CAAAA;AACZ,YAAA,CAAA,CAAE,OAAM;AACN,gBAAA,MAAM,IAAIC,KAAAA,CAAM,CAAC,0BAA0B,EAAEL,IAAAA,CAAK,SAAS,CAAC,CAAA;AAC9D,YAAA;AACF,QAAA;AACF,IAAA,CAAA;IAEA,OAAO;AACLA,QAAAA,IAAAA;QACAC,SAAAA,EAAWE,gBAAAA;AACXD,QAAAA;AACF,KAAA;AACF;AAEA,MAAMI,mBAAAA,GAAsB,CAACC,IAAAA,EAAcC,GAAAA,GAAAA;IACzC,OAAOC,MAAAA,CAAOC,MAAM,CAClB;AACEC,QAAAA,EAAAA,EAAIC,KAAAA,CAAGL,IAAAA,CAAAA;AACP,QAAA,IAAIA,IAAAA,CAAAA,GAAO;YACT,OAAOA,IAAAA;AACT,QAAA;KACF,EACAC,GAAAA,CAAAA;AAEJ;;;;;"}
|
package/dist/policy.mjs
CHANGED
package/dist/policy.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.mjs","sources":["../src/policy.ts"],"sourcesContent":["import { eq } from 'lodash/fp';\n\ninterface Options {\n name: string;\n validator?(config: unknown): void;\n handler(...args: any[]): any;\n}\n\nconst createPolicy = (options: Options) => {\n const { name = 'unnamed', validator, handler } = options;\n\n const wrappedValidator = (config: unknown) => {\n if (validator) {\n try {\n validator(config);\n } catch
|
|
1
|
+
{"version":3,"file":"policy.mjs","sources":["../src/policy.ts"],"sourcesContent":["import { eq } from 'lodash/fp';\n\ninterface Options {\n name: string;\n validator?(config: unknown): void;\n handler(...args: any[]): any;\n}\n\nconst createPolicy = (options: Options) => {\n const { name = 'unnamed', validator, handler } = options;\n\n const wrappedValidator = (config: unknown) => {\n if (validator) {\n try {\n validator(config);\n } catch {\n throw new Error(`Invalid config passed to \"${name}\" policy.`);\n }\n }\n };\n\n return {\n name,\n validator: wrappedValidator,\n handler,\n };\n};\n\nconst createPolicyContext = (type: string, ctx: object) => {\n return Object.assign(\n {\n is: eq(type),\n get type() {\n return type;\n },\n },\n ctx\n );\n};\n\nexport { createPolicy, createPolicyContext };\n"],"names":["createPolicy","options","name","validator","handler","wrappedValidator","config","Error","createPolicyContext","type","ctx","Object","assign","is","eq"],"mappings":";;AAQA,MAAMA,eAAe,CAACC,OAAAA,GAAAA;IACpB,MAAM,EAAEC,OAAO,SAAS,EAAEC,SAAS,EAAEC,OAAO,EAAE,GAAGH,OAAAA;AAEjD,IAAA,MAAMI,mBAAmB,CAACC,MAAAA,GAAAA;AACxB,QAAA,IAAIH,SAAAA,EAAW;YACb,IAAI;gBACFA,SAAAA,CAAUG,MAAAA,CAAAA;AACZ,YAAA,CAAA,CAAE,OAAM;AACN,gBAAA,MAAM,IAAIC,KAAAA,CAAM,CAAC,0BAA0B,EAAEL,IAAAA,CAAK,SAAS,CAAC,CAAA;AAC9D,YAAA;AACF,QAAA;AACF,IAAA,CAAA;IAEA,OAAO;AACLA,QAAAA,IAAAA;QACAC,SAAAA,EAAWE,gBAAAA;AACXD,QAAAA;AACF,KAAA;AACF;AAEA,MAAMI,mBAAAA,GAAsB,CAACC,IAAAA,EAAcC,GAAAA,GAAAA;IACzC,OAAOC,MAAAA,CAAOC,MAAM,CAClB;AACEC,QAAAA,EAAAA,EAAIC,EAAAA,CAAGL,IAAAA,CAAAA;AACP,QAAA,IAAIA,IAAAA,CAAAA,GAAO;YACT,OAAOA,IAAAA;AACT,QAAA;KACF,EACAC,GAAAA,CAAAA;AAEJ;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const sanitizeRouteForSerialization: ({ request, response, ...rest }: Record<string, unknown>) => Record<string, unknown>;
|
|
1
|
+
export declare const sanitizeRouteForSerialization: ({ request: _request, response: _response, ...rest }: Record<string, unknown>) => Record<string, unknown>;
|
|
2
2
|
export declare const sanitizeRoutesArrayForSerialization: (routes: unknown[]) => Record<string, unknown>[];
|
|
3
3
|
export declare const sanitizeRoutesMapForSerialization: (map: Record<string, unknown[]>) => Record<string, unknown>;
|
|
4
4
|
//# sourceMappingURL=route-serialization.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-serialization.d.ts","sourceRoot":"","sources":["../src/route-serialization.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,6BAA6B,GAAI,
|
|
1
|
+
{"version":3,"file":"route-serialization.d.ts","sourceRoot":"","sources":["../src/route-serialization.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,6BAA6B,GAAI,qDAI3C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE/D,eAAO,MAAM,mCAAmC,GAAI,QAAQ,OAAO,EAAE,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAGxD,CAAC;AAExC,eAAO,MAAM,iCAAiC,GAC5C,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,KAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAOtB,CAAC"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* NOTE: some content API routes are returned to the admin panel e.g. to
|
|
8
8
|
* populate the users and permissions roles page. We need to ensure that the
|
|
9
9
|
* routes can be serialized to JSON without errors.
|
|
10
|
-
*/ const sanitizeRouteForSerialization = ({ request, response, ...rest })=>rest;
|
|
10
|
+
*/ const sanitizeRouteForSerialization = ({ request: _request, response: _response, ...rest })=>rest;
|
|
11
11
|
const sanitizeRoutesArrayForSerialization = (routes)=>routes.filter((route)=>!!route && typeof route === 'object').map(sanitizeRouteForSerialization);
|
|
12
12
|
const sanitizeRoutesMapForSerialization = (map)=>Object.entries(map).reduce((acc, [key, value])=>({
|
|
13
13
|
...acc,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-serialization.js","sources":["../src/route-serialization.ts"],"sourcesContent":["/*\n * Utility to sanitize content API route objects for safe JSON serialization.\n * Removes Zod validation fields (request/response) for safe serialization.\n *\n * NOTE: some content API routes are returned to the admin panel e.g. to\n * populate the users and permissions roles page. We need to ensure that the\n * routes can be serialized to JSON without errors.\n */\n\nexport const sanitizeRouteForSerialization = ({\n request,\n response,\n ...rest\n}: Record<string, unknown>) => rest as Record<string, unknown>;\n\nexport const sanitizeRoutesArrayForSerialization = (routes: unknown[]): Record<string, unknown>[] =>\n routes\n .filter((route): route is Record<string, unknown> => !!route && typeof route === 'object')\n .map(sanitizeRouteForSerialization);\n\nexport const sanitizeRoutesMapForSerialization = (\n map: Record<string, unknown[]>\n): Record<string, unknown> =>\n Object.entries(map).reduce(\n (acc, [key, value]) => ({\n ...acc,\n [key]: Array.isArray(value) ? sanitizeRoutesArrayForSerialization(value) : value,\n }),\n {}\n );\n"],"names":["sanitizeRouteForSerialization","request","response","rest","sanitizeRoutesArrayForSerialization","routes","filter","route","map","sanitizeRoutesMapForSerialization","Object","entries","reduce","acc","key","value","Array","isArray"],"mappings":";;AAAA;;;;;;;AAOC,IAEM,MAAMA,6BAAAA,GAAgC,CAAC,EAC5CC,
|
|
1
|
+
{"version":3,"file":"route-serialization.js","sources":["../src/route-serialization.ts"],"sourcesContent":["/*\n * Utility to sanitize content API route objects for safe JSON serialization.\n * Removes Zod validation fields (request/response) for safe serialization.\n *\n * NOTE: some content API routes are returned to the admin panel e.g. to\n * populate the users and permissions roles page. We need to ensure that the\n * routes can be serialized to JSON without errors.\n */\n\nexport const sanitizeRouteForSerialization = ({\n request: _request,\n response: _response,\n ...rest\n}: Record<string, unknown>) => rest as Record<string, unknown>;\n\nexport const sanitizeRoutesArrayForSerialization = (routes: unknown[]): Record<string, unknown>[] =>\n routes\n .filter((route): route is Record<string, unknown> => !!route && typeof route === 'object')\n .map(sanitizeRouteForSerialization);\n\nexport const sanitizeRoutesMapForSerialization = (\n map: Record<string, unknown[]>\n): Record<string, unknown> =>\n Object.entries(map).reduce(\n (acc, [key, value]) => ({\n ...acc,\n [key]: Array.isArray(value) ? sanitizeRoutesArrayForSerialization(value) : value,\n }),\n {}\n );\n"],"names":["sanitizeRouteForSerialization","request","_request","response","_response","rest","sanitizeRoutesArrayForSerialization","routes","filter","route","map","sanitizeRoutesMapForSerialization","Object","entries","reduce","acc","key","value","Array","isArray"],"mappings":";;AAAA;;;;;;;AAOC,IAEM,MAAMA,6BAAAA,GAAgC,CAAC,EAC5CC,OAAAA,EAASC,QAAQ,EACjBC,QAAAA,EAAUC,SAAS,EACnB,GAAGC,IAAAA,EACqB,GAAKA;MAElBC,mCAAAA,GAAsC,CAACC,MAAAA,GAClDA,MAAAA,CACGC,MAAM,CAAC,CAACC,KAAAA,GAA4C,CAAC,CAACA,KAAAA,IAAS,OAAOA,UAAU,QAAA,CAAA,CAChFC,GAAG,CAACV,6BAAAA;MAEIW,iCAAAA,GAAoC,CAC/CD,GAAAA,GAEAE,MAAAA,CAAOC,OAAO,CAACH,GAAAA,CAAAA,CAAKI,MAAM,CACxB,CAACC,GAAAA,EAAK,CAACC,GAAAA,EAAKC,KAAAA,CAAM,IAAM;AACtB,YAAA,GAAGF,GAAG;AACN,YAAA,CAACC,MAAME,KAAAA,CAAMC,OAAO,CAACF,KAAAA,CAAAA,GAASX,oCAAoCW,KAAAA,CAAAA,GAASA;SAC7E,CAAA,EACA,EAAC;;;;;;"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* NOTE: some content API routes are returned to the admin panel e.g. to
|
|
6
6
|
* populate the users and permissions roles page. We need to ensure that the
|
|
7
7
|
* routes can be serialized to JSON without errors.
|
|
8
|
-
*/ const sanitizeRouteForSerialization = ({ request, response, ...rest })=>rest;
|
|
8
|
+
*/ const sanitizeRouteForSerialization = ({ request: _request, response: _response, ...rest })=>rest;
|
|
9
9
|
const sanitizeRoutesArrayForSerialization = (routes)=>routes.filter((route)=>!!route && typeof route === 'object').map(sanitizeRouteForSerialization);
|
|
10
10
|
const sanitizeRoutesMapForSerialization = (map)=>Object.entries(map).reduce((acc, [key, value])=>({
|
|
11
11
|
...acc,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-serialization.mjs","sources":["../src/route-serialization.ts"],"sourcesContent":["/*\n * Utility to sanitize content API route objects for safe JSON serialization.\n * Removes Zod validation fields (request/response) for safe serialization.\n *\n * NOTE: some content API routes are returned to the admin panel e.g. to\n * populate the users and permissions roles page. We need to ensure that the\n * routes can be serialized to JSON without errors.\n */\n\nexport const sanitizeRouteForSerialization = ({\n request,\n response,\n ...rest\n}: Record<string, unknown>) => rest as Record<string, unknown>;\n\nexport const sanitizeRoutesArrayForSerialization = (routes: unknown[]): Record<string, unknown>[] =>\n routes\n .filter((route): route is Record<string, unknown> => !!route && typeof route === 'object')\n .map(sanitizeRouteForSerialization);\n\nexport const sanitizeRoutesMapForSerialization = (\n map: Record<string, unknown[]>\n): Record<string, unknown> =>\n Object.entries(map).reduce(\n (acc, [key, value]) => ({\n ...acc,\n [key]: Array.isArray(value) ? sanitizeRoutesArrayForSerialization(value) : value,\n }),\n {}\n );\n"],"names":["sanitizeRouteForSerialization","request","response","rest","sanitizeRoutesArrayForSerialization","routes","filter","route","map","sanitizeRoutesMapForSerialization","Object","entries","reduce","acc","key","value","Array","isArray"],"mappings":"AAAA;;;;;;;AAOC,IAEM,MAAMA,6BAAAA,GAAgC,CAAC,EAC5CC,
|
|
1
|
+
{"version":3,"file":"route-serialization.mjs","sources":["../src/route-serialization.ts"],"sourcesContent":["/*\n * Utility to sanitize content API route objects for safe JSON serialization.\n * Removes Zod validation fields (request/response) for safe serialization.\n *\n * NOTE: some content API routes are returned to the admin panel e.g. to\n * populate the users and permissions roles page. We need to ensure that the\n * routes can be serialized to JSON without errors.\n */\n\nexport const sanitizeRouteForSerialization = ({\n request: _request,\n response: _response,\n ...rest\n}: Record<string, unknown>) => rest as Record<string, unknown>;\n\nexport const sanitizeRoutesArrayForSerialization = (routes: unknown[]): Record<string, unknown>[] =>\n routes\n .filter((route): route is Record<string, unknown> => !!route && typeof route === 'object')\n .map(sanitizeRouteForSerialization);\n\nexport const sanitizeRoutesMapForSerialization = (\n map: Record<string, unknown[]>\n): Record<string, unknown> =>\n Object.entries(map).reduce(\n (acc, [key, value]) => ({\n ...acc,\n [key]: Array.isArray(value) ? sanitizeRoutesArrayForSerialization(value) : value,\n }),\n {}\n );\n"],"names":["sanitizeRouteForSerialization","request","_request","response","_response","rest","sanitizeRoutesArrayForSerialization","routes","filter","route","map","sanitizeRoutesMapForSerialization","Object","entries","reduce","acc","key","value","Array","isArray"],"mappings":"AAAA;;;;;;;AAOC,IAEM,MAAMA,6BAAAA,GAAgC,CAAC,EAC5CC,OAAAA,EAASC,QAAQ,EACjBC,QAAAA,EAAUC,SAAS,EACnB,GAAGC,IAAAA,EACqB,GAAKA;MAElBC,mCAAAA,GAAsC,CAACC,MAAAA,GAClDA,MAAAA,CACGC,MAAM,CAAC,CAACC,KAAAA,GAA4C,CAAC,CAACA,KAAAA,IAAS,OAAOA,UAAU,QAAA,CAAA,CAChFC,GAAG,CAACV,6BAAAA;MAEIW,iCAAAA,GAAoC,CAC/CD,GAAAA,GAEAE,MAAAA,CAAOC,OAAO,CAACH,GAAAA,CAAAA,CAAKI,MAAM,CACxB,CAACC,GAAAA,EAAK,CAACC,GAAAA,EAAKC,KAAAA,CAAM,IAAM;AACtB,YAAA,GAAGF,GAAG;AACN,YAAA,CAACC,MAAME,KAAAA,CAAMC,OAAO,CAACF,KAAAA,CAAAA,GAASX,oCAAoCW,KAAAA,CAAAA,GAASA;SAC7E,CAAA,EACA,EAAC;;;;"}
|
package/dist/sanitize/index.js
CHANGED
|
@@ -99,7 +99,9 @@ const createAPISanitizers = (opts)=>{
|
|
|
99
99
|
throw new Error('Missing schema in sanitizeOutput');
|
|
100
100
|
}
|
|
101
101
|
if (fp.isArray(data)) {
|
|
102
|
-
const res =
|
|
102
|
+
const res = Array.from({
|
|
103
|
+
length: data.length
|
|
104
|
+
});
|
|
103
105
|
for(let i = 0; i < data.length; i += 1){
|
|
104
106
|
res[i] = await sanitizeOutput(data[i], schema, {
|
|
105
107
|
auth
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/sanitize/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, cloneDeep, omit, pick } from 'lodash/fp';\nimport type { z } from 'zod/v4';\n\nimport { constants, getNonWritableAttributes } from '../content-types';\nimport { ALLOWED_QUERY_PARAM_KEYS } from '../content-api-constants';\nimport {\n type RouteLike,\n getExtraQueryKeysFromRoute,\n getExtraRootKeysFromRouteBody,\n} from '../content-api-route-params';\nimport { pipe as pipeAsync } from '../async';\n\nimport * as visitors from './visitors';\nimport * as sanitizers from './sanitizers';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\nimport type { Model, Data } from '../types';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\nimport { hasSort } from '../sort-query';\n\nexport interface Options {\n auth?: unknown;\n /**\n * If true, removes fields that are not declared in the schema (input) or keeps only allowed query param keys (query).\n * Defaults to false for backward compatibility.\n * TODO: In Strapi 6, strictParams will default to true (and may be removed as an option)\n */\n strictParams?: boolean;\n /**\n * When set, extra query/input params are derived from the route's request schema (and validated/sanitized with Zod).\n * When absent, no extra params are allowed in strict mode.\n */\n route?: RouteLike;\n}\n\nexport interface Sanitizer {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface SanitizeFunc {\n (data: unknown, schema: Model, options?: Options): Promise<unknown>;\n}\n\nexport type SanitizeQueryParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport type SanitizeBodyParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport interface APIOptions {\n sanitizers?: Sanitizers;\n getModel: (model: string) => Model;\n}\n\nexport interface Sanitizers {\n input?: Sanitizer[];\n output?: Sanitizer[];\n}\n\nconst createAPISanitizers = (opts: APIOptions) => {\n const { getModel } = opts;\n\n const sanitizeInput: SanitizeFunc = (\n data: unknown,\n schema: Model,\n { auth, strictParams = false, route } = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeInput');\n }\n if (isArray(data)) {\n return Promise.all(\n data.map((entry) => sanitizeInput(entry, schema, { auth, strictParams, route }))\n );\n }\n\n const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n // Remove first level ID in inputs\n omit(constants.ID_ATTRIBUTE),\n omit(constants.DOC_ID_ATTRIBUTE),\n // Remove non-writable attributes\n traverseEntity(visitors.removeRestrictedFields(nonWritableAttributes), { schema, getModel }),\n ];\n\n if (strictParams) {\n // Remove unrecognized fields (allowedExtraRootKeys = registered input param keys)\n transforms.push(\n traverseEntity(visitors.removeUnrecognizedFields, {\n schema,\n getModel,\n allowedExtraRootKeys,\n })\n );\n }\n\n if (auth) {\n // Remove restricted relations\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.input?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n /**\n * For each extra root key from the route's body schema present in data, run Zod safeParse.\n * If parsing fails, the key is removed from the output.\n *\n * Content-api sends the document payload as body.data; the controller calls sanitizeInput(body.data, ctx),\n * so the input we receive here is the inner payload (keys like \"relatedMedia\", \"name\"), not the full body.\n * The route's body schema is z.object({ data: ... }), so its shape includes \"data\". We skip \"data\" because\n * the main document payload is already sanitized above by traverseEntity (removeUnrecognizedFields, etc.);\n * relation ops (connect/disconnect/set) are handled there, not by the route's Zod schema. We only run\n * Zod here for truly extra root keys added via addInputParams (e.g. clientMutationId).\n */\n const routeBodySanitizeTransform = async (data: Data): Promise<Data> => {\n if (!data || typeof data !== 'object' || Array.isArray(data)) return data;\n const obj = data as Record<string, unknown>;\n const bodySchema = route?.request?.body?.['application/json'];\n if (bodySchema && typeof bodySchema === 'object' && 'shape' in bodySchema) {\n const shape = (bodySchema as { shape: Record<string, z.ZodTypeAny> }).shape;\n for (const key of Object.keys(shape)) {\n if (key === 'data' || !(key in obj)) continue;\n const zodSchema = shape[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(obj[key]);\n if (result.success) {\n obj[key] = result.data;\n } else {\n delete obj[key];\n }\n }\n }\n }\n return data;\n };\n (transforms as Array<(data: Data) => Data | Promise<Data>>).push(routeBodySanitizeTransform);\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeOutput: SanitizeFunc = async (data, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeOutput');\n }\n if (isArray(data)) {\n const res = new Array(data.length);\n for (let i = 0; i < data.length; i += 1) {\n res[i] = await sanitizeOutput(data[i], schema, { auth });\n }\n return res;\n }\n\n const transforms = [\n (data: Data) => sanitizers.defaultSanitizeOutput({ schema, getModel }, data),\n ];\n\n if (auth) {\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.output?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeQuery = async (\n query: Record<string, unknown>,\n schema: Model,\n { auth, strictParams = false, route }: Options = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeQuery');\n }\n const { filters, sort, fields, populate } = query;\n\n const sanitizedQuery = cloneDeep(query);\n\n if ('publicationFilter' in sanitizedQuery) {\n validatePublicationFilterQueryParam(sanitizedQuery.publicationFilter);\n }\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (hasSort(sort)) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n } else if ('sort' in sanitizedQuery) {\n delete sanitizedQuery.sort;\n }\n\n if (fields) {\n Object.assign(sanitizedQuery, { fields: await sanitizeFields(fields, schema) });\n }\n\n if (populate) {\n Object.assign(sanitizedQuery, { populate: await sanitizePopulate(populate, schema) });\n }\n\n const extraQueryKeys = getExtraQueryKeysFromRoute(route);\n const routeQuerySchema = route?.request?.query;\n if (routeQuerySchema) {\n for (const key of extraQueryKeys) {\n if (key in query) {\n const zodSchema = routeQuerySchema[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(query[key]);\n if (result.success) {\n sanitizedQuery[key] = result.data;\n } else {\n delete sanitizedQuery[key];\n }\n }\n }\n }\n }\n\n if (strictParams) {\n const allowedKeys = [...ALLOWED_QUERY_PARAM_KEYS, ...extraQueryKeys];\n return pick(allowedKeys, sanitizedQuery) as Record<string, unknown>;\n }\n\n return sanitizedQuery;\n };\n\n const sanitizeFilters: SanitizeFunc = (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFilters');\n }\n if (isArray(filters)) {\n return Promise.all(filters.map((filter) => sanitizeFilters(filter, schema, { auth })));\n }\n\n const transforms = [sanitizers.defaultSanitizeFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(filters);\n };\n\n const sanitizeSort: SanitizeFunc = (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeSort');\n }\n const transforms = [sanitizers.defaultSanitizeSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(sort);\n };\n\n const sanitizeFields: SanitizeFunc = (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFields');\n }\n const transforms = [sanitizers.defaultSanitizeFields({ schema, getModel })];\n\n return pipeAsync(...transforms)(fields);\n };\n\n const sanitizePopulate: SanitizeFunc = (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [sanitizers.defaultSanitizePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(populate);\n };\n\n return {\n input: sanitizeInput,\n output: sanitizeOutput,\n query: sanitizeQuery,\n filters: sanitizeFilters,\n sort: sanitizeSort,\n fields: sanitizeFields,\n populate: sanitizePopulate,\n };\n};\n\nexport { createAPISanitizers, sanitizers, visitors };\n\nexport type APISanitiers = ReturnType<typeof createAPISanitizers>;\n"],"names":["createAPISanitizers","opts","getModel","sanitizeInput","data","schema","auth","strictParams","route","Error","isArray","Promise","all","map","entry","allowedExtraRootKeys","getExtraRootKeysFromRouteBody","nonWritableAttributes","getNonWritableAttributes","transforms","omit","constants","ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","traverseEntity","visitors","push","sanitizers","input","forEach","sanitizer","routeBodySanitizeTransform","Array","obj","bodySchema","request","body","shape","key","Object","keys","zodSchema","safeParse","result","success","pipeAsync","sanitizeOutput","res","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","validatePublicationFilterQueryParam","publicationFilter","assign","sanitizeFilters","hasSort","sanitizeSort","sanitizeFields","sanitizePopulate","extraQueryKeys","getExtraQueryKeysFromRoute","routeQuerySchema","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","pick","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/sanitize/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, cloneDeep, omit, pick } from 'lodash/fp';\nimport type { z } from 'zod/v4';\n\nimport { constants, getNonWritableAttributes } from '../content-types';\nimport { ALLOWED_QUERY_PARAM_KEYS } from '../content-api-constants';\nimport {\n type RouteLike,\n getExtraQueryKeysFromRoute,\n getExtraRootKeysFromRouteBody,\n} from '../content-api-route-params';\nimport { pipe as pipeAsync } from '../async';\n\nimport * as visitors from './visitors';\nimport * as sanitizers from './sanitizers';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\nimport type { Model, Data } from '../types';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\nimport { hasSort } from '../sort-query';\n\nexport interface Options {\n auth?: unknown;\n /**\n * If true, removes fields that are not declared in the schema (input) or keeps only allowed query param keys (query).\n * Defaults to false for backward compatibility.\n * TODO: In Strapi 6, strictParams will default to true (and may be removed as an option)\n */\n strictParams?: boolean;\n /**\n * When set, extra query/input params are derived from the route's request schema (and validated/sanitized with Zod).\n * When absent, no extra params are allowed in strict mode.\n */\n route?: RouteLike;\n}\n\nexport interface Sanitizer {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface SanitizeFunc {\n (data: unknown, schema: Model, options?: Options): Promise<unknown>;\n}\n\nexport type SanitizeQueryParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport type SanitizeBodyParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport interface APIOptions {\n sanitizers?: Sanitizers;\n getModel: (model: string) => Model;\n}\n\nexport interface Sanitizers {\n input?: Sanitizer[];\n output?: Sanitizer[];\n}\n\nconst createAPISanitizers = (opts: APIOptions) => {\n const { getModel } = opts;\n\n const sanitizeInput: SanitizeFunc = (\n data: unknown,\n schema: Model,\n { auth, strictParams = false, route } = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeInput');\n }\n if (isArray(data)) {\n return Promise.all(\n data.map((entry) => sanitizeInput(entry, schema, { auth, strictParams, route }))\n );\n }\n\n const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n // Remove first level ID in inputs\n omit(constants.ID_ATTRIBUTE),\n omit(constants.DOC_ID_ATTRIBUTE),\n // Remove non-writable attributes\n traverseEntity(visitors.removeRestrictedFields(nonWritableAttributes), { schema, getModel }),\n ];\n\n if (strictParams) {\n // Remove unrecognized fields (allowedExtraRootKeys = registered input param keys)\n transforms.push(\n traverseEntity(visitors.removeUnrecognizedFields, {\n schema,\n getModel,\n allowedExtraRootKeys,\n })\n );\n }\n\n if (auth) {\n // Remove restricted relations\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.input?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n /**\n * For each extra root key from the route's body schema present in data, run Zod safeParse.\n * If parsing fails, the key is removed from the output.\n *\n * Content-api sends the document payload as body.data; the controller calls sanitizeInput(body.data, ctx),\n * so the input we receive here is the inner payload (keys like \"relatedMedia\", \"name\"), not the full body.\n * The route's body schema is z.object({ data: ... }), so its shape includes \"data\". We skip \"data\" because\n * the main document payload is already sanitized above by traverseEntity (removeUnrecognizedFields, etc.);\n * relation ops (connect/disconnect/set) are handled there, not by the route's Zod schema. We only run\n * Zod here for truly extra root keys added via addInputParams (e.g. clientMutationId).\n */\n const routeBodySanitizeTransform = async (data: Data): Promise<Data> => {\n if (!data || typeof data !== 'object' || Array.isArray(data)) return data;\n const obj = data as Record<string, unknown>;\n const bodySchema = route?.request?.body?.['application/json'];\n if (bodySchema && typeof bodySchema === 'object' && 'shape' in bodySchema) {\n const shape = (bodySchema as { shape: Record<string, z.ZodTypeAny> }).shape;\n for (const key of Object.keys(shape)) {\n if (key === 'data' || !(key in obj)) continue;\n const zodSchema = shape[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(obj[key]);\n if (result.success) {\n obj[key] = result.data;\n } else {\n delete obj[key];\n }\n }\n }\n }\n return data;\n };\n (transforms as Array<(data: Data) => Data | Promise<Data>>).push(routeBodySanitizeTransform);\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeOutput: SanitizeFunc = async (data, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeOutput');\n }\n if (isArray(data)) {\n const res: unknown[] = Array.from({ length: data.length });\n for (let i = 0; i < data.length; i += 1) {\n res[i] = await sanitizeOutput(data[i], schema, { auth });\n }\n return res;\n }\n\n const transforms = [\n (data: Data) => sanitizers.defaultSanitizeOutput({ schema, getModel }, data),\n ];\n\n if (auth) {\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.output?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeQuery = async (\n query: Record<string, unknown>,\n schema: Model,\n { auth, strictParams = false, route }: Options = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeQuery');\n }\n const { filters, sort, fields, populate } = query;\n\n const sanitizedQuery = cloneDeep(query);\n\n if ('publicationFilter' in sanitizedQuery) {\n validatePublicationFilterQueryParam(sanitizedQuery.publicationFilter);\n }\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (hasSort(sort)) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n } else if ('sort' in sanitizedQuery) {\n delete sanitizedQuery.sort;\n }\n\n if (fields) {\n Object.assign(sanitizedQuery, { fields: await sanitizeFields(fields, schema) });\n }\n\n if (populate) {\n Object.assign(sanitizedQuery, { populate: await sanitizePopulate(populate, schema) });\n }\n\n const extraQueryKeys = getExtraQueryKeysFromRoute(route);\n const routeQuerySchema = route?.request?.query;\n if (routeQuerySchema) {\n for (const key of extraQueryKeys) {\n if (key in query) {\n const zodSchema = routeQuerySchema[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(query[key]);\n if (result.success) {\n sanitizedQuery[key] = result.data;\n } else {\n delete sanitizedQuery[key];\n }\n }\n }\n }\n }\n\n if (strictParams) {\n const allowedKeys = [...ALLOWED_QUERY_PARAM_KEYS, ...extraQueryKeys];\n return pick(allowedKeys, sanitizedQuery) as Record<string, unknown>;\n }\n\n return sanitizedQuery;\n };\n\n const sanitizeFilters: SanitizeFunc = (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFilters');\n }\n if (isArray(filters)) {\n return Promise.all(filters.map((filter) => sanitizeFilters(filter, schema, { auth })));\n }\n\n const transforms = [sanitizers.defaultSanitizeFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(filters);\n };\n\n const sanitizeSort: SanitizeFunc = (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeSort');\n }\n const transforms = [sanitizers.defaultSanitizeSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(sort);\n };\n\n const sanitizeFields: SanitizeFunc = (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFields');\n }\n const transforms = [sanitizers.defaultSanitizeFields({ schema, getModel })];\n\n return pipeAsync(...transforms)(fields);\n };\n\n const sanitizePopulate: SanitizeFunc = (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [sanitizers.defaultSanitizePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(populate);\n };\n\n return {\n input: sanitizeInput,\n output: sanitizeOutput,\n query: sanitizeQuery,\n filters: sanitizeFilters,\n sort: sanitizeSort,\n fields: sanitizeFields,\n populate: sanitizePopulate,\n };\n};\n\nexport { createAPISanitizers, sanitizers, visitors };\n\nexport type APISanitiers = ReturnType<typeof createAPISanitizers>;\n"],"names":["createAPISanitizers","opts","getModel","sanitizeInput","data","schema","auth","strictParams","route","Error","isArray","Promise","all","map","entry","allowedExtraRootKeys","getExtraRootKeysFromRouteBody","nonWritableAttributes","getNonWritableAttributes","transforms","omit","constants","ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","traverseEntity","visitors","push","sanitizers","input","forEach","sanitizer","routeBodySanitizeTransform","Array","obj","bodySchema","request","body","shape","key","Object","keys","zodSchema","safeParse","result","success","pipeAsync","sanitizeOutput","res","from","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","validatePublicationFilterQueryParam","publicationFilter","assign","sanitizeFilters","hasSort","sanitizeSort","sanitizeFields","sanitizePopulate","extraQueryKeys","getExtraQueryKeysFromRoute","routeQuerySchema","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","pick","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
|
package/dist/sanitize/index.mjs
CHANGED
|
@@ -100,7 +100,9 @@ const createAPISanitizers = (opts)=>{
|
|
|
100
100
|
throw new Error('Missing schema in sanitizeOutput');
|
|
101
101
|
}
|
|
102
102
|
if (isArray(data)) {
|
|
103
|
-
const res =
|
|
103
|
+
const res = Array.from({
|
|
104
|
+
length: data.length
|
|
105
|
+
});
|
|
104
106
|
for(let i = 0; i < data.length; i += 1){
|
|
105
107
|
res[i] = await sanitizeOutput(data[i], schema, {
|
|
106
108
|
auth
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../src/sanitize/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, cloneDeep, omit, pick } from 'lodash/fp';\nimport type { z } from 'zod/v4';\n\nimport { constants, getNonWritableAttributes } from '../content-types';\nimport { ALLOWED_QUERY_PARAM_KEYS } from '../content-api-constants';\nimport {\n type RouteLike,\n getExtraQueryKeysFromRoute,\n getExtraRootKeysFromRouteBody,\n} from '../content-api-route-params';\nimport { pipe as pipeAsync } from '../async';\n\nimport * as visitors from './visitors';\nimport * as sanitizers from './sanitizers';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\nimport type { Model, Data } from '../types';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\nimport { hasSort } from '../sort-query';\n\nexport interface Options {\n auth?: unknown;\n /**\n * If true, removes fields that are not declared in the schema (input) or keeps only allowed query param keys (query).\n * Defaults to false for backward compatibility.\n * TODO: In Strapi 6, strictParams will default to true (and may be removed as an option)\n */\n strictParams?: boolean;\n /**\n * When set, extra query/input params are derived from the route's request schema (and validated/sanitized with Zod).\n * When absent, no extra params are allowed in strict mode.\n */\n route?: RouteLike;\n}\n\nexport interface Sanitizer {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface SanitizeFunc {\n (data: unknown, schema: Model, options?: Options): Promise<unknown>;\n}\n\nexport type SanitizeQueryParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport type SanitizeBodyParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport interface APIOptions {\n sanitizers?: Sanitizers;\n getModel: (model: string) => Model;\n}\n\nexport interface Sanitizers {\n input?: Sanitizer[];\n output?: Sanitizer[];\n}\n\nconst createAPISanitizers = (opts: APIOptions) => {\n const { getModel } = opts;\n\n const sanitizeInput: SanitizeFunc = (\n data: unknown,\n schema: Model,\n { auth, strictParams = false, route } = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeInput');\n }\n if (isArray(data)) {\n return Promise.all(\n data.map((entry) => sanitizeInput(entry, schema, { auth, strictParams, route }))\n );\n }\n\n const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n // Remove first level ID in inputs\n omit(constants.ID_ATTRIBUTE),\n omit(constants.DOC_ID_ATTRIBUTE),\n // Remove non-writable attributes\n traverseEntity(visitors.removeRestrictedFields(nonWritableAttributes), { schema, getModel }),\n ];\n\n if (strictParams) {\n // Remove unrecognized fields (allowedExtraRootKeys = registered input param keys)\n transforms.push(\n traverseEntity(visitors.removeUnrecognizedFields, {\n schema,\n getModel,\n allowedExtraRootKeys,\n })\n );\n }\n\n if (auth) {\n // Remove restricted relations\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.input?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n /**\n * For each extra root key from the route's body schema present in data, run Zod safeParse.\n * If parsing fails, the key is removed from the output.\n *\n * Content-api sends the document payload as body.data; the controller calls sanitizeInput(body.data, ctx),\n * so the input we receive here is the inner payload (keys like \"relatedMedia\", \"name\"), not the full body.\n * The route's body schema is z.object({ data: ... }), so its shape includes \"data\". We skip \"data\" because\n * the main document payload is already sanitized above by traverseEntity (removeUnrecognizedFields, etc.);\n * relation ops (connect/disconnect/set) are handled there, not by the route's Zod schema. We only run\n * Zod here for truly extra root keys added via addInputParams (e.g. clientMutationId).\n */\n const routeBodySanitizeTransform = async (data: Data): Promise<Data> => {\n if (!data || typeof data !== 'object' || Array.isArray(data)) return data;\n const obj = data as Record<string, unknown>;\n const bodySchema = route?.request?.body?.['application/json'];\n if (bodySchema && typeof bodySchema === 'object' && 'shape' in bodySchema) {\n const shape = (bodySchema as { shape: Record<string, z.ZodTypeAny> }).shape;\n for (const key of Object.keys(shape)) {\n if (key === 'data' || !(key in obj)) continue;\n const zodSchema = shape[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(obj[key]);\n if (result.success) {\n obj[key] = result.data;\n } else {\n delete obj[key];\n }\n }\n }\n }\n return data;\n };\n (transforms as Array<(data: Data) => Data | Promise<Data>>).push(routeBodySanitizeTransform);\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeOutput: SanitizeFunc = async (data, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeOutput');\n }\n if (isArray(data)) {\n const res = new Array(data.length);\n for (let i = 0; i < data.length; i += 1) {\n res[i] = await sanitizeOutput(data[i], schema, { auth });\n }\n return res;\n }\n\n const transforms = [\n (data: Data) => sanitizers.defaultSanitizeOutput({ schema, getModel }, data),\n ];\n\n if (auth) {\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.output?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeQuery = async (\n query: Record<string, unknown>,\n schema: Model,\n { auth, strictParams = false, route }: Options = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeQuery');\n }\n const { filters, sort, fields, populate } = query;\n\n const sanitizedQuery = cloneDeep(query);\n\n if ('publicationFilter' in sanitizedQuery) {\n validatePublicationFilterQueryParam(sanitizedQuery.publicationFilter);\n }\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (hasSort(sort)) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n } else if ('sort' in sanitizedQuery) {\n delete sanitizedQuery.sort;\n }\n\n if (fields) {\n Object.assign(sanitizedQuery, { fields: await sanitizeFields(fields, schema) });\n }\n\n if (populate) {\n Object.assign(sanitizedQuery, { populate: await sanitizePopulate(populate, schema) });\n }\n\n const extraQueryKeys = getExtraQueryKeysFromRoute(route);\n const routeQuerySchema = route?.request?.query;\n if (routeQuerySchema) {\n for (const key of extraQueryKeys) {\n if (key in query) {\n const zodSchema = routeQuerySchema[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(query[key]);\n if (result.success) {\n sanitizedQuery[key] = result.data;\n } else {\n delete sanitizedQuery[key];\n }\n }\n }\n }\n }\n\n if (strictParams) {\n const allowedKeys = [...ALLOWED_QUERY_PARAM_KEYS, ...extraQueryKeys];\n return pick(allowedKeys, sanitizedQuery) as Record<string, unknown>;\n }\n\n return sanitizedQuery;\n };\n\n const sanitizeFilters: SanitizeFunc = (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFilters');\n }\n if (isArray(filters)) {\n return Promise.all(filters.map((filter) => sanitizeFilters(filter, schema, { auth })));\n }\n\n const transforms = [sanitizers.defaultSanitizeFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(filters);\n };\n\n const sanitizeSort: SanitizeFunc = (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeSort');\n }\n const transforms = [sanitizers.defaultSanitizeSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(sort);\n };\n\n const sanitizeFields: SanitizeFunc = (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFields');\n }\n const transforms = [sanitizers.defaultSanitizeFields({ schema, getModel })];\n\n return pipeAsync(...transforms)(fields);\n };\n\n const sanitizePopulate: SanitizeFunc = (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [sanitizers.defaultSanitizePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(populate);\n };\n\n return {\n input: sanitizeInput,\n output: sanitizeOutput,\n query: sanitizeQuery,\n filters: sanitizeFilters,\n sort: sanitizeSort,\n fields: sanitizeFields,\n populate: sanitizePopulate,\n };\n};\n\nexport { createAPISanitizers, sanitizers, visitors };\n\nexport type APISanitiers = ReturnType<typeof createAPISanitizers>;\n"],"names":["createAPISanitizers","opts","getModel","sanitizeInput","data","schema","auth","strictParams","route","Error","isArray","Promise","all","map","entry","allowedExtraRootKeys","getExtraRootKeysFromRouteBody","nonWritableAttributes","getNonWritableAttributes","transforms","omit","constants","ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","traverseEntity","visitors","push","sanitizers","input","forEach","sanitizer","routeBodySanitizeTransform","Array","obj","bodySchema","request","body","shape","key","Object","keys","zodSchema","safeParse","result","success","pipeAsync","sanitizeOutput","res","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","validatePublicationFilterQueryParam","publicationFilter","assign","sanitizeFilters","hasSort","sanitizeSort","sanitizeFields","sanitizePopulate","extraQueryKeys","getExtraQueryKeysFromRoute","routeQuerySchema","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","pick","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../src/sanitize/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, cloneDeep, omit, pick } from 'lodash/fp';\nimport type { z } from 'zod/v4';\n\nimport { constants, getNonWritableAttributes } from '../content-types';\nimport { ALLOWED_QUERY_PARAM_KEYS } from '../content-api-constants';\nimport {\n type RouteLike,\n getExtraQueryKeysFromRoute,\n getExtraRootKeysFromRouteBody,\n} from '../content-api-route-params';\nimport { pipe as pipeAsync } from '../async';\n\nimport * as visitors from './visitors';\nimport * as sanitizers from './sanitizers';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\nimport type { Model, Data } from '../types';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\nimport { hasSort } from '../sort-query';\n\nexport interface Options {\n auth?: unknown;\n /**\n * If true, removes fields that are not declared in the schema (input) or keeps only allowed query param keys (query).\n * Defaults to false for backward compatibility.\n * TODO: In Strapi 6, strictParams will default to true (and may be removed as an option)\n */\n strictParams?: boolean;\n /**\n * When set, extra query/input params are derived from the route's request schema (and validated/sanitized with Zod).\n * When absent, no extra params are allowed in strict mode.\n */\n route?: RouteLike;\n}\n\nexport interface Sanitizer {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface SanitizeFunc {\n (data: unknown, schema: Model, options?: Options): Promise<unknown>;\n}\n\nexport type SanitizeQueryParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport type SanitizeBodyParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport interface APIOptions {\n sanitizers?: Sanitizers;\n getModel: (model: string) => Model;\n}\n\nexport interface Sanitizers {\n input?: Sanitizer[];\n output?: Sanitizer[];\n}\n\nconst createAPISanitizers = (opts: APIOptions) => {\n const { getModel } = opts;\n\n const sanitizeInput: SanitizeFunc = (\n data: unknown,\n schema: Model,\n { auth, strictParams = false, route } = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeInput');\n }\n if (isArray(data)) {\n return Promise.all(\n data.map((entry) => sanitizeInput(entry, schema, { auth, strictParams, route }))\n );\n }\n\n const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n // Remove first level ID in inputs\n omit(constants.ID_ATTRIBUTE),\n omit(constants.DOC_ID_ATTRIBUTE),\n // Remove non-writable attributes\n traverseEntity(visitors.removeRestrictedFields(nonWritableAttributes), { schema, getModel }),\n ];\n\n if (strictParams) {\n // Remove unrecognized fields (allowedExtraRootKeys = registered input param keys)\n transforms.push(\n traverseEntity(visitors.removeUnrecognizedFields, {\n schema,\n getModel,\n allowedExtraRootKeys,\n })\n );\n }\n\n if (auth) {\n // Remove restricted relations\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.input?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n /**\n * For each extra root key from the route's body schema present in data, run Zod safeParse.\n * If parsing fails, the key is removed from the output.\n *\n * Content-api sends the document payload as body.data; the controller calls sanitizeInput(body.data, ctx),\n * so the input we receive here is the inner payload (keys like \"relatedMedia\", \"name\"), not the full body.\n * The route's body schema is z.object({ data: ... }), so its shape includes \"data\". We skip \"data\" because\n * the main document payload is already sanitized above by traverseEntity (removeUnrecognizedFields, etc.);\n * relation ops (connect/disconnect/set) are handled there, not by the route's Zod schema. We only run\n * Zod here for truly extra root keys added via addInputParams (e.g. clientMutationId).\n */\n const routeBodySanitizeTransform = async (data: Data): Promise<Data> => {\n if (!data || typeof data !== 'object' || Array.isArray(data)) return data;\n const obj = data as Record<string, unknown>;\n const bodySchema = route?.request?.body?.['application/json'];\n if (bodySchema && typeof bodySchema === 'object' && 'shape' in bodySchema) {\n const shape = (bodySchema as { shape: Record<string, z.ZodTypeAny> }).shape;\n for (const key of Object.keys(shape)) {\n if (key === 'data' || !(key in obj)) continue;\n const zodSchema = shape[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(obj[key]);\n if (result.success) {\n obj[key] = result.data;\n } else {\n delete obj[key];\n }\n }\n }\n }\n return data;\n };\n (transforms as Array<(data: Data) => Data | Promise<Data>>).push(routeBodySanitizeTransform);\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeOutput: SanitizeFunc = async (data, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeOutput');\n }\n if (isArray(data)) {\n const res: unknown[] = Array.from({ length: data.length });\n for (let i = 0; i < data.length; i += 1) {\n res[i] = await sanitizeOutput(data[i], schema, { auth });\n }\n return res;\n }\n\n const transforms = [\n (data: Data) => sanitizers.defaultSanitizeOutput({ schema, getModel }, data),\n ];\n\n if (auth) {\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.output?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeQuery = async (\n query: Record<string, unknown>,\n schema: Model,\n { auth, strictParams = false, route }: Options = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeQuery');\n }\n const { filters, sort, fields, populate } = query;\n\n const sanitizedQuery = cloneDeep(query);\n\n if ('publicationFilter' in sanitizedQuery) {\n validatePublicationFilterQueryParam(sanitizedQuery.publicationFilter);\n }\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (hasSort(sort)) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n } else if ('sort' in sanitizedQuery) {\n delete sanitizedQuery.sort;\n }\n\n if (fields) {\n Object.assign(sanitizedQuery, { fields: await sanitizeFields(fields, schema) });\n }\n\n if (populate) {\n Object.assign(sanitizedQuery, { populate: await sanitizePopulate(populate, schema) });\n }\n\n const extraQueryKeys = getExtraQueryKeysFromRoute(route);\n const routeQuerySchema = route?.request?.query;\n if (routeQuerySchema) {\n for (const key of extraQueryKeys) {\n if (key in query) {\n const zodSchema = routeQuerySchema[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(query[key]);\n if (result.success) {\n sanitizedQuery[key] = result.data;\n } else {\n delete sanitizedQuery[key];\n }\n }\n }\n }\n }\n\n if (strictParams) {\n const allowedKeys = [...ALLOWED_QUERY_PARAM_KEYS, ...extraQueryKeys];\n return pick(allowedKeys, sanitizedQuery) as Record<string, unknown>;\n }\n\n return sanitizedQuery;\n };\n\n const sanitizeFilters: SanitizeFunc = (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFilters');\n }\n if (isArray(filters)) {\n return Promise.all(filters.map((filter) => sanitizeFilters(filter, schema, { auth })));\n }\n\n const transforms = [sanitizers.defaultSanitizeFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(filters);\n };\n\n const sanitizeSort: SanitizeFunc = (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeSort');\n }\n const transforms = [sanitizers.defaultSanitizeSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(sort);\n };\n\n const sanitizeFields: SanitizeFunc = (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFields');\n }\n const transforms = [sanitizers.defaultSanitizeFields({ schema, getModel })];\n\n return pipeAsync(...transforms)(fields);\n };\n\n const sanitizePopulate: SanitizeFunc = (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [sanitizers.defaultSanitizePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(populate);\n };\n\n return {\n input: sanitizeInput,\n output: sanitizeOutput,\n query: sanitizeQuery,\n filters: sanitizeFilters,\n sort: sanitizeSort,\n fields: sanitizeFields,\n populate: sanitizePopulate,\n };\n};\n\nexport { createAPISanitizers, sanitizers, visitors };\n\nexport type APISanitiers = ReturnType<typeof createAPISanitizers>;\n"],"names":["createAPISanitizers","opts","getModel","sanitizeInput","data","schema","auth","strictParams","route","Error","isArray","Promise","all","map","entry","allowedExtraRootKeys","getExtraRootKeysFromRouteBody","nonWritableAttributes","getNonWritableAttributes","transforms","omit","constants","ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","traverseEntity","visitors","push","sanitizers","input","forEach","sanitizer","routeBodySanitizeTransform","Array","obj","bodySchema","request","body","shape","key","Object","keys","zodSchema","safeParse","result","success","pipeAsync","sanitizeOutput","res","from","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","validatePublicationFilterQueryParam","publicationFilter","assign","sanitizeFilters","hasSort","sanitizeSort","sanitizeFields","sanitizePopulate","extraQueryKeys","getExtraQueryKeysFromRoute","routeQuerySchema","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","pick","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAkEA,MAAMA,sBAAsB,CAACC,IAAAA,GAAAA;IAC3B,MAAM,EAAEC,QAAQ,EAAE,GAAGD,IAAAA;AAErB,IAAA,MAAME,aAAAA,GAA8B,CAClCC,IAAAA,EACAC,MAAAA,EACA,EAAEC,IAAI,EAAEC,YAAAA,GAAe,KAAK,EAAEC,KAAK,EAAE,GAAG,EAAE,GAAA;AAE1C,QAAA,IAAI,CAACH,MAAAA,EAAQ;AACX,YAAA,MAAM,IAAII,KAAAA,CAAM,iCAAA,CAAA;AAClB,QAAA;AACA,QAAA,IAAIC,QAAQN,IAAAA,CAAAA,EAAO;YACjB,OAAOO,OAAAA,CAAQC,GAAG,CAChBR,IAAAA,CAAKS,GAAG,CAAC,CAACC,KAAAA,GAAUX,aAAAA,CAAcW,KAAAA,EAAOT,MAAAA,EAAQ;AAAEC,oBAAAA,IAAAA;AAAMC,oBAAAA,YAAAA;AAAcC,oBAAAA;AAAM,iBAAA,CAAA,CAAA,CAAA;AAEjF,QAAA;AAEA,QAAA,MAAMO,uBAAuBC,6BAAAA,CAA8BR,KAAAA,CAAAA;AAE3D,QAAA,MAAMS,wBAAwBC,wBAAAA,CAAyBb,MAAAA,CAAAA;AAEvD,QAAA,MAAMc,UAAAA,GAAa;;AAEjBC,YAAAA,IAAAA,CAAKC,UAAUC,YAAY,CAAA;AAC3BF,YAAAA,IAAAA,CAAKC,UAAUE,gBAAgB,CAAA;;YAE/BC,cAAAA,CAAeC,sBAA+B,CAACR,qBAAAA,CAAAA,EAAwB;AAAEZ,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA;AAC3F,SAAA;AAED,QAAA,IAAIK,YAAAA,EAAc;;AAEhBY,YAAAA,UAAAA,CAAWO,IAAI,CACbF,cAAAA,CAAeC,wBAAiC,EAAE;AAChDpB,gBAAAA,MAAAA;AACAH,gBAAAA,QAAAA;AACAa,gBAAAA;AACF,aAAA,CAAA,CAAA;AAEJ,QAAA;AAEA,QAAA,IAAIT,IAAAA,EAAM;;AAERa,YAAAA,UAAAA,CAAWO,IAAI,CACbF,cAAAA,CAAeC,yBAAkC,CAACnB,IAAAA,CAAAA,EAAO;AAAED,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA,CAAA,CAAA;AAEhF,QAAA;;QAGAD,IAAAA,EAAM0B,UAAAA,EAAYC,OAAOC,OAAAA,CAAQ,CAACC,YAAyBX,UAAAA,CAAWO,IAAI,CAACI,SAAAA,CAAUzB,MAAAA,CAAAA,CAAAA,CAAAA;AAErF;;;;;;;;;;QAWA,MAAM0B,6BAA6B,OAAO3B,IAAAA,GAAAA;YACxC,IAAI,CAACA,QAAQ,OAAOA,IAAAA,KAAS,YAAY4B,KAAAA,CAAMtB,OAAO,CAACN,IAAAA,CAAAA,EAAO,OAAOA,IAAAA;AACrE,YAAA,MAAM6B,GAAAA,GAAM7B,IAAAA;AACZ,YAAA,MAAM8B,UAAAA,GAAa1B,KAAAA,EAAO2B,OAAAA,EAASC,IAAAA,GAAO,kBAAA,CAAmB;AAC7D,YAAA,IAAIF,UAAAA,IAAc,OAAOA,UAAAA,KAAe,QAAA,IAAY,WAAWA,UAAAA,EAAY;gBACzE,MAAMG,KAAAA,GAAQ,UAACH,CAAuDG,KAAK;AAC3E,gBAAA,KAAK,MAAMC,GAAAA,IAAOC,MAAAA,CAAOC,IAAI,CAACH,KAAAA,CAAAA,CAAQ;AACpC,oBAAA,IAAIC,QAAQ,MAAA,IAAU,EAAEA,GAAAA,IAAOL,GAAE,CAAA,EAAI;oBACrC,MAAMQ,SAAAA,GAAYJ,KAAK,CAACC,GAAAA,CAAI;AAC5B,oBAAA,IAAIG,aAAa,OAAQA,SAAAA,CAA2BC,SAAS,KAAK,UAAA,EAAY;AAC5E,wBAAA,MAAMC,SAAS,SAACF,CAA2BC,SAAS,CAACT,GAAG,CAACK,GAAAA,CAAI,CAAA;wBAC7D,IAAIK,MAAAA,CAAOC,OAAO,EAAE;AAClBX,4BAAAA,GAAG,CAACK,GAAAA,CAAI,GAAGK,MAAAA,CAAOvC,IAAI;wBACxB,CAAA,MAAO;4BACL,OAAO6B,GAAG,CAACK,GAAAA,CAAI;AACjB,wBAAA;AACF,oBAAA;AACF,gBAAA;AACF,YAAA;YACA,OAAOlC,IAAAA;AACT,QAAA,CAAA;AACCe,QAAAA,UAAAA,CAA2DO,IAAI,CAACK,0BAAAA,CAAAA;AAEjE,QAAA,OAAOc,QAAa1B,UAAAA,CAAAA,CAAYf,IAAAA,CAAAA;AAClC,IAAA,CAAA;IAEA,MAAM0C,cAAAA,GAA+B,OAAO1C,IAAAA,EAAMC,MAAAA,EAAe,EAAEC,IAAI,EAAE,GAAG,EAAE,GAAA;AAC5E,QAAA,IAAI,CAACD,MAAAA,EAAQ;AACX,YAAA,MAAM,IAAII,KAAAA,CAAM,kCAAA,CAAA;AAClB,QAAA;AACA,QAAA,IAAIC,QAAQN,IAAAA,CAAAA,EAAO;YACjB,MAAM2C,GAAAA,GAAiBf,KAAAA,CAAMgB,IAAI,CAAC;AAAEC,gBAAAA,MAAAA,EAAQ7C,KAAK6C;AAAO,aAAA,CAAA;YACxD,IAAK,IAAIC,IAAI,CAAA,EAAGA,CAAAA,GAAI9C,KAAK6C,MAAM,EAAEC,KAAK,CAAA,CAAG;gBACvCH,GAAG,CAACG,EAAE,GAAG,MAAMJ,eAAe1C,IAAI,CAAC8C,CAAAA,CAAE,EAAE7C,MAAAA,EAAQ;AAAEC,oBAAAA;AAAK,iBAAA,CAAA;AACxD,YAAA;YACA,OAAOyC,GAAAA;AACT,QAAA;AAEA,QAAA,MAAM5B,UAAAA,GAAa;YACjB,CAACf,IAAAA,GAAeuB,qBAAgC,CAAC;AAAEtB,oBAAAA,MAAAA;AAAQH,oBAAAA;iBAAS,EAAGE,IAAAA;AACxE,SAAA;AAED,QAAA,IAAIE,IAAAA,EAAM;AACRa,YAAAA,UAAAA,CAAWO,IAAI,CACbF,cAAAA,CAAeC,yBAAkC,CAACnB,IAAAA,CAAAA,EAAO;AAAED,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA,CAAA,CAAA;AAEhF,QAAA;;QAGAD,IAAAA,EAAM0B,UAAAA,EAAYwB,QAAQtB,OAAAA,CAAQ,CAACC,YAAyBX,UAAAA,CAAWO,IAAI,CAACI,SAAAA,CAAUzB,MAAAA,CAAAA,CAAAA,CAAAA;AAEtF,QAAA,OAAOwC,QAAa1B,UAAAA,CAAAA,CAAYf,IAAAA,CAAAA;AAClC,IAAA,CAAA;AAEA,IAAA,MAAMgD,aAAAA,GAAgB,OACpBC,KAAAA,EACAhD,MAAAA,EACA,EAAEC,IAAI,EAAEC,YAAAA,GAAe,KAAK,EAAEC,KAAK,EAAW,GAAG,EAAE,GAAA;AAEnD,QAAA,IAAI,CAACH,MAAAA,EAAQ;AACX,YAAA,MAAM,IAAII,KAAAA,CAAM,iCAAA,CAAA;AAClB,QAAA;QACA,MAAM,EAAE6C,OAAO,EAAEC,IAAI,EAAEC,MAAM,EAAEC,QAAQ,EAAE,GAAGJ,KAAAA;AAE5C,QAAA,MAAMK,iBAAiBC,SAAAA,CAAUN,KAAAA,CAAAA;AAEjC,QAAA,IAAI,uBAAuBK,cAAAA,EAAgB;AACzCE,YAAAA,mCAAAA,CAAoCF,eAAeG,iBAAiB,CAAA;AACtE,QAAA;AAEA,QAAA,IAAIP,OAAAA,EAAS;YACXf,MAAAA,CAAOuB,MAAM,CAACJ,cAAAA,EAAgB;gBAAEJ,OAAAA,EAAS,MAAMS,eAAAA,CAAgBT,OAAAA,EAASjD,MAAAA,EAAQ;AAAEC,oBAAAA;AAAK,iBAAA;AAAG,aAAA,CAAA;AAC5F,QAAA;AAEA,QAAA,IAAI0D,QAAQT,IAAAA,CAAAA,EAAO;YACjBhB,MAAAA,CAAOuB,MAAM,CAACJ,cAAAA,EAAgB;gBAAEH,IAAAA,EAAM,MAAMU,YAAAA,CAAaV,IAAAA,EAAMlD,MAAAA,EAAQ;AAAEC,oBAAAA;AAAK,iBAAA;AAAG,aAAA,CAAA;QACnF,CAAA,MAAO,IAAI,UAAUoD,cAAAA,EAAgB;AACnC,YAAA,OAAOA,eAAeH,IAAI;AAC5B,QAAA;AAEA,QAAA,IAAIC,MAAAA,EAAQ;YACVjB,MAAAA,CAAOuB,MAAM,CAACJ,cAAAA,EAAgB;gBAAEF,MAAAA,EAAQ,MAAMU,eAAeV,MAAAA,EAAQnD,MAAAA;AAAQ,aAAA,CAAA;AAC/E,QAAA;AAEA,QAAA,IAAIoD,QAAAA,EAAU;YACZlB,MAAAA,CAAOuB,MAAM,CAACJ,cAAAA,EAAgB;gBAAED,QAAAA,EAAU,MAAMU,iBAAiBV,QAAAA,EAAUpD,MAAAA;AAAQ,aAAA,CAAA;AACrF,QAAA;AAEA,QAAA,MAAM+D,iBAAiBC,0BAAAA,CAA2B7D,KAAAA,CAAAA;QAClD,MAAM8D,gBAAAA,GAAmB9D,OAAO2B,OAAAA,EAASkB,KAAAA;AACzC,QAAA,IAAIiB,gBAAAA,EAAkB;YACpB,KAAK,MAAMhC,OAAO8B,cAAAA,CAAgB;AAChC,gBAAA,IAAI9B,OAAOe,KAAAA,EAAO;oBAChB,MAAMZ,SAAAA,GAAY6B,gBAAgB,CAAChC,GAAAA,CAAI;AACvC,oBAAA,IAAIG,aAAa,OAAQA,SAAAA,CAA2BC,SAAS,KAAK,UAAA,EAAY;AAC5E,wBAAA,MAAMC,SAAS,SAACF,CAA2BC,SAAS,CAACW,KAAK,CAACf,GAAAA,CAAI,CAAA;wBAC/D,IAAIK,MAAAA,CAAOC,OAAO,EAAE;AAClBc,4BAAAA,cAAc,CAACpB,GAAAA,CAAI,GAAGK,MAAAA,CAAOvC,IAAI;wBACnC,CAAA,MAAO;4BACL,OAAOsD,cAAc,CAACpB,GAAAA,CAAI;AAC5B,wBAAA;AACF,oBAAA;AACF,gBAAA;AACF,YAAA;AACF,QAAA;AAEA,QAAA,IAAI/B,YAAAA,EAAc;AAChB,YAAA,MAAMgE,WAAAA,GAAc;AAAIC,gBAAAA,GAAAA,wBAAAA;AAA6BJ,gBAAAA,GAAAA;AAAe,aAAA;AACpE,YAAA,OAAOK,KAAKF,WAAAA,EAAab,cAAAA,CAAAA;AAC3B,QAAA;QAEA,OAAOA,cAAAA;AACT,IAAA,CAAA;IAEA,MAAMK,eAAAA,GAAgC,CAACT,OAAAA,EAASjD,MAAAA,EAAe,EAAEC,IAAI,EAAE,GAAG,EAAE,GAAA;AAC1E,QAAA,IAAI,CAACD,MAAAA,EAAQ;AACX,YAAA,MAAM,IAAII,KAAAA,CAAM,mCAAA,CAAA;AAClB,QAAA;AACA,QAAA,IAAIC,QAAQ4C,OAAAA,CAAAA,EAAU;YACpB,OAAO3C,OAAAA,CAAQC,GAAG,CAAC0C,OAAAA,CAAQzC,GAAG,CAAC,CAAC6D,MAAAA,GAAWX,eAAAA,CAAgBW,MAAAA,EAAQrE,MAAAA,EAAQ;AAAEC,oBAAAA;AAAK,iBAAA,CAAA,CAAA,CAAA;AACpF,QAAA;AAEA,QAAA,MAAMa,UAAAA,GAAa;AAACQ,YAAAA,sBAAiC,CAAC;AAAEtB,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA;AAAG,SAAA;AAE5E,QAAA,IAAII,IAAAA,EAAM;AACRa,YAAAA,UAAAA,CAAWO,IAAI,CACbiD,oBAAAA,CAAqBlD,yBAAkC,CAACnB,IAAAA,CAAAA,EAAO;AAAED,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA,CAAA,CAAA;AAEtF,QAAA;AAEA,QAAA,OAAO2C,QAAa1B,UAAAA,CAAAA,CAAYmC,OAAAA,CAAAA;AAClC,IAAA,CAAA;IAEA,MAAMW,YAAAA,GAA6B,CAACV,IAAAA,EAAMlD,MAAAA,EAAe,EAAEC,IAAI,EAAE,GAAG,EAAE,GAAA;AACpE,QAAA,IAAI,CAACD,MAAAA,EAAQ;AACX,YAAA,MAAM,IAAII,KAAAA,CAAM,gCAAA,CAAA;AAClB,QAAA;AACA,QAAA,MAAMU,UAAAA,GAAa;AAACQ,YAAAA,mBAA8B,CAAC;AAAEtB,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA;AAAG,SAAA;AAEzE,QAAA,IAAII,IAAAA,EAAM;AACRa,YAAAA,UAAAA,CAAWO,IAAI,CACbkD,iBAAAA,CAAkBnD,yBAAkC,CAACnB,IAAAA,CAAAA,EAAO;AAAED,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA,CAAA,CAAA;AAEnF,QAAA;AAEA,QAAA,OAAO2C,QAAa1B,UAAAA,CAAAA,CAAYoC,IAAAA,CAAAA;AAClC,IAAA,CAAA;IAEA,MAAMW,cAAAA,GAA+B,CAACV,MAAAA,EAAQnD,MAAAA,GAAAA;AAC5C,QAAA,IAAI,CAACA,MAAAA,EAAQ;AACX,YAAA,MAAM,IAAII,KAAAA,CAAM,kCAAA,CAAA;AAClB,QAAA;AACA,QAAA,MAAMU,UAAAA,GAAa;AAACQ,YAAAA,qBAAgC,CAAC;AAAEtB,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA;AAAG,SAAA;AAE3E,QAAA,OAAO2C,QAAa1B,UAAAA,CAAAA,CAAYqC,MAAAA,CAAAA;AAClC,IAAA,CAAA;IAEA,MAAMW,gBAAAA,GAAiC,CAACV,QAAAA,EAAUpD,MAAAA,EAAe,EAAEC,IAAI,EAAE,GAAG,EAAE,GAAA;AAC5E,QAAA,IAAI,CAACD,MAAAA,EAAQ;AACX,YAAA,MAAM,IAAII,KAAAA,CAAM,oCAAA,CAAA;AAClB,QAAA;AACA,QAAA,MAAMU,UAAAA,GAAa;AAACQ,YAAAA,uBAAkC,CAAC;AAAEtB,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA;AAAG,SAAA;AAE7E,QAAA,IAAII,IAAAA,EAAM;AACRa,YAAAA,UAAAA,CAAWO,IAAI,CACbmD,qBAAAA,CAAsBpD,yBAAkC,CAACnB,IAAAA,CAAAA,EAAO;AAAED,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA,CAAA,CAAA;AAEvF,QAAA;AAEA,QAAA,OAAO2C,QAAa1B,UAAAA,CAAAA,CAAYsC,QAAAA,CAAAA;AAClC,IAAA,CAAA;IAEA,OAAO;QACL7B,KAAAA,EAAOzB,aAAAA;QACPgD,MAAAA,EAAQL,cAAAA;QACRO,KAAAA,EAAOD,aAAAA;QACPE,OAAAA,EAASS,eAAAA;QACTR,IAAAA,EAAMU,YAAAA;QACNT,MAAAA,EAAQU,cAAAA;QACRT,QAAAA,EAAUU;AACZ,KAAA;AACF;;;;"}
|