@naturalcycles/nodejs-lib 15.107.1 → 15.107.2
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.
|
@@ -1178,6 +1178,9 @@ function executeValidation(fn, builtSchema, input, opt = {}, defaultInputName) {
|
|
|
1178
1178
|
const errors = fn.errors;
|
|
1179
1179
|
const { inputId = _isObject(input) ? input['id'] : undefined, inputName = defaultInputName || 'Object', } = opt;
|
|
1180
1180
|
const dataVar = [inputName, inputId].filter(Boolean).join('.');
|
|
1181
|
+
// Build fingerprint before applyImprovementsOnErrorMessages: after it, /items/0/name becomes
|
|
1182
|
+
// .items[0].name, embedding the index into the segment and making it harder to strip without regex
|
|
1183
|
+
const fingerprint = buildAjvErrorFingerprint(errors[0], inputName);
|
|
1181
1184
|
applyImprovementsOnErrorMessages(errors, builtSchema);
|
|
1182
1185
|
let message = getAjv().errorsText(errors, {
|
|
1183
1186
|
dataVar,
|
|
@@ -1187,9 +1190,6 @@ function executeValidation(fn, builtSchema, input, opt = {}, defaultInputName) {
|
|
|
1187
1190
|
// the error message Input would contain already mutated object print, such as Input: {}
|
|
1188
1191
|
// Unless `getOriginalInput` function is provided - then it will be used to preserve the Input pureness.
|
|
1189
1192
|
const inputStringified = _inspect(opt.getOriginalInput?.() || input, { maxLen: 4000 });
|
|
1190
|
-
// fingerprint is captured before appending the dynamic Input snippet,
|
|
1191
|
-
// so we can group repeated validation errors by rule rather than by unique request content.
|
|
1192
|
-
const fingerprint = message;
|
|
1193
1193
|
message = [message, 'Input: ' + inputStringified].join(separator);
|
|
1194
1194
|
const err = new AjvValidationError(message, _filterNullishValues({
|
|
1195
1195
|
errors,
|
|
@@ -1222,6 +1222,22 @@ function applyImprovementsOnErrorMessages(errors, schema) {
|
|
|
1222
1222
|
error.instancePath = error.instancePath.replaceAll(/\/(\d+)/g, `[$1]`).replaceAll('/', '.');
|
|
1223
1223
|
}
|
|
1224
1224
|
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Groups repeated validation errors by rule rather than by unique request content.
|
|
1227
|
+
* Excludes instance-specific data like record IDs and array indices.
|
|
1228
|
+
*/
|
|
1229
|
+
function buildAjvErrorFingerprint(e, inputName) {
|
|
1230
|
+
const value = Object.values(e.params || {})[0];
|
|
1231
|
+
let rule = e.keyword;
|
|
1232
|
+
if (value !== undefined)
|
|
1233
|
+
rule += `:${value}`;
|
|
1234
|
+
const path = e.instancePath
|
|
1235
|
+
.split('/')
|
|
1236
|
+
.filter(s => s && isNaN(Number(s)))
|
|
1237
|
+
.join('.');
|
|
1238
|
+
const location = [inputName, path].filter(Boolean).join('.');
|
|
1239
|
+
return [location, rule].join(' ');
|
|
1240
|
+
}
|
|
1225
1241
|
/**
|
|
1226
1242
|
* Filters out noisy errors produced by nullable anyOf patterns.
|
|
1227
1243
|
* When `nullable()` wraps a schema in `anyOf: [realSchema, { type: 'null' }]`,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/nodejs-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "15.107.
|
|
4
|
+
"version": "15.107.2",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@naturalcycles/js-lib": "^15",
|
|
7
7
|
"@standard-schema/spec": "^1",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"yargs": "^18"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
20
|
+
"@typescript/native-preview": "7.0.0-dev.20260415.1",
|
|
21
21
|
"@naturalcycles/dev-lib": "18.4.2"
|
|
22
22
|
},
|
|
23
23
|
"exports": {
|
|
@@ -1692,6 +1692,10 @@ function executeValidation<OUT>(
|
|
|
1692
1692
|
} = opt
|
|
1693
1693
|
const dataVar = [inputName, inputId].filter(Boolean).join('.')
|
|
1694
1694
|
|
|
1695
|
+
// Build fingerprint before applyImprovementsOnErrorMessages: after it, /items/0/name becomes
|
|
1696
|
+
// .items[0].name, embedding the index into the segment and making it harder to strip without regex
|
|
1697
|
+
const fingerprint = buildAjvErrorFingerprint(errors[0], inputName)
|
|
1698
|
+
|
|
1695
1699
|
applyImprovementsOnErrorMessages(errors, builtSchema)
|
|
1696
1700
|
|
|
1697
1701
|
let message = getAjv().errorsText(errors, {
|
|
@@ -1703,9 +1707,6 @@ function executeValidation<OUT>(
|
|
|
1703
1707
|
// the error message Input would contain already mutated object print, such as Input: {}
|
|
1704
1708
|
// Unless `getOriginalInput` function is provided - then it will be used to preserve the Input pureness.
|
|
1705
1709
|
const inputStringified = _inspect(opt.getOriginalInput?.() || input, { maxLen: 4000 })
|
|
1706
|
-
// fingerprint is captured before appending the dynamic Input snippet,
|
|
1707
|
-
// so we can group repeated validation errors by rule rather than by unique request content.
|
|
1708
|
-
const fingerprint = message
|
|
1709
1710
|
message = [message, 'Input: ' + inputStringified].join(separator)
|
|
1710
1711
|
|
|
1711
1712
|
const err = new AjvValidationError(
|
|
@@ -1723,7 +1724,7 @@ function executeValidation<OUT>(
|
|
|
1723
1724
|
// ==== Error formatting helpers ====
|
|
1724
1725
|
|
|
1725
1726
|
function applyImprovementsOnErrorMessages(
|
|
1726
|
-
errors: ErrorObject
|
|
1727
|
+
errors: ErrorObject[] | null | undefined,
|
|
1727
1728
|
schema: JsonSchema,
|
|
1728
1729
|
): void {
|
|
1729
1730
|
if (!errors) return
|
|
@@ -1750,16 +1751,29 @@ function applyImprovementsOnErrorMessages(
|
|
|
1750
1751
|
}
|
|
1751
1752
|
}
|
|
1752
1753
|
|
|
1754
|
+
/**
|
|
1755
|
+
* Groups repeated validation errors by rule rather than by unique request content.
|
|
1756
|
+
* Excludes instance-specific data like record IDs and array indices.
|
|
1757
|
+
*/
|
|
1758
|
+
function buildAjvErrorFingerprint(e: ErrorObject, inputName: string): string {
|
|
1759
|
+
const value = Object.values(e.params || {})[0]
|
|
1760
|
+
let rule = e.keyword
|
|
1761
|
+
if (value !== undefined) rule += `:${value}`
|
|
1762
|
+
const path = e.instancePath
|
|
1763
|
+
.split('/')
|
|
1764
|
+
.filter(s => s && isNaN(Number(s)))
|
|
1765
|
+
.join('.')
|
|
1766
|
+
const location = [inputName, path].filter(Boolean).join('.')
|
|
1767
|
+
return [location, rule].join(' ')
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1753
1770
|
/**
|
|
1754
1771
|
* Filters out noisy errors produced by nullable anyOf patterns.
|
|
1755
1772
|
* When `nullable()` wraps a schema in `anyOf: [realSchema, { type: 'null' }]`,
|
|
1756
1773
|
* AJV produces "must be null" and "must match a schema in anyOf" errors
|
|
1757
1774
|
* that are confusing. This method splices them out, keeping only the real errors.
|
|
1758
1775
|
*/
|
|
1759
|
-
function filterNullableAnyOfErrors(
|
|
1760
|
-
errors: ErrorObject<string, Record<string, any>, unknown>[],
|
|
1761
|
-
schema: JsonSchema,
|
|
1762
|
-
): void {
|
|
1776
|
+
function filterNullableAnyOfErrors(errors: ErrorObject[], schema: JsonSchema): void {
|
|
1763
1777
|
// Collect exact schemaPaths to remove (anyOf aggregates) and prefixes (null branches)
|
|
1764
1778
|
const exactPaths: string[] = []
|
|
1765
1779
|
const nullBranchPrefixes: string[] = []
|