@beseif-solutions/prow-core 0.0.16

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 (47) hide show
  1. package/dist/core.d.ts +21 -0
  2. package/dist/core.js +46 -0
  3. package/dist/entities/category.d.ts +1 -0
  4. package/dist/entities/category.js +20 -0
  5. package/dist/entities/commons.d.ts +26 -0
  6. package/dist/entities/commons.js +15 -0
  7. package/dist/entities/credentials.d.ts +85 -0
  8. package/dist/entities/credentials.js +48 -0
  9. package/dist/entities/events.d.ts +131 -0
  10. package/dist/entities/events.js +82 -0
  11. package/dist/entities/provider.d.ts +12 -0
  12. package/dist/entities/provider.js +25 -0
  13. package/dist/index.d.ts +8 -0
  14. package/dist/index.js +33 -0
  15. package/dist/minified-utils/context.d.ts +6 -0
  16. package/dist/minified-utils/context.js +55 -0
  17. package/dist/minified-utils/env.d.ts +13 -0
  18. package/dist/minified-utils/env.js +96 -0
  19. package/dist/minified-utils/fields.d.ts +128 -0
  20. package/dist/minified-utils/fields.js +458 -0
  21. package/dist/minified-utils/locales.d.ts +27 -0
  22. package/dist/minified-utils/locales.js +88 -0
  23. package/dist/minified-utils/proxy.d.ts +9 -0
  24. package/dist/minified-utils/proxy.js +55 -0
  25. package/dist/minified-utils/types.d.ts +11 -0
  26. package/dist/minified-utils/types.js +2 -0
  27. package/dist/minified-utils/utils.d.ts +1 -0
  28. package/dist/minified-utils/utils.js +26 -0
  29. package/dist/minified-utils/where.d.ts +40 -0
  30. package/dist/minified-utils/where.js +265 -0
  31. package/package.json +44 -0
  32. package/src/core.ts +56 -0
  33. package/src/entities/category.ts +9 -0
  34. package/src/entities/commons.ts +40 -0
  35. package/src/entities/credentials.ts +131 -0
  36. package/src/entities/events.ts +198 -0
  37. package/src/entities/provider.ts +29 -0
  38. package/src/index.ts +8 -0
  39. package/src/minified-utils/context.ts +56 -0
  40. package/src/minified-utils/env.ts +90 -0
  41. package/src/minified-utils/fields.ts +628 -0
  42. package/src/minified-utils/locales.ts +124 -0
  43. package/src/minified-utils/proxy.ts +44 -0
  44. package/src/minified-utils/types.ts +18 -0
  45. package/src/minified-utils/utils.ts +18 -0
  46. package/src/minified-utils/where.ts +330 -0
  47. package/tsconfig.json +21 -0
