@naturalcycles/nodejs-lib 15.87.0 → 15.88.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.
|
@@ -389,7 +389,7 @@ export function createAjv(opt) {
|
|
|
389
389
|
validate: function validate(optionalValues, data, _schema, ctx) {
|
|
390
390
|
if (!optionalValues)
|
|
391
391
|
return true;
|
|
392
|
-
_assert(ctx?.parentData && ctx.parentDataProperty !== undefined, 'You should only use `optional([x, y, z]) on a property of an object, or on an element of an array due to Ajv mutation issues.');
|
|
392
|
+
_assert(ctx?.parentData && ctx.parentDataProperty !== undefined, 'You should only use `optional([x, y, z])` on a property of an object, or on an element of an array due to Ajv mutation issues.');
|
|
393
393
|
if (!optionalValues.includes(data))
|
|
394
394
|
return true;
|
|
395
395
|
ctx.parentData[ctx.parentDataProperty] = undefined;
|
|
@@ -578,11 +578,51 @@ export function createAjv(opt) {
|
|
|
578
578
|
validate: function validate(numberOfDigits, data, _schema, ctx) {
|
|
579
579
|
if (!numberOfDigits)
|
|
580
580
|
return true;
|
|
581
|
-
_assert(ctx?.parentData && ctx.parentDataProperty !== undefined, 'You should only use `precision(n) on a property of an object, or on an element of an array due to Ajv mutation issues.');
|
|
581
|
+
_assert(ctx?.parentData && ctx.parentDataProperty !== undefined, 'You should only use `precision(n)` on a property of an object, or on an element of an array due to Ajv mutation issues.');
|
|
582
582
|
ctx.parentData[ctx.parentDataProperty] = _round(data, 10 ** (-1 * numberOfDigits));
|
|
583
583
|
return true;
|
|
584
584
|
},
|
|
585
585
|
});
|
|
586
|
+
ajv.addKeyword({
|
|
587
|
+
keyword: 'customValidations',
|
|
588
|
+
modifying: false,
|
|
589
|
+
errors: true,
|
|
590
|
+
schemaType: 'array',
|
|
591
|
+
validate: function validate(customValidations, data, _schema, ctx) {
|
|
592
|
+
if (!customValidations?.length)
|
|
593
|
+
return true;
|
|
594
|
+
for (const validator of customValidations) {
|
|
595
|
+
const error = validator(data);
|
|
596
|
+
if (error) {
|
|
597
|
+
;
|
|
598
|
+
validate.errors = [
|
|
599
|
+
{
|
|
600
|
+
instancePath: ctx?.instancePath ?? '',
|
|
601
|
+
message: error,
|
|
602
|
+
},
|
|
603
|
+
];
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return true;
|
|
608
|
+
},
|
|
609
|
+
});
|
|
610
|
+
ajv.addKeyword({
|
|
611
|
+
keyword: 'customConversions',
|
|
612
|
+
modifying: true,
|
|
613
|
+
errors: false,
|
|
614
|
+
schemaType: 'array',
|
|
615
|
+
validate: function validate(customConversions, data, _schema, ctx) {
|
|
616
|
+
if (!customConversions?.length)
|
|
617
|
+
return true;
|
|
618
|
+
_assert(ctx?.parentData && ctx.parentDataProperty !== undefined, 'You should only use `convert()` on a property of an object, or on an element of an array due to Ajv mutation issues.');
|
|
619
|
+
for (const converter of customConversions) {
|
|
620
|
+
data = converter(data);
|
|
621
|
+
}
|
|
622
|
+
ctx.parentData[ctx.parentDataProperty] = data;
|
|
623
|
+
return true;
|
|
624
|
+
},
|
|
625
|
+
});
|
|
586
626
|
return ajv;
|
|
587
627
|
}
|
|
588
628
|
const monthLengths = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
@@ -161,6 +161,24 @@ export declare class JsonSchemaAnyBuilder<IN, OUT, Opt> extends JsonSchemaTermin
|
|
|
161
161
|
* Locks the given schema chain and no other modification can be done to it.
|
|
162
162
|
*/
|
|
163
163
|
final(): JsonSchemaTerminal<IN, OUT, Opt>;
|
|
164
|
+
/**
|
|
165
|
+
*
|
|
166
|
+
* @param validator A validator function that returns an error message or undefined.
|
|
167
|
+
*
|
|
168
|
+
* You may add multiple custom validators and they will be executed in the order you added them.
|
|
169
|
+
*/
|
|
170
|
+
custom<OUT2 = OUT>(validator: CustomValidatorFn): JsonSchemaAnyBuilder<IN, OUT2, Opt>;
|
|
171
|
+
/**
|
|
172
|
+
*
|
|
173
|
+
* @param converter A converter function that returns a new value.
|
|
174
|
+
*
|
|
175
|
+
* You may add multiple converters and they will be executed in the order you added them,
|
|
176
|
+
* each converter receiving the result from the previous one.
|
|
177
|
+
*
|
|
178
|
+
* This feature only works when the current schema is nested in an object or array schema,
|
|
179
|
+
* due to how mutability works in Ajv.
|
|
180
|
+
*/
|
|
181
|
+
convert<OUT2>(converter: CustomConverterFn<OUT2>): JsonSchemaAnyBuilder<IN, OUT2, Opt>;
|
|
164
182
|
}
|
|
165
183
|
export declare class JsonSchemaStringBuilder<IN extends string | undefined = string, OUT = IN, Opt extends boolean = false> extends JsonSchemaAnyBuilder<IN, OUT, Opt> {
|
|
166
184
|
constructor();
|
|
@@ -520,6 +538,8 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
|
|
|
520
538
|
};
|
|
521
539
|
anyOfThese?: JsonSchema[];
|
|
522
540
|
precision?: number;
|
|
541
|
+
customValidations?: CustomValidatorFn[];
|
|
542
|
+
customConversions?: CustomConverterFn<any>[];
|
|
523
543
|
}
|
|
524
544
|
declare function object(props: AnyObject): never;
|
|
525
545
|
declare function object<IN extends AnyObject>(props: {
|
|
@@ -618,4 +638,6 @@ type TupleIn<T extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
|
|
|
618
638
|
type TupleOut<T extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
|
|
619
639
|
[K in keyof T]: T[K] extends JsonSchemaAnyBuilder<any, infer O, any> ? O : never;
|
|
620
640
|
};
|
|
641
|
+
export type CustomValidatorFn = (v: any) => string | undefined;
|
|
642
|
+
export type CustomConverterFn<OUT> = (v: any) => OUT;
|
|
621
643
|
export {};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { _isUndefined, _numberEnumValues, _stringEnumValues, getEnumType, } from '@naturalcycles/js-lib';
|
|
4
4
|
import { _uniq } from '@naturalcycles/js-lib/array';
|
|
5
5
|
import { _assert } from '@naturalcycles/js-lib/error';
|
|
6
|
-
import {
|
|
6
|
+
import { _sortObject } from '@naturalcycles/js-lib/object';
|
|
7
7
|
import { _objectAssign, _typeCast, JWT_REGEX, } from '@naturalcycles/js-lib/types';
|
|
8
8
|
import { BASE64URL_REGEX, COUNTRY_CODE_REGEX, CURRENCY_REGEX, IPV4_REGEX, IPV6_REGEX, LANGUAGE_TAG_REGEX, SEMVER_REGEX, SLUG_REGEX, URL_REGEX, UUID_REGEX, } from '../regexes.js';
|
|
9
9
|
import { TIMEZONES } from '../timezones.js';
|
|
@@ -223,13 +223,13 @@ export class JsonSchemaTerminal {
|
|
|
223
223
|
*/
|
|
224
224
|
build() {
|
|
225
225
|
_assert(!(this.schema.optionalField && this.schema.default !== undefined), '.optional() and .default() should not be used together - the default value makes .optional() redundant and causes incorrect type inference');
|
|
226
|
-
const jsonSchema = _sortObject(
|
|
226
|
+
const jsonSchema = _sortObject(deepCopyPreservingFunctions(this.schema), JSON_SCHEMA_ORDER);
|
|
227
227
|
delete jsonSchema.optionalField;
|
|
228
228
|
return jsonSchema;
|
|
229
229
|
}
|
|
230
230
|
clone() {
|
|
231
231
|
const cloned = Object.create(Object.getPrototypeOf(this));
|
|
232
|
-
cloned.schema =
|
|
232
|
+
cloned.schema = deepCopyPreservingFunctions(this.schema);
|
|
233
233
|
return cloned;
|
|
234
234
|
}
|
|
235
235
|
cloneAndUpdateSchema(schema) {
|
|
@@ -318,6 +318,34 @@ export class JsonSchemaAnyBuilder extends JsonSchemaTerminal {
|
|
|
318
318
|
final() {
|
|
319
319
|
return new JsonSchemaTerminal(this.schema);
|
|
320
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
*
|
|
323
|
+
* @param validator A validator function that returns an error message or undefined.
|
|
324
|
+
*
|
|
325
|
+
* You may add multiple custom validators and they will be executed in the order you added them.
|
|
326
|
+
*/
|
|
327
|
+
custom(validator) {
|
|
328
|
+
const { customValidations = [] } = this.schema;
|
|
329
|
+
return this.cloneAndUpdateSchema({
|
|
330
|
+
customValidations: [...customValidations, validator],
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
*
|
|
335
|
+
* @param converter A converter function that returns a new value.
|
|
336
|
+
*
|
|
337
|
+
* You may add multiple converters and they will be executed in the order you added them,
|
|
338
|
+
* each converter receiving the result from the previous one.
|
|
339
|
+
*
|
|
340
|
+
* This feature only works when the current schema is nested in an object or array schema,
|
|
341
|
+
* due to how mutability works in Ajv.
|
|
342
|
+
*/
|
|
343
|
+
convert(converter) {
|
|
344
|
+
const { customConversions = [] } = this.schema;
|
|
345
|
+
return this.cloneAndUpdateSchema({
|
|
346
|
+
customConversions: [...customConversions, converter],
|
|
347
|
+
});
|
|
348
|
+
}
|
|
321
349
|
}
|
|
322
350
|
export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
|
|
323
351
|
constructor() {
|
|
@@ -729,7 +757,7 @@ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
|
|
|
729
757
|
}
|
|
730
758
|
extend(props) {
|
|
731
759
|
const newBuilder = new JsonSchemaObjectBuilder();
|
|
732
|
-
_objectAssign(newBuilder.schema,
|
|
760
|
+
_objectAssign(newBuilder.schema, deepCopyPreservingFunctions(this.schema));
|
|
733
761
|
const incomingSchemaBuilder = new JsonSchemaObjectBuilder(props);
|
|
734
762
|
mergeJsonSchemaObjects(newBuilder.schema, incomingSchemaBuilder.schema);
|
|
735
763
|
_objectAssign(newBuilder.schema, { hasIsOfTypeCheck: false });
|
|
@@ -838,7 +866,7 @@ export class JsonSchemaObjectInferringBuilder extends JsonSchemaAnyBuilder {
|
|
|
838
866
|
}
|
|
839
867
|
extend(props) {
|
|
840
868
|
const newBuilder = new JsonSchemaObjectInferringBuilder();
|
|
841
|
-
_objectAssign(newBuilder.schema,
|
|
869
|
+
_objectAssign(newBuilder.schema, deepCopyPreservingFunctions(this.schema));
|
|
842
870
|
const incomingSchemaBuilder = new JsonSchemaObjectInferringBuilder(props);
|
|
843
871
|
mergeJsonSchemaObjects(newBuilder.schema, incomingSchemaBuilder.schema);
|
|
844
872
|
// This extend function is not type-safe as it is inferring,
|
|
@@ -1057,3 +1085,22 @@ function hasNoObjectSchemas(schema) {
|
|
|
1057
1085
|
}
|
|
1058
1086
|
return false;
|
|
1059
1087
|
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Deep copy that preserves functions in customValidations/customConversions.
|
|
1090
|
+
* Unlike structuredClone, this handles function references (which only exist in those two properties).
|
|
1091
|
+
*/
|
|
1092
|
+
function deepCopyPreservingFunctions(obj) {
|
|
1093
|
+
if (obj === null || typeof obj !== 'object')
|
|
1094
|
+
return obj;
|
|
1095
|
+
if (Array.isArray(obj))
|
|
1096
|
+
return obj.map(deepCopyPreservingFunctions);
|
|
1097
|
+
const copy = {};
|
|
1098
|
+
for (const key of Object.keys(obj)) {
|
|
1099
|
+
const value = obj[key];
|
|
1100
|
+
copy[key] =
|
|
1101
|
+
(key === 'customValidations' || key === 'customConversions') && Array.isArray(value)
|
|
1102
|
+
? [...value]
|
|
1103
|
+
: deepCopyPreservingFunctions(value);
|
|
1104
|
+
}
|
|
1105
|
+
return copy;
|
|
1106
|
+
}
|
package/package.json
CHANGED
|
@@ -6,6 +6,8 @@ import type { AnyObject } from '@naturalcycles/js-lib/types'
|
|
|
6
6
|
import { Ajv2020, type Options, type ValidateFunction } from 'ajv/dist/2020.js'
|
|
7
7
|
import { validTLDs } from '../tlds.js'
|
|
8
8
|
import type {
|
|
9
|
+
CustomConverterFn,
|
|
10
|
+
CustomValidatorFn,
|
|
9
11
|
JsonSchemaIsoDateOptions,
|
|
10
12
|
JsonSchemaIsoMonthOptions,
|
|
11
13
|
JsonSchemaStringEmailOptions,
|
|
@@ -450,7 +452,7 @@ export function createAjv(opt?: Options): Ajv2020 {
|
|
|
450
452
|
|
|
451
453
|
_assert(
|
|
452
454
|
ctx?.parentData && ctx.parentDataProperty !== undefined,
|
|
453
|
-
'You should only use `optional([x, y, z]) on a property of an object, or on an element of an array due to Ajv mutation issues.',
|
|
455
|
+
'You should only use `optional([x, y, z])` on a property of an object, or on an element of an array due to Ajv mutation issues.',
|
|
454
456
|
)
|
|
455
457
|
|
|
456
458
|
if (!optionalValues.includes(data)) return true
|
|
@@ -660,7 +662,7 @@ export function createAjv(opt?: Options): Ajv2020 {
|
|
|
660
662
|
|
|
661
663
|
_assert(
|
|
662
664
|
ctx?.parentData && ctx.parentDataProperty !== undefined,
|
|
663
|
-
'You should only use `precision(n) on a property of an object, or on an element of an array due to Ajv mutation issues.',
|
|
665
|
+
'You should only use `precision(n)` on a property of an object, or on an element of an array due to Ajv mutation issues.',
|
|
664
666
|
)
|
|
665
667
|
|
|
666
668
|
ctx.parentData[ctx.parentDataProperty] = _round(data, 10 ** (-1 * numberOfDigits))
|
|
@@ -669,6 +671,59 @@ export function createAjv(opt?: Options): Ajv2020 {
|
|
|
669
671
|
},
|
|
670
672
|
})
|
|
671
673
|
|
|
674
|
+
ajv.addKeyword({
|
|
675
|
+
keyword: 'customValidations',
|
|
676
|
+
modifying: false,
|
|
677
|
+
errors: true,
|
|
678
|
+
schemaType: 'array',
|
|
679
|
+
validate: function validate(customValidations: CustomValidatorFn[], data: any, _schema, ctx) {
|
|
680
|
+
if (!customValidations?.length) return true
|
|
681
|
+
|
|
682
|
+
for (const validator of customValidations) {
|
|
683
|
+
const error = validator(data)
|
|
684
|
+
if (error) {
|
|
685
|
+
;(validate as any).errors = [
|
|
686
|
+
{
|
|
687
|
+
instancePath: ctx?.instancePath ?? '',
|
|
688
|
+
message: error,
|
|
689
|
+
},
|
|
690
|
+
]
|
|
691
|
+
return false
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return true
|
|
696
|
+
},
|
|
697
|
+
})
|
|
698
|
+
|
|
699
|
+
ajv.addKeyword({
|
|
700
|
+
keyword: 'customConversions',
|
|
701
|
+
modifying: true,
|
|
702
|
+
errors: false,
|
|
703
|
+
schemaType: 'array',
|
|
704
|
+
validate: function validate(
|
|
705
|
+
customConversions: CustomConverterFn<any>[],
|
|
706
|
+
data: any,
|
|
707
|
+
_schema,
|
|
708
|
+
ctx,
|
|
709
|
+
) {
|
|
710
|
+
if (!customConversions?.length) return true
|
|
711
|
+
|
|
712
|
+
_assert(
|
|
713
|
+
ctx?.parentData && ctx.parentDataProperty !== undefined,
|
|
714
|
+
'You should only use `convert()` on a property of an object, or on an element of an array due to Ajv mutation issues.',
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
for (const converter of customConversions) {
|
|
718
|
+
data = converter(data)
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
ctx.parentData[ctx.parentDataProperty] = data
|
|
722
|
+
|
|
723
|
+
return true
|
|
724
|
+
},
|
|
725
|
+
})
|
|
726
|
+
|
|
672
727
|
return ajv
|
|
673
728
|
}
|
|
674
729
|
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { _uniq } from '@naturalcycles/js-lib/array'
|
|
11
11
|
import { _assert } from '@naturalcycles/js-lib/error'
|
|
12
12
|
import type { Set2 } from '@naturalcycles/js-lib/object'
|
|
13
|
-
import {
|
|
13
|
+
import { _sortObject } from '@naturalcycles/js-lib/object'
|
|
14
14
|
import {
|
|
15
15
|
_objectAssign,
|
|
16
16
|
_typeCast,
|
|
@@ -338,7 +338,7 @@ export class JsonSchemaTerminal<IN, OUT, Opt> {
|
|
|
338
338
|
)
|
|
339
339
|
|
|
340
340
|
const jsonSchema = _sortObject(
|
|
341
|
-
|
|
341
|
+
deepCopyPreservingFunctions(this.schema) as AnyObject,
|
|
342
342
|
JSON_SCHEMA_ORDER,
|
|
343
343
|
) as JsonSchema<IN, OUT>
|
|
344
344
|
|
|
@@ -349,7 +349,7 @@ export class JsonSchemaTerminal<IN, OUT, Opt> {
|
|
|
349
349
|
|
|
350
350
|
clone(): this {
|
|
351
351
|
const cloned = Object.create(Object.getPrototypeOf(this))
|
|
352
|
-
cloned.schema =
|
|
352
|
+
cloned.schema = deepCopyPreservingFunctions(this.schema)
|
|
353
353
|
return cloned
|
|
354
354
|
}
|
|
355
355
|
|
|
@@ -455,6 +455,36 @@ export class JsonSchemaAnyBuilder<IN, OUT, Opt> extends JsonSchemaTerminal<IN, O
|
|
|
455
455
|
final(): JsonSchemaTerminal<IN, OUT, Opt> {
|
|
456
456
|
return new JsonSchemaTerminal<IN, OUT, Opt>(this.schema)
|
|
457
457
|
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
*
|
|
461
|
+
* @param validator A validator function that returns an error message or undefined.
|
|
462
|
+
*
|
|
463
|
+
* You may add multiple custom validators and they will be executed in the order you added them.
|
|
464
|
+
*/
|
|
465
|
+
custom<OUT2 = OUT>(validator: CustomValidatorFn): JsonSchemaAnyBuilder<IN, OUT2, Opt> {
|
|
466
|
+
const { customValidations = [] } = this.schema
|
|
467
|
+
return this.cloneAndUpdateSchema({
|
|
468
|
+
customValidations: [...customValidations, validator],
|
|
469
|
+
}) as unknown as JsonSchemaAnyBuilder<IN, OUT2, Opt>
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
*
|
|
474
|
+
* @param converter A converter function that returns a new value.
|
|
475
|
+
*
|
|
476
|
+
* You may add multiple converters and they will be executed in the order you added them,
|
|
477
|
+
* each converter receiving the result from the previous one.
|
|
478
|
+
*
|
|
479
|
+
* This feature only works when the current schema is nested in an object or array schema,
|
|
480
|
+
* due to how mutability works in Ajv.
|
|
481
|
+
*/
|
|
482
|
+
convert<OUT2>(converter: CustomConverterFn<OUT2>): JsonSchemaAnyBuilder<IN, OUT2, Opt> {
|
|
483
|
+
const { customConversions = [] } = this.schema
|
|
484
|
+
return this.cloneAndUpdateSchema({
|
|
485
|
+
customConversions: [...customConversions, converter],
|
|
486
|
+
}) as unknown as JsonSchemaAnyBuilder<IN, OUT2, Opt>
|
|
487
|
+
}
|
|
458
488
|
}
|
|
459
489
|
|
|
460
490
|
export class JsonSchemaStringBuilder<
|
|
@@ -1051,7 +1081,7 @@ export class JsonSchemaObjectBuilder<
|
|
|
1051
1081
|
false
|
|
1052
1082
|
> {
|
|
1053
1083
|
const newBuilder = new JsonSchemaObjectBuilder()
|
|
1054
|
-
_objectAssign(newBuilder.schema,
|
|
1084
|
+
_objectAssign(newBuilder.schema, deepCopyPreservingFunctions(this.schema))
|
|
1055
1085
|
|
|
1056
1086
|
const incomingSchemaBuilder = new JsonSchemaObjectBuilder(props)
|
|
1057
1087
|
mergeJsonSchemaObjects(newBuilder.schema as any, incomingSchemaBuilder.schema as any)
|
|
@@ -1305,7 +1335,7 @@ export class JsonSchemaObjectInferringBuilder<
|
|
|
1305
1335
|
Opt
|
|
1306
1336
|
> {
|
|
1307
1337
|
const newBuilder = new JsonSchemaObjectInferringBuilder<PROPS, Opt>()
|
|
1308
|
-
_objectAssign(newBuilder.schema,
|
|
1338
|
+
_objectAssign(newBuilder.schema, deepCopyPreservingFunctions(this.schema))
|
|
1309
1339
|
|
|
1310
1340
|
const incomingSchemaBuilder = new JsonSchemaObjectInferringBuilder<NEW_PROPS, false>(props)
|
|
1311
1341
|
mergeJsonSchemaObjects(newBuilder.schema as any, incomingSchemaBuilder.schema as any)
|
|
@@ -1587,6 +1617,8 @@ export interface JsonSchema<IN = unknown, OUT = IN> {
|
|
|
1587
1617
|
}
|
|
1588
1618
|
anyOfThese?: JsonSchema[]
|
|
1589
1619
|
precision?: number
|
|
1620
|
+
customValidations?: CustomValidatorFn[]
|
|
1621
|
+
customConversions?: CustomConverterFn<any>[]
|
|
1590
1622
|
}
|
|
1591
1623
|
|
|
1592
1624
|
function object(props: AnyObject): never
|
|
@@ -1893,3 +1925,25 @@ type TupleIn<T extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
|
|
|
1893
1925
|
type TupleOut<T extends readonly JsonSchemaAnyBuilder<any, any, any>[]> = {
|
|
1894
1926
|
[K in keyof T]: T[K] extends JsonSchemaAnyBuilder<any, infer O, any> ? O : never
|
|
1895
1927
|
}
|
|
1928
|
+
|
|
1929
|
+
export type CustomValidatorFn = (v: any) => string | undefined
|
|
1930
|
+
export type CustomConverterFn<OUT> = (v: any) => OUT
|
|
1931
|
+
|
|
1932
|
+
/**
|
|
1933
|
+
* Deep copy that preserves functions in customValidations/customConversions.
|
|
1934
|
+
* Unlike structuredClone, this handles function references (which only exist in those two properties).
|
|
1935
|
+
*/
|
|
1936
|
+
function deepCopyPreservingFunctions<T>(obj: T): T {
|
|
1937
|
+
if (obj === null || typeof obj !== 'object') return obj
|
|
1938
|
+
if (Array.isArray(obj)) return obj.map(deepCopyPreservingFunctions) as T
|
|
1939
|
+
const copy = {} as T
|
|
1940
|
+
for (const key of Object.keys(obj)) {
|
|
1941
|
+
const value = (obj as any)[key]
|
|
1942
|
+
// customValidations/customConversions are arrays of functions - shallow copy the array
|
|
1943
|
+
;(copy as any)[key] =
|
|
1944
|
+
(key === 'customValidations' || key === 'customConversions') && Array.isArray(value)
|
|
1945
|
+
? [...value]
|
|
1946
|
+
: deepCopyPreservingFunctions(value)
|
|
1947
|
+
}
|
|
1948
|
+
return copy
|
|
1949
|
+
}
|