badmfck-api-server 3.9.4 → 3.9.6
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.
@@ -1,3 +1,9 @@
|
|
1
|
+
type SyncOptions = {
|
2
|
+
fillOnlyWhenNull?: boolean;
|
3
|
+
resetOnTypeMismatch?: boolean;
|
4
|
+
repairArrayItems?: "coerce";
|
5
|
+
coerceArrayFromObject?: "values" | "numericKeys" | "wrap" | "off";
|
6
|
+
};
|
1
7
|
export type TValidatorType = "string" | "number" | "boolean" | "date" | "array" | "object" | "email" | "phone";
|
2
8
|
export interface IStructureOptions {
|
3
9
|
optional?: boolean;
|
@@ -31,24 +37,32 @@ export declare enum ValidationReport {
|
|
31
37
|
}
|
32
38
|
type WidenLiteral<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
|
33
39
|
type WidenLiterals<T> = {
|
34
|
-
[K in keyof T]: T[K] extends (infer R)[] ? WidenLiteral<R>[] : T[K] extends object ? WidenLiterals<T[K]> : WidenLiteral<T[K]>;
|
35
|
-
};
|
36
|
-
type DeepMutable<T> = {
|
37
|
-
-readonly [K in keyof T]: T[K] extends object ? T[K] extends (...args: any[]) => any ? T[K] : DeepMutable<T[K]> : T[K];
|
40
|
+
[K in keyof T]: T[K] extends readonly (infer R)[] ? readonly WidenLiteral<R>[] : T[K] extends (infer R)[] ? WidenLiteral<R>[] : T[K] extends object ? WidenLiterals<T[K]> : WidenLiteral<T[K]>;
|
38
41
|
};
|
42
|
+
type DeepMutable<T> = T extends ReadonlyArray<infer U> ? DeepMutable<U>[] : T extends Array<infer U> ? DeepMutable<U>[] : T extends (...args: any[]) => any ? T : T extends object ? {
|
43
|
+
-readonly [K in keyof T]: DeepMutable<T[K]>;
|
44
|
+
} : T;
|
45
|
+
type IsNever<T> = [T] extends [never] ? true : false;
|
46
|
+
type Or<A, B> = A extends true ? true : (B extends true ? true : false);
|
47
|
+
type CleanFields<T> = WidenLiterals<{
|
48
|
+
[K in keyof T as K extends `$__${string}` ? never : K]: T[K];
|
49
|
+
}>;
|
50
|
+
type HasOptional<T, K extends string> = Or<`$__${K}_optional` extends keyof T ? T[`$__${K}_optional`] extends true ? true : false : false, `$__${K}` extends keyof T ? T[`$__${K}`] extends {
|
51
|
+
optional: true;
|
52
|
+
} ? true : false : false>;
|
39
53
|
type ExtractOptionalFieldNames<T> = {
|
40
|
-
[K in keyof T
|
54
|
+
[K in (keyof T & string) as HasOptional<T, K> extends true ? K : never]: true;
|
41
55
|
};
|
42
56
|
type OptionalFields<T> = keyof ExtractOptionalFieldNames<T> & string;
|
57
|
+
type ValuesArr<T, K extends string> = `$__${K}_values` extends keyof T ? T[`$__${K}_values`] extends readonly (infer V)[] ? readonly V[] : never : `$__${K}` extends keyof T ? T[`$__${K}`] extends {
|
58
|
+
values: readonly (infer V)[];
|
59
|
+
} ? readonly V[] : never : never;
|
43
60
|
type ExtractEnumValues<T> = {
|
44
|
-
[K in keyof T
|
61
|
+
[K in (keyof T & string) as IsNever<ValuesArr<T, K>> extends true ? never : K]: ValuesArr<T, K> extends ReadonlyArray<infer V> ? V : never;
|
45
62
|
};
|
46
63
|
type EnumFieldNames<T> = keyof ExtractEnumValues<T> & string;
|
47
64
|
type EnumFieldValues<T> = ExtractEnumValues<T>;
|
48
|
-
type
|
49
|
-
[K in keyof T as K extends `$__${string}` ? never : K]: T[K];
|
50
|
-
}>;
|
51
|
-
type ProcessField<F, Meta> = F extends readonly (infer Item)[] ? Item extends object ? ValidationModel<Item>[] : F : F extends object ? ValidationModel<F> : F;
|
65
|
+
type ProcessField<F, Meta> = F extends ReadonlyArray<infer Item> ? Item extends object ? ValidationModel<Item>[] : WidenLiteral<Item>[] : F extends Array<infer Item> ? Item extends object ? ValidationModel<Item>[] : WidenLiteral<Item>[] : F extends object ? ValidationModel<F> : WidenLiteral<F>;
|
52
66
|
export type ValidationModel<T> = DeepMutable<{
|
53
67
|
[K in keyof CleanFields<T> as K extends OptionalFields<T> ? K : never]?: K extends EnumFieldNames<T> ? EnumFieldValues<T>[K] : ProcessField<CleanFields<T>[K], T>;
|
54
68
|
} & {
|
@@ -60,7 +74,9 @@ export declare class Validator {
|
|
60
74
|
static parseStructureOptions(key: string, structure: Record<string, any>): IStructureOptions;
|
61
75
|
static validateStructure(structure: any, object: any, level?: number, parentPath?: string): Promise<string[] | undefined>;
|
62
76
|
static filterStructure(structure: any, object: any): void;
|
63
|
-
static
|
77
|
+
static isPlainObject(v: any): boolean;
|
78
|
+
static clone<T>(v: T): T;
|
79
|
+
static syncStructure(structure: any, object: any, opts?: SyncOptions): any;
|
64
80
|
static convertToType<T>(value: any, type: "string" | "number" | "boolean" | "date"): T | null;
|
65
81
|
static validateValue(value: string | number | boolean, opt?: IValidatorOptions): ValidationReport;
|
66
82
|
}
|
@@ -293,48 +293,120 @@ class Validator {
|
|
293
293
|
this.filterStructure(structure[key], object[key]);
|
294
294
|
}
|
295
295
|
}
|
296
|
-
static
|
297
|
-
|
298
|
-
|
296
|
+
static isPlainObject(v) {
|
297
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
298
|
+
}
|
299
|
+
static clone(v) {
|
300
|
+
if (typeof structuredClone === "function")
|
301
|
+
return structuredClone(v);
|
302
|
+
return JSON.parse(JSON.stringify(v));
|
303
|
+
}
|
304
|
+
static syncStructure(structure, object, opts = {
|
305
|
+
fillOnlyWhenNull: true,
|
306
|
+
resetOnTypeMismatch: true,
|
307
|
+
repairArrayItems: "coerce",
|
308
|
+
coerceArrayFromObject: "values"
|
309
|
+
}) {
|
310
|
+
if (structure === null || typeof structure !== "object")
|
311
|
+
return object;
|
312
|
+
if (Array.isArray(structure)) {
|
313
|
+
const tmpl = structure[0];
|
314
|
+
if (!Array.isArray(object) && this.isPlainObject(object)) {
|
315
|
+
const mode = opts.coerceArrayFromObject ?? "values";
|
316
|
+
if (mode === "numericKeys") {
|
317
|
+
const numeric = Object.keys(object).filter(k => String(+k) === k).map(k => +k).sort((a, b) => a - b);
|
318
|
+
object = numeric.map(i => object[String(i)]);
|
319
|
+
}
|
320
|
+
else if (mode === "wrap" && this.isPlainObject(tmpl)) {
|
321
|
+
object = [object];
|
322
|
+
}
|
323
|
+
else if (mode === "values") {
|
324
|
+
object = Object.values(object);
|
325
|
+
}
|
326
|
+
else {
|
327
|
+
return object;
|
328
|
+
}
|
329
|
+
}
|
330
|
+
if (!Array.isArray(object))
|
331
|
+
return object;
|
332
|
+
if (tmpl !== undefined) {
|
333
|
+
for (let i = 0; i < object.length; i++) {
|
334
|
+
let item = object[i];
|
335
|
+
if (item === null || item === undefined) {
|
336
|
+
object[i] = this.clone(tmpl);
|
337
|
+
this.syncStructure(tmpl, object[i], opts);
|
338
|
+
continue;
|
339
|
+
}
|
340
|
+
if (this.isPlainObject(tmpl) && this.isPlainObject(item)) {
|
341
|
+
this.syncStructure(tmpl, item, opts);
|
342
|
+
continue;
|
343
|
+
}
|
344
|
+
if (Array.isArray(tmpl) && Array.isArray(item)) {
|
345
|
+
this.syncStructure(tmpl, item, opts);
|
346
|
+
continue;
|
347
|
+
}
|
348
|
+
if (opts.repairArrayItems === "coerce") {
|
349
|
+
object[i] = this.clone(tmpl);
|
350
|
+
this.syncStructure(tmpl, object[i], opts);
|
351
|
+
}
|
352
|
+
}
|
353
|
+
}
|
354
|
+
return object;
|
355
|
+
}
|
356
|
+
if (!this.isPlainObject(object))
|
357
|
+
return object;
|
299
358
|
for (const key in object) {
|
300
|
-
if (!(key in structure))
|
359
|
+
if (!(key in structure))
|
301
360
|
delete object[key];
|
302
|
-
}
|
303
361
|
}
|
304
362
|
for (const key in structure) {
|
305
363
|
const structureValue = structure[key];
|
364
|
+
const hasKey = Object.prototype.hasOwnProperty.call(object, key);
|
365
|
+
const { optional = false, skip_validation = false } = (Validator.parseStructureOptions(key, structure) || {});
|
306
366
|
const objectValue = object[key];
|
307
|
-
if (!
|
308
|
-
object[key] = structureValue;
|
367
|
+
if (!hasKey) {
|
368
|
+
object[key] = this.clone(structureValue);
|
309
369
|
continue;
|
310
370
|
}
|
311
|
-
const options = Validator.parseStructureOptions(key, structure);
|
312
|
-
if (objectValue === null && structureValue !== null) {
|
313
|
-
if (!options.optional) {
|
314
|
-
object[key] = structureValue;
|
315
|
-
continue;
|
316
|
-
}
|
317
|
-
if (options.skip_validation) {
|
318
|
-
object[key] = structureValue;
|
319
|
-
continue;
|
320
|
-
}
|
321
|
-
}
|
322
371
|
if (key.startsWith("$__")) {
|
323
|
-
object[key] =
|
372
|
+
object[key] = this.clone(structureValue);
|
324
373
|
continue;
|
325
374
|
}
|
326
|
-
if (objectValue
|
327
|
-
if (
|
328
|
-
|
329
|
-
|
330
|
-
|
375
|
+
if ((objectValue === null || objectValue === undefined) && structureValue !== null) {
|
376
|
+
if (!optional || skip_validation) {
|
377
|
+
object[key] = this.clone(structureValue);
|
378
|
+
}
|
379
|
+
continue;
|
380
|
+
}
|
381
|
+
const svIsObj = this.isPlainObject(structureValue);
|
382
|
+
const ovIsObj = this.isPlainObject(objectValue);
|
383
|
+
const svIsArr = Array.isArray(structureValue);
|
384
|
+
const ovIsArr = Array.isArray(objectValue);
|
385
|
+
if (svIsObj || svIsArr) {
|
386
|
+
if ((svIsObj && !ovIsObj) || (svIsArr && !ovIsArr)) {
|
387
|
+
if (opts.resetOnTypeMismatch && svIsObj) {
|
388
|
+
const base = this.clone(structureValue);
|
389
|
+
if (this.isPlainObject(objectValue)) {
|
390
|
+
for (const k in objectValue)
|
391
|
+
base[k] = objectValue[k];
|
392
|
+
this.syncStructure(structureValue, base, opts);
|
393
|
+
object[key] = base;
|
394
|
+
}
|
395
|
+
else {
|
396
|
+
object[key] = base;
|
397
|
+
}
|
331
398
|
}
|
332
399
|
}
|
333
400
|
else {
|
334
|
-
this.syncStructure(structureValue, objectValue);
|
401
|
+
this.syncStructure(structureValue, objectValue, opts);
|
335
402
|
}
|
336
403
|
continue;
|
337
404
|
}
|
405
|
+
if (typeof objectValue !== typeof structureValue) {
|
406
|
+
if (opts.resetOnTypeMismatch) {
|
407
|
+
object[key] = this.clone(structureValue);
|
408
|
+
}
|
409
|
+
}
|
338
410
|
}
|
339
411
|
return object;
|
340
412
|
}
|