badmfck-api-server 3.9.3 → 3.9.5

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.
@@ -93,7 +93,7 @@ async function Initializer(services) {
93
93
  }
94
94
  exports.Initializer = Initializer;
95
95
  class APIService extends BaseService_1.BaseService {
96
- version = "3.9.3";
96
+ version = "3.9.5";
97
97
  options;
98
98
  monitor = null;
99
99
  started = new Date();
@@ -0,0 +1 @@
1
+ export declare function JSONStableStringify(obj: any): string;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JSONStableStringify = void 0;
4
+ function JSONStableStringify(obj) {
5
+ if (obj === null || typeof obj !== "object") {
6
+ return JSON.stringify(obj);
7
+ }
8
+ if (Array.isArray(obj)) {
9
+ return "[" + obj.map(JSONStableStringify).join(",") + "]";
10
+ }
11
+ const keys = Object.keys(obj).sort();
12
+ return ("{" +
13
+ keys.map(k => JSON.stringify(k) + ":" + JSONStableStringify(obj[k])).join(",") +
14
+ "}");
15
+ }
16
+ exports.JSONStableStringify = JSONStableStringify;
@@ -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]>;
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]>;
35
41
  };
36
42
  type DeepMutable<T> = {
37
43
  -readonly [K in keyof T]: T[K] extends object ? T[K] extends (...args: any[]) => any ? T[K] : DeepMutable<T[K]> : T[K];
38
44
  };
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 as K extends `$__${infer Base}_optional` ? Base extends keyof T ? T[K] extends true ? Base : never : never : never]: true;
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 as K extends `$__${infer Base}_values` ? Base extends keyof T ? T[K] extends readonly (infer V)[] ? Base : never : never : never]: T[K] extends readonly (infer V)[] ? V : never;
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 CleanFields<T> = WidenLiterals<{
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 readonly (infer Item)[] ? Item extends object ? ValidationModel<Item>[] : F : F extends (infer Item)[] ? Item extends object ? ValidationModel<Item>[] : F : F extends object ? ValidationModel<F> : 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 syncStructure(structure: any, object: any): any;
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 syncStructure(structure, object) {
297
- if (typeof structure !== "object")
298
- return;
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 (!(key in object)) {
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] = structure[key];
372
+ object[key] = this.clone(structureValue);
324
373
  continue;
325
374
  }
326
- if (objectValue && typeof objectValue === "object") {
327
- if (Array.isArray(objectValue) && Array.isArray(structureValue) && structureValue.length > 0) {
328
- for (let i of objectValue) {
329
- if (typeof i === "object")
330
- this.syncStructure(structureValue[0], i);
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
  }
@@ -15,6 +15,9 @@ declare class DefaultErrors {
15
15
  static FORBIDDEN: IError;
16
16
  static NOT_FOUND: IError;
17
17
  static INTERNAL_SERVER_ERROR: IError;
18
+ static EXTERNAL_SERVICE_ERROR: IError;
19
+ static MAINTENANCE_MODE: IError;
20
+ static CONFLICT: IError;
18
21
  static INVALID_TOKEN: IError;
19
22
  static INVALID_CREDENTIALS: IError;
20
23
  static INVALID_DATA: IError;
@@ -18,6 +18,9 @@ class DefaultErrors {
18
18
  static FORBIDDEN = { code: 12, message: "Forbidden", httpStatus: 403 };
19
19
  static NOT_FOUND = { code: 13, message: "Not found", httpStatus: 404 };
20
20
  static INTERNAL_SERVER_ERROR = { code: 14, message: "Internal server error.", httpStatus: 500 };
21
+ static EXTERNAL_SERVICE_ERROR = { code: 19, message: "External service error", httpStatus: 502 };
22
+ static MAINTENANCE_MODE = { code: 21, message: "Service in maintenance mode", httpStatus: 503 };
23
+ static CONFLICT = { code: 22, message: "Conflict", httpStatus: 409 };
21
24
  static INVALID_TOKEN = { code: 15, message: "Invalid token", httpStatus: 401 };
22
25
  static INVALID_CREDENTIALS = { code: 16, message: "Invalid credentials", httpStatus: 401 };
23
26
  static INVALID_DATA = { code: 17, message: "Invalid data", httpStatus: 422 };
package/dist/index.d.ts CHANGED
@@ -11,4 +11,5 @@ import { ExternalService } from "./apiServer/external/ExternalService";
11
11
  import { DBService } from "./apiServer/DBService";
12
12
  import { YYYYMMDDHH } from "./apiServer/helper/YYYYMMDDHH";
13
13
  import { TimeframeService } from "./apiServer/TimeframeService";
14
- export { UID, YYYYMMDDHH, APIService, Initializer, LocalRequest, ValidationModel, MysqlService, TimeframeService, Validator, LogService, DataProvider, ErrorUtils, ExternalService, DBService, S_MONITOR_REGISTRATE_ACTION };
14
+ import { JSONStableStringify } from "./apiServer/helper/JSONStableStringify";
15
+ export { UID, YYYYMMDDHH, JSONStableStringify, APIService, Initializer, LocalRequest, ValidationModel, MysqlService, TimeframeService, Validator, LogService, DataProvider, ErrorUtils, ExternalService, DBService, S_MONITOR_REGISTRATE_ACTION };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.S_MONITOR_REGISTRATE_ACTION = exports.DBService = exports.ExternalService = exports.ErrorUtils = exports.DataProvider = exports.LogService = exports.Validator = exports.TimeframeService = exports.MysqlService = exports.LocalRequest = exports.Initializer = exports.APIService = exports.YYYYMMDDHH = exports.UID = void 0;
3
+ exports.S_MONITOR_REGISTRATE_ACTION = exports.DBService = exports.ExternalService = exports.ErrorUtils = exports.DataProvider = exports.LogService = exports.Validator = exports.TimeframeService = exports.MysqlService = exports.LocalRequest = exports.Initializer = exports.APIService = exports.JSONStableStringify = exports.YYYYMMDDHH = exports.UID = void 0;
4
4
  const APIService_1 = require("./apiServer/APIService");
5
5
  Object.defineProperty(exports, "APIService", { enumerable: true, get: function () { return APIService_1.APIService; } });
6
6
  Object.defineProperty(exports, "Initializer", { enumerable: true, get: function () { return APIService_1.Initializer; } });
@@ -28,3 +28,5 @@ const YYYYMMDDHH_1 = require("./apiServer/helper/YYYYMMDDHH");
28
28
  Object.defineProperty(exports, "YYYYMMDDHH", { enumerable: true, get: function () { return YYYYMMDDHH_1.YYYYMMDDHH; } });
29
29
  const TimeframeService_1 = require("./apiServer/TimeframeService");
30
30
  Object.defineProperty(exports, "TimeframeService", { enumerable: true, get: function () { return TimeframeService_1.TimeframeService; } });
31
+ const JSONStableStringify_1 = require("./apiServer/helper/JSONStableStringify");
32
+ Object.defineProperty(exports, "JSONStableStringify", { enumerable: true, get: function () { return JSONStableStringify_1.JSONStableStringify; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "badmfck-api-server",
3
- "version": "3.9.3",
3
+ "version": "3.9.5",
4
4
  "description": "Simple API http server based on express",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",