@@ -0,0 +1,628 @@
1
+ import Joi, { ArraySchema, DateSchema, NumberSchema, StringSchema } from "joi";
2
+ import _ from "lodash";
3
+ import { OneOf } from "./types";
4
+ import { where, Where } from "./where";
5
+ import moment from "moment-timezone";
6
+ import axios from "axios";
7
+ import context from "./context";
8
+
9
+ export type Condition = {
10
+ condition: Where,
11
+ } & OneOf<{
12
+ overrides: Partial<Field>,
13
+ show: boolean,
14
+ }>;
15
+
16
+ type Common = {
17
+ key: string,
18
+ altered_by?: Condition[],
19
+ disabled?: boolean,
20
+ required?: boolean,
21
+ as_password?: boolean,
22
+ } & ({
23
+ as_array?: false,
24
+ } | {
25
+ as_array: true,
26
+ array_min?: number,
27
+ array_max?: number,
28
+ array_length?: number,
29
+ });
30
+
31
+ export type Field<Extend = Record<never, never>> = Common & InputField<Extend>;
32
+
33
+ type InputField<Extend = Record<never, never>> = (
34
+ | AnyInputField<Extend>
35
+ | StringInputField<Extend>
36
+ | NumberInputField<Extend>
37
+ | BooleanInputField<Extend>
38
+ | DateTimeInputField<Extend>
39
+ | DateInputField<Extend>
40
+ | TimeInputField<Extend>
41
+ | DictInputField<Extend>
42
+ ) & Extend;
43
+
44
+ type Any = string | number | boolean;
45
+
46
+ type AnyInputField<Extend = Record<never, never>> = {
47
+ type: `any`,
48
+ default?: Any | Any[],
49
+ } & (
50
+ | SelectField<Any, Extend>
51
+ | {}
52
+ );
53
+
54
+ type StringInputField<Extend = Record<never, never>> = {
55
+ type: `string`,
56
+ default?: string | string[],
57
+ length?: number,
58
+ min?: number,
59
+ max?: number,
60
+ regexp?: string,
61
+ pattern?: `email` | `uri`,
62
+ } & (
63
+ | SelectField<string, Extend>
64
+ | FileField
65
+ | TextareaField
66
+ | RichTextField
67
+ | {}
68
+ );
69
+
70
+ type NumberInputField<Extend = Record<never, never>> = {
71
+ type: `number`,
72
+ default?: number | number[],
73
+ min?: number,
74
+ max?: number,
75
+ } & (
76
+ | { float: true, decimals?: number }
77
+ | { float: false }
78
+ ) & (
79
+ | SelectField<number, Extend>
80
+ | {}
81
+ );
82
+
83
+ type BooleanInputField<Extend = Record<never, never>> = {
84
+ type: `boolean`,
85
+ default?: boolean | boolean[],
86
+ } & (
87
+ | SelectField<boolean, Extend>
88
+ | { format: `checkbox` }
89
+ | { format: `switch` }
90
+ | {}
91
+ );
92
+
93
+ type DateTimeInputField<Extend = Record<never, never>> = {
94
+ type: `datetime`,
95
+ timezone?: string,
96
+ default?: string | number | (string | number)[],
97
+ min?: number | string,
98
+ max?: number | string,
99
+ } & (
100
+ | SelectField<string | number, Extend>
101
+ | {}
102
+ );
103
+
104
+ type DateInputField<Extend = Record<never, never>> = {
105
+ type: `date`,
106
+ default?: string | string[],
107
+ min?: string,
108
+ max?: string,
109
+ } & (
110
+ | SelectField<string, Extend>
111
+ | {}
112
+ );
113
+
114
+ type TimeInputField<Extend = Record<never, never>> = {
115
+ type: `time`,
116
+ timezone?: string,
117
+ default?: string | string[],
118
+ } & (
119
+ | SelectField<string, Extend>
120
+ | {}
121
+ );
122
+
123
+ type DictInputField<Extend = Record<never, never>> = {
124
+ type: `dict`,
125
+ default?: Record<string, any> | Record<string, any>[],
126
+ items?: Field<Extend>[],
127
+ unknown?: boolean,
128
+ };
129
+
130
+ /* select configuration */
131
+ type SelectField<Values = string | number | boolean, Extend = Record<never, never>> = {
132
+ format: `select`,
133
+ // el type string representa el nombre de una función del repositorio que devuelve SelectFieldChoice[]
134
+ choices: SelectFieldChoice<Values, Extend>[] | string,
135
+ unknown?: boolean,
136
+ };
137
+
138
+ export type SelectFieldChoice<Values = string | number | boolean, Extend = Record<never, never>> = {
139
+ key: string,
140
+ value: Values,
141
+ } & Extend;
142
+
143
+ /* file configuration */
144
+ type FileField = {
145
+ format: `file`,
146
+ };
147
+
148
+ /* textarea configuration */
149
+ type TextareaField = {
150
+ format: `textarea`,
151
+ };
152
+
153
+ /* rich text configuration */
154
+ type RichTextField = {
155
+ format: `rich-text`,
156
+ };
157
+
158
+ type ValidationExtended = {
159
+ original: any,
160
+ value: any,
161
+ } & ({
162
+ valid: true,
163
+ } | {
164
+ valid: false,
165
+ errors: {
166
+ path: string,
167
+ message: string,
168
+ type: string,
169
+ }[],
170
+ });
171
+
172
+ const conditionSchema = () =>
173
+ Joi.object({
174
+ condition: Joi.object(),
175
+ overrides: Joi.object(),
176
+ show: Joi.bool(),
177
+ }).xor(`overrides`, `show`);
178
+
179
+ const commonFieldSchema = () =>
180
+ Joi.object({
181
+ key: Joi.string().required(),
182
+ altered_by: Joi.array().items(conditionSchema()),
183
+ disabled: Joi.bool(),
184
+ required: Joi.bool(),
185
+ as_password: Joi.bool(),
186
+ as_array: Joi.bool(),
187
+ array_min: Joi.when(`as_array`, {
188
+ is: true,
189
+ then: Joi.number(),
190
+ otherwise: Joi.forbidden(),
191
+ }),
192
+ array_max: Joi.when(`as_array`, {
193
+ is: true,
194
+ then: Joi.number(),
195
+ otherwise: Joi.forbidden(),
196
+ }),
197
+ array_length: Joi.when(`as_array`, {
198
+ is: true,
199
+ then: Joi.number(),
200
+ otherwise: Joi.forbidden(),
201
+ }),
202
+ });
203
+
204
+ const types = [`any`, `string`, `number`, `boolean`, `date`, `datetime`, `time`, `dict`];
205
+
206
+ const inputFieldBaseSchema = () =>
207
+ Joi.object({
208
+ type: Joi.string().required()
209
+ .valid(...types),
210
+ // switch by type properties
211
+ })
212
+ .concat(commonFieldSchema())
213
+ .unknown();
214
+
215
+ const choiceSchema = (type: Joi.Schema) =>
216
+ Joi.object({
217
+ key: Joi.string().required(),
218
+ value: type.required(),
219
+ }).unknown();
220
+
221
+ const choicesSchema = (type: Joi.Schema) =>
222
+ Joi.alternatives(
223
+ Joi.string(),
224
+ Joi.array().min(1)
225
+ .items(choiceSchema(type))
226
+ );
227
+
228
+ const defaultSchema = (type: Joi.Schema) =>
229
+ Joi.when(`as_array`, {
230
+ is: true,
231
+ then: Joi.array().items(type),
232
+ otherwise: type,
233
+ });
234
+
235
+ const anySchema = () =>
236
+ Joi.alternatives(Joi.string(), Joi.bool(), Joi.number());
237
+
238
+ const anyInputFieldSchema = () =>
239
+ Joi.object({
240
+ default: defaultSchema(anySchema()),
241
+ })
242
+ .concat(Joi.object({
243
+ format: Joi.string().valid(`select`),
244
+ choices: Joi.when(`format`, {
245
+ is: `select`,
246
+ then: choicesSchema(anySchema()).required(),
247
+ otherwise: Joi.forbidden(),
248
+ }),
249
+ }));
250
+
251
+ const stringInputFieldSchema = () =>
252
+ Joi.object({
253
+ default: defaultSchema(Joi.string()),
254
+ length: Joi.number(),
255
+ min: Joi.number(),
256
+ max: Joi.number(),
257
+ regexp: Joi.string(),
258
+ pattern: Joi.string().valid(`email`, `uri`),
259
+ })
260
+ .concat(
261
+ Joi.object({
262
+ format: Joi.string().valid(`file`, `select`, `textarea`, `rich-text`),
263
+ choices: Joi.when(`format`, {
264
+ is: `select`,
265
+ then: choicesSchema(Joi.string()).required(),
266
+ otherwise: Joi.forbidden(),
267
+ }),
268
+ })
269
+ );
270
+
271
+ const numberInputFieldSchema = () =>
272
+ Joi.object({
273
+ default: defaultSchema(Joi.number()),
274
+ min: Joi.number(),
275
+ max: Joi.number(),
276
+ float: Joi.bool(),
277
+ decimals: Joi.when(`float`, {
278
+ is: true,
279
+ then: Joi.number().required(),
280
+ otherwise: Joi.forbidden(),
281
+ }),
282
+ })
283
+ .concat(Joi.object({
284
+ format: Joi.string().valid(`select`),
285
+ choices: Joi.when(`format`, {
286
+ is: `select`,
287
+ then: choicesSchema(Joi.number()).required(),
288
+ otherwise: Joi.forbidden(),
289
+ }),
290
+ }));
291
+
292
+ const booleanInputFieldSchema = () =>
293
+ Joi.object({
294
+ default: defaultSchema(Joi.bool()),
295
+ })
296
+ .concat(Joi.object({
297
+ format: Joi.string().valid(`select`, `checkbox`, `switch`),
298
+ choices: Joi.when(`format`, {
299
+ is: `select`,
300
+ then: choicesSchema(Joi.bool()).required(),
301
+ otherwise: Joi.forbidden(),
302
+ }),
303
+ }));
304
+
305
+ const dateInputFieldSchema = () =>
306
+ Joi.object({
307
+ default: defaultSchema(Joi.alternatives(Joi.string(), Joi.number())),
308
+ min: Joi.alternatives(Joi.string(), Joi.number()),
309
+ max: Joi.alternatives(Joi.string(), Joi.number()),
310
+ })
311
+ .concat(Joi.object({
312
+ format: Joi.string().valid(`select`),
313
+ choices: Joi.when(`format`, {
314
+ is: `select`,
315
+ then: choicesSchema(Joi.alternatives(Joi.string(), Joi.number())).required(),
316
+ otherwise: Joi.forbidden(),
317
+ }),
318
+ }));
319
+
320
+ const timeInputFieldSchema = () =>
321
+ Joi.object({
322
+ default: defaultSchema(Joi.alternatives(Joi.string(), Joi.number())),
323
+ })
324
+ .concat(Joi.object({
325
+ format: Joi.string().valid(`select`),
326
+ choices: Joi.when(`format`, {
327
+ is: `select`,
328
+ then: choicesSchema(Joi.alternatives(Joi.string(), Joi.number())).required(),
329
+ otherwise: Joi.forbidden(),
330
+ }),
331
+ }));
332
+
333
+ const dictInputFieldSchema = () =>
334
+ Joi.object({
335
+ default: defaultSchema(Joi.object()),
336
+ items: Joi.array().items(Joi.link(`#field`)).min(1),
337
+ unknown: Joi.when(`items`, {
338
+ is: Joi.exist(),
339
+ then: Joi.bool(),
340
+ otherwise: Joi.forbidden(),
341
+ }),
342
+ });
343
+
344
+ export const fieldSchema = () =>
345
+ inputFieldBaseSchema()
346
+ .id(`field`)
347
+ .when(Joi.object({ type: Joi.valid(`any`) }).unknown(), {
348
+ then: inputFieldBaseSchema()
349
+ .concat(anyInputFieldSchema()),
350
+ })
351
+ .when(Joi.object({ type: Joi.valid(`string`) }).unknown(), {
352
+ then: inputFieldBaseSchema()
353
+ .concat(stringInputFieldSchema()),
354
+ })
355
+ .when(Joi.object({ type: Joi.valid(`number`) }).unknown(), {
356
+ then: inputFieldBaseSchema()
357
+ .concat(numberInputFieldSchema()),
358
+ })
359
+ .when(Joi.object({ type: Joi.valid(`boolean`) }).unknown(), {
360
+ then: inputFieldBaseSchema()
361
+ .concat(booleanInputFieldSchema()),
362
+ })
363
+ .when(Joi.object({ type: Joi.valid(`date`, `datetime`) }).unknown(), {
364
+ then: inputFieldBaseSchema()
365
+ .concat(dateInputFieldSchema()),
366
+ })
367
+ .when(Joi.object({ type: Joi.valid(`time`) }).unknown(), {
368
+ then: inputFieldBaseSchema()
369
+ .concat(timeInputFieldSchema()),
370
+ })
371
+ .when(Joi.object({ type: Joi.valid(`dict`) }).unknown(), {
372
+ then: inputFieldBaseSchema()
373
+ .concat(dictInputFieldSchema()),
374
+ })
375
+ .when(Joi.object({ type: Joi.invalid(...types) }).unknown(), {
376
+ then: Joi.forbidden(),
377
+ });
378
+
379
+ export const fieldsSchema = () =>
380
+ Joi.array().items(fieldSchema());
381
+
382
+ const getFieldSchema = (field: Field) => {
383
+ let typeSchema: Joi.Schema;
384
+ switch (field.type) {
385
+ case `any`:
386
+ typeSchema = anySchema();
387
+ break;
388
+ case `boolean`:
389
+ typeSchema = Joi.bool();
390
+ break;
391
+ case `string`:
392
+ typeSchema = Joi.string();
393
+ if (field.length) { typeSchema = (typeSchema as StringSchema).length(field.length); }
394
+ if (field.min) {
395
+ typeSchema = (typeSchema as StringSchema).min(field.min);
396
+ } else {
397
+ typeSchema = typeSchema.allow(``);
398
+ }
399
+ if (field.max) { typeSchema = (typeSchema as StringSchema).max(field.max); }
400
+ if (field.regexp) { typeSchema = (typeSchema as StringSchema).regex(new RegExp(field.regexp)); }
401
+ if (field.pattern === `email`) { typeSchema = (typeSchema as StringSchema).email(); }
402
+ if (field.pattern === `uri`) { typeSchema = (typeSchema as StringSchema).uri(); }
403
+
404
+ if (`format` in field && field.format === `file`) {
405
+ typeSchema = typeSchema.external(async (value, helpers) => {
406
+ if (!helpers.original && !field.required) { return helpers.original; }
407
+
408
+ const [asURL, asBase64] = await Promise.allSettled([
409
+ (async () => {
410
+ try {
411
+ const { value: url, error } = Joi.string().uri().validate(helpers.original);
412
+ if (error) { throw new Error(`Invalid URL schema`); }
413
+
414
+ const { data: content } = await axios.get(url, { responseType: `arraybuffer` });
415
+ return Buffer.from(content).toString(`base64`);
416
+ } catch (e) { throw e; }
417
+ })(),
418
+ (async () => {
419
+ try {
420
+ // fastest approach: https://stackoverflow.com/a/8571649
421
+ const base64regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
422
+ if (!base64regex.test(helpers.original)) { throw new Error(`Invalid regexp`); }
423
+ return Buffer.from(helpers.original, `base64`).toString(`base64`);
424
+ } catch (e) { throw e; }
425
+ })(),
426
+ ]);
427
+
428
+ if (asURL.status === `fulfilled`) {
429
+ return asURL.value;
430
+ } else if (asBase64.status === `fulfilled`) {
431
+ return asBase64.value;
432
+ }
433
+
434
+ return helpers.message({ external: `invalid value, expected URL or Base64 string` });
435
+ });
436
+ }
437
+ break;
438
+ case `date`:
439
+ case `datetime`:
440
+ case `time`:
441
+ if (field.type === `time`) {
442
+ typeSchema = Joi.string();
443
+ } else {
444
+ typeSchema = Joi.date().strict(false);
445
+ if (field.min) { typeSchema = (typeSchema as DateSchema).min(field.min); }
446
+ if (field.max) { typeSchema = (typeSchema as DateSchema).max(field.max); }
447
+ }
448
+
449
+ typeSchema = typeSchema.external(async (value, helpers) => {
450
+ if (!helpers.original && !field.required) { return helpers.original; }
451
+
452
+ const format = field.type === `date` ? `YYYY-MM-DD` :
453
+ field.type === `time` ? `HH:mmZ` :
454
+ `YYYY-MM-DDTHH:mm:ssZ`;
455
+ const d = (field.type === `datetime` && typeof helpers.original === `number`) ?
456
+ moment.unix(helpers.original).tz(`UTC`)
457
+ : moment.tz(helpers.original, format, `UTC`);
458
+ if (!d.isValid()) { return helpers.message({ external: `invalid format, expected ${format}` }); }
459
+
460
+ const timezone = (field.type === `date` ? `UTC` : field.timezone) || `UTC`;
461
+ return d.tz(timezone).format(format);
462
+ });
463
+
464
+ break;
465
+ case `number`:
466
+ typeSchema = Joi.number();
467
+ if (field.min) { typeSchema = (typeSchema as NumberSchema).min(field.min); }
468
+ if (field.max) { typeSchema = (typeSchema as NumberSchema).max(field.max); }
469
+ if (field.float) {
470
+ typeSchema = (typeSchema as NumberSchema).precision(field.decimals || 2);
471
+ } else {
472
+ typeSchema = (typeSchema as NumberSchema).precision(0);
473
+ }
474
+ break;
475
+ case `dict`:
476
+ if (field.items) {
477
+ typeSchema = getFieldsSchema(field.items).unknown(field.unknown || false);
478
+ } else {
479
+ typeSchema = Joi.object().unknown(true);
480
+ }
481
+ break;
482
+ }
483
+
484
+ if (`format` in field && field.format === `select`) {
485
+ if (!field.unknown) {
486
+ if (_.isArray(field.choices)) {
487
+ typeSchema = typeSchema.valid(...field.choices.map((c: SelectFieldChoice) => c.value));
488
+ } else {
489
+ // async select values are not checked
490
+ }
491
+ }
492
+ }
493
+
494
+ if (field.as_array) {
495
+ typeSchema = Joi.array().items(typeSchema);
496
+
497
+ if (field.array_length) { typeSchema = (typeSchema as ArraySchema).length(field.array_length); }
498
+ if (field.array_min) { typeSchema = (typeSchema as ArraySchema).min(field.array_min); }
499
+ if (field.array_max) { typeSchema = (typeSchema as ArraySchema).max(field.array_max); }
500
+ }
501
+
502
+ if (field.disabled) { typeSchema = typeSchema.forbidden(); }
503
+
504
+ return field.required ? typeSchema.required() : typeSchema;
505
+ };
506
+
507
+ const getFieldsSchema = (fields: Field[]) => {
508
+ let schema = Joi.object();
509
+
510
+ for (const field of fields) {
511
+ const fSchema = getFieldSchema(field);
512
+ schema = schema.concat(Joi.object({ [field.key]: fSchema }));
513
+ }
514
+
515
+ return schema;
516
+ };
517
+
518
+ const convertPath = (parts: (string | number)[]): string =>
519
+ parts.map((p, i) => typeof p === `string` ? (i === 0 ? p : `.${p}`) : `[${p}]`).join(``);
520
+
521
+ const secure = async (field: Field, validation: ValidationExtended): Promise<ValidationExtended> =>
522
+ field.as_password ? _.omit(validation, [`value`, `original`]) as any : validation;
523
+
524
+ export type ValidationResult<Extended = Record<string, never>> = {
525
+ valid: boolean,
526
+ fields: Field<Extended & ValidationExtended>[],
527
+ };
528
+
529
+ const validate_internal = async (fields: Field[], data: Record<string, any>, options: { secure: boolean, break: boolean, context: Record<string, any> }): Promise<ValidationResult> => {
530
+ try {
531
+ const response: ValidationResult<{}> = { valid: true, fields: [] };
532
+
533
+ const clonedData = _.cloneDeep(data);
534
+
535
+ for (const field of _.compact(fields)) {
536
+ if (options.break && !response.valid) { break; }
537
+
538
+ // alterations over data
539
+ if (field.altered_by?.length > 0) {
540
+ let hide = false;
541
+ for (const alteration of field.altered_by) {
542
+ const matches = where(clonedData, [alteration.condition]);
543
+ if (matches) {
544
+ if (`overrides` in alteration) {
545
+ _.assign(field, alteration.overrides);
546
+ } else if (`show` in alteration) {
547
+ hide = !alteration.show;
548
+ }
549
+ }
550
+ }
551
+
552
+ delete field.altered_by;
553
+
554
+ if (hide) { continue; }
555
+ }
556
+
557
+ const value = _.get(clonedData, field.key, field.default);
558
+
559
+ // resolve value with context
560
+ const resolved = context.resolve(value, options.context);
561
+
562
+ // validate resolved value
563
+ const schema = getFieldSchema(field);
564
+ let validation: any;
565
+ let error: any;
566
+ try {
567
+ validation = await schema.validateAsync(resolved, { abortEarly: false });
568
+ } catch (e) { error = e; }
569
+
570
+ let extended: ValidationExtended;
571
+ if (error) {
572
+ extended = {
573
+ valid: false,
574
+ errors: error.details.map((d) => ({
575
+ type: d.type,
576
+ path: convertPath([field.key, ...d.path]),
577
+ message: d.message,
578
+ })),
579
+ original: value,
580
+ value: null,
581
+ };
582
+ } else {
583
+ extended = {
584
+ valid: true,
585
+ original: value,
586
+ value: validation,
587
+ };
588
+ }
589
+
590
+ _.set(clonedData, field.key, validation);
591
+ response.valid &&= error ? false : true;
592
+ response.fields.push({
593
+ ...field as any,
594
+ ...options.secure ? await secure(field, extended) : extended,
595
+ });
596
+
597
+ }
598
+
599
+ return response;
600
+ } catch (e) { throw e; }
601
+ };
602
+
603
+ export const validate = async <I>(fields: Field<I>[], data: Record<string, any>, options: { secure?: boolean, break?: boolean, context?: Record<string, any> } = {}): Promise<ValidationResult<I>> => {
604
+ try {
605
+ const { error } = fieldsSchema().validate(fields);
606
+ if (error) { throw new Error(`Invalid fields definition: ${error.message}`); }
607
+ const defaultOptions = _.assign({ secure: true, break: false, context: {} }, options);
608
+ const response = await validate_internal(fields, data, defaultOptions);
609
+ return response as any;
610
+ } catch (e) { throw e; }
611
+ };
612
+
613
+ const extract_recursive = async (validation: Field<ValidationExtended>[], options: { original?: boolean }, path = ``): Promise<Record<string, any>> => {
614
+ try {
615
+ const response = {};
616
+ for (const field of validation) {
617
+ const key = _.compact([path, field.key]).join(`.`);
618
+ _.set(response, key, options.original ? field.original : field.value);
619
+ }
620
+ return response;
621
+ } catch (e) { throw e; }
622
+ };
623
+
624
+ export const extract = async (validation: Field<ValidationExtended>[], options: { original?: boolean } = {}): Promise<Record<string, any>> => {
625
+ try {
626
+ return await extract_recursive(validation, options);
627
+ } catch (e) { throw e; }
628
+ };