@naturalcycles/nodejs-lib 15.121.1 → 15.122.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/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,34 @@ 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
|
+
const failingValueByError = new Map(errors.map(e => [
|
|
1206
|
+
e,
|
|
1207
|
+
e.instancePath ? getValueAtJsonPointer(inputForPrint, e.instancePath) : undefined,
|
|
1208
|
+
]));
|
|
1209
|
+
applyImprovementsOnErrorMessages(errors, builtSchema);
|
|
1210
|
+
// Same line format as Ajv's errorsText().
|
|
1211
|
+
// Each nested error line additionally ends with the narrow failing value itself (`, got: ...`),
|
|
1212
|
+
// so it stays visible even when the Input print below gets truncated for large inputs.
|
|
1213
|
+
let message = errors
|
|
1214
|
+
.map(e => {
|
|
1215
|
+
let line = `${dataVar}${e.instancePath} ${e.message}`;
|
|
1216
|
+
const value = failingValueByError.get(e);
|
|
1217
|
+
if (value !== undefined) {
|
|
1218
|
+
line += `, got: ${_inspect(value, { maxLen: 500 })}`;
|
|
1219
|
+
}
|
|
1220
|
+
return line;
|
|
1221
|
+
})
|
|
1222
|
+
.join(separator);
|
|
1223
|
+
const inputStringified = _inspect(inputForPrint, { maxLen: 4000 });
|
|
1202
1224
|
message = [message, 'Input: ' + inputStringified].join(separator);
|
|
1203
1225
|
const err = new AjvValidationError(message, _filterNullishValues({
|
|
1204
1226
|
errors,
|
|
@@ -1209,6 +1231,19 @@ function executeValidation(fn, builtSchema, input, opt = {}, defaultInputName) {
|
|
|
1209
1231
|
return [err, output];
|
|
1210
1232
|
}
|
|
1211
1233
|
// ==== Error formatting helpers ====
|
|
1234
|
+
/**
|
|
1235
|
+
* Resolves the value at an Ajv error's raw instancePath, which is a JSON pointer, e.g `/events/2/name`.
|
|
1236
|
+
* Returns undefined if the path cannot be resolved (e.g the input was mutated).
|
|
1237
|
+
*/
|
|
1238
|
+
function getValueAtJsonPointer(input, pointer) {
|
|
1239
|
+
let value = input;
|
|
1240
|
+
for (const segment of pointer.split('/').slice(1)) {
|
|
1241
|
+
if (value === null || typeof value !== 'object')
|
|
1242
|
+
return undefined;
|
|
1243
|
+
value = value[segment.replaceAll('~1', '/').replaceAll('~0', '~')];
|
|
1244
|
+
}
|
|
1245
|
+
return value;
|
|
1246
|
+
}
|
|
1212
1247
|
function applyImprovementsOnErrorMessages(errors, schema) {
|
|
1213
1248
|
if (!errors)
|
|
1214
1249
|
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.0",
|
|
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,45 @@ 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
|
+
const failingValueByError = new Map<ErrorObject, unknown>(
|
|
1736
|
+
errors.map(e => [
|
|
1737
|
+
e,
|
|
1738
|
+
e.instancePath ? getValueAtJsonPointer(inputForPrint, e.instancePath) : undefined,
|
|
1739
|
+
]),
|
|
1740
|
+
)
|
|
1741
|
+
|
|
1742
|
+
applyImprovementsOnErrorMessages(errors, builtSchema)
|
|
1743
|
+
|
|
1744
|
+
// Same line format as Ajv's errorsText().
|
|
1745
|
+
// Each nested error line additionally ends with the narrow failing value itself (`, got: ...`),
|
|
1746
|
+
// so it stays visible even when the Input print below gets truncated for large inputs.
|
|
1747
|
+
let message = errors
|
|
1748
|
+
.map(e => {
|
|
1749
|
+
let line = `${dataVar}${e.instancePath} ${e.message}`
|
|
1750
|
+
const value = failingValueByError.get(e)
|
|
1751
|
+
if (value !== undefined) {
|
|
1752
|
+
line += `, got: ${_inspect(value, { maxLen: 500 })}`
|
|
1753
|
+
}
|
|
1754
|
+
return line
|
|
1755
|
+
})
|
|
1756
|
+
.join(separator)
|
|
1757
|
+
|
|
1758
|
+
const inputStringified = _inspect(inputForPrint, { maxLen: 4000 })
|
|
1725
1759
|
message = [message, 'Input: ' + inputStringified].join(separator)
|
|
1726
1760
|
|
|
1727
1761
|
const err = new AjvValidationError(
|
|
@@ -1738,6 +1772,19 @@ function executeValidation<OUT>(
|
|
|
1738
1772
|
|
|
1739
1773
|
// ==== Error formatting helpers ====
|
|
1740
1774
|
|
|
1775
|
+
/**
|
|
1776
|
+
* Resolves the value at an Ajv error's raw instancePath, which is a JSON pointer, e.g `/events/2/name`.
|
|
1777
|
+
* Returns undefined if the path cannot be resolved (e.g the input was mutated).
|
|
1778
|
+
*/
|
|
1779
|
+
function getValueAtJsonPointer(input: unknown, pointer: string): unknown {
|
|
1780
|
+
let value: any = input
|
|
1781
|
+
for (const segment of pointer.split('/').slice(1)) {
|
|
1782
|
+
if (value === null || typeof value !== 'object') return undefined
|
|
1783
|
+
value = value[segment.replaceAll('~1', '/').replaceAll('~0', '~')]
|
|
1784
|
+
}
|
|
1785
|
+
return value
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1741
1788
|
function applyImprovementsOnErrorMessages(
|
|
1742
1789
|
errors: ErrorObject[] | null | undefined,
|
|
1743
1790
|
schema: JsonSchema,
|
|
@@ -2146,13 +2193,15 @@ type StripIndexSignatureDeep<T> = T extends readonly unknown[]
|
|
|
2146
2193
|
? T
|
|
2147
2194
|
: T extends Record<string, any>
|
|
2148
2195
|
? {
|
|
2149
|
-
[
|
|
2150
|
-
|
|
2151
|
-
: number extends K
|
|
2196
|
+
[
|
|
2197
|
+
K in keyof T as string extends K
|
|
2152
2198
|
? never
|
|
2153
|
-
:
|
|
2199
|
+
: number extends K
|
|
2154
2200
|
? never
|
|
2155
|
-
:
|
|
2201
|
+
: symbol extends K
|
|
2202
|
+
? never
|
|
2203
|
+
: K
|
|
2204
|
+
]: StripIndexSignatureDeep<T[K]>
|
|
2156
2205
|
}
|
|
2157
2206
|
: T
|
|
2158
2207
|
|