@naturalcycles/nodejs-lib 15.121.1 → 15.122.1
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/exec2/exec2.js
CHANGED
|
@@ -277,8 +277,15 @@ class Exec2 {
|
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
logFinish(cmd, opt, started, isSuccessful) {
|
|
280
|
-
if (
|
|
281
|
-
|
|
280
|
+
if (!opt.logFinish) {
|
|
281
|
+
if (isSuccessful)
|
|
282
|
+
return;
|
|
283
|
+
// Failures are logged even with logFinish: false,
|
|
284
|
+
// unless explicitly silenced with `log: false`
|
|
285
|
+
// (e.g when the caller buffers the output to print it later, like runCheck does)
|
|
286
|
+
if (opt.log === false)
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
282
289
|
console.log([
|
|
283
290
|
isSuccessful ? ' ✓' : ' ×',
|
|
284
291
|
white(opt.name || _substringAfterLast(cmd, '/')),
|
|
@@ -2,7 +2,7 @@ import type { ValidationFunction, ValidationFunctionResult } from '@naturalcycle
|
|
|
2
2
|
import type { Set2 } from '@naturalcycles/js-lib/object';
|
|
3
3
|
import type { AnyObject, BaseDBEntity, IANATimezone, Inclusiveness, IsoDate, IsoDateTime, IsoMonth, NumberEnum, StringEnum, StringMap, UnixTimestamp, UnixTimestampMillis } from '@naturalcycles/js-lib/types';
|
|
4
4
|
import type { StandardJSONSchemaV1, StandardSchemaV1 } from '@standard-schema/spec';
|
|
5
|
-
import type { Ajv } from 'ajv';
|
|
5
|
+
import type { Ajv, ValidateFunction } from 'ajv';
|
|
6
6
|
import { AjvValidationError } from './ajvValidationError.js';
|
|
7
7
|
export declare const j: {
|
|
8
8
|
/**
|
|
@@ -480,12 +480,12 @@ export declare class AjvSchema<OUT> {
|
|
|
480
480
|
* Creates a minimal AjvSchema wrapper from a pre-compiled validate function.
|
|
481
481
|
* Used internally by JSchema to cache a compatible AjvSchema instance.
|
|
482
482
|
*/
|
|
483
|
-
static _wrap<OUT>(schema: JsonSchema<OUT>, compiledFn:
|
|
483
|
+
static _wrap<OUT>(schema: JsonSchema<OUT>, compiledFn: ValidateFunction): AjvSchema<OUT>;
|
|
484
484
|
static isSchemaWithCachedAjvSchema<Base, OUT>(schema: Base): schema is WithCachedAjvSchema<Base, OUT>;
|
|
485
485
|
static cacheAjvSchema<Base extends AnyObject, OUT>(schema: Base, ajvSchema: AjvSchema<OUT>): WithCachedAjvSchema<Base, OUT>;
|
|
486
486
|
static requireCachedAjvSchema<Base, OUT>(schema: WithCachedAjvSchema<Base, OUT>): AjvSchema<OUT>;
|
|
487
487
|
readonly cfg: AjvSchemaCfg;
|
|
488
|
-
private _compiledFn
|
|
488
|
+
private _compiledFn?;
|
|
489
489
|
private _getValidateFn;
|
|
490
490
|
/**
|
|
491
491
|
* It returns the original object just for convenience.
|
|
@@ -1174,6 +1174,9 @@ function executeValidation(fn, builtSchema, input, opt = {}, defaultInputName) {
|
|
|
1174
1174
|
fn.errors = [
|
|
1175
1175
|
{
|
|
1176
1176
|
instancePath: '',
|
|
1177
|
+
schemaPath: '',
|
|
1178
|
+
keyword: 'postValidation',
|
|
1179
|
+
params: {},
|
|
1177
1180
|
message: err.message,
|
|
1178
1181
|
},
|
|
1179
1182
|
];
|
|
@@ -1190,15 +1193,40 @@ function executeValidation(fn, builtSchema, input, opt = {}, defaultInputName) {
|
|
|
1190
1193
|
// Build fingerprint before applyImprovementsOnErrorMessages: after it, /items/0/name becomes
|
|
1191
1194
|
// .items[0].name, embedding the index into the segment and making it harder to strip without regex
|
|
1192
1195
|
const fingerprint = buildAjvErrorFingerprint(errors[0], inputName, resolveCustomErrorMessage(builtSchema, errors[0]));
|
|
1193
|
-
applyImprovementsOnErrorMessages(errors, builtSchema);
|
|
1194
|
-
let message = getAjv().errorsText(errors, {
|
|
1195
|
-
dataVar,
|
|
1196
|
-
separator,
|
|
1197
|
-
});
|
|
1198
1196
|
// Note: if we mutated the input already, e.g stripped unknown properties,
|
|
1199
1197
|
// the error message Input would contain already mutated object print, such as Input: {}
|
|
1200
1198
|
// Unless `getOriginalInput` function is provided - then it will be used to preserve the Input pureness.
|
|
1201
|
-
const
|
|
1199
|
+
const inputForPrint = opt.getOriginalInput?.() || input;
|
|
1200
|
+
// Resolve each error's failing value while its instancePath is still a raw JSON pointer
|
|
1201
|
+
// (e.g `/events/2/name`), which is unambiguous to walk - unlike the dot notation it gets
|
|
1202
|
+
// rewritten into below (a property name may itself contain a dot).
|
|
1203
|
+
// Root errors (empty instancePath) resolve to undefined - there the value is the whole Input,
|
|
1204
|
+
// which is printed anyway.
|
|
1205
|
+
// `required` errors also resolve to undefined - their instancePath points at the parent object
|
|
1206
|
+
// that lacks the property, so `got:` would print that whole object - noise next to the Input print.
|
|
1207
|
+
const failingValueByError = new Map(errors.map(e => [
|
|
1208
|
+
e,
|
|
1209
|
+
e.instancePath && e.keyword !== 'required'
|
|
1210
|
+
? getValueAtJsonPointer(inputForPrint, e.instancePath)
|
|
1211
|
+
: undefined,
|
|
1212
|
+
]));
|
|
1213
|
+
applyImprovementsOnErrorMessages(errors, builtSchema);
|
|
1214
|
+
// Same line format as Ajv's errorsText().
|
|
1215
|
+
// Each error line is followed by a `Got: ...` line printing the narrow failing value itself,
|
|
1216
|
+
// so it stays visible even when the Input print below gets truncated for large inputs.
|
|
1217
|
+
// `Got` is a separate line (and is allowed to print multi-line) on purpose: consumers that
|
|
1218
|
+
// only take the first line of the message keep getting the unchanged (pre-Got) error line.
|
|
1219
|
+
let message = errors
|
|
1220
|
+
.flatMap(e => {
|
|
1221
|
+
const lines = [`${dataVar}${e.instancePath} ${e.message}`];
|
|
1222
|
+
const value = failingValueByError.get(e);
|
|
1223
|
+
if (value !== undefined) {
|
|
1224
|
+
lines.push(`Got: ${_inspect(value, { maxLen: 500 })}`);
|
|
1225
|
+
}
|
|
1226
|
+
return lines;
|
|
1227
|
+
})
|
|
1228
|
+
.join(separator);
|
|
1229
|
+
const inputStringified = _inspect(inputForPrint, { maxLen: 4000 });
|
|
1202
1230
|
message = [message, 'Input: ' + inputStringified].join(separator);
|
|
1203
1231
|
const err = new AjvValidationError(message, _filterNullishValues({
|
|
1204
1232
|
errors,
|
|
@@ -1209,6 +1237,19 @@ function executeValidation(fn, builtSchema, input, opt = {}, defaultInputName) {
|
|
|
1209
1237
|
return [err, output];
|
|
1210
1238
|
}
|
|
1211
1239
|
// ==== Error formatting helpers ====
|
|
1240
|
+
/**
|
|
1241
|
+
* Resolves the value at an Ajv error's raw instancePath, which is a JSON pointer, e.g `/events/2/name`.
|
|
1242
|
+
* Returns undefined if the path cannot be resolved (e.g the input was mutated).
|
|
1243
|
+
*/
|
|
1244
|
+
function getValueAtJsonPointer(input, pointer) {
|
|
1245
|
+
let value = input;
|
|
1246
|
+
for (const segment of pointer.split('/').slice(1)) {
|
|
1247
|
+
if (value === null || typeof value !== 'object')
|
|
1248
|
+
return undefined;
|
|
1249
|
+
value = value[segment.replaceAll('~1', '/').replaceAll('~0', '~')];
|
|
1250
|
+
}
|
|
1251
|
+
return value;
|
|
1252
|
+
}
|
|
1212
1253
|
function applyImprovementsOnErrorMessages(errors, schema) {
|
|
1213
1254
|
if (!errors)
|
|
1214
1255
|
return;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/nodejs-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.122.1",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@naturalcycles/js-lib": "^15",
|
|
7
7
|
"@standard-schema/spec": "^1",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"yaml": "^2"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"
|
|
18
|
-
"
|
|
17
|
+
"@naturalcycles/dev-lib": "0.0.0",
|
|
18
|
+
"typescript": "^7"
|
|
19
19
|
},
|
|
20
20
|
"exports": {
|
|
21
21
|
".": "./dist/index.js",
|
package/src/exec2/exec2.ts
CHANGED
|
@@ -332,7 +332,13 @@ class Exec2 {
|
|
|
332
332
|
started: UnixTimestampMillis,
|
|
333
333
|
isSuccessful: boolean,
|
|
334
334
|
): void {
|
|
335
|
-
if (
|
|
335
|
+
if (!opt.logFinish) {
|
|
336
|
+
if (isSuccessful) return
|
|
337
|
+
// Failures are logged even with logFinish: false,
|
|
338
|
+
// unless explicitly silenced with `log: false`
|
|
339
|
+
// (e.g when the caller buffers the output to print it later, like runCheck does)
|
|
340
|
+
if (opt.log === false) return
|
|
341
|
+
}
|
|
336
342
|
|
|
337
343
|
console.log(
|
|
338
344
|
[
|
|
@@ -27,7 +27,7 @@ import type {
|
|
|
27
27
|
} from '@naturalcycles/js-lib/types'
|
|
28
28
|
import { _objectAssign, _typeCast, JWT_REGEX } from '@naturalcycles/js-lib/types'
|
|
29
29
|
import type { StandardJSONSchemaV1, StandardSchemaV1 } from '@standard-schema/spec'
|
|
30
|
-
import type { Ajv, ErrorObject } from 'ajv'
|
|
30
|
+
import type { Ajv, ErrorObject, ValidateFunction } from 'ajv'
|
|
31
31
|
import { _inspect } from '../../string/inspect.js'
|
|
32
32
|
import {
|
|
33
33
|
BASE64URL_REGEX,
|
|
@@ -367,7 +367,7 @@ export class JSchema<OUT, Opt>
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
private _builtSchema?: JsonSchema
|
|
370
|
-
private _compiledFns?: WeakMap<Ajv,
|
|
370
|
+
private _compiledFns?: WeakMap<Ajv, ValidateFunction>
|
|
371
371
|
|
|
372
372
|
private _getBuiltSchema(): JsonSchema {
|
|
373
373
|
if (!this._builtSchema) {
|
|
@@ -387,7 +387,7 @@ export class JSchema<OUT, Opt>
|
|
|
387
387
|
return this._builtSchema
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
-
private _getCompiled(overrideAjv?: Ajv): { fn:
|
|
390
|
+
private _getCompiled(overrideAjv?: Ajv): { fn: ValidateFunction; builtSchema: JsonSchema } {
|
|
391
391
|
const builtSchema = this._getBuiltSchema()
|
|
392
392
|
const ajv = overrideAjv ?? this._cfg?.ajv ?? getAjv()
|
|
393
393
|
|
|
@@ -1105,18 +1105,22 @@ export class JObject<OUT extends AnyObject, Opt extends boolean = false> extends
|
|
|
1105
1105
|
OUT,
|
|
1106
1106
|
{
|
|
1107
1107
|
// required keys
|
|
1108
|
-
[
|
|
1109
|
-
|
|
1110
|
-
?
|
|
1111
|
-
|
|
1112
|
-
|
|
1108
|
+
[
|
|
1109
|
+
K in keyof P as P[K] extends JSchema<any, infer IsOpt>
|
|
1110
|
+
? IsOpt extends true
|
|
1111
|
+
? never
|
|
1112
|
+
: K
|
|
1113
|
+
: never
|
|
1114
|
+
]: P[K] extends JSchema<infer OUT2, any> ? OUT2 : never
|
|
1113
1115
|
} & {
|
|
1114
1116
|
// optional keys
|
|
1115
|
-
[
|
|
1116
|
-
|
|
1117
|
-
?
|
|
1117
|
+
[
|
|
1118
|
+
K in keyof P as P[K] extends JSchema<any, infer IsOpt>
|
|
1119
|
+
? IsOpt extends true
|
|
1120
|
+
? K
|
|
1121
|
+
: never
|
|
1118
1122
|
: never
|
|
1119
|
-
|
|
1123
|
+
]?: P[K] extends JSchema<infer OUT2, any> ? OUT2 : never
|
|
1120
1124
|
}
|
|
1121
1125
|
>
|
|
1122
1126
|
>,
|
|
@@ -1185,17 +1189,21 @@ export class JObjectInfer<
|
|
|
1185
1189
|
> extends JBuilder<
|
|
1186
1190
|
Expand<
|
|
1187
1191
|
{
|
|
1188
|
-
[
|
|
1189
|
-
|
|
1190
|
-
?
|
|
1191
|
-
|
|
1192
|
-
|
|
1192
|
+
[
|
|
1193
|
+
K in keyof PROPS as PROPS[K] extends JSchema<any, infer IsOpt>
|
|
1194
|
+
? IsOpt extends true
|
|
1195
|
+
? never
|
|
1196
|
+
: K
|
|
1197
|
+
: never
|
|
1198
|
+
]: PROPS[K] extends JSchema<infer OUT, any> ? OUT : never
|
|
1193
1199
|
} & {
|
|
1194
|
-
[
|
|
1195
|
-
|
|
1196
|
-
?
|
|
1200
|
+
[
|
|
1201
|
+
K in keyof PROPS as PROPS[K] extends JSchema<any, infer IsOpt>
|
|
1202
|
+
? IsOpt extends true
|
|
1203
|
+
? K
|
|
1204
|
+
: never
|
|
1197
1205
|
: never
|
|
1198
|
-
|
|
1206
|
+
]?: PROPS[K] extends JSchema<infer OUT, any> ? OUT : never
|
|
1199
1207
|
}
|
|
1200
1208
|
>,
|
|
1201
1209
|
Opt
|
|
@@ -1516,7 +1524,7 @@ export class AjvSchema<OUT> {
|
|
|
1516
1524
|
private constructor(
|
|
1517
1525
|
public schema: JsonSchema<OUT>,
|
|
1518
1526
|
cfg: Partial<AjvSchemaCfg> = {},
|
|
1519
|
-
preCompiledFn?:
|
|
1527
|
+
preCompiledFn?: ValidateFunction,
|
|
1520
1528
|
) {
|
|
1521
1529
|
this.cfg = {
|
|
1522
1530
|
lazy: false,
|
|
@@ -1585,7 +1593,7 @@ export class AjvSchema<OUT> {
|
|
|
1585
1593
|
* Creates a minimal AjvSchema wrapper from a pre-compiled validate function.
|
|
1586
1594
|
* Used internally by JSchema to cache a compatible AjvSchema instance.
|
|
1587
1595
|
*/
|
|
1588
|
-
static _wrap<OUT>(schema: JsonSchema<OUT>, compiledFn:
|
|
1596
|
+
static _wrap<OUT>(schema: JsonSchema<OUT>, compiledFn: ValidateFunction): AjvSchema<OUT> {
|
|
1589
1597
|
return new AjvSchema<OUT>(schema, {}, compiledFn)
|
|
1590
1598
|
}
|
|
1591
1599
|
|
|
@@ -1608,9 +1616,9 @@ export class AjvSchema<OUT> {
|
|
|
1608
1616
|
|
|
1609
1617
|
readonly cfg: AjvSchemaCfg
|
|
1610
1618
|
|
|
1611
|
-
private _compiledFn
|
|
1619
|
+
private _compiledFn?: ValidateFunction
|
|
1612
1620
|
|
|
1613
|
-
private _getValidateFn():
|
|
1621
|
+
private _getValidateFn(): ValidateFunction {
|
|
1614
1622
|
this._compiledFn ||= this.cfg.ajv.compile(this.schema as any)
|
|
1615
1623
|
return this._compiledFn
|
|
1616
1624
|
}
|
|
@@ -1663,7 +1671,7 @@ export class AjvSchema<OUT> {
|
|
|
1663
1671
|
const separator = '\n'
|
|
1664
1672
|
|
|
1665
1673
|
function executeValidation<OUT>(
|
|
1666
|
-
fn:
|
|
1674
|
+
fn: ValidateFunction,
|
|
1667
1675
|
builtSchema: JsonSchema,
|
|
1668
1676
|
input: unknown,
|
|
1669
1677
|
opt: AjvValidationOptions = {},
|
|
@@ -1685,6 +1693,9 @@ function executeValidation<OUT>(
|
|
|
1685
1693
|
fn.errors = [
|
|
1686
1694
|
{
|
|
1687
1695
|
instancePath: '',
|
|
1696
|
+
schemaPath: '',
|
|
1697
|
+
keyword: 'postValidation',
|
|
1698
|
+
params: {},
|
|
1688
1699
|
message: err.message,
|
|
1689
1700
|
},
|
|
1690
1701
|
]
|
|
@@ -1706,22 +1717,51 @@ function executeValidation<OUT>(
|
|
|
1706
1717
|
// Build fingerprint before applyImprovementsOnErrorMessages: after it, /items/0/name becomes
|
|
1707
1718
|
// .items[0].name, embedding the index into the segment and making it harder to strip without regex
|
|
1708
1719
|
const fingerprint = buildAjvErrorFingerprint(
|
|
1709
|
-
errors[0]
|
|
1720
|
+
errors[0]!,
|
|
1710
1721
|
inputName,
|
|
1711
|
-
resolveCustomErrorMessage(builtSchema, errors[0]),
|
|
1722
|
+
resolveCustomErrorMessage(builtSchema, errors[0]!),
|
|
1712
1723
|
)
|
|
1713
1724
|
|
|
1714
|
-
applyImprovementsOnErrorMessages(errors, builtSchema)
|
|
1715
|
-
|
|
1716
|
-
let message = getAjv().errorsText(errors, {
|
|
1717
|
-
dataVar,
|
|
1718
|
-
separator,
|
|
1719
|
-
})
|
|
1720
|
-
|
|
1721
1725
|
// Note: if we mutated the input already, e.g stripped unknown properties,
|
|
1722
1726
|
// the error message Input would contain already mutated object print, such as Input: {}
|
|
1723
1727
|
// Unless `getOriginalInput` function is provided - then it will be used to preserve the Input pureness.
|
|
1724
|
-
const
|
|
1728
|
+
const inputForPrint = opt.getOriginalInput?.() || input
|
|
1729
|
+
|
|
1730
|
+
// Resolve each error's failing value while its instancePath is still a raw JSON pointer
|
|
1731
|
+
// (e.g `/events/2/name`), which is unambiguous to walk - unlike the dot notation it gets
|
|
1732
|
+
// rewritten into below (a property name may itself contain a dot).
|
|
1733
|
+
// Root errors (empty instancePath) resolve to undefined - there the value is the whole Input,
|
|
1734
|
+
// which is printed anyway.
|
|
1735
|
+
// `required` errors also resolve to undefined - their instancePath points at the parent object
|
|
1736
|
+
// that lacks the property, so `got:` would print that whole object - noise next to the Input print.
|
|
1737
|
+
const failingValueByError = new Map<ErrorObject, unknown>(
|
|
1738
|
+
errors.map(e => [
|
|
1739
|
+
e,
|
|
1740
|
+
e.instancePath && e.keyword !== 'required'
|
|
1741
|
+
? getValueAtJsonPointer(inputForPrint, e.instancePath)
|
|
1742
|
+
: undefined,
|
|
1743
|
+
]),
|
|
1744
|
+
)
|
|
1745
|
+
|
|
1746
|
+
applyImprovementsOnErrorMessages(errors, builtSchema)
|
|
1747
|
+
|
|
1748
|
+
// Same line format as Ajv's errorsText().
|
|
1749
|
+
// Each error line is followed by a `Got: ...` line printing the narrow failing value itself,
|
|
1750
|
+
// so it stays visible even when the Input print below gets truncated for large inputs.
|
|
1751
|
+
// `Got` is a separate line (and is allowed to print multi-line) on purpose: consumers that
|
|
1752
|
+
// only take the first line of the message keep getting the unchanged (pre-Got) error line.
|
|
1753
|
+
let message = errors
|
|
1754
|
+
.flatMap(e => {
|
|
1755
|
+
const lines = [`${dataVar}${e.instancePath} ${e.message}`]
|
|
1756
|
+
const value = failingValueByError.get(e)
|
|
1757
|
+
if (value !== undefined) {
|
|
1758
|
+
lines.push(`Got: ${_inspect(value, { maxLen: 500 })}`)
|
|
1759
|
+
}
|
|
1760
|
+
return lines
|
|
1761
|
+
})
|
|
1762
|
+
.join(separator)
|
|
1763
|
+
|
|
1764
|
+
const inputStringified = _inspect(inputForPrint, { maxLen: 4000 })
|
|
1725
1765
|
message = [message, 'Input: ' + inputStringified].join(separator)
|
|
1726
1766
|
|
|
1727
1767
|
const err = new AjvValidationError(
|
|
@@ -1738,6 +1778,19 @@ function executeValidation<OUT>(
|
|
|
1738
1778
|
|
|
1739
1779
|
// ==== Error formatting helpers ====
|
|
1740
1780
|
|
|
1781
|
+
/**
|
|
1782
|
+
* Resolves the value at an Ajv error's raw instancePath, which is a JSON pointer, e.g `/events/2/name`.
|
|
1783
|
+
* Returns undefined if the path cannot be resolved (e.g the input was mutated).
|
|
1784
|
+
*/
|
|
1785
|
+
function getValueAtJsonPointer(input: unknown, pointer: string): unknown {
|
|
1786
|
+
let value: any = input
|
|
1787
|
+
for (const segment of pointer.split('/').slice(1)) {
|
|
1788
|
+
if (value === null || typeof value !== 'object') return undefined
|
|
1789
|
+
value = value[segment.replaceAll('~1', '/').replaceAll('~0', '~')]
|
|
1790
|
+
}
|
|
1791
|
+
return value
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1741
1794
|
function applyImprovementsOnErrorMessages(
|
|
1742
1795
|
errors: ErrorObject[] | null | undefined,
|
|
1743
1796
|
schema: JsonSchema,
|
|
@@ -2146,13 +2199,15 @@ type StripIndexSignatureDeep<T> = T extends readonly unknown[]
|
|
|
2146
2199
|
? T
|
|
2147
2200
|
: T extends Record<string, any>
|
|
2148
2201
|
? {
|
|
2149
|
-
[
|
|
2150
|
-
|
|
2151
|
-
: number extends K
|
|
2202
|
+
[
|
|
2203
|
+
K in keyof T as string extends K
|
|
2152
2204
|
? never
|
|
2153
|
-
:
|
|
2205
|
+
: number extends K
|
|
2154
2206
|
? never
|
|
2155
|
-
:
|
|
2207
|
+
: symbol extends K
|
|
2208
|
+
? never
|
|
2209
|
+
: K
|
|
2210
|
+
]: StripIndexSignatureDeep<T[K]>
|
|
2156
2211
|
}
|
|
2157
2212
|
: T
|
|
2158
2213
|
|