ejv 1.1.10 → 2.0.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.
Files changed (75) hide show
  1. package/.eslintrc.json +88 -0
  2. package/.mocharc.json +8 -0
  3. package/CHANGELOG.md +70 -29
  4. package/README-KR.md +6 -2
  5. package/README.md +6 -2
  6. package/build/{constants.js → cjs/constants.js} +117 -118
  7. package/build/cjs/constants.js.map +1 -0
  8. package/build/cjs/ejv.js +1263 -0
  9. package/build/cjs/ejv.js.map +1 -0
  10. package/build/{public_api.js → cjs/index.js} +14 -14
  11. package/build/cjs/index.js.map +1 -0
  12. package/build/cjs/interfaces.js +29 -0
  13. package/build/cjs/interfaces.js.map +1 -0
  14. package/build/cjs/package.json +1 -0
  15. package/build/{tester.js → cjs/tester.js} +273 -268
  16. package/build/cjs/tester.js.map +1 -0
  17. package/build/cjs/util.js +103 -0
  18. package/build/cjs/util.js.map +1 -0
  19. package/build/constants.d.ts +101 -104
  20. package/build/ejv.d.ts +2 -2
  21. package/build/esm/constants.js +115 -0
  22. package/build/esm/constants.js.map +1 -0
  23. package/build/esm/ejv.js +1261 -0
  24. package/build/esm/ejv.js.map +1 -0
  25. package/build/esm/index.js +4 -0
  26. package/build/esm/index.js.map +1 -0
  27. package/build/esm/interfaces.js +33 -0
  28. package/build/esm/interfaces.js.map +1 -0
  29. package/build/esm/package.json +1 -0
  30. package/build/esm/tester.js +240 -0
  31. package/build/esm/tester.js.map +1 -0
  32. package/build/esm/util.js +96 -0
  33. package/build/esm/util.js.map +1 -0
  34. package/build/index.d.ts +3 -0
  35. package/build/interfaces.d.ts +78 -38
  36. package/build/scripts/add-js-extensions.js +46 -0
  37. package/build/scripts/add-js-extensions.js.map +1 -0
  38. package/build/tester.d.ts +33 -34
  39. package/build/util.d.ts +7 -1
  40. package/package.json +48 -37
  41. package/scripts/add-js-extensions.ts +59 -0
  42. package/spec/ArrayScheme.ts +1021 -0
  43. package/spec/CommonScheme.ts +251 -0
  44. package/spec/DateScheme.ts +472 -0
  45. package/spec/NumberScheme.ts +1032 -0
  46. package/spec/ObjectScheme.ts +499 -0
  47. package/spec/RegExpScheme.ts +112 -0
  48. package/spec/StringScheme.ts +1239 -0
  49. package/spec/common-test-util.ts +63 -0
  50. package/spec/ejv.spec.ts +133 -4558
  51. package/spec/testers.spec.ts +17 -16
  52. package/src/constants.ts +41 -42
  53. package/src/ejv.ts +1141 -564
  54. package/src/index.ts +14 -0
  55. package/src/interfaces.ts +127 -41
  56. package/src/tester.ts +75 -69
  57. package/src/util.ts +106 -41
  58. package/tsconfig.cjs.json +8 -0
  59. package/tsconfig.esm.json +7 -0
  60. package/tsconfig.json +21 -18
  61. package/tsconfig.scripts.json +14 -0
  62. package/tsconfig.types.json +9 -0
  63. package/build/constants.js.map +0 -1
  64. package/build/ejv.js +0 -685
  65. package/build/ejv.js.map +0 -1
  66. package/build/interfaces.js +0 -15
  67. package/build/interfaces.js.map +0 -1
  68. package/build/public_api.d.ts +0 -3
  69. package/build/public_api.js.map +0 -1
  70. package/build/tester.js.map +0 -1
  71. package/build/util.js +0 -66
  72. package/build/util.js.map +0 -1
  73. package/spec/common-test-runner.ts +0 -17
  74. package/src/public_api.ts +0 -3
  75. package/tsconfig.spec.json +0 -19
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export { ejv } from './ejv';
2
+ export { DataType, NumberFormat, StringFormat, ErrorMsg, ErrorType } from './constants';
3
+ export {
4
+ BooleanScheme,
5
+ NumberScheme,
6
+ StringScheme,
7
+ ObjectScheme,
8
+ ArrayScheme,
9
+ RegExpScheme,
10
+ DateScheme,
11
+ EjvError,
12
+ Scheme,
13
+ Options
14
+ } from './interfaces';
package/src/interfaces.ts CHANGED
@@ -1,63 +1,149 @@
1
1
  import { DataType, ErrorType, NumberFormat, StringFormat } from './constants';
2
2
 
3
- // use common Scheme for multiple types
4
- export interface Scheme {
5
- // common
6
- key? : string; // can be omitted in array items
7
- type : string | string[] | DataType | DataType[];
8
- optional? : boolean; // false
9
- nullable? : boolean; // false
10
- // reverse? not?
3
+ export type AllDataType = string | string[] | DataType | DataType[];
11
4
 
12
- // common - number & Date (date string, Date)
13
- min? : number | string | Date;
14
- exclusiveMin? : boolean; // false
15
5
 
16
- max? : number | string | Date;
17
- exclusiveMax? : boolean; // false
6
+ interface CommonScheme {
7
+ parent?: Scheme;
18
8
 
19
- // common - number & string
20
- enum? : number[] | string[];
21
- enumReverse? : number[] | string[];
9
+ key?: string; // can be omitted in array items
10
+ type?: AllDataType; // optional for not
22
11
 
23
- // common - number & string
24
- format? : string | string[] | NumberFormat | NumberFormat[] | StringFormat | StringFormat[];
12
+ optional?: boolean; // false
13
+ nullable?: boolean; // false
14
+ }
15
+
16
+ // no additional rule
17
+ export type BooleanScheme = CommonScheme;
18
+
19
+ export interface MinMax<T> {
20
+ min?: T;
21
+ exclusiveMin?: boolean; // default false
22
+
23
+ max?: T;
24
+ exclusiveMax?: boolean; // default false
25
+ }
26
+
27
+ export interface MinMaxScheme<T> extends CommonScheme, MinMax<T> {
28
+ }
29
+
30
+ export interface NumberScheme extends MinMaxScheme<number> {
31
+ value?: number; // TODO: need to add
32
+
33
+ enum?: number[];
34
+
35
+ format?: string | string[] | NumberFormat | NumberFormat[];
36
+ }
37
+
38
+ export interface StringScheme extends CommonScheme {
39
+ value?: string; // TODO: need to add
40
+
41
+ enum?: string[];
42
+
43
+ format?: string | string[] | StringFormat | StringFormat[];
44
+ pattern?: string | string[] | RegExp | RegExp[];
45
+
46
+ length?: number;
47
+ minLength?: number;
48
+ maxLength?: number;
49
+ }
50
+
51
+ export interface ObjectScheme extends CommonScheme {
52
+ properties?: Scheme[];
53
+ allowNoProperty?: boolean; // true
54
+ }
25
55
 
26
- // common - string & array
27
- length? : number;
28
- minLength? : number;
29
- maxLength? : number;
30
56
 
31
- // string
32
- pattern? : string | string[] | RegExp | RegExp[];
57
+ /* eslint-disable @typescript-eslint/no-empty-interface */
58
+ export interface DateScheme extends MinMaxScheme<string | Date> {
59
+ // min, max string for date string
60
+ }
61
+
62
+ // no additional rule
63
+ export type RegExpScheme = CommonScheme;
33
64
 
34
- // object
35
- properties? : Scheme[];
36
- allowNoProperty? : boolean; // true
65
+ export interface ArrayScheme extends CommonScheme {
66
+ unique?: boolean; // false
67
+ items?: AllDataType | Scheme | Scheme[];
37
68
 
38
- // array
39
- unique? : boolean; // false
40
- items? : string | string[] | DataType | DataType[] | Scheme | Scheme[];
69
+ length?: number;
70
+ minLength?: number;
71
+ maxLength?: number;
41
72
  }
42
73
 
74
+ export type Scheme =
75
+ BooleanScheme
76
+ | NumberScheme
77
+ | StringScheme
78
+ | ObjectScheme
79
+ | DateScheme
80
+ | RegExpScheme
81
+ | ArrayScheme;
82
+
83
+
43
84
  export interface Options {
44
- customErrorMsg? : {
45
- [key in ErrorType]? : string;
85
+ customErrorMsg?: {
86
+ [key in ErrorType]?: string;
46
87
  };
47
88
  }
48
89
 
49
90
  export interface InternalOptions extends Options {
50
- path : string[];
91
+ path: string[];
51
92
  }
52
93
 
53
94
  export class EjvError {
54
- public path : string;
55
-
56
- constructor (public type : ErrorType,
57
- public message : string,
58
- path : string[],
59
- public data : any,
60
- public errorData : any) {
61
- this.path = path.join('/');
95
+ public type: ErrorType;
96
+ public message: string;
97
+
98
+ public data: unknown;
99
+ public path: string | undefined;
100
+
101
+ public errorScheme: Scheme | undefined;
102
+ public errorData: unknown | undefined;
103
+
104
+ public isSchemeError: boolean;
105
+ public isDataError: boolean;
106
+
107
+ constructor (param: {
108
+ type: ErrorType,
109
+ message: string,
110
+
111
+ data: unknown,
112
+ path?: string[],
113
+
114
+ errorScheme?: Scheme,
115
+ errorData?: unknown,
116
+
117
+ isSchemeError?: boolean
118
+ }) {
119
+ this.type = param.type;
120
+ this.message = param.message;
121
+
122
+ this.data = param.data;
123
+
124
+ if ('path' in param && param.path !== undefined) {
125
+ this.path = param.path.join('/');
126
+ }
127
+
128
+ if ('errorScheme' in param) {
129
+ this.errorScheme = param.errorScheme;
130
+ }
131
+
132
+ if ('errorData' in param) {
133
+ this.errorData = param.errorData;
134
+ }
135
+
136
+ if (param.isSchemeError) {
137
+ this.isSchemeError = true;
138
+ this.isDataError = false;
139
+ }
140
+ else {
141
+ this.isSchemeError = false;
142
+ this.isDataError = true;
143
+ }
62
144
  }
63
145
  }
146
+
147
+ export interface AnyObject {
148
+ [key: string]: unknown;
149
+ }
package/src/tester.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { DataType } from './constants';
2
+ import { AnyObject } from './interfaces';
2
3
 
3
- export const typeTester = (value : any, type : DataType) : boolean => {
4
- let valid : boolean;
4
+ export const typeTester = (value: unknown, type: DataType): boolean => {
5
+ let valid: boolean;
5
6
 
6
7
  switch (type) {
7
8
  case DataType.BOOLEAN:
@@ -36,66 +37,66 @@ export const typeTester = (value : any, type : DataType) : boolean => {
36
37
  return valid;
37
38
  };
38
39
 
39
- export const definedTester = (value : any) : value is boolean => {
40
+ export const definedTester = (value: unknown): value is boolean => {
40
41
  return value !== undefined;
41
42
  };
42
43
 
43
- export const enumTester = <T> (value : T, arr : T[]) : boolean => {
44
+ export const enumTester = <T> (value: T, arr: T[]): boolean => {
44
45
  return arr.includes(value);
45
46
  };
46
47
 
47
- export const lengthTester = (value : string | any[], length : number) : boolean => {
48
+ export const lengthTester = (value: string | unknown[], length: number): boolean => {
48
49
  return value.length === length;
49
50
  };
50
51
 
51
- export const minLengthTester = (value : string | any[], minLength : number) : boolean => {
52
+ export const minLengthTester = (value: string | unknown[], minLength: number): boolean => {
52
53
  return value.length >= minLength;
53
54
  };
54
55
 
55
- export const maxLengthTester = (value : string | any[], maxLength : number) : boolean => {
56
+ export const maxLengthTester = (value: string | unknown[], maxLength: number): boolean => {
56
57
  return value.length <= maxLength;
57
58
  };
58
59
 
59
- export const booleanTester = (value : any) : boolean => {
60
+ export const booleanTester = (value: unknown): value is boolean => {
60
61
  return typeof value === 'boolean';
61
62
  };
62
63
 
63
- export const numberTester = (value : any) : value is number => {
64
+ export const numberTester = (value: unknown): value is number => {
64
65
  return typeof value === 'number' && !isNaN(value);
65
66
  };
66
67
 
67
- export const integerTester = (value : number) : boolean => {
68
+ export const integerTester = (value: number): boolean => {
68
69
  return +value.toFixed(0) === value;
69
70
  };
70
71
 
71
- export const indexTester = (value : number) : value is number => {
72
+ export const indexTester = (value: number): value is number => {
72
73
  return integerTester(value) && value >= 0;
73
74
  };
74
75
 
75
- export const minNumberTester = (value : number, min : number) : boolean => {
76
+ export const minNumberTester = (value: number, min: number): boolean => {
76
77
  return value >= min;
77
78
  };
78
79
 
79
- export const exclusiveMinNumberTester = (value : number, min : number) : boolean => {
80
+ export const exclusiveMinNumberTester = (value: number, min: number): boolean => {
80
81
  return value > min;
81
82
  };
82
83
 
83
- export const maxNumberTester = (value : number, max : number) : boolean => {
84
+ export const maxNumberTester = (value: number, max: number): boolean => {
84
85
  return value <= max;
85
86
  };
86
87
 
87
- export const exclusiveMaxNumberTester = (value : number, max : number) : boolean => {
88
+ export const exclusiveMaxNumberTester = (value: number, max: number): boolean => {
88
89
  return value < max;
89
90
  };
90
91
 
91
- export const stringTester = (value : any) : value is string => {
92
+ export const stringTester = (value: unknown): value is string => {
92
93
  return typeof value === 'string';
93
94
  };
94
95
 
95
- export const stringRegExpTester = (value : string, regExp : string | RegExp) : boolean => {
96
- let valid : boolean = false;
96
+ export const stringRegExpTester = (value: string, regExp: string | RegExp): boolean => {
97
+ let valid = false;
97
98
 
98
- let _regExp : RegExp | undefined = undefined;
99
+ let _regExp: RegExp | undefined = undefined;
99
100
 
100
101
  if (regExpTester(regExp)) {
101
102
  _regExp = regExp as RegExp;
@@ -112,30 +113,30 @@ export const stringRegExpTester = (value : string, regExp : string | RegExp) : b
112
113
  };
113
114
 
114
115
  // RFC 5322, 3.4.1. spec
115
- export const emailTester = (value : string) : boolean => {
116
- let valid : boolean = false;
116
+ export const emailTester = (value: string): boolean => {
117
+ let valid = false;
117
118
 
118
119
  if (stringTester(value) && stringRegExpTester(value, /^.+@.+$/)) {
119
- const valueAsString : string = value as string;
120
+ const valueAsString: string = value as string;
120
121
 
121
- const atIndex : number = valueAsString.lastIndexOf('@');
122
- const localPart : string = valueAsString.substr(0, atIndex);
123
- const domain : string = valueAsString.substr(atIndex + 1);
122
+ const atIndex: number = valueAsString.lastIndexOf('@');
123
+ const localPart: string = valueAsString.substr(0, atIndex);
124
+ const domain: string = valueAsString.substr(atIndex + 1);
124
125
 
125
126
  // regular expression sources
126
127
  // const aTextRegExpStr : string = '[-a-zA-Z0-9!#$%&\\\'*+/=?^_`{|}~]+';
127
128
 
128
- const dotAtomRegExp : RegExp = /^(\.?[-a-zA-Z0-9!#$%&'*+/=?^_`{|}~]+)*$/;
129
- const quotedStringRegExp : RegExp = /^"[\u0020-\u005b\u005d-\u007e\\]*"$/; // include space (\u005b)
130
- const domainLiteralRegExp : RegExp = /^\[[\u0020-\u005a\u005c-\u007e\\]*]$/;
129
+ const dotAtomRegExp = /^(\.?[-a-zA-Z0-9!#$%&'*+/=?^_`{|}~]+)*$/;
130
+ const quotedStringRegExp = /^"[\u0020-\u005b\u005d-\u007e\\]*"$/; // include space (\u005b)
131
+ const domainLiteralRegExp = /^\[[\u0020-\u005a\u005c-\u007e\\]*]$/;
131
132
 
132
- const validLocalPart : boolean = localPart.length <= 64
133
+ const validLocalPart: boolean = localPart.length <= 64
133
134
  && (
134
135
  dotAtomRegExp.test(localPart)
135
136
  || quotedStringRegExp.test(localPart)
136
137
  );
137
138
 
138
- const validDomain : boolean = !domain.startsWith('.') && !domain.endsWith('.')
139
+ const validDomain: boolean = !domain.startsWith('.') && !domain.endsWith('.')
139
140
  && (
140
141
  dotAtomRegExp.test(domain)
141
142
  || domainLiteralRegExp.test(domain)
@@ -148,17 +149,17 @@ export const emailTester = (value : string) : boolean => {
148
149
  };
149
150
 
150
151
  // RFC 3339 (https://www.ietf.org/rfc/rfc3339.txt) : YYYY-MM-DDThh:mm:ss[.SSSZ]
151
- const rfc3339Tester = (value : string) : boolean => {
152
+ const rfc3339Tester = (value: string): boolean => {
152
153
  return /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([0-1][0-9]|2[0-3])(:([0-5][0-9])){2}(\.\d+)?(Z|[-+]\d{2}:\d{2})?$/.test(value);
153
154
  };
154
155
 
155
- const iso8601DateTester = (value : string) : boolean => {
156
- const years : string = '(\\d{4})';
157
- const months : string = '(0[1-9]|1[0-2])';
158
- const dates : string = '(0[1-9]|[1-2][0-9]|3[0-1])';
159
- const dateOfYear : string = '(00[1-9]|0[1-9][0-9]|[1-2]\\d{2}|3[0-5]\\d|36[0-6])'; // 366 for leap year
160
- const weeks : string = '(W(0[1-9]|[2-4][0-9]|5[0-3]))';
161
- const days : string = '[1-7]';
156
+ const iso8601DateTester = (value: string): boolean => {
157
+ const years = '(\\d{4})';
158
+ const months = '(0[1-9]|1[0-2])';
159
+ const dates = '(0[1-9]|[1-2][0-9]|3[0-1])';
160
+ const dateOfYear = '(00[1-9]|0[1-9][0-9]|[1-2]\\d{2}|3[0-5]\\d|36[0-6])'; // 366 for leap year
161
+ const weeks = '(W(0[1-9]|[2-4][0-9]|5[0-3]))';
162
+ const days = '[1-7]';
162
163
 
163
164
  return [
164
165
  new RegExp(`^[-+]?${ years }$`), // years : YYYY, +YYYY, -YYYY
@@ -168,45 +169,49 @@ const iso8601DateTester = (value : string) : boolean => {
168
169
  new RegExp(`^${ years }-${ weeks }(-${ days })?$`), // week dates : YYYY-Www, YYYY-Www-D
169
170
  new RegExp(`^${ years }${ weeks }(${ days })?$`), // week dates : YYYYWww, YYYYWwwD
170
171
  new RegExp(`^${ years }-?${ dateOfYear }$`) // ordinal dates : YYYY-DDD, YYYYDDD
171
- ].some((regExp : RegExp) => {
172
+ ].some((regExp: RegExp): boolean => {
172
173
  return regExp.test(value);
173
174
  });
174
175
  };
175
176
 
176
- const iso8601TimeTester = (value : string) : boolean => {
177
- const hours : string = '([0-1]\\d|2[0-3])';
178
- const minutes : string = '([0-5]\\d)';
179
- const seconds : string = '([0-5]\\d|60)'; // 60 for leap second
180
- const ms : string = '(\\.[0-9]+)';
177
+ const iso8601TimeTester = (value: string): boolean => {
178
+ const hours = '([0-1]\\d|2[0-3])';
179
+ const minutes = '([0-5]\\d)';
180
+ const seconds = '([0-5]\\d|60)'; // 60 for leap second
181
+ const ms = '(\\.[0-9]+)';
181
182
 
182
183
  return [
183
184
  new RegExp(`^(${ hours }|24)$`), // hh
184
185
  new RegExp(`^((${ hours }:${ minutes })|24:00)$`), // hh:mm
185
186
  new RegExp(`^((${ hours }:${ minutes }:${ seconds })|24:00:00)$`), // hh:mm:ss
186
- new RegExp(`^((${ hours }:${ minutes }:${ seconds }${ ms })|24:00:00\.0+)$`), // hh:mm:ss
187
+ new RegExp(`^((${ hours }:${ minutes }:${ seconds }${ ms })|24:00:00.0+)$`), // hh:mm:ss
187
188
 
188
189
  new RegExp(`^(${ hours }${ minutes }|2400)$`), // hhmm
189
190
  new RegExp(`^(${ hours }${ minutes }${ seconds }|240000)$`), // hhmmss
190
- new RegExp(`^(${ hours }${ minutes }${ seconds }${ ms }|240000\.0+)$`) // hhmmss.sss
191
+ new RegExp(`^(${ hours }${ minutes }${ seconds }${ ms }|240000.0+)$`) // hhmmss.sss
191
192
  ]
192
- .some((regExp : RegExp) => {
193
+ .some((regExp: RegExp): boolean => {
193
194
  return regExp.test(value);
194
195
  });
195
196
  };
196
197
 
197
- const iso8601DateTimeTester = (value : string) : boolean => {
198
- let valid : boolean = false;
198
+ const iso8601DateTimeTester = (value: string): boolean => {
199
+ let valid = false;
199
200
 
200
201
  if (/.+T.+/.test(value) // should have 1 'T'
201
202
  && /(Z|[-+]\d{2}:?\d{2})$/.test(value) // should end with 'Z' or timezone
202
203
  ) {
203
- let [date, time] = value.split('T');
204
+ const dateAndTime: string[] = value.split('T');
205
+ const date: string = dateAndTime[0];
206
+ let time: string = dateAndTime[1];
204
207
 
205
208
  if (time.endsWith('Z')) {
206
209
  time = time.replace('Z', '');
207
210
  }
208
211
  else {
209
- const timezoneStartIndex : number = time.includes('+') ? time.indexOf('+') : time.indexOf('-');
212
+ const timezoneStartIndex: number = time.includes('+')
213
+ ? time.indexOf('+')
214
+ : time.indexOf('-');
210
215
 
211
216
  time = time.substr(0, timezoneStartIndex);
212
217
  }
@@ -217,15 +222,15 @@ const iso8601DateTimeTester = (value : string) : boolean => {
217
222
  return valid;
218
223
  };
219
224
 
220
- export const dateFormatTester = (value : string) : boolean => {
225
+ export const dateFormatTester = (value: string): boolean => {
221
226
  return iso8601DateTester(value);
222
227
  };
223
228
 
224
- export const timeFormatTester = (value : string) : boolean => {
229
+ export const timeFormatTester = (value: string): boolean => {
225
230
  return iso8601TimeTester(value);
226
231
  };
227
232
 
228
- export const dateTimeFormatTester = (value : string) : boolean => {
233
+ export const dateTimeFormatTester = (value: string): boolean => {
229
234
  return rfc3339Tester(value) || iso8601DateTimeTester(value);
230
235
  };
231
236
 
@@ -250,53 +255,54 @@ export const dateTimeFormatTester = (value : string) : boolean => {
250
255
  // return ipv4Tester(value) || ipv6Tester(value);
251
256
  // };
252
257
 
253
- export const objectTester = (value : any) : value is { [key : string] : any } => {
258
+ export const objectTester = (value: unknown): boolean => {
254
259
  return typeof value === 'object';
255
260
  };
256
261
 
257
- export const hasPropertyTester = (value : object) : boolean => {
262
+ export const hasPropertyTester = (value: AnyObject): boolean => {
258
263
  return Object.keys(value).length > 0;
259
264
  };
260
265
 
261
- export const dateTester = (value : any) : value is Date => {
266
+ export const dateTester = (value: unknown): value is Date => {
262
267
  return objectTester(value)
263
268
  && value !== null
264
- && typeof value.getFullYear === 'function'
269
+ && typeof value === 'object'
270
+ && value instanceof Date
265
271
  && !isNaN(value.getFullYear());
266
272
  };
267
273
 
268
- export const minDateTester = (value : Date, min : Date) : boolean => {
274
+ export const minDateTester = (value: Date, min: Date): boolean => {
269
275
  return +value >= +min;
270
276
  };
271
277
 
272
- export const exclusiveMinDateTester = (value : Date, min : Date) : boolean => {
278
+ export const exclusiveMinDateTester = (value: Date, min: Date): boolean => {
273
279
  return +value > +min;
274
280
  };
275
281
 
276
- export const maxDateTester = (value : Date, max : Date) : boolean => {
282
+ export const maxDateTester = (value: Date, max: Date): boolean => {
277
283
  return +value <= +max;
278
284
  };
279
285
 
280
- export const exclusiveMaxDateTester = (value : Date, max : Date) : boolean => {
286
+ export const exclusiveMaxDateTester = (value: Date, max: Date): boolean => {
281
287
  return +value < +max;
282
288
  };
283
289
 
284
- export const arrayTester = (value : any) : value is any[] => {
290
+ export const arrayTester = (value: unknown): value is unknown[] => {
285
291
  return Array.isArray(value);
286
292
  };
287
293
 
288
- export const arrayTypeOfTester = (array : any[], type : DataType) : boolean => {
289
- return array.every((item : any) => {
294
+ export const arrayTypeOfTester = (array: unknown[], type: DataType): boolean => {
295
+ return array.every((item: unknown): boolean => {
290
296
  return typeTester(item, type);
291
297
  });
292
298
  };
293
299
 
294
- export const uniqueItemsTester = (array : any[]) : boolean => {
295
- return array.every(item => {
296
- return array.filter((target : any) => target === item).length === 1;
300
+ export const uniqueItemsTester = (array: unknown[]): boolean => {
301
+ return array.every((item: unknown): boolean => {
302
+ return array.filter((target: unknown): boolean => target === item).length === 1;
297
303
  });
298
304
  };
299
305
 
300
- export const regExpTester = (value : any) : value is RegExp => {
306
+ export const regExpTester = (value: unknown): value is RegExp => {
301
307
  return value instanceof RegExp;
302
308
  };
package/src/util.ts CHANGED
@@ -1,59 +1,124 @@
1
- export const clone = (obj : any) : any => {
2
- let result : any = null;
3
-
4
- if (!!obj) {
5
- let type : string = typeof obj;
6
-
7
- if (type === 'object') {
8
- if (obj.push !== undefined && typeof obj.push === 'function') {
9
- type = 'array';
10
- } else if (obj.getFullYear !== undefined && typeof obj.getFullYear === 'function') {
11
- type = 'date';
12
- } else if (obj.byteLength !== undefined) {
13
- type = 'buffer';
14
- } else if (obj.exec !== undefined && obj.test !== undefined) {
15
- type = 'regexp';
1
+ import { AnyObject } from './interfaces';
2
+ import { ErrorMsg } from './constants';
3
+
4
+
5
+ enum CloneDataType {
6
+ Boolean = 'boolean',
7
+ Number = 'number',
8
+ Function = 'function',
9
+ String = 'string',
10
+ Buffer = 'buffer',
11
+ Object = 'object',
12
+ Array = 'array',
13
+ Date = 'date',
14
+ RegExp = 'regexp'
15
+ }
16
+
17
+ export const isArray = <T> (value: unknown): value is T[] => {
18
+ return value !== undefined
19
+ && value !== null
20
+ && Array.isArray(value);
21
+ };
22
+
23
+
24
+ // sanitize removes undefined & null fields from object. default false
25
+ export const clone = <T> (obj: T, sanitize?: boolean): T => {
26
+ let result !: T;
27
+
28
+ if (obj) {
29
+ let type: CloneDataType = typeof obj as CloneDataType;
30
+
31
+ if (type === CloneDataType.Object) {
32
+ const objAsObject: AnyObject = obj as unknown as AnyObject;
33
+
34
+ if (isArray(objAsObject)) {
35
+ type = CloneDataType.Array;
36
+ }
37
+ else if (objAsObject instanceof Date) {
38
+ type = CloneDataType.Date;
39
+ }
40
+ else if (objAsObject instanceof RegExp) {
41
+ type = CloneDataType.RegExp;
42
+ }
43
+ else if (objAsObject.byteLength
44
+ && typeof objAsObject.byteLength === 'function') {
45
+ type = CloneDataType.Buffer;
16
46
  }
17
47
  }
18
48
 
19
49
  switch (type) {
20
- case 'boolean':
21
- case 'number':
22
- case 'function':
23
- case 'string':
24
- case 'buffer':
25
- // ok with simple copy
26
- result = obj;
27
- break;
28
-
29
- case 'regexp':
30
- result = new RegExp(obj);
31
- break;
32
-
33
- case 'date':
34
- result = new Date(obj);
50
+ case CloneDataType.Date: {
51
+ const objAsDate: Date = obj as unknown as Date;
52
+ result = new Date(objAsDate) as unknown as T;
35
53
  break;
54
+ }
36
55
 
37
- case 'array':
38
- result = [...obj.map((one : any) => {
56
+ case CloneDataType.Array: {
57
+ const objAsArray: unknown[] = obj as unknown as unknown[];
58
+ result = objAsArray.map((one: unknown): unknown => {
39
59
  return clone(one);
40
- })];
60
+ }) as unknown as T;
41
61
  break;
62
+ }
42
63
 
43
- case 'object':
64
+ case CloneDataType.Object: {
44
65
  // sanitize default false
45
- result = {};
66
+ result = {} as unknown as T;
46
67
 
47
- Object.keys(obj)
48
- .forEach(key => {
49
- // recursively call
50
- result[key] = clone(obj[key]);
68
+ const entries: [string, unknown][] = Object.entries(obj)
69
+ .filter(([, value]): boolean => {
70
+ return sanitize
71
+ ? value !== undefined && value !== null
72
+ : true;
51
73
  });
74
+
75
+
76
+ for (const [key, value] of entries) {
77
+ // call recursively
78
+ (result as unknown as AnyObject)[key] = clone(value, sanitize);
79
+ }
52
80
  break;
81
+ }
82
+
83
+ default:
84
+ // simple copy
85
+ result = obj;
53
86
  }
54
- } else {
87
+ }
88
+ else {
55
89
  result = obj; // do not copy null & undefined
56
90
  }
57
91
 
58
92
  return result;
59
- };
93
+ };
94
+
95
+
96
+ export const sift = <T> (arr: T[]): T[] => {
97
+ return arr.reduce((acc: T[], cur: T) => {
98
+ if (cur !== null && cur !== undefined && !acc.includes(cur)) {
99
+ acc.push(cur);
100
+ }
101
+
102
+ return acc;
103
+ }, []);
104
+ };
105
+
106
+
107
+ export const createErrorMsg = (errorMsg: ErrorMsg, param?: {
108
+ placeholders?: (string | number)[]
109
+ }): string => {
110
+ let result: string = errorMsg;
111
+
112
+ if (param?.placeholders) {
113
+ param.placeholders.forEach((strToReplace: string | number, i: number): void => {
114
+ result = result.replace(
115
+ `<<${ i + 1 }>>`,
116
+ typeof strToReplace === 'string'
117
+ ? strToReplace
118
+ : '' + strToReplace
119
+ );
120
+ });
121
+ }
122
+
123
+ return result;
124
+ };