@bolttech/form-engine-core 0.0.1-beta.1

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 (40) hide show
  1. package/index.esm.d.ts +1 -0
  2. package/index.esm.js +3518 -0
  3. package/package.json +14 -0
  4. package/src/formatters/creditCard.d.ts +23 -0
  5. package/src/formatters/custom.d.ts +29 -0
  6. package/src/formatters/handler.d.ts +2 -0
  7. package/src/formatters/regex.d.ts +47 -0
  8. package/src/formatters/splitter.d.ts +17 -0
  9. package/src/formatters/string.d.ts +88 -0
  10. package/src/helpers/creditCard.d.ts +95 -0
  11. package/src/helpers/helpers.d.ts +64 -0
  12. package/src/index.d.ts +10 -0
  13. package/src/interfaces/schema.d.ts +82 -0
  14. package/src/interfaces/state.d.ts +27 -0
  15. package/src/managers/field.d.ts +294 -0
  16. package/src/managers/form.d.ts +224 -0
  17. package/src/managers/formGroup.d.ts +64 -0
  18. package/src/masks/creditCard.d.ts +60 -0
  19. package/src/masks/generic.d.ts +39 -0
  20. package/src/masks/handler.d.ts +2 -0
  21. package/src/masks/string.d.ts +97 -0
  22. package/src/types/event.d.ts +43 -0
  23. package/src/types/form.d.ts +51 -0
  24. package/src/types/mapper.d.ts +94 -0
  25. package/src/types/schema.d.ts +742 -0
  26. package/src/types/template.d.ts +32 -0
  27. package/src/types/utility.d.ts +4 -0
  28. package/src/validations/creditCard.d.ts +52 -0
  29. package/src/validations/custom.d.ts +25 -0
  30. package/src/validations/date.d.ts +78 -0
  31. package/src/validations/document.d.ts +25 -0
  32. package/src/validations/handler.d.ts +2 -0
  33. package/src/validations/length.d.ts +39 -0
  34. package/src/validations/list.d.ts +32 -0
  35. package/src/validations/logical.d.ts +75 -0
  36. package/src/validations/multiple.d.ts +31 -0
  37. package/src/validations/number.d.ts +115 -0
  38. package/src/validations/object.d.ts +44 -0
  39. package/src/validations/regex.d.ts +217 -0
  40. package/src/validations/string.d.ts +53 -0
package/index.esm.js ADDED
@@ -0,0 +1,3518 @@
1
+ import { Subscription, Subject, combineLatest, startWith, groupBy, mergeMap, debounceTime, map } from 'rxjs';
2
+ import creditCardType from 'credit-card-type';
3
+ import { isNumber as isNumber$1, isNil, isEqual, get, set } from 'lodash';
4
+ import { getCurrencySymbol } from '@gaignoux/currency';
5
+
6
+ var TMutationEnum;
7
+ (function (TMutationEnum) {
8
+ TMutationEnum["ON_VALUE"] = "value";
9
+ TMutationEnum["ON_PROPS"] = "props";
10
+ // ON_VALIDATION = 'valid',
11
+ TMutationEnum["ON_VISIBILITY"] = "visibility";
12
+ TMutationEnum["ON_API"] = "api";
13
+ TMutationEnum["ON_IVARS"] = "iVars";
14
+ TMutationEnum["ON_FIELDS"] = "fields";
15
+ })(TMutationEnum || (TMutationEnum = {}));
16
+
17
+ /******************************************************************************
18
+ Copyright (c) Microsoft Corporation.
19
+
20
+ Permission to use, copy, modify, and/or distribute this software for any
21
+ purpose with or without fee is hereby granted.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
24
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
25
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
26
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
27
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
28
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
29
+ PERFORMANCE OF THIS SOFTWARE.
30
+ ***************************************************************************** */
31
+
32
+ function __awaiter(thisArg, _arguments, P, generator) {
33
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
34
+ return new (P || (P = Promise))(function (resolve, reject) {
35
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
36
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
37
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
38
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
39
+ });
40
+ }
41
+
42
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
43
+ var e = new Error(message);
44
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
45
+ };
46
+
47
+ /**
48
+ * Makes an HTTP request using XMLHttpRequest.
49
+ *
50
+ * @param {string} method - The HTTP method (GET, POST, PUT, DELETE, etc.).
51
+ * @param {string} url - The URL to which the request is sent.
52
+ * @param {OutgoingHttpHeaders} [headers] - Optional request headers.
53
+ * @param {Record<string,unknown>} [body] - Optional object body.
54
+ * @returns {Promise<string>} A promise that resolves with the response text if the request is successful, otherwise rejects with an error message.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const response = await makeRequest('GET', 'https://api.example.com/data');
59
+ * ```
60
+ */
61
+ function makeRequest(method, url, headers, body) {
62
+ return new Promise(function (resolve, reject) {
63
+ const xhr = new XMLHttpRequest();
64
+ xhr.open(method, url, true);
65
+ if (headers) {
66
+ Object.keys(headers).forEach(header => {
67
+ xhr.setRequestHeader(header, headers[header]);
68
+ });
69
+ }
70
+ xhr.onload = function () {
71
+ if (xhr.status >= 200 && xhr.status < 300) {
72
+ resolve(xhr.responseText);
73
+ } else {
74
+ reject(xhr.statusText);
75
+ }
76
+ };
77
+ xhr.onerror = function () {
78
+ reject(xhr.statusText);
79
+ };
80
+ xhr.send(JSON.stringify(body) || null);
81
+ });
82
+ }
83
+ /**
84
+ * Extracts keys enclosed in `${}` from a given expression.
85
+ *
86
+ * @param {string} expression - The expression to extract keys from.
87
+ * @returns {
88
+ * originFieldKeys: string[];
89
+ * originPropertyKeys: string[];
90
+ * } An object containing the field names and properties from the template expression.
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * const keys = extractFieldKeys('Hello ${name.value}, your age is ${age.props.label}.');
95
+ * // keys will be {originFieldKeys:['name', 'age'],originPropertyKeys:[value,props]}
96
+ * ```
97
+ */
98
+ function extractFieldKeys(expression) {
99
+ const regex = /\${(.*?)}/g;
100
+ const extractedValues = [];
101
+ let match;
102
+ while ((match = regex.exec(expression)) !== null) {
103
+ extractedValues.push(match[1]);
104
+ }
105
+ return {
106
+ originFieldKeys: Array.from(new Set(extractedValues.map(el => el.split('.')[0]))),
107
+ originPropertyKeys: Array.from(new Set(extractedValues.map(el => el.split('.')[1])))
108
+ };
109
+ }
110
+ /**
111
+ * Traverses an object and extracts expressions containing keys.
112
+ *
113
+ * @param {any} obj - The object to traverse.
114
+ * @param {string} [path] - Optional path within the object (used internally during recursion).
115
+ * @returns {TSubscribedTemplates[]} An array of extracted expressions along with their keys and paths.
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * const data = {
120
+ * user: {
121
+ * name: 'John',
122
+ * age: 30,
123
+ * address: {
124
+ * street: '123 Main St',
125
+ * city: 'Example City'
126
+ * }
127
+ * },
128
+ * message: 'Hello ${user.name}, your age is ${user.age}.'
129
+ * };
130
+ *
131
+ * const expressions = traverseObject(data);
132
+ * // expressions will contain an array of objects with origin expressions, field keys, and destination paths.
133
+ * ```
134
+ */
135
+ function traverseObject(obj, path) {
136
+ const result = [];
137
+ for (const key in obj) {
138
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
139
+ const value = obj[key];
140
+ if (Array.isArray(value)) {
141
+ value.forEach((item, index) => {
142
+ if (typeof item === 'object') {
143
+ result.push(...traverseObject(item, `${path ? `${path}.` : ``}${key}.${index}`));
144
+ } else if (typeof item === 'string') {
145
+ if (String(item).includes('$')) {
146
+ // const extractedPath = item.replace(/\$|{|}/g, '').split('.');
147
+ const extractedOriginPath = `${path ? `${path}.` : ``}${key}`.split('.');
148
+ result.push(Object.assign(Object.assign({
149
+ originExpression: item
150
+ }, extractFieldKeys(item)), {
151
+ destinationKey: extractedOriginPath[0],
152
+ destinationProperty: extractedOriginPath[1],
153
+ destinationPath: extractedOriginPath.slice(2)
154
+ }));
155
+ }
156
+ }
157
+ });
158
+ } else if (typeof value === 'object') {
159
+ result.push(...traverseObject(value, `${path ? `${path}.` : ``}${key}`));
160
+ } else if (typeof value === 'string') {
161
+ if (value.includes('$')) {
162
+ // const extractedPath = value.replace(/\$|{|}/g, '').split('.');
163
+ const destinationPath = `${path ? `${path}.` : ``}${key}`.split('.');
164
+ result.push(Object.assign(Object.assign({
165
+ originExpression: value
166
+ }, extractFieldKeys(value)), {
167
+ destinationKey: destinationPath[0],
168
+ destinationProperty: destinationPath[1],
169
+ destinationPath: destinationPath.slice(2)
170
+ }));
171
+ }
172
+ }
173
+ }
174
+ }
175
+ return result;
176
+ }
177
+
178
+ /**
179
+ * Splits a string by inserting specified values at given positions.
180
+ *
181
+ * @param value - The value to be processed.
182
+ * @param formatters - An object containing formatting options.
183
+ * @returns The processed value with splitters applied.
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * import { splitter } from './path/to/formatterFunctions';
188
+ *
189
+ * const processedValue = splitter('HelloWorld', { splitter: [{ position: 5, value: '_' }] });
190
+ * console.log(processedValue); // Output: 'Hello_World'
191
+ * ```
192
+ */
193
+ const splitter = (value, formatters) => {
194
+ if (!value || !formatters.splitter || typeof value !== 'string') return value;
195
+ formatters.splitter.sort((a, b) => a.position - b.position);
196
+ let result = value;
197
+ let offset = 0;
198
+ for (const splitter of formatters.splitter) {
199
+ const {
200
+ position,
201
+ value
202
+ } = splitter;
203
+ const adjustedPosition = position + offset;
204
+ if (adjustedPosition >= 0 && adjustedPosition <= result.length) {
205
+ result = result.slice(0, adjustedPosition) + value + result.slice(adjustedPosition);
206
+ offset += value.length;
207
+ }
208
+ }
209
+ return result;
210
+ };
211
+
212
+ /**
213
+ * Capitalizes the first letter of a string.
214
+ *
215
+ * @param value - The value to be capitalized.
216
+ * @returns The value with the first letter capitalized.
217
+ *
218
+ * @example
219
+ * ```typescript
220
+ * import { capitalize } from './path/to/formatterFunctions';
221
+ *
222
+ * const capitalizedValue = capitalize('hello world');
223
+ * console.log(capitalizedValue); // Output: 'Hello world'
224
+ * ```
225
+ */
226
+ const capitalize = value => String(value).charAt(0).toUpperCase() + String(value).slice(1);
227
+ /**
228
+ * Converts a string to uppercase.
229
+ *
230
+ * @param value - The value to be converted.
231
+ * @returns The value in uppercase.
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * import { uppercase } from './path/to/formatterFunctions';
236
+ *
237
+ * const uppercasedValue = uppercase('hello world');
238
+ * console.log(uppercasedValue); // Output: 'HELLO WORLD'
239
+ * ```
240
+ */
241
+ const uppercase = value => String(value).toUpperCase();
242
+ /**
243
+ * Formats a string as a float number with a specific precision and decimal separator.
244
+ *
245
+ * @param value - The value to be formatted.
246
+ * @param formatters - An object containing formatting options.
247
+ * @returns The formatted float number.
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * import { onlyFloatNumber } from './path/to/formatterFunctions';
252
+ *
253
+ * const formattedNumber = onlyFloatNumber('1234567.89', { onlyFloatNumber: { precision: 2, decimal: '.' } });
254
+ * console.log(formattedNumber); // Output: '1234567.89'
255
+ * ```
256
+ */
257
+ const onlyFloatNumber = (value, formatters) => {
258
+ const {
259
+ onlyFloatNumber
260
+ } = formatters;
261
+ if (!onlyFloatNumber || typeof value !== 'string' || !value) return value;
262
+ const {
263
+ precision = 2,
264
+ decimal = '.'
265
+ } = onlyFloatNumber;
266
+ if (!value.includes(decimal)) {
267
+ return value;
268
+ }
269
+ const replacedValue = value.replace(/[^0-9]/g, '');
270
+ const partOf = replacedValue.slice(0, replacedValue.length - precision);
271
+ const sliceOf = replacedValue.slice(replacedValue.length - precision);
272
+ return parseFloat(`${partOf}.${sliceOf}`).toFixed(precision);
273
+ };
274
+ /**
275
+ * Trims whitespace from the beginning and end of a string.
276
+ *
277
+ * @param value - The value to be trimmed.
278
+ * @param formatters - An object containing formatting options.
279
+ * @returns The trimmed value.
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * import { trim } from './path/to/formatterFunctions';
284
+ *
285
+ * const trimmedValue = trim(' hello world ');
286
+ * console.log(trimmedValue); // Output: 'hello world'
287
+ * ```
288
+ */
289
+ const trim = (value, formatters) => {
290
+ if (!value || (formatters === null || formatters === void 0 ? void 0 : formatters.trim)) return value;
291
+ return String(value).trim();
292
+ };
293
+ /**
294
+ * Truncates the input value to a specified maximum length if necessary.
295
+ *
296
+ * @param {string | number} value - The input value to be formatted.
297
+ * @param {TFormatters} formatters - An object containing formatting options.
298
+ * @param {number} formatters.maxLength - The maximum allowed length for the input value.
299
+ * @returns {string | number} - The formatted value truncated to the maximum length, if applicable.
300
+ *
301
+ * @example
302
+ * ```typescript
303
+ * const result = maxLength('Hello, World!', { maxLength: 5 });
304
+ * console.log(result); // "Hello"
305
+ * ```
306
+ * @example
307
+ * ```typescript
308
+ * const result = maxLength(123456789, { maxLength: 4 });
309
+ * console.log(result); // "1234"
310
+ * ```
311
+ * @example
312
+ * ```typescript
313
+ * const result = maxLength('Short', { maxLength: 10 });
314
+ * console.log(result); // "Short" (no truncation since input is shorter than maxLength)
315
+ * ```
316
+ */
317
+ const maxLength = (value, formatters) => {
318
+ if (!value || !formatters.maxLength) return value;
319
+ const input = String(value);
320
+ if (input.length > formatters.maxLength) {
321
+ return input.substring(0, formatters.maxLength);
322
+ }
323
+ return value;
324
+ };
325
+
326
+ /**
327
+ * Removes all non-numeric characters from a string.
328
+ *
329
+ * @param value - The value to be processed.
330
+ * @returns The processed value with only numbers.
331
+ *
332
+ * @example
333
+ * ```typescript
334
+ * import { onlyNumbers } from './path/to/formatterFunctions';
335
+ *
336
+ * const processedValue = onlyNumbers('abc123def456');
337
+ * console.log(processedValue); // Output: '123456'
338
+ * ```
339
+ */
340
+ const onlyNumbers = value => String(value).replace(/(\D)+/gim, '');
341
+ /**
342
+ * Removes all non-letter characters from a string.
343
+ *
344
+ * @param value - The value to be processed.
345
+ * @returns The processed value with only letters.
346
+ *
347
+ * @example
348
+ * ```typescript
349
+ * import { onlyLetters } from './path/to/formatterFunctions';
350
+ *
351
+ * const processedValue = onlyLetters('abc123def456');
352
+ * console.log(processedValue); // Output: 'abcdef'
353
+ * ```
354
+ */
355
+ const onlyLetters$1 = value => String(value).replace(/[^\p{L}\s]/gu, '');
356
+ /**
357
+ * Applies a regular expression pattern to remove matching substrings from a string.
358
+ *
359
+ * @param value - The value to be processed.
360
+ * @param formatters - An object containing formatting options.
361
+ * @returns The processed value with matched substrings removed.
362
+ *
363
+ * @example
364
+ * ```typescript
365
+ * import { regex } from './path/to/formatterFunctions';
366
+ *
367
+ * const processedValue = regex('abc123def456', { regex: '\\d+' });
368
+ * console.log(processedValue); // Output: 'abcdef'
369
+ * ```
370
+ */
371
+ const regex$1 = (value, formatters) => {
372
+ if (!formatters.regex) return value;
373
+ return String(value).replace(new RegExp(formatters.regex, 'g'), '');
374
+ };
375
+
376
+ /**
377
+ * Retrieves the type of the credit card and the raw value without spaces.
378
+ *
379
+ * @param value - The credit card number as a string.
380
+ * @param availableOptions - An optional array of credit card types to consider.
381
+ * @returns An array containing the detected credit card type and the raw value without spaces.
382
+ *
383
+ * @example
384
+ * ```typescript
385
+ * import { getTypeCard } from './path/to/helperFunctions';
386
+ *
387
+ * const [creditCardType, rawValue] = getTypeCard('4111 1111 1111 1111');
388
+ * console.log(creditCardType); // Output: { niceType: 'Visa', type: 'visa', ... }
389
+ * console.log(rawValue); // Output: '4111111111111111'
390
+ * ```
391
+ */
392
+ const getTypeCard = (value, availableOptions) => {
393
+ const rawValue = removeGaps(value === null || value === void 0 ? void 0 : value.toString());
394
+ const types = creditCardType(rawValue);
395
+ const selected = (availableOptions === null || availableOptions === void 0 ? void 0 : availableOptions.length) ? types === null || types === void 0 ? void 0 : types.filter(({
396
+ type: id1
397
+ }) => availableOptions.some(id2 => id2 === id1))[0] : types[0];
398
+ return [selected, rawValue];
399
+ };
400
+ /**
401
+ * Removes all spaces from a string.
402
+ *
403
+ * @param value - The string to remove spaces from.
404
+ * @returns The string without spaces.
405
+ */
406
+ const removeGaps = value => value === null || value === void 0 ? void 0 : value.replace(/ /g, '');
407
+ /**
408
+ * Adds specified gaps to a string based on given offsets.
409
+ *
410
+ * @param value - The string to add gaps to.
411
+ * @param gaps - An array containing gap positions.
412
+ * @returns The string with added gaps.
413
+ */
414
+ const addGaps = (value, gaps = []) => {
415
+ const [regexString, replaceString] = gaps.reduce(([regexString, replaceString], offset, i) => {
416
+ const lastOffset = gaps[i - 1] || 0;
417
+ const digitNumber = offset - lastOffset;
418
+ return [`${regexString}([0-9]{0,${digitNumber}})`, `${replaceString}$${i + 1} `];
419
+ }, ['', '']);
420
+ return value.replace(new RegExp(regexString), replaceString).trim();
421
+ };
422
+ /**
423
+ * Formats a credit card number according to its type's gaps and lengths.
424
+ *
425
+ * @param value - The credit card number as a string.
426
+ * @param type - The type of the credit card.
427
+ * @returns The formatted credit card number.
428
+ */
429
+ const formatValue = (value, type) => {
430
+ const DEFAULT_LENGTH = 19;
431
+ return type ? addGaps(value.slice(0, type === null || type === void 0 ? void 0 : type.lengths[0]), type.gaps) : value === null || value === void 0 ? void 0 : value.slice(0, DEFAULT_LENGTH);
432
+ };
433
+ /**
434
+ * Formats a date string by adding a prefix and/or trimming the string based on its length.
435
+ *
436
+ * @param value - The date string to be formatted.
437
+ * @param end - The ending index to slice the string to.
438
+ * @param prefix - The prefix to add between date components.
439
+ * @returns The formatted date string.
440
+ *
441
+ * @example
442
+ * ```typescript
443
+ * import { formatDateCard } from './path/to/helperFunctions';
444
+ *
445
+ * const formattedDate = formatDateCard('1223', 4, '/');
446
+ * console.log(formattedDate); // Output: '12/23'
447
+ * ```
448
+ */
449
+ const formatDateCard = (value, end = 4, prefix = '/') => {
450
+ const fixedValue = value.replace(/\D/g, '');
451
+ const valZeroTwo = fixedValue.slice(0, 2);
452
+ return fixedValue.length >= 5 ? `${valZeroTwo}${prefix}${fixedValue.slice(2, end)}` : fixedValue.length >= 3 ? `${valZeroTwo}${prefix}${fixedValue.slice(2)}` : fixedValue;
453
+ };
454
+
455
+ /**
456
+ * Formats a credit card number by adding gaps based on the card type.
457
+ *
458
+ * @param {unknown} value - The input value to be formatted, expected to be a string representing a credit card number.
459
+ * @param {TFormatters} formatters - An object containing formatting options.
460
+ * @param {boolean} formatters.gapsCreditCard - A flag indicating whether to apply credit card gap formatting.
461
+ * @returns {unknown} - The formatted credit card number with appropriate gaps, or the original value if formatting is not applied.
462
+ *
463
+ * @example
464
+ * ```typescript
465
+ * const formatters = { gapsCreditCard: true };
466
+ * const result = gapsCreditCard('4111111111111111', formatters);
467
+ * console.log(result); // "4111 1111 1111 1111" (formatted based on card type, e.g., Visa)
468
+ * ```
469
+ * @example
470
+ * ```typescript
471
+ * const formatters = { gapsCreditCard: false };
472
+ * const result = gapsCreditCard('4111111111111111', formatters);
473
+ * console.log(result); // "4111111111111111" (no formatting applied)
474
+ * ```
475
+ */
476
+ const gapsCreditCard = (value, formatters) => {
477
+ if (!formatters.gapsCreditCard) return value;
478
+ const [type, rawValue] = getTypeCard(value, formatters === null || formatters === void 0 ? void 0 : formatters.gapsCreditCard);
479
+ return formatValue(rawValue, type);
480
+ };
481
+
482
+ /**
483
+ * Applies a custom callback function to format a value.
484
+ *
485
+ * @param {unknown} value - The input value to be formatted.
486
+ * @param {TFormatters} formatters - An object containing formatting options.
487
+ * @param {(value: unknown) => unknown} formatters.callback - A custom callback function to apply to the value.
488
+ * @returns {unknown} - The value after applying the custom callback function, or the original value if no callback is provided.
489
+ *
490
+ * @example
491
+ * ```typescript
492
+ * const formatters = { callback: (val) => `Formatted: ${val}` };
493
+ * const result = callback('example', formatters);
494
+ * console.log(result); // "Formatted: example"
495
+ * ```
496
+ * @example
497
+ * ```typescript
498
+ * const formatters = { callback: (val) => val.toString().toUpperCase() };
499
+ * const result = callback('example', formatters);
500
+ * console.log(result); // "EXAMPLE"
501
+ * ```
502
+ * @example
503
+ * ```typescript
504
+ * const formatters = { callback: null };
505
+ * const result = callback('example', formatters);
506
+ * console.log(result); // "example" (no formatting applied)
507
+ * ```
508
+ */
509
+ const callback$1 = (value, formatters) => {
510
+ if (!(formatters === null || formatters === void 0 ? void 0 : formatters.callback)) return value;
511
+ return formatters.callback(value);
512
+ };
513
+
514
+ const formatters = {
515
+ capitalize,
516
+ uppercase,
517
+ onlyNumbers,
518
+ onlyLetters: onlyLetters$1,
519
+ regex: regex$1,
520
+ splitter,
521
+ maxLength,
522
+ onlyFloatNumber,
523
+ trim,
524
+ gapsCreditCard,
525
+ callback: callback$1,
526
+ // todo: to be removed
527
+ dotEvery3chars: value => {
528
+ const result = String(value).replace(/\./g, '').replace(/(.{3})/g, '$1.');
529
+ return result.endsWith('.') ? result.slice(0, -1) : result;
530
+ }
531
+ };
532
+
533
+ /**
534
+ * Applies multiple generic masks to a string based on the provided mask configuration.
535
+ *
536
+ * @param value - The value to be masked.
537
+ * @param masks - An object containing the mask configuration.
538
+ * @returns The masked value.
539
+ *
540
+ * @example
541
+ * ```typescript
542
+ * import maskValue from './path/to/maskFunctions';
543
+ *
544
+ * const masks = {
545
+ * generic: [
546
+ * { from: 2, to: 4, mask: '*' },
547
+ * { from: 7, to: 9, mask: '#' }
548
+ * ]
549
+ * };
550
+ *
551
+ * const maskedValue = maskValue('123456789', masks);
552
+ * console.log(maskedValue); // Output: '1**45###9'
553
+ *
554
+ * // Using from a JSON config
555
+ * const config = {
556
+ * value: 'abcdefghij',
557
+ * masks: {
558
+ * generic: [
559
+ * { from: 3, to: 6, mask: '*' },
560
+ * { from: 8, to: 10, mask: '#' }
561
+ * ]
562
+ * }
563
+ * };
564
+ *
565
+ * const maskedValue = maskValue(config.value, config.masks);
566
+ * console.log(maskedValue); // Output: 'ab***gh###'
567
+ * ```
568
+ */
569
+ var generic = ((value, masks) => {
570
+ if (!(masks === null || masks === void 0 ? void 0 : masks.generic)) return value;
571
+ let masked = value;
572
+ masks.generic.forEach(item => {
573
+ const {
574
+ to = masked.length,
575
+ mask
576
+ } = item;
577
+ let {
578
+ from
579
+ } = item;
580
+ if (to > value.length - 1) return;
581
+ if (from === 0) {
582
+ from = 1;
583
+ }
584
+ const maskedPortion = new Array(to - from + 2).join(mask);
585
+ masked = masked.slice(0, from - 1) + maskedPortion + masked.slice(to);
586
+ });
587
+ return masked;
588
+ });
589
+
590
+ /**
591
+ * Replaces all characters in a string with a specified replacement string or character.
592
+ *
593
+ * @param value - The value to be masked.
594
+ * @param masks - An object containing the mask configuration.
595
+ * @returns The masked value.
596
+ *
597
+ * @example
598
+ * ```typescript
599
+ * import { replaceAll } from './path/to/maskFunctions';
600
+ *
601
+ * const masks = { replaceAll: '*' };
602
+ *
603
+ * const maskedValue = replaceAll('12345', masks);
604
+ * console.log(maskedValue); // Output: '*****'
605
+ *
606
+ * // Using from a JSON config
607
+ * const config = {
608
+ * value: 'hello',
609
+ * masks: { replaceAll: '*' }
610
+ * };
611
+ *
612
+ * const maskedValue = replaceAll(config.value, config.masks);
613
+ * console.log(maskedValue); // Output: '*****'
614
+ * ```
615
+ */
616
+ const replaceAll = (value, masks) => {
617
+ let targetReplaceMask = masks.replaceAll;
618
+ if (!targetReplaceMask) return value;
619
+ if (typeof targetReplaceMask === 'number') {
620
+ targetReplaceMask = targetReplaceMask === null || targetReplaceMask === void 0 ? void 0 : targetReplaceMask.toString();
621
+ }
622
+ return new Array(String(value).length + 1).join(targetReplaceMask);
623
+ };
624
+ /**
625
+ * Formats a numeric value as a currency string based on the provided mask configuration.
626
+ *
627
+ * @param value - The numeric value to be formatted.
628
+ * @param masks - An object containing the mask configuration.
629
+ * @returns The formatted currency string.
630
+ *
631
+ * @example
632
+ * ```typescript
633
+ * import { currency } from './path/to/maskFunctions';
634
+ *
635
+ * const masks = {
636
+ * currency: {
637
+ * align: 'right',
638
+ * decimal: ',',
639
+ * precision: 2,
640
+ * prefix: 'USD',
641
+ * thousands: '.'
642
+ * }
643
+ * };
644
+ *
645
+ * const formattedValue = currency(12345.67, masks);
646
+ * console.log(formattedValue); // Output: '12.345,67 $'
647
+ *
648
+ * // Using from a JSON config
649
+ * const config = {
650
+ * value: 9876.54,
651
+ * masks: {
652
+ * currency: {
653
+ * align: 'right',
654
+ * decimal: ',',
655
+ * precision: 2,
656
+ * prefix: 'EUR',
657
+ * thousands: '.'
658
+ * }
659
+ * }
660
+ * };
661
+ *
662
+ * const formattedValue = currency(config.value, config.masks);
663
+ * console.log(formattedValue); // Output: '9.876,54 €'
664
+ * ```
665
+ */
666
+ const currency = (value, masks) => {
667
+ if (!(masks === null || masks === void 0 ? void 0 : masks.currency)) return value;
668
+ const {
669
+ align = 'right',
670
+ decimal = '.',
671
+ precision = 2,
672
+ prefix = 'BBD',
673
+ thousands = ','
674
+ } = masks.currency;
675
+ const rawValue = isNumber$1(value) ? Number(value).toFixed(precision) : value;
676
+ let convertedValue = rawValue.replace(/[^0-9]/g, '');
677
+ if (!convertedValue) {
678
+ return '';
679
+ }
680
+ let integerPart = convertedValue.slice(0, convertedValue.length - precision).replace(/^0*/g, '').replace(/\B(?=(\d{3})+(?!\d))/g, thousands);
681
+ if (integerPart === '') {
682
+ integerPart = '0';
683
+ }
684
+ console.log('Before', {
685
+ convertedValue
686
+ });
687
+ if (align === 'right' && String(value).endsWith(' ')) {
688
+ convertedValue = convertedValue.slice(0, -1);
689
+ }
690
+ console.log('After', {
691
+ value,
692
+ convertedValue
693
+ });
694
+ let newRawValue = integerPart;
695
+ let decimalPart = convertedValue.slice(convertedValue.length - precision);
696
+ if (precision > 0) {
697
+ decimalPart = '0'.repeat(precision - decimalPart.length) + decimalPart;
698
+ newRawValue += decimal + decimalPart;
699
+ }
700
+ const symbol = getCurrencySymbol(prefix);
701
+ return align === 'left' ? `${symbol} ${newRawValue}` : `${newRawValue} ${symbol}`;
702
+ };
703
+ /**
704
+ * Applies a custom mask to a string based on the provided mask configuration.
705
+ *
706
+ * @param value - The value to be masked.
707
+ * @param masks - An object containing the mask configuration.
708
+ * @returns The masked value.
709
+ *
710
+ * @example
711
+ * ```typescript
712
+ * import { custom } from './path/to/maskFunctions';
713
+ *
714
+ * const masks = { custom: '##-##' };
715
+ *
716
+ * const maskedValue = custom('123456', masks);
717
+ * console.log(maskedValue); // Output: '12-34'
718
+ *
719
+ * // Using from a JSON config
720
+ * const config = {
721
+ * masks: { custom: '###-###' }
722
+ * };
723
+ *
724
+ * const maskedValue = custom(config.value, config.masks);
725
+ * console.log(maskedValue); // Output: '987-654'
726
+ * ```
727
+ */
728
+ const custom = (value, masks) => {
729
+ if (!masks.custom || !value) return value;
730
+ let mask = '';
731
+ let index = 0;
732
+ for (let i = 0; i < masks.custom.length; i++) {
733
+ if (masks.custom[i] === '#') {
734
+ if (index < value.length) {
735
+ mask += value[index];
736
+ index++;
737
+ } else {
738
+ break;
739
+ }
740
+ } else {
741
+ mask += masks.custom[i];
742
+ }
743
+ }
744
+ return mask;
745
+ };
746
+
747
+ /**
748
+ * Applies a secure mask to a credit card number, hiding most of its digits.
749
+ *
750
+ * @param value - The credit card number to be masked.
751
+ * @returns The masked credit card number.
752
+ *
753
+ * @example
754
+ * ```typescript
755
+ * import { secureCreditCard } from './path/to/creditCardFunctions';
756
+ *
757
+ * const maskedCardNumber = secureCreditCard('1234567890123456');
758
+ * console.log(maskedCardNumber); // Output: '1xxxxxxxxxxxx456'
759
+ * ```
760
+ */
761
+ const secureCreditCard = value => {
762
+ const maskValue = [{
763
+ from: 1,
764
+ to: 4,
765
+ mask: 'x'
766
+ }, {
767
+ from: 6,
768
+ to: 9,
769
+ mask: 'x'
770
+ }, {
771
+ from: 11,
772
+ to: 14,
773
+ mask: 'x'
774
+ }, {
775
+ from: 16,
776
+ to: 19,
777
+ mask: 'x'
778
+ }];
779
+ return generic(value, {
780
+ generic: maskValue
781
+ });
782
+ };
783
+ /**
784
+ * Formats a credit card number by inserting spaces every four characters.
785
+ *
786
+ * @param value - The credit card number to be formatted.
787
+ * @returns The formatted credit card number.
788
+ *
789
+ * @example
790
+ * ```typescript
791
+ * import { card } from './path/to/creditCardFunctions';
792
+ *
793
+ * const formattedCardNumber = card('1234567890123456');
794
+ * console.log(formattedCardNumber); // Output: '1234 5678 9012 3456'
795
+ * ```
796
+ */
797
+ const card = value => {
798
+ return value.replace(/[^\dA-Z]/g, '').replace(/(.{4})/g, '$1 ').trim();
799
+ };
800
+ /**
801
+ * Formats a credit card expiration date (MM/YY).
802
+ *
803
+ * @param value - The expiration date to be formatted (in MMYY or MM/YY format).
804
+ * @returns The formatted expiration date.
805
+ *
806
+ * @example
807
+ * ```typescript
808
+ * import { cardDate } from './path/to/creditCardFunctions';
809
+ *
810
+ * const formattedCardDate = cardDate('1223');
811
+ * console.log(formattedCardDate); // Output: '12/23'
812
+ * ```
813
+ */
814
+ const cardDate = value => formatDateCard(value);
815
+ /**
816
+ * Formats a credit card FEIN (Federal Employer Identification Number) in a specific format.
817
+ *
818
+ * @param value - The FEIN to be formatted.
819
+ * @returns The formatted FEIN.
820
+ *
821
+ * @example
822
+ * ```typescript
823
+ * import { fein } from './path/to/creditCardFunctions';
824
+ *
825
+ * const formattedFEIN = fein('123456789');
826
+ * console.log(formattedFEIN); // Output: '12-3456789'
827
+ * ```
828
+ */
829
+ const fein = value => formatDateCard(value, 9, '-');
830
+
831
+ const masks = {
832
+ currency,
833
+ custom,
834
+ callback: (value, masks) => (masks === null || masks === void 0 ? void 0 : masks.callback) && masks.callback(value),
835
+ generic,
836
+ secureCreditCard,
837
+ card,
838
+ cardDate,
839
+ fein,
840
+ replaceAll
841
+ };
842
+
843
+ /**
844
+ * Validates the length of a value based on the provided validation rules.
845
+ *
846
+ * @param value - The value to be validated. Can be of any type but will be converted to a string for length validation.
847
+ * @param validations - An object containing the length validation rules.
848
+ * @returns `true` if the value meets the length validation rule, otherwise `false`.
849
+ *
850
+ * @example
851
+ * ```typescript
852
+ * import validateLength from './path/to/validationFunctions';
853
+ *
854
+ * const validations = {
855
+ * length: {
856
+ * target: 5,
857
+ * rule: 'equal'
858
+ * }
859
+ * };
860
+ *
861
+ * const isValid = validateLength('hello', validations);
862
+ * console.log(isValid); // Output: true
863
+ *
864
+ * // Using from a JSON config
865
+ * const config = {
866
+ * inputValue: 'test',
867
+ * validations: {
868
+ * length: {
869
+ * target: 4,
870
+ * rule: 'equal'
871
+ * }
872
+ * }
873
+ * };
874
+ *
875
+ * const isValid = validateLength(config.inputValue, config.validations);
876
+ * console.log(isValid); // Output: true or false based on validation
877
+ * ```
878
+ */
879
+ var length = ((value, validations) => {
880
+ if (!validations.length || !value) return false;
881
+ let targetValue = value;
882
+ // We want length even if it is a numeric
883
+ if (typeof targetValue !== 'string') {
884
+ targetValue = (value === null || value === void 0 ? void 0 : value.toString()) || targetValue;
885
+ }
886
+ const condition = {
887
+ equal: targetValue.length !== validations.length.target,
888
+ notEqual: targetValue.length === validations.length.target,
889
+ less: targetValue.length >= validations.length.target,
890
+ lessOrEqual: targetValue.length > validations.length.target,
891
+ greater: targetValue.length <= validations.length.target,
892
+ greaterOrEqual: targetValue.length < validations.length.target
893
+ };
894
+ return condition[validations.length.rule];
895
+ });
896
+
897
+ /**
898
+ * Validates a Spanish NIF (Número de Identificación Fiscal).
899
+ *
900
+ * @param value - The NIF value to validate.
901
+ * @returns `true` if the NIF is invalid, otherwise `false`.
902
+ */
903
+ const NIF_ES = value => {
904
+ if (value.length !== 9) return true;
905
+ if (!/^\d{8}$/.test(value.slice(0, 8))) {
906
+ return true;
907
+ }
908
+ const validLetters = 'TRWAGMYFPDXBNJZSQVHLCKE';
909
+ const numberPart = parseInt(value.slice(0, 8), 10);
910
+ const expectedLetter = validLetters[numberPart % 23];
911
+ return !(value[8].toUpperCase() === expectedLetter);
912
+ };
913
+ /**
914
+ * Validates a Portuguese NIF (Número de Identificação Fiscal).
915
+ *
916
+ * @param value - The NIF value to validate.
917
+ * @returns `true` if the NIF is valid, otherwise `false`.
918
+ */
919
+ const NIF_PT = value => {
920
+ const regex = /^\d{9}$/;
921
+ if (!regex.test(value)) return false;
922
+ const checkDigit = parseInt(value[8], 10);
923
+ const sum = Array.from(value.substring(0, 8)).reduce((acc, digit, index) => acc + parseInt(digit, 10) * (9 - index), 0);
924
+ const calculatedCheckDigit = 11 - sum % 11;
925
+ return calculatedCheckDigit === 10 || calculatedCheckDigit === 11 ? 0 === checkDigit : calculatedCheckDigit === checkDigit;
926
+ };
927
+ /**
928
+ * Validates an Italian NIF (Codice Fiscale).
929
+ *
930
+ * @param value - The NIF value to validate.
931
+ * @returns `true` if the NIF is valid, otherwise `false`.
932
+ */
933
+ const NIF_IT = value => {
934
+ const regex = /^[A-Z]{6}\d{2}[A-Z]\d{2}[A-Z]\d{3}[A-Z]$/;
935
+ return regex.test(value);
936
+ };
937
+ /**
938
+ * Validates a German NIF (Steuerliche Identifikationsnummer).
939
+ *
940
+ * @param nif - The NIF value to validate.
941
+ * @returns `true` if the NIF is valid, otherwise `false`.
942
+ */
943
+ const NIF_DE = nif => {
944
+ const regex = /^\d{11}$/;
945
+ return regex.test(nif);
946
+ };
947
+ /**
948
+ * Validates a French NIF (Numéro d'Immatriculation Fiscale).
949
+ *
950
+ * @param nif - The NIF value to validate.
951
+ * @returns `true` if the NIF is valid, otherwise `false`.
952
+ */
953
+ const NIF_FR = nif => {
954
+ const regex = /^\d{2} \d{3} \d{3} \d{3} \d{3}$/;
955
+ return regex.test(nif);
956
+ };
957
+ /**
958
+ * Validates a UK NIF (National Insurance Number).
959
+ *
960
+ * @param value - The NIF value to validate.
961
+ * @returns `true` if the NIF is valid, otherwise `false`.
962
+ */
963
+ const NIF_UK = value => {
964
+ const regex = /^GB\d{9}|\d{12}|\d{3}[A-Z]{5}$/;
965
+ return regex.test(value);
966
+ };
967
+ /**
968
+ * Validates a Belgian NIF (Numéro d'Identification Fiscale).
969
+ *
970
+ * @param value - The NIF value to validate.
971
+ * @returns `true` if the NIF is valid, otherwise `false`.
972
+ */
973
+ const NIF_BE = value => {
974
+ const regex = /^\d{11}$/;
975
+ if (!regex.test(value)) return false;
976
+ const number = parseInt(value.substring(0, 9), 10);
977
+ const checkDigits = parseInt(value.substring(9), 10);
978
+ const mod = 97 - number % 97;
979
+ return mod === checkDigits;
980
+ };
981
+ /**
982
+ * Validates a Dutch NIF (Burgerservicenummer).
983
+ *
984
+ * @param value - The NIF value to validate.
985
+ * @returns `true` if the NIF is valid, otherwise `false`.
986
+ */
987
+ const NIF_NL = value => {
988
+ const regex = /^\d{9}$/;
989
+ if (!regex.test(value)) return false;
990
+ const total = value.split('').map((digit, index) => parseInt(digit, 10) * (9 - index)).reduce((sum, val) => sum + val, 0);
991
+ return total % 11 === 0;
992
+ };
993
+ /**
994
+ * Validates a Swedish NIF (Personnummer).
995
+ *
996
+ * @param value - The NIF value to validate.
997
+ * @returns `true` if the NIF is valid, otherwise `false`.
998
+ */
999
+ const NIF_SE = value => {
1000
+ const regex = /^\d{10,12}$/;
1001
+ if (!regex.test(value)) return false;
1002
+ const normalizedNIF = value.length === 12 ? value.substring(2) : value;
1003
+ const total = normalizedNIF.split('').map((digit, index) => {
1004
+ let num = parseInt(digit, 10) * (index % 2 === 0 ? 2 : 1);
1005
+ if (num > 9) num -= 9;
1006
+ return num;
1007
+ }).reduce((sum, val) => sum + val, 0);
1008
+ return total % 10 === 0;
1009
+ };
1010
+ /**
1011
+ * Validates a NIF based on the locale.
1012
+ *
1013
+ * @param nif - The NIF value to validate.
1014
+ * @param locale - The locale code to determine the type of NIF.
1015
+ * @returns `true` if the NIF is valid, otherwise `false`.
1016
+ *
1017
+ * @example
1018
+ * ```typescript
1019
+ * import { NIF } from './path/to/validationFunctions';
1020
+ *
1021
+ * const isValid = NIF('123456789', 'pt-PT');
1022
+ * console.log(isValid); // Output: true or false based on validation
1023
+ * ```
1024
+ */
1025
+ const NIF = (nif, locale) => {
1026
+ switch (locale) {
1027
+ case 'pt-PT':
1028
+ return NIF_PT(nif);
1029
+ case 'es-ES':
1030
+ return NIF_ES(nif);
1031
+ case 'it-IT':
1032
+ return NIF_IT(nif);
1033
+ case 'de-DE':
1034
+ return NIF_DE(nif);
1035
+ case 'fr-FR':
1036
+ return NIF_FR(nif);
1037
+ case 'en-GB':
1038
+ return NIF_UK(nif);
1039
+ case 'nl-BE':
1040
+ case 'fr-BE':
1041
+ return NIF_BE(nif);
1042
+ case 'nl-NL':
1043
+ return NIF_NL(nif);
1044
+ case 'sv-SE':
1045
+ // Suécia
1046
+ return NIF_SE(nif);
1047
+ default:
1048
+ throw new Error(`NIF validation not supported for locale: ${locale}`);
1049
+ }
1050
+ };
1051
+ /**
1052
+ * Validates a Spanish NIE (Número de Identificación de Extranjeros).
1053
+ *
1054
+ * @param value - The NIE value to validate.
1055
+ * @returns `true` if the NIE is invalid, otherwise `false`.
1056
+ */
1057
+ const NIE = value => {
1058
+ if (value.length !== 9) return true;
1059
+ const nieRegex = /^[XYZ]\d{7}[TRWAGMYFPDXBNJZSQVHLCKE]$/;
1060
+ if (!nieRegex.test(value)) {
1061
+ return true;
1062
+ }
1063
+ let fixedDocNumber = value.toUpperCase().padStart(9, '0');
1064
+ fixedDocNumber = fixedDocNumber[0].replace(/[XYZ]/g, char => {
1065
+ return {
1066
+ X: '0',
1067
+ Y: '1',
1068
+ Z: '2'
1069
+ }[char] || char;
1070
+ }) + fixedDocNumber.slice(1);
1071
+ return NIF_ES(fixedDocNumber);
1072
+ };
1073
+ const mod9710 = validationString => {
1074
+ while (validationString.length > 2) {
1075
+ const part = validationString.slice(0, 6);
1076
+ const partInt = parseInt(part, 10);
1077
+ if (isNaN(partInt)) {
1078
+ return NaN;
1079
+ }
1080
+ validationString = partInt % 97 + validationString.slice(part.length);
1081
+ }
1082
+ return parseInt(validationString, 10) % 97;
1083
+ };
1084
+ /**
1085
+ * Validates an IBAN (International Bank Account Number).
1086
+ *
1087
+ * @param value - The IBAN value to validate.
1088
+ * @returns `true` if the IBAN is invalid, otherwise `false`.
1089
+ */
1090
+ const IBAN = value => {
1091
+ const iban = value.replace(/\s/g, '');
1092
+ if (iban.length !== 24) return true;
1093
+ const ibanRegex = /^(ES)[0-9]{22}$/;
1094
+ if (!ibanRegex.test(iban)) {
1095
+ return true;
1096
+ }
1097
+ const ES_ALPHABET_NUMBER = 1428;
1098
+ const transformedIban = iban.substring(4) + ES_ALPHABET_NUMBER + iban.substring(2, 4);
1099
+ return mod9710(transformedIban) !== 1;
1100
+ };
1101
+ /**
1102
+ * Validates a Spanish CIF (Código de Identificación Fiscal).
1103
+ *
1104
+ * @param value - The CIF value to validate.
1105
+ * @returns `true` if the CIF is invalid, otherwise `false`.
1106
+ */
1107
+ const CIF = value => {
1108
+ const cifRegex = /^[A-Z][0-9]{7}[A-J0-9]$/;
1109
+ return !cifRegex.test(value);
1110
+ };
1111
+ /**
1112
+ * General validation function for various document types based on the provided validation methods.
1113
+ *
1114
+ * @param value - The document value to validate.
1115
+ * @param validations - An object containing validation methods.
1116
+ * @returns `true` if the document is valid, otherwise `false`.
1117
+ *
1118
+ * @example
1119
+ * ```typescript
1120
+ * import validateDocument from './path/to/validationFunctions';
1121
+ *
1122
+ * const validations = {
1123
+ * document: {
1124
+ * type: 'NIF',
1125
+ * locale: 'pt-PT'
1126
+ * }
1127
+ * };
1128
+ *
1129
+ * const isValid = validateDocument('123456789', validations);
1130
+ * console.log(isValid); // Output: true or false based on validation
1131
+ * ```
1132
+ */
1133
+ var document = ((value, validations) => {
1134
+ if (!value || !validations.document) return true;
1135
+ const validation = {
1136
+ NIF: (value, locale) => NIF(value, locale),
1137
+ NIE: value => NIE(value),
1138
+ CIF: value => CIF(value),
1139
+ IBAN: value => IBAN(value)
1140
+ };
1141
+ return validation[validations.document.type](value, validations.document.locale);
1142
+ });
1143
+
1144
+ /**
1145
+ * Validates if a value exceeds the maximum allowed value.
1146
+ *
1147
+ * @param value - The value to be checked.
1148
+ * @param validations - An object containing the maximum value for validation.
1149
+ * @returns `true` if the value exceeds the maximum, otherwise `false`.
1150
+ *
1151
+ * @example
1152
+ * ```typescript
1153
+ * import { max } from './path/to/validationFunctions';
1154
+ *
1155
+ * const validations = { max: 10 };
1156
+ *
1157
+ * const isValid = max(15, validations);
1158
+ * console.log(isValid); // Output: true
1159
+ *
1160
+ * // Using from a JSON config
1161
+ * const config = {
1162
+ * inputValue: '8',
1163
+ * validations: { max: 10 }
1164
+ * };
1165
+ *
1166
+ * const isValid = max(config.inputValue, config.validations);
1167
+ * console.log(isValid); // Output: false
1168
+ * ```
1169
+ */
1170
+ const max = (value, validations) => {
1171
+ const number = Number(value);
1172
+ if (!validations.max || Number.isNaN(number)) return false;
1173
+ return number > Number(validations.max);
1174
+ };
1175
+ /**
1176
+ * Validates if a value is less than the minimum allowed value.
1177
+ *
1178
+ * @param value - The value to be checked.
1179
+ * @param validations - An object containing the minimum value for validation.
1180
+ * @returns `true` if the value is less than the minimum, otherwise `false`.
1181
+ *
1182
+ * @example
1183
+ * ```typescript
1184
+ * import { min } from './path/to/validationFunctions';
1185
+ *
1186
+ * const validations = { min: 5 };
1187
+ *
1188
+ * const isValid = min(3, validations);
1189
+ * console.log(isValid); // Output: true
1190
+ *
1191
+ * // Using from a JSON config
1192
+ * const config = {
1193
+ * inputValue: '8',
1194
+ * validations: { min: 10 }
1195
+ * };
1196
+ *
1197
+ * const isValid = min(config.inputValue, config.validations);
1198
+ * console.log(isValid); // Output: true
1199
+ * ```
1200
+ */
1201
+ const min = (value, validations) => {
1202
+ const number = Number(value);
1203
+ if (!validations.min || Number.isNaN(number)) return false;
1204
+ return number < Number(validations.min);
1205
+ };
1206
+ /**
1207
+ * Validates if a value falls within a specified range.
1208
+ *
1209
+ * @param value - The value to be checked.
1210
+ * @param validations - An object containing the range (start and end) for validation.
1211
+ * @returns `true` if the value falls within the range, otherwise `false`.
1212
+ *
1213
+ * @example
1214
+ * ```typescript
1215
+ * import { between } from './path/to/validationFunctions';
1216
+ *
1217
+ * const validations = { between: { start: 5, end: 10 } };
1218
+ *
1219
+ * const isValid = between(7, validations);
1220
+ * console.log(isValid); // Output: true
1221
+ *
1222
+ * // Using from a JSON config
1223
+ * const config = {
1224
+ * inputValue: '4',
1225
+ * validations: { between: { start: 5, end: 10 } }
1226
+ * };
1227
+ *
1228
+ * const isValid = between(config.inputValue, config.validations);
1229
+ * console.log(isValid); // Output: false
1230
+ * ```
1231
+ */
1232
+ const between = (value, validations) => {
1233
+ if (!validations.between || !value) return false;
1234
+ const num = Number(value);
1235
+ return !Number.isNaN(num) && !(+num >= validations.between.start && +num <= validations.between.end);
1236
+ };
1237
+ /**
1238
+ * Validates if a value contains sequential numbers.
1239
+ *
1240
+ * @param value - The value to be checked.
1241
+ * @param validations - An object containing the validation methods.
1242
+ * @returns `true` if the value contains sequential numbers, otherwise `false`.
1243
+ *
1244
+ * @example
1245
+ * ```typescript
1246
+ * import { sequential } from './path/to/validationFunctions';
1247
+ *
1248
+ * const validations = { sequential: true };
1249
+ *
1250
+ * const isValid = sequential('12345', validations);
1251
+ * console.log(isValid); // Output: true
1252
+ *
1253
+ * // Using in a React component
1254
+ * const MyComponent = ({ inputValue }) => {
1255
+ * const isValid = sequential(inputValue, validations);
1256
+ * return <div>{isValid ? 'Valid' : 'Invalid'}</div>;
1257
+ * };
1258
+ *
1259
+ * // Using from a JSON config
1260
+ * const config = {
1261
+ * inputValue: '98765',
1262
+ * validations: { sequential: true }
1263
+ * };
1264
+ *
1265
+ * const isValid = sequential(config.inputValue, config.validations);
1266
+ * console.log(isValid); // Output: true
1267
+ * ```
1268
+ */
1269
+ const sequential = (value, validations) => {
1270
+ if (!validations.sequential) return false;
1271
+ const numbers = '0123456789';
1272
+ const numbersRev = '9876543210';
1273
+ const replacedValue = String(value).replace(/[^0-9]/g, '');
1274
+ return !(numbers.indexOf(replacedValue) === -1 && numbersRev.indexOf(replacedValue) === -1);
1275
+ };
1276
+
1277
+ /**
1278
+ * Validates if a value matches a given regular expression.
1279
+ *
1280
+ * @param value - The value to be checked.
1281
+ * @param validations - An object containing the regex pattern for validation.
1282
+ * @returns `true` if the value does not match the regex, otherwise `false`.
1283
+ *
1284
+ * @example
1285
+ * ```typescript
1286
+ * import { regex } from './path/to/validationFunctions';
1287
+ *
1288
+ * const validations = { regex: '^[a-zA-Z0-9]*$' };
1289
+ *
1290
+ * const isValid = regex('abc123', validations);
1291
+ * console.log(isValid); // Output: false
1292
+ *
1293
+ * // Using from a JSON config
1294
+ * const config = {
1295
+ * inputValue: 'abc123',
1296
+ * validations: { regex: '^[a-zA-Z0-9]*$' }
1297
+ * };
1298
+ *
1299
+ * const isValid = regex(config.inputValue, config.validations);
1300
+ * console.log(isValid); // Output: false
1301
+ * ```
1302
+ */
1303
+ const regex = (value, validations) => {
1304
+ if (!validations.regex || !value) return false;
1305
+ const regex = new RegExp(validations.regex);
1306
+ return !regex.test(value);
1307
+ };
1308
+ /**
1309
+ * Validates if a value is a valid email address.
1310
+ *
1311
+ * @param value - The value to be checked.
1312
+ * @param validations - An object containing the email validation flag.
1313
+ * @returns `true` if the value is not a valid email, otherwise `false`.
1314
+ *
1315
+ * @example
1316
+ * ```typescript
1317
+ * import { email } from './path/to/validationFunctions';
1318
+ *
1319
+ * const validations = { email: true };
1320
+ *
1321
+ * const isValid = email('test@example.com', validations);
1322
+ * console.log(isValid); // Output: false
1323
+ *
1324
+ * // Using from a JSON config
1325
+ * const config = {
1326
+ * inputValue: 'invalid-email',
1327
+ * validations: { email: true }
1328
+ * };
1329
+ *
1330
+ * const isValid = email(config.inputValue, config.validations);
1331
+ * console.log(isValid); // Output: true
1332
+ * ```
1333
+ */
1334
+ const email = (value, validations) => {
1335
+ if (!validations.email || !value) return false;
1336
+ const regex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
1337
+ return !regex.test(value);
1338
+ };
1339
+ /**
1340
+ * Validates if a value is a valid URL.
1341
+ *
1342
+ * @param value - The value to be checked.
1343
+ * @param validations - An object containing the URL validation flag.
1344
+ * @returns `true` if the value is not a valid URL, otherwise `false`.
1345
+ *
1346
+ * @example
1347
+ * ```typescript
1348
+ * import { url } from './path/to/validationFunctions';
1349
+ *
1350
+ * const validations = { url: true };
1351
+ *
1352
+ * const isValid = url('https://example.com', validations);
1353
+ * console.log(isValid); // Output: false
1354
+ *
1355
+ * // Using from a JSON config
1356
+ * const config = {
1357
+ * inputValue: 'invalid-url',
1358
+ * validations: { url: true }
1359
+ * };
1360
+ *
1361
+ * const isValid = url(config.inputValue, config.validations);
1362
+ * console.log(isValid); // Output: true
1363
+ * ```
1364
+ */
1365
+ const url = (value, validations) => {
1366
+ if (!validations.url || !value) return false;
1367
+ const regex = /[(http(s)?)://(www.)?a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/gi;
1368
+ return !regex.test(value);
1369
+ };
1370
+ /**
1371
+ * Validates if a value contains only letters.
1372
+ *
1373
+ * @param value - The value to be checked.
1374
+ * @param validations - An object containing the onlyLetters validation flag.
1375
+ * @returns `true` if the value contains non-letter characters, otherwise `false`.
1376
+ *
1377
+ * @example
1378
+ * ```typescript
1379
+ * import { onlyLetters } from './path/to/validationFunctions';
1380
+ *
1381
+ * const validations = { onlyLetters: true };
1382
+ *
1383
+ * const isValid = onlyLetters('abc', validations);
1384
+ * console.log(isValid); // Output: false
1385
+ *
1386
+ * // Using from a JSON config
1387
+ * const config = {
1388
+ * inputValue: '123',
1389
+ * validations: { onlyLetters: true }
1390
+ * };
1391
+ *
1392
+ * const isValid = onlyLetters(config.inputValue, config.validations);
1393
+ * console.log(isValid); // Output: true
1394
+ * ```
1395
+ */
1396
+ const onlyLetters = (value, validations) => {
1397
+ if (!validations.onlyLetters) return false;
1398
+ return !/^[\p{L}\s]*$/u.test(value);
1399
+ };
1400
+ /**
1401
+ * Validates if a value does not contain spaces.
1402
+ *
1403
+ * @param value - The value to be checked.
1404
+ * @param validations - An object containing the notAllowSpaces validation flag.
1405
+ * @returns `true` if the value contains spaces, otherwise `false`.
1406
+ *
1407
+ * @example
1408
+ * ```typescript
1409
+ * import { notAllowSpaces } from './path/to/validationFunctions';
1410
+ *
1411
+ * const validations = { notAllowSpaces: true };
1412
+ *
1413
+ * const isValid = notAllowSpaces('no spaces', validations);
1414
+ * console.log(isValid); // Output: true
1415
+ *
1416
+ * // Using from a JSON config
1417
+ * const config = {
1418
+ * inputValue: 'nospaces',
1419
+ * validations: { notAllowSpaces: true }
1420
+ * };
1421
+ *
1422
+ * const isValid = notAllowSpaces(config.inputValue, config.validations);
1423
+ * console.log(isValid); // Output: false
1424
+ * ```
1425
+ */
1426
+ const notAllowSpaces = (value, validations) => {
1427
+ if (!validations.notAllowSpaces) return false;
1428
+ return /\s/.test(value);
1429
+ };
1430
+ /**
1431
+ * Validates if a value is a number.
1432
+ *
1433
+ * @param value - The value to be checked.
1434
+ * @param validations - An object containing the isNumber validation flag.
1435
+ * @returns `true` if the value is not a number, otherwise `false`.
1436
+ *
1437
+ * @example
1438
+ * ```typescript
1439
+ * import { isNumber } from './path/to/validationFunctions';
1440
+ *
1441
+ * const validations = { isNumber: true };
1442
+ *
1443
+ * const isValid = isNumber('123', validations);
1444
+ * console.log(isValid); // Output: false
1445
+ *
1446
+ * // Using from a JSON config
1447
+ * const config = {
1448
+ * inputValue: 'abc',
1449
+ * validations: { isNumber: true }
1450
+ * };
1451
+ *
1452
+ * const isValid = isNumber(config.inputValue, config.validations);
1453
+ * console.log(isValid); // Output: true
1454
+ * ```
1455
+ */
1456
+ const isNumber = (value, validations) => {
1457
+ if (!validations.isNumber) return false;
1458
+ return !!value && !/^[0-9\s]*$/.test(value);
1459
+ };
1460
+ /**
1461
+ * Validates if a value has no trailing or leading spaces.
1462
+ *
1463
+ * @param value - The value to be checked.
1464
+ * @param validations - An object containing the hasNoExtraSpaces validation flag.
1465
+ * @returns `true` if the value has trailing or leading spaces, otherwise `false`.
1466
+ *
1467
+ * @example
1468
+ * ```typescript
1469
+ * import { hasNoExtraSpaces } from './path/to/validationFunctions';
1470
+ *
1471
+ * const validations = { hasNoExtraSpaces: true };
1472
+ *
1473
+ * const isValid = hasNoExtraSpaces('Hello', validations);
1474
+ * console.log(isValid); // Output: true
1475
+ *
1476
+ * // Using from a JSON config
1477
+ * const config = {
1478
+ * inputValue: ' Hello ',
1479
+ * validations: { hasNoExtraSpaces: true }
1480
+ * };
1481
+ *
1482
+ * const isValid = hasNoExtraSpaces(config.inputValue, config.validations);
1483
+ * console.log(isValid); // Output: false
1484
+ * ```
1485
+ */
1486
+ const hasNoExtraSpaces = (value, validations) => {
1487
+ if (!validations.hasNoExtraSpaces || !value) return false;
1488
+ return regex(value, {
1489
+ regex: '^[A-Za-zÀ-ÖØ-öø-ÿäöüÄÖÜß0-9]+$'
1490
+ });
1491
+ };
1492
+ /**
1493
+ * Validates if a value contains repeated digits.
1494
+ *
1495
+ * @param value - The value to be checked.
1496
+ * @param validations - An object containing the repeated validation flag.
1497
+ * @returns `true` if the value contains repeated digits, otherwise `false`.
1498
+ *
1499
+ * @example
1500
+ * ```typescript
1501
+ * import { repeated } from './path/to/validationFunctions';
1502
+ *
1503
+ * const validations = { repeated: true };
1504
+ *
1505
+ * const isValid = repeated('1231', validations);
1506
+ * console.log(isValid); // Output: true
1507
+ *
1508
+ * // Using from a JSON config
1509
+ * const config = {
1510
+ * inputValue: '1234',
1511
+ * validations: { repeated: true }
1512
+ * };
1513
+ *
1514
+ * const isValid = repeated(config.inputValue, config.validations);
1515
+ * console.log(isValid); // Output: false
1516
+ * ```
1517
+ */
1518
+ const repeated = (value, validations) => {
1519
+ if (!validations.repeated) return false;
1520
+ const numCount = {};
1521
+ for (const char of String(value)) {
1522
+ if (/\d/.test(char)) {
1523
+ if (numCount[char]) {
1524
+ return true;
1525
+ }
1526
+ numCount[char] = 1;
1527
+ }
1528
+ }
1529
+ return false;
1530
+ };
1531
+
1532
+ /**
1533
+ * Executes a custom callback validation function if provided.
1534
+ *
1535
+ * @param value - The value to be validated.
1536
+ * @param validations - An object containing validation methods, including a custom callback function.
1537
+ * @returns `true` if the custom callback validation function returns `true`, otherwise `false`.
1538
+ *
1539
+ * @example
1540
+ * ```typescript
1541
+ * import { callback } from './path/to/validationFunctions';
1542
+ *
1543
+ * const validations = {
1544
+ * callback: (value) => typeof value === 'string' && value.length > 5
1545
+ * };
1546
+ *
1547
+ * // Or from a JSON config
1548
+ * const schema = {
1549
+ * validations: {
1550
+ * callback: (value) => typeof value === 'string' && value.length > 5
1551
+ * }
1552
+ * };
1553
+ * ```
1554
+ */
1555
+ const callback = (value, validations) => {
1556
+ if (!validations.callback || !((validations === null || validations === void 0 ? void 0 : validations.callback) instanceof Function)) return false;
1557
+ return validations.callback(value);
1558
+ };
1559
+
1560
+ /**
1561
+ * Checks if a value is included in the specified array within the validation rules.
1562
+ *
1563
+ * @param value - The value to be checked.
1564
+ * @param validations - An object containing validation methods, including an array of allowed values.
1565
+ * @returns `true` if the value is included in the array, otherwise `false`.
1566
+ *
1567
+ * @example
1568
+ * ```typescript
1569
+ * import { includes } from './path/to/validationFunctions';
1570
+ *
1571
+ * const validations = {
1572
+ * includes: ['apple', 'banana', 'cherry']
1573
+ * };
1574
+ *
1575
+ * const isValid = includes('banana', validations);
1576
+ * console.log(isValid); // Output: true
1577
+ *
1578
+ * // Using from a JSON config
1579
+ * const config = {
1580
+ * inputValue: 'apple',
1581
+ * validations: {
1582
+ * includes: ['apple', 'banana', 'cherry']
1583
+ * }
1584
+ * };
1585
+ *
1586
+ * const isValid = includes(config.inputValue, config.validations);
1587
+ * console.log(isValid); // Output: true or false based on validation
1588
+ * ```
1589
+ */
1590
+ const includes = (value, validations) => {
1591
+ if (!value || !Array.isArray(validations === null || validations === void 0 ? void 0 : validations.includes)) return false;
1592
+ return !validations.includes.some(code => code === value || JSON.stringify(code) === value);
1593
+ };
1594
+
1595
+ /**
1596
+ * Validates if a given value is a valid credit card number based on predefined validation methods.
1597
+ *
1598
+ * @param value - The value to be validated as a credit card number.
1599
+ * @param validations - An object containing validation methods.
1600
+ * @returns `true` if the value is either falsy or does not match a valid credit card type, otherwise `false`.
1601
+ *
1602
+ * @example
1603
+ * ```typescript
1604
+ * import { isCreditCard } from './path/to/validationFunctions';
1605
+ * import { validationMethods } from './path/to/validationConfig';
1606
+ *
1607
+ * const isValid = isCreditCard('4111111111111111', validationMethods);
1608
+ * console.log(isValid); // Output: true or false based on validation
1609
+ * ```
1610
+ */
1611
+ const isCreditCard = (value, validations) => {
1612
+ if (!value) return true;
1613
+ const [type] = getTypeCard(String(value), validations.isCreditCard);
1614
+ return !type;
1615
+ };
1616
+ /**
1617
+ * Validates if a given security code matches the expected length for a specified credit card type.
1618
+ *
1619
+ * @param value - The security code to be validated.
1620
+ * @param validations - An object containing validation methods, specifically with `isCreditCodeMatch` details.
1621
+ * @returns `true` if the value is either falsy or if the validation methods are not provided. Otherwise, it checks if the security code length matches the expected length for the card type.
1622
+ *
1623
+ * @example
1624
+ * ```typescript
1625
+ * import { isCreditCodeMatch } from './path/to/validationFunctions';
1626
+ * import { validationMethods } from './path/to/validationConfig';
1627
+ *
1628
+ * const isValid = isCreditCodeMatch('123', validationMethods);
1629
+ * console.log(isValid); // Output: true or false based on validation
1630
+ * ```
1631
+ */
1632
+ const isCreditCodeMatch = (value, validations) => {
1633
+ var _a;
1634
+ if (!value || !validations.isCreditCodeMatch) return false;
1635
+ const [type] = getTypeCard(validations.isCreditCodeMatch.numberCard, validations.isCreditCodeMatch.availableOptions);
1636
+ return ((_a = type === null || type === void 0 ? void 0 : type.code) === null || _a === void 0 ? void 0 : _a.size) !== value.length;
1637
+ };
1638
+ /**
1639
+ * Validates if a given value is a valid credit card number and checks if its length matches the expected lengths for the card type.
1640
+ *
1641
+ * @param value - The credit card number to be validated.
1642
+ * @param validations - An object containing validation methods, specifically with `isCreditCardAndLength` details.
1643
+ * @returns `true` if the value is either falsy or if the card number does not match any valid lengths for the card type, otherwise `false`.
1644
+ *
1645
+ * @example
1646
+ * ```typescript
1647
+ * import { isCreditCardAndLength } from './path/to/validationFunctions';
1648
+ * import { validationMethods } from './path/to/validationConfig';
1649
+ *
1650
+ * const isValid = isCreditCardAndLength('4111111111111111', validationMethods);
1651
+ * console.log(isValid); // Output: true or false based on validation
1652
+ * ```
1653
+ */
1654
+ const isCreditCardAndLength = (value, validations) => {
1655
+ if (!value || !validations.isCreditCardAndLength) return false;
1656
+ const [type, rawValue] = getTypeCard(value, validations.isCreditCardAndLength);
1657
+ return type && !type.lengths.includes(rawValue.length);
1658
+ };
1659
+
1660
+ /**
1661
+ * Validates that a string value is not empty.
1662
+ *
1663
+ * @param {string} value - The value to be validated.
1664
+ * @param {TValidationMethods} validations - The validation methods object containing the notEmpty validation rule.
1665
+ * @returns {boolean} - Returns `true` if the value is empty and the notEmpty validation rule is set, otherwise `false`.
1666
+ *
1667
+ * @example
1668
+ * ```typescript
1669
+ * // Assume validations is an object with a notEmpty property
1670
+ * const validations = { notEmpty: true };
1671
+ *
1672
+ * // Returns true because the string is empty
1673
+ * const result1 = notEmpty('', validations);
1674
+ * console.log(result1); // true
1675
+ *
1676
+ * // Returns false because the string is not empty
1677
+ * const result2 = notEmpty('hello', validations);
1678
+ * console.log(result2); // false
1679
+ *
1680
+ * // Returns false because the notEmpty validation rule is not set
1681
+ * const result3 = notEmpty('', { notEmpty: false });
1682
+ * console.log(result3); // false
1683
+ * ```
1684
+ */
1685
+ const notEmpty = (value, validations) => {
1686
+ var _a;
1687
+ if (!(validations === null || validations === void 0 ? void 0 : validations.notEmpty) || typeof value === 'undefined') return false;
1688
+ return value === null || value === '' || value === 0 || !((_a = String(value)) === null || _a === void 0 ? void 0 : _a.trim().length);
1689
+ };
1690
+ /**
1691
+ * Validates that a value matches a specified value.
1692
+ *
1693
+ * @param {unknown} value - The value to be validated.
1694
+ * @param {TValidationMethods} validations - The validation methods object containing the value validation rule.
1695
+ * @returns {boolean} - Returns `true` if the value does not match the specified validation value, otherwise `false`.
1696
+ *
1697
+ * @example
1698
+ * ```typescript
1699
+ * // Assume validations is an object with a value property
1700
+ * const validations = { value: 42 };
1701
+ *
1702
+ * // Returns true because the value does not match the validation value
1703
+ * const result1 = value(10, validations);
1704
+ * console.log(result1); // true
1705
+ *
1706
+ * // Returns false because the value matches the validation value
1707
+ * const result2 = value(42, validations);
1708
+ * console.log(result2); // false
1709
+ *
1710
+ * // Returns false because the value validation rule is not set
1711
+ * const result3 = value(10, { value: undefined });
1712
+ * console.log(result3); // false
1713
+ * ```
1714
+ */
1715
+ const value = (value, validations) => {
1716
+ if (typeof (validations === null || validations === void 0 ? void 0 : validations.value) === 'undefined' || (validations === null || validations === void 0 ? void 0 : validations.value) === null || typeof value === 'undefined' || value === null) return false;
1717
+ return value != validations.value;
1718
+ };
1719
+
1720
+ /**
1721
+ * Validates that a value is required.
1722
+ *
1723
+ * @param {unknown} value - The value to be validated.
1724
+ * @param {TValidationMethods} validations - The validation methods object containing the required validation rule.
1725
+ * @returns {boolean} - Returns `true` if the value is required and is empty or not provided, otherwise `false`.
1726
+ *
1727
+ * @example
1728
+ * ```typescript
1729
+ * // Assume validations is an object with a required property
1730
+ * const validations = { required: true };
1731
+ *
1732
+ * // Returns true because the value is required and is empty
1733
+ * const result1 = required('', validations);
1734
+ * console.log(result1); // true
1735
+ *
1736
+ * // Returns false because the value is required and is provided
1737
+ * const result2 = required('text', validations);
1738
+ * console.log(result2); // false
1739
+ *
1740
+ * // Returns false because the required validation rule is not set
1741
+ * const result3 = required('', { required: false });
1742
+ * console.log(result3); // false
1743
+ * ```
1744
+ */
1745
+ const required = (value, validations) => !!(validations.required && (!value || typeof value === 'string' && value.trim().length === 0));
1746
+ /**
1747
+ * Validates that a value matches a boolean rule. Useful with iVars property.
1748
+ *
1749
+ * @param _
1750
+ * @param {TValidationMethods} validations - The validation methods object containing the boolean validation rule.
1751
+ * @returns {boolean} - Returns `true` if the boolean validation rule is set and fails, otherwise `false`.
1752
+ *
1753
+ * @example
1754
+ * ```typescript
1755
+ * // Assume validations is an object with a bool property
1756
+ * const validations = { bool: true };
1757
+ *
1758
+ * // Returns true because the boolean validation rule is set to fail
1759
+ * const result1 = bool(null, validations);
1760
+ * console.log(result1); // true
1761
+ *
1762
+ * // Returns false because the boolean validation rule is not set
1763
+ * const result2 = bool(null, { bool: false });
1764
+ * console.log(result2); // false
1765
+ * ```
1766
+ */
1767
+ const bool = (_, validations) => {
1768
+ if (!(validations === null || validations === void 0 ? void 0 : validations.bool)) return false;
1769
+ let fail = false;
1770
+ if (typeof validations.bool === 'boolean') {
1771
+ fail = validations.bool;
1772
+ }
1773
+ return fail;
1774
+ };
1775
+ /**
1776
+ * Validates that a value exists. Useful with iVars property.
1777
+ *
1778
+ * @param {unknown} value - The value to be validated.
1779
+ * @param {TValidationMethods} validations - The validation methods object containing the exists validation rule.
1780
+ * @returns {boolean} - Returns `true` if the value does not exist when the exists rule is set, otherwise `false`.
1781
+ *
1782
+ * @example
1783
+ * ```typescript
1784
+ * // Assume validations is an object with an exists property
1785
+ * const validations = { exists: true };
1786
+ *
1787
+ * // Returns true because the value does not exist
1788
+ * const result1 = exists(null, validations);
1789
+ * console.log(result1); // true
1790
+ *
1791
+ * // Returns false because the value exists
1792
+ * const result2 = exists('text', validations);
1793
+ * console.log(result2); // false
1794
+ *
1795
+ * // Returns false because the exists validation rule is not set
1796
+ * const result3 = exists(null, { exists: false });
1797
+ * console.log(result3); // false
1798
+ * ```
1799
+ */
1800
+ const exists = (value, validations) => {
1801
+ if (!validations.exists) return false;
1802
+ let fail = !validations.exists;
1803
+ if (!fail) {
1804
+ fail = !value;
1805
+ }
1806
+ return fail;
1807
+ };
1808
+
1809
+ /**
1810
+ * @internal
1811
+ * Evaluates a condition based on the provided rule and value.
1812
+ *
1813
+ * @param {number | string | boolean} value - The value to be used in the condition evaluation.
1814
+ * @param {TConditionsValidationSet} rule - The rule defining the condition to be evaluated.
1815
+ * @returns {boolean} - The result of the condition evaluation.
1816
+ *
1817
+ * @example
1818
+ * const rule = {
1819
+ * condition: '===',
1820
+ * origin: 5,
1821
+ * target: 5,
1822
+ * };
1823
+ * const result = conditionResult(5, rule);
1824
+ * console.log(result); // true (5 === 5)
1825
+ *
1826
+ * @example
1827
+ * const rule = {
1828
+ * condition: '!==',
1829
+ * origin: 5,
1830
+ * target: 10,
1831
+ * };
1832
+ * const result = conditionResult(5, rule);
1833
+ * console.log(result); // true (5 !== 10)
1834
+ *
1835
+ * @example
1836
+ * const rule = {
1837
+ * condition: '<',
1838
+ * origin: 5,
1839
+ * target: 10,
1840
+ * };
1841
+ * const result = conditionResult(5, rule);
1842
+ * console.log(result); // true (5 < 10)
1843
+ */
1844
+ const conditionResult = (value, rule) => {
1845
+ if (rule.forceDefinedOrigin && rule.origin === undefined || rule.forceDefinedTarget && rule.target === undefined) {
1846
+ return false;
1847
+ }
1848
+ const origin = rule.origin === undefined ? value : rule.origin;
1849
+ const target = rule.target === undefined ? value : rule.target;
1850
+ const conditionMapper = {
1851
+ '!==': (origin || value) !== (target || value),
1852
+ '===': (origin || value) === (target || value),
1853
+ '<': (origin || value) < (target || value),
1854
+ '>': (origin || value) > (target || value),
1855
+ '<=': (origin || value) <= (target || value),
1856
+ '>=': (origin || value) >= (target || value),
1857
+ '!!origin': !!String(origin || value).trim().length
1858
+ };
1859
+ return conditionMapper[rule.condition];
1860
+ };
1861
+ /**
1862
+ * Validates that a value meets specified conditions.
1863
+ *
1864
+ * @param {number | string | boolean} value - The value to be validated.
1865
+ * @param {TValidationMethods} validations - The validation methods object containing the conditions validation rule.
1866
+ * @returns {boolean} - Returns `true` if the value meets the specified conditions, otherwise `false`.
1867
+ *
1868
+ * @example
1869
+ * ```typescript
1870
+ * // Assume validations is an object with a conditions property
1871
+ * const validations = {
1872
+ * conditions: {
1873
+ * rule: 'and',
1874
+ * set: [
1875
+ * { condition: '===', origin: 10, target: 10 },
1876
+ * { condition: '!==', origin: 5, target: 3 }
1877
+ * ]
1878
+ * }
1879
+ * };
1880
+ *
1881
+ * // Returns true because both conditions are met
1882
+ * const result1 = conditions(10, validations);
1883
+ * console.log(result1); // true
1884
+ *
1885
+ * // Returns false because the second condition is not met
1886
+ * const result2 = conditions(5, validations);
1887
+ * console.log(result2); // false
1888
+ *
1889
+ * // Returns true because at least one condition is met with 'or' rule
1890
+ * const orValidations = {
1891
+ * conditions: {
1892
+ * rule: 'or',
1893
+ * set: [
1894
+ * { condition: '===', origin: 10, target: 5 },
1895
+ * { condition: '!==', origin: 5, target: 3 }
1896
+ * ]
1897
+ * }
1898
+ * };
1899
+ * const result3 = conditions(10, orValidations);
1900
+ * console.log(result3); // true
1901
+ * ```
1902
+ */
1903
+ const conditions = (value, validations) => {
1904
+ if (!validations.conditions) return false;
1905
+ const rulesMapper = {
1906
+ and: () => {
1907
+ var _a;
1908
+ return !!((_a = validations.conditions) === null || _a === void 0 ? void 0 : _a.set.every(validate => conditionResult(value, validate)));
1909
+ },
1910
+ or: () => {
1911
+ var _a;
1912
+ return !!((_a = validations.conditions) === null || _a === void 0 ? void 0 : _a.set.some(validate => conditionResult(value, validate)));
1913
+ }
1914
+ };
1915
+ let result = rulesMapper[validations.conditions.rule]();
1916
+ if (validations.conditions.conditions) {
1917
+ if (validations.conditions.rule === 'and') {
1918
+ result = result && conditions(value, {
1919
+ conditions: validations.conditions.conditions
1920
+ });
1921
+ } else {
1922
+ result = result || conditions(value, {
1923
+ conditions: validations.conditions.conditions
1924
+ });
1925
+ }
1926
+ }
1927
+ return !result;
1928
+ };
1929
+
1930
+ /**
1931
+ * Validates if a date value falls between two specified dates.
1932
+ *
1933
+ * @param {string} value - The date value to be validated in string format.
1934
+ * @param {TValidationMethods} validations - The validation methods object containing the betweenDates validation rules.
1935
+ * @returns {boolean} - Returns `true` if the date value fails the betweenDates validation, otherwise `false`.
1936
+ *
1937
+ * @example
1938
+ * ```typescript
1939
+ * const validations = {
1940
+ * betweenDates: [
1941
+ * { origin: { value: '2023-01-01', format: 'yyyy-MM-dd' }, operator: '>=' },
1942
+ * { origin: { value: '2023-12-31', format: 'yyyy-MM-dd' }, operator: '<=' }
1943
+ * ]
1944
+ * };
1945
+ *
1946
+ * const result1 = betweenDates('2023-06-01', validations);
1947
+ * console.log(result1); // false (date is within the range)
1948
+ *
1949
+ * const result2 = betweenDates('2024-01-01', validations);
1950
+ * console.log(result2); // true (date is outside the range)
1951
+ * ```
1952
+ */
1953
+ const betweenDates = (value, validations) => {
1954
+ var _a;
1955
+ let fail = false;
1956
+ if (((_a = validations.betweenDates) === null || _a === void 0 ? void 0 : _a.length) != 2) return false;
1957
+ for (const validation of validations.betweenDates) {
1958
+ if (date(value, {
1959
+ date: validation
1960
+ })) {
1961
+ fail = true;
1962
+ break;
1963
+ }
1964
+ }
1965
+ return fail;
1966
+ };
1967
+ /**
1968
+ * @internal
1969
+ * Adjusts a date by adding intervals of years, months, or days.
1970
+ *
1971
+ * @param {Date} date - The initial date.
1972
+ * @param {TDateInterval} intervals - An object specifying the intervals to add.
1973
+ * @returns {Date} - The adjusted date.
1974
+ *
1975
+ * @example
1976
+ * const initialDate = new Date('2023-01-01');
1977
+ * const intervals = { years: 1, months: 6, days: 15 };
1978
+ * const newDate = getIntervalsDate(initialDate, intervals);
1979
+ * console.log(newDate); // Expected date: '2024-07-16'
1980
+ */
1981
+ const getIntervalsDate = (date, intervals) => {
1982
+ const intervalsMapper = {
1983
+ years: (date, value) => new Date(date.setUTCFullYear(date.getUTCFullYear() + value)),
1984
+ months: (date, value) => new Date(date.setUTCMonth(date.getUTCMonth() + value)),
1985
+ days: (date, value) => new Date(date.setDate(date.getUTCDate() + value))
1986
+ };
1987
+ return Object.keys(intervals).reduce((acc, interval) => intervalsMapper[interval](acc, intervals[interval]), new Date(date));
1988
+ };
1989
+ /**
1990
+ * @internal
1991
+ * A mapper object for rearranging date strings into different formats.
1992
+ *
1993
+ * @type {Record<TDateFormatsValidation, (value: string) => string>}
1994
+ *
1995
+ * @example
1996
+ * const formattedDate1 = dateRearrangeMapper['DDMMYYYY']('01-12-2023');
1997
+ * console.log(formattedDate1); // '12/01/2023'
1998
+ *
1999
+ * const formattedDate2 = dateRearrangeMapper['YYYYMMDD']('2023-12-01');
2000
+ * console.log(formattedDate2); // '12/01/2023'
2001
+ */
2002
+ const dateRearrangeMapper = {
2003
+ DDMMYYYY: value => {
2004
+ const dateParts = value.split(value.includes('/') ? '/' : '-');
2005
+ return `${dateParts[1]}/${dateParts[0]}/${dateParts[2]}`;
2006
+ },
2007
+ YYYYMMDD: value => {
2008
+ const dateParts = value.split(value.includes('/') ? '/' : '-');
2009
+ return `${dateParts[1]}/${dateParts[2]}/${dateParts[0]}`;
2010
+ },
2011
+ YYYYDDMM: value => {
2012
+ const dateParts = value.split(value.includes('/') ? '/' : '-');
2013
+ return `${dateParts[2]}/${dateParts[1]}/${dateParts[0]}`;
2014
+ },
2015
+ MMDDYYYY: value => value,
2016
+ timestamp: value => new Date(value).toString()
2017
+ };
2018
+ /**
2019
+ * @function date
2020
+ * Validates a date value based on various date conditions and intervals.
2021
+ *
2022
+ * @param {string} value - The date value to be validated in string format.
2023
+ * @param {TValidationMethods} validations - The validation methods object containing the date validation rules.
2024
+ * @returns {boolean} - Returns `true` if the date validation fails, otherwise `false`.
2025
+ *
2026
+ * @example
2027
+ * ```typescript
2028
+ * const validations = {
2029
+ * date: {
2030
+ * origin: {
2031
+ * value: '2023-01-01',
2032
+ * format: 'YYYY-MM-DD'
2033
+ * },
2034
+ * target: {
2035
+ * value: '2023-12-31',
2036
+ * format: 'YYYY-MM-DD'
2037
+ * },
2038
+ * operator: '<=',
2039
+ * onlyValidDate: true
2040
+ * }
2041
+ * };
2042
+ *
2043
+ * const result1 = date('2023-06-01', validations);
2044
+ * console.log(result1); // false (date is within the range)
2045
+ *
2046
+ * const result2 = date('2024-01-01', validations);
2047
+ * console.log(result2); // true (date is outside the range)
2048
+ * ```
2049
+ */
2050
+ const date = (value, validations) => {
2051
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
2052
+ if (!((_b = (_a = validations.date) === null || _a === void 0 ? void 0 : _a.target) === null || _b === void 0 ? void 0 : _b.value) && !((_d = (_c = validations.date) === null || _c === void 0 ? void 0 : _c.origin) === null || _d === void 0 ? void 0 : _d.intervals)) {
2053
+ return false;
2054
+ }
2055
+ const originValue = validations.date.origin.value || value;
2056
+ let originDate = new Date(dateRearrangeMapper[(_e = validations.date) === null || _e === void 0 ? void 0 : _e.origin.format](originValue).toString());
2057
+ let targetDate = new Date();
2058
+ let target = new Date();
2059
+ if ((_g = (_f = validations.date) === null || _f === void 0 ? void 0 : _f.target) === null || _g === void 0 ? void 0 : _g.format) {
2060
+ target = new Date(dateRearrangeMapper[(_j = (_h = validations.date) === null || _h === void 0 ? void 0 : _h.target) === null || _j === void 0 ? void 0 : _j.format](validations.date.target.value).toString());
2061
+ targetDate = target;
2062
+ }
2063
+ if (validations.date.origin.intervals) {
2064
+ targetDate = getIntervalsDate(originDate, validations.date.origin.intervals);
2065
+ const date = new Date();
2066
+ if (((_k = validations.date.target) === null || _k === void 0 ? void 0 : _k.value) && target) {
2067
+ date.setDate(target.getDate());
2068
+ date.setMonth(target.getMonth());
2069
+ }
2070
+ originDate = new Date(`${date.getUTCMonth() + 1}/${date.getUTCDate()}/${date.getUTCFullYear()}`);
2071
+ }
2072
+ if (validations.date.onlyValidDate && (!(targetDate instanceof Date && isFinite(targetDate)) || !(targetDate instanceof Date && isFinite(originDate)) || originValue.length < 8)) {
2073
+ return true;
2074
+ }
2075
+ const originTimestamp = originDate.getTime();
2076
+ const targetTimestamp = targetDate.getTime();
2077
+ const operationsMapper = {
2078
+ '>': originTimestamp > targetTimestamp,
2079
+ '>=': originTimestamp >= targetTimestamp,
2080
+ '<': originTimestamp < targetTimestamp,
2081
+ '<=': originTimestamp <= targetTimestamp,
2082
+ '===': originTimestamp === targetTimestamp,
2083
+ '!==': originTimestamp !== targetTimestamp
2084
+ };
2085
+ return operationsMapper[(_l = validations.date) === null || _l === void 0 ? void 0 : _l.operator];
2086
+ };
2087
+ /**
2088
+ * @function validDate
2089
+ * Validates that a date string is a valid date according to the given format.
2090
+ *
2091
+ * @param {string} value - The date value to be validated in string format.
2092
+ * @param {TValidationMethods} validations - The validation methods object containing the validDate rule.
2093
+ * @returns {boolean} - Returns `true` if the date is not valid, otherwise `false`.
2094
+ *
2095
+ * @example
2096
+ * ```typescript
2097
+ * const validations = { validDate: 'MM-dd-yyyy' };
2098
+ *
2099
+ * const result1 = validDate('12-31-2023', validations);
2100
+ * console.log(result1); // false (date is valid)
2101
+ *
2102
+ * const result2 = validDate('02-30-2023', validations);
2103
+ * console.log(result2); // true (date is invalid)
2104
+ * ```
2105
+ */
2106
+ const validDate = (value, validations) => {
2107
+ if (!validations.validDate || !value) return false;
2108
+ if (/[^a-zA-Z0-9\s-]/.test(value)) return true;
2109
+ const dateParts = dateRearrangeMapper[validations.validDate](value).toString().split(/[/-]/);
2110
+ const year = parseInt(dateParts[2], 10);
2111
+ const month = parseInt(dateParts[0], 10) - 1; // Month is zero-based
2112
+ const day = parseInt(dateParts[1], 10);
2113
+ const date = new Date(year, month, day);
2114
+ // Check if the date is valid
2115
+ const isValidDate = date.getFullYear() === year && date.getMonth() === month && date.getDate() === day;
2116
+ return !isValidDate;
2117
+ };
2118
+
2119
+ /**
2120
+ * @internal
2121
+ * An object mapping validation keys to their respective validation functions.
2122
+ *
2123
+ * @type {Record<keyof TAvailableValidations, (value: unknown, validations: TAvailableValidations) => boolean>}
2124
+ *
2125
+ * @example
2126
+ * const isValid = validations.max(5, { max: 10 });
2127
+ * console.log(isValid); // false (5 is not greater than 10)
2128
+ */
2129
+ const validations$1 = {
2130
+ max,
2131
+ min,
2132
+ length,
2133
+ regex,
2134
+ url,
2135
+ email,
2136
+ onlyLetters,
2137
+ notAllowSpaces,
2138
+ callback,
2139
+ hasNoExtraSpaces,
2140
+ between,
2141
+ sequential,
2142
+ includes,
2143
+ repeated,
2144
+ document,
2145
+ isCreditCard,
2146
+ isCreditCodeMatch,
2147
+ isCreditCardAndLength,
2148
+ required,
2149
+ value,
2150
+ notEmpty,
2151
+ bool,
2152
+ exists,
2153
+ greaterThan: () => true,
2154
+ isNumber,
2155
+ conditions,
2156
+ validDate,
2157
+ date,
2158
+ betweenDates
2159
+ };
2160
+ /**
2161
+ * @internal
2162
+ * Runs a set of validation handlers against a given value.
2163
+ *
2164
+ * @param {unknown} value - The value to be validated.
2165
+ * @param {TValidationMethods} handlers - An object containing validation methods to be applied.
2166
+ * @returns {boolean[]} - An array of boolean results for each validation method.
2167
+ *
2168
+ * @example
2169
+ * const handlers = {
2170
+ * max: { max: 10 },
2171
+ * required: true,
2172
+ * email: true
2173
+ * };
2174
+ * const results = run('test@example.com', handlers);
2175
+ * console.log(results); // [false, false, true] (value fails 'max', passes 'required', passes 'email')
2176
+ */
2177
+ const run = (value, handlers) => {
2178
+ const runner = [];
2179
+ Object.keys(handlers).forEach(rule => {
2180
+ runner.push(validations$1[rule](value, {
2181
+ [rule]: handlers[rule]
2182
+ }));
2183
+ });
2184
+ return runner;
2185
+ };
2186
+ /**
2187
+ * Validates that a value meets multiple validation rules.
2188
+ *
2189
+ * @param {number | string | boolean} value - The value to be validated.
2190
+ * @param {TValidationMethods} validations - The validation methods object containing the multipleValidations rule set.
2191
+ * @returns {boolean} - Returns `true` if the value meets the specified multiple validation rules, otherwise `false`.
2192
+ *
2193
+ * @example
2194
+ * ```typescript
2195
+ * // Assume validations is an object with a multipleValidations property
2196
+ * const validations = {
2197
+ * multipleValidations: {
2198
+ * rule: 'AND',
2199
+ * validations: {
2200
+ * required: true,
2201
+ * isNumber: true
2202
+ * }
2203
+ * }
2204
+ * };
2205
+ *
2206
+ * // Returns true because both required and isNumber validations pass
2207
+ * const result1 = multipleValidations(123, validations);
2208
+ * console.log(result1); // true
2209
+ *
2210
+ * // Returns false because the value is not a number
2211
+ * const result2 = multipleValidations('abc', validations);
2212
+ * console.log(result2); // false
2213
+ * ```
2214
+ */
2215
+ const multipleValidations = (value, validations) => {
2216
+ if (!validations.multipleValidations) return false;
2217
+ const runner = run(value, validations.multipleValidations.validations);
2218
+ const rulesMapper = {
2219
+ AND: () => runner.every(validation => validation),
2220
+ OR: () => runner.some(validation => validation),
2221
+ NOT: () => !runner.every(validation => validation)
2222
+ };
2223
+ return rulesMapper[validations.multipleValidations.rule]();
2224
+ };
2225
+
2226
+ const validations = {
2227
+ max,
2228
+ min,
2229
+ length,
2230
+ regex,
2231
+ url,
2232
+ email,
2233
+ onlyLetters,
2234
+ notAllowSpaces,
2235
+ callback,
2236
+ hasNoExtraSpaces,
2237
+ between,
2238
+ sequential,
2239
+ includes,
2240
+ repeated,
2241
+ document,
2242
+ isCreditCard,
2243
+ isCreditCodeMatch,
2244
+ isCreditCardAndLength,
2245
+ required,
2246
+ value,
2247
+ notEmpty,
2248
+ bool,
2249
+ exists,
2250
+ greaterThan: () => true,
2251
+ isNumber,
2252
+ conditions,
2253
+ multipleValidations,
2254
+ date,
2255
+ betweenDates,
2256
+ validDate
2257
+ };
2258
+
2259
+ /**
2260
+ * Represents a form field with observables for managing form state, validations, and API requests.
2261
+ */
2262
+ class FormField {
2263
+ /**
2264
+ * Creates an instance of FormField.
2265
+ *
2266
+ * @param {object} options - Configuration options for the form field.
2267
+ * @param {IComponentSchema} options.schemaComponent - The schema definition for the form field.
2268
+ * @param {string} [options.path] - The path within the form field (used internally during recursion).
2269
+ * @param {string[]} options.children - An array of children fields names.
2270
+ * @param {Function} options.validateVisibility - A function to validate the visibility of the field.
2271
+ * @param {Function} options.resetValue - A function to reset the field value.
2272
+ * @param {unknown} [options.initialValue] - The initial value of the form field.
2273
+ * @param {Subject<{ key: string }>} options.templateSubject$ - A subject for template updates.
2274
+ */
2275
+ constructor({
2276
+ schemaComponent,
2277
+ path,
2278
+ children,
2279
+ validateVisibility,
2280
+ resetValue,
2281
+ initialValue,
2282
+ templateSubject$,
2283
+ apiResponseSubject$,
2284
+ dataSubject$,
2285
+ mapper
2286
+ }) {
2287
+ var _a, _b, _c, _d, _e, _f, _g;
2288
+ this.fieldStateSubscription$ = new Subscription();
2289
+ this.name = schemaComponent.name;
2290
+ this.component = schemaComponent.component;
2291
+ this.path = path;
2292
+ this.children = children;
2293
+ this.validations = schemaComponent.validations;
2294
+ this.errorMessages = schemaComponent.errorMessages;
2295
+ this.visibilityConditions = schemaComponent.visibilityConditions;
2296
+ this.resetValues = schemaComponent.resetValues;
2297
+ this.apiSchema = schemaComponent.api;
2298
+ this.formatters = schemaComponent.formatters;
2299
+ this.masks = schemaComponent.masks;
2300
+ if (mapper.valueChangeEvent) this.valueChangeEvent = mapper.valueChangeEvent;
2301
+ if ((_a = mapper.events) === null || _a === void 0 ? void 0 : _a.setValue) this.valuePropName = mapper.events.setValue;
2302
+ if ((_b = mapper.events) === null || _b === void 0 ? void 0 : _b.setErrorMessage) this.errorMessagePropName = mapper.events.setErrorMessage;
2303
+ this.mapper = mapper;
2304
+ this.validateVisibility = validateVisibility;
2305
+ this.resetValue = resetValue;
2306
+ this.templateSubject$ = templateSubject$;
2307
+ this.apiResponseSubject$ = apiResponseSubject$;
2308
+ this.dataSubject$ = dataSubject$;
2309
+ this._props = schemaComponent.props || {};
2310
+ this._value = '';
2311
+ this._stateValue = '';
2312
+ this._metadata = '';
2313
+ this.initialValue = initialValue;
2314
+ this._visibility = true;
2315
+ this._api = {
2316
+ default: {
2317
+ response: ((_e = (_d = (_c = this.apiSchema) === null || _c === void 0 ? void 0 : _c.defaultConfig) === null || _d === void 0 ? void 0 : _d.config) === null || _e === void 0 ? void 0 : _e.fallbackValue) || ''
2318
+ },
2319
+ named: ((_f = this.apiSchema) === null || _f === void 0 ? void 0 : _f.configs) && Object.keys((_g = this.apiSchema) === null || _g === void 0 ? void 0 : _g.configs).reduce((acc, curr) => {
2320
+ var _a, _b;
2321
+ acc[curr] = {
2322
+ response: ((_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.configs) === null || _b === void 0 ? void 0 : _b[curr].config.fallbackValue) || ''
2323
+ };
2324
+ return acc;
2325
+ }, {})
2326
+ };
2327
+ this._errorsString = '';
2328
+ this._valid = false;
2329
+ this.initializeObservers();
2330
+ }
2331
+ /**
2332
+ * method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
2333
+ */
2334
+ initializeObservers() {
2335
+ if (!this.valueSubject$ || this.valueSubject$.closed) {
2336
+ this.valueSubject$ = new Subject();
2337
+ }
2338
+ if (!this.errorSubject$ || this.errorSubject$.closed) {
2339
+ this.errorSubject$ = new Subject();
2340
+ }
2341
+ if (!this.visibilitySubject$ || this.visibilitySubject$.closed) {
2342
+ this.visibilitySubject$ = new Subject();
2343
+ }
2344
+ if (!this.apiSubject$ || this.apiSubject$.closed) {
2345
+ this.apiSubject$ = new Subject();
2346
+ }
2347
+ if (!this.propsSubject$ || this.propsSubject$.closed) {
2348
+ this.propsSubject$ = new Subject();
2349
+ }
2350
+ if (!this.fieldStateSubscription$ || this.fieldStateSubscription$.closed) {
2351
+ this.fieldStateSubscription$ = new Subscription();
2352
+ }
2353
+ if (!this.apiEventQueueSubject$ || this.apiEventQueueSubject$.closed) {
2354
+ this.apiEventQueueSubject$ = new Subject();
2355
+ }
2356
+ this.fieldState$ = combineLatest({
2357
+ errors: this.errorSubject$.pipe(startWith([])),
2358
+ visibility: this.visibilitySubject$.pipe(startWith(this._visibility)),
2359
+ apiResponse: this.apiSubject$.pipe(startWith(this._api)),
2360
+ props: this.propsSubject$.pipe(startWith(this._props))
2361
+ });
2362
+ !this.apiEventQueueSubject$.observed && this.apiEventQueueSubject$.pipe(this.debounceDistinct(({
2363
+ event
2364
+ }) => event, 1000)).subscribe(payload => {
2365
+ this.apiRequest(payload);
2366
+ });
2367
+ if (!isNil(this.initialValue)) {
2368
+ this.value = this.initialValue;
2369
+ }
2370
+ }
2371
+ /**
2372
+ * Observable function to emit api events debounced and distinct for each event type,
2373
+ * avoiding previous events being cancelled by new events if they occur inside the debounce time interval
2374
+ *
2375
+ * @param {(event: { event: TEvents }) => TEvents} keyExtractor function that will pass the event key to the groupBy operator
2376
+ * @param {number} debounceTimeMs time to wait for each individual event emmited
2377
+ * @returns
2378
+ */
2379
+ debounceDistinct(keyExtractor, debounceTimeMs) {
2380
+ return source$ => source$.pipe(groupBy(keyExtractor), mergeMap(group$ => group$.pipe(debounceTime(debounceTimeMs), map(() => ({
2381
+ event: group$.key
2382
+ })))));
2383
+ }
2384
+ /**
2385
+ * Retrieves the properties associated with the form field.
2386
+ *
2387
+ * @returns {Record<string, unknown>} - The properties of the form field.
2388
+ */
2389
+ get props() {
2390
+ return this._props;
2391
+ }
2392
+ /**
2393
+ * Sets the properties of the form field and notifies subscribers about the change.
2394
+ *
2395
+ * @param {Record<string, unknown>} props - The new properties to be set.
2396
+ */
2397
+ set props(props) {
2398
+ if (typeof props === 'undefined' || isEqual(props, this.props)) return;
2399
+ this._props = props;
2400
+ this.propsSubject$.next(this.props);
2401
+ this.templateSubject$.next({
2402
+ key: this.name,
2403
+ event: 'ON_PROPS'
2404
+ });
2405
+ }
2406
+ /**
2407
+ * Retrieves the current state value of the form field.
2408
+ *
2409
+ * @returns {unknown} - The current state value of the form field.
2410
+ */
2411
+ get stateValue() {
2412
+ return this._stateValue;
2413
+ }
2414
+ get metadata() {
2415
+ return this._metadata;
2416
+ }
2417
+ /**
2418
+ * Retrieves the concatenated string of errors associated with the form field.
2419
+ *
2420
+ * @returns {string} - The concatenated string of errors.
2421
+ */
2422
+ get errorsString() {
2423
+ return this._errorsString;
2424
+ }
2425
+ /**
2426
+ * Retrieves the current value of the form field.
2427
+ *
2428
+ * @returns {unknown} - The current value of the form field.
2429
+ */
2430
+ get value() {
2431
+ return this._value;
2432
+ }
2433
+ /**
2434
+ * Sets the value of the form field and notifies subscribers about the change.
2435
+ *
2436
+ * @param {unknown} value - The new value to be set.
2437
+ */
2438
+ set value(value) {
2439
+ /*
2440
+ too much unstable, if the valueChangeEvent parses the template event
2441
+ value, might occur unexpected results
2442
+ */
2443
+ let val;
2444
+ if (this.valueChangeEvent) {
2445
+ try {
2446
+ val = this.valueChangeEvent(value, {
2447
+ props: this.props
2448
+ });
2449
+ } catch (e) {
2450
+ val = value;
2451
+ }
2452
+ } else {
2453
+ val = value;
2454
+ }
2455
+ if (typeof val === 'undefined' || val === null) return;
2456
+ if (typeof val === 'object' && '_value' in val && '_metadata' in val) {
2457
+ this._value = this.formatValue(val['_value']);
2458
+ this._stateValue = this.maskValue(this.formatValue(val['_value']));
2459
+ this._metadata = val._metadata;
2460
+ } else {
2461
+ this._value = this.formatValue(val);
2462
+ this._stateValue = this.maskValue(this.formatValue(val));
2463
+ this._metadata = val;
2464
+ }
2465
+ /*
2466
+ update prop value attribute to sync with templating
2467
+ currently doesn't need prop Subject emission since it's synced with value
2468
+ to avoid excessive prop subject emissions on each keystroke
2469
+ */
2470
+ if (this.valuePropName) this._props = Object.assign(Object.assign({}, this.props), {
2471
+ [this.valuePropName]: this.value
2472
+ });
2473
+ this.valueSubject$.next(this._stateValue);
2474
+ this.templateSubject$.next({
2475
+ key: this.name,
2476
+ event: 'ON_VALUE'
2477
+ });
2478
+ }
2479
+ /**
2480
+ * Retrieves the visibility status of the form field.
2481
+ *
2482
+ * @returns {boolean} - The visibility status of the form field.
2483
+ */
2484
+ get visibility() {
2485
+ return this._visibility;
2486
+ }
2487
+ /**
2488
+ * Sets the visibility status of the form field and notifies subscribers about the change.
2489
+ *
2490
+ * @param {boolean} visible - The new visibility status to be set.
2491
+ */
2492
+ set visibility(visible) {
2493
+ if (typeof visible === 'undefined' || visible === this.visibility) return;
2494
+ this._visibility = visible;
2495
+ this.visibilitySubject$.next(this.visibility);
2496
+ this.templateSubject$.next({
2497
+ key: this.name,
2498
+ event: 'ON_VISIBILITY'
2499
+ });
2500
+ }
2501
+ /**
2502
+ * Retrieves the validity status of the form field.
2503
+ *
2504
+ * @returns {boolean} - The validity status of the form field.
2505
+ */
2506
+ get valid() {
2507
+ return this._valid;
2508
+ }
2509
+ /**
2510
+ * Retrieves the error messages associated with the form field.
2511
+ *
2512
+ * @returns {TErrorMessages} - The error messages associated with the form field.
2513
+ */
2514
+ get errors() {
2515
+ return this._errors;
2516
+ }
2517
+ /**
2518
+ * Sets the error messages associated with the form field and notifies subscribers about the change.
2519
+ *
2520
+ * @param {TErrorMessages} errors - The new error messages to be set.
2521
+ */
2522
+ set errors(errors) {
2523
+ if (typeof errors === 'undefined' || isEqual(errors, this.errors)) return;
2524
+ this._errors = errors;
2525
+ this._errorsString = Object.values(this.errors).join(', ');
2526
+ this.errorSubject$.next(Object.values(this.errors));
2527
+ this.templateSubject$.next({
2528
+ key: this.name,
2529
+ event: 'ON_PROPS'
2530
+ });
2531
+ }
2532
+ /**
2533
+ * Retrieves the API response data associated with the form field.
2534
+ *
2535
+ * @returns {TApiResponse} - The API response data associated with the form field.
2536
+ */
2537
+ get api() {
2538
+ return this._api;
2539
+ }
2540
+ /**
2541
+ * Sets the API response data associated with the form field and notifies subscribers about the change.
2542
+ *
2543
+ * @param {TApiResponse} response - The new API response data to be set.
2544
+ */
2545
+ set api(response) {
2546
+ if (typeof response === 'undefined' || isEqual(response, this.api)) return;
2547
+ this._api = response;
2548
+ this.apiSubject$.next(this.api);
2549
+ this.templateSubject$.next({
2550
+ key: this.name,
2551
+ event: 'ON_API'
2552
+ });
2553
+ // this.apiResponseSubject$.next({ key: this.name });
2554
+ this.emitEvents({
2555
+ event: 'ON_API_FIELD_RESPONSE'
2556
+ });
2557
+ }
2558
+ /**
2559
+ * Mounts the form field by initializing necessary subjects and combining their streams.
2560
+ *
2561
+ * @param {object} mountOpts - Adapter mount options.
2562
+ * @param {string} prop.valuePropName - Adapter value property name.
2563
+ * @param {(event: unknown) => unknown} prop.valueChangeEvent - Adapter change event handler function
2564
+ * @param {(value: unknown) => unknown} prop.valueSubscription - Adapter value change function
2565
+ * @param {(payload: Partial<IState>) => unknown} prop.propsSubscription - Adapter prop change function
2566
+ * @param {string} prop.errorMessagePropName - error message property name to set errors onto component
2567
+ * @returns {void}
2568
+ */
2569
+ mountField({
2570
+ valueSubscription,
2571
+ propsSubscription
2572
+ }) {
2573
+ this.initializeObservers();
2574
+ this.subscribeValue(valueSubscription);
2575
+ this.subscribeState(propsSubscription);
2576
+ }
2577
+ /**
2578
+ * Sets the value of the form field and emits associated events.
2579
+ *
2580
+ * @param {unknown} prop.value - The new value to be set.
2581
+ * @param {TEvents} prop.event - The event associated with setting the value.
2582
+ * @returns {void}
2583
+ */
2584
+ emitValue(prop) {
2585
+ this.value = prop.value;
2586
+ this.emitEvents({
2587
+ event: prop.event
2588
+ });
2589
+ this.dataSubject$.next({
2590
+ key: this.name,
2591
+ event: prop.event
2592
+ });
2593
+ }
2594
+ /**
2595
+ * Emits events to trigger field-related actions such as validation, visibility checks, value resets, and API requests.
2596
+ *
2597
+ * @param {TEvents} event - The event type that triggers the field actions.
2598
+ * @returns {void}
2599
+ */
2600
+ emitEvents({
2601
+ event
2602
+ }) {
2603
+ this.setFieldValidity({
2604
+ event
2605
+ });
2606
+ this.validateVisibility({
2607
+ event,
2608
+ key: this.name
2609
+ });
2610
+ this.resetValue({
2611
+ event,
2612
+ key: this.name
2613
+ });
2614
+ this.apiEventQueueSubject$.next({
2615
+ event
2616
+ });
2617
+ }
2618
+ /**
2619
+ * Sets the validity state of the field based on the provided validation rules and triggers error message updates.
2620
+ *
2621
+ * @param {TEvents} event - The event type associated with the field action.
2622
+ * @returns {void}
2623
+ */
2624
+ setFieldValidity({
2625
+ event
2626
+ }) {
2627
+ var _a;
2628
+ if (!this.validations) {
2629
+ this._valid = true;
2630
+ return;
2631
+ }
2632
+ /*
2633
+ @TODO evaluate if _valid flag needs to be updated on all events, this condition saves resources,
2634
+ currently form submition needs to be controlled with form instance submit property function that
2635
+ will evaluate if all fields are valid regardless the events that triggers error messages
2636
+ */
2637
+ if (!this.validations.events.includes(event) && event !== 'ON_FORM_SUBMIT') return;
2638
+ let valid = true;
2639
+ const errors = Object.assign({}, this.errors);
2640
+ const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.config;
2641
+ schemaValidations && Object.keys(schemaValidations).forEach(validationKey => {
2642
+ var _a, _b;
2643
+ const error = validations[validationKey](this.value, schemaValidations);
2644
+ // setting valid flag
2645
+ valid = !error && valid;
2646
+ // setting error messages
2647
+ if (((_a = this.validations) === null || _a === void 0 ? void 0 : _a.events.includes(event)) || event === 'ON_FORM_SUBMIT') {
2648
+ if (error && ((_b = this.errorMessages) === null || _b === void 0 ? void 0 : _b[validationKey])) {
2649
+ errors[validationKey] = this.errorMessages[validationKey];
2650
+ } else {
2651
+ delete errors[validationKey];
2652
+ }
2653
+ }
2654
+ });
2655
+ this._valid = valid;
2656
+ this.errors = errors;
2657
+ // remove later
2658
+ if (this.errorMessagePropName) this.props = Object.assign(Object.assign({}, this.props), {
2659
+ [this.errorMessagePropName]: this.errorsString
2660
+ });
2661
+ }
2662
+ /**
2663
+ * Formats the field value using the specified formatters, if available.
2664
+ *
2665
+ * @param {unknown} value - The value to be formatted.
2666
+ * @returns {unknown} - The formatted value.
2667
+ */
2668
+ formatValue(value) {
2669
+ if (this.formatters) {
2670
+ return Object.keys(this.formatters).reduce((acc, curr) => {
2671
+ return formatters[curr](acc, this.formatters);
2672
+ }, value);
2673
+ }
2674
+ return value;
2675
+ }
2676
+ /**
2677
+ * Masks the field value using the specified masks, if available.
2678
+ *
2679
+ * @param {unknown} value - The value to be masked.
2680
+ * @returns {unknown} - The masked value.
2681
+ */
2682
+ maskValue(value) {
2683
+ if (this.masks) {
2684
+ return Object.keys(this.masks).reduce((acc, curr) => {
2685
+ return masks[curr](acc, this.masks);
2686
+ }, value);
2687
+ }
2688
+ return value;
2689
+ }
2690
+ checkApiRequestValidations(config) {
2691
+ let valid = true;
2692
+ const preConditions = config.preConditions;
2693
+ preConditions && Object.keys(preConditions).forEach(validationKey => {
2694
+ const error = validations[validationKey](this.value, preConditions);
2695
+ valid = valid && !error;
2696
+ });
2697
+ if (config.blockRequestWhenInvalid) {
2698
+ valid = valid && this.valid;
2699
+ }
2700
+ return valid;
2701
+ }
2702
+ /**
2703
+ * Makes an API request based on the field's API configuration and event type, updating the field's API response data.
2704
+ *
2705
+ * @param {TEvents} event - The event type associated with the API request.
2706
+ * @returns {Promise<void>}
2707
+ */
2708
+ apiRequest({
2709
+ event
2710
+ }) {
2711
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
2712
+ return __awaiter(this, void 0, void 0, function* () {
2713
+ if (!((_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.defaultConfig) === null || _b === void 0 ? void 0 : _b.events.includes(event)) && !(((_c = this.apiSchema) === null || _c === void 0 ? void 0 : _c.configs) && Object.keys((_d = this.apiSchema) === null || _d === void 0 ? void 0 : _d.configs).some(key => {
2714
+ var _a, _b;
2715
+ return (_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.configs) === null || _b === void 0 ? void 0 : _b[key].events.includes(event);
2716
+ }))) return;
2717
+ const responses = {
2718
+ default: Object.assign({}, this.api.default),
2719
+ named: Object.assign({}, this.api.named)
2720
+ };
2721
+ const config = (_e = this.apiSchema.defaultConfig) === null || _e === void 0 ? void 0 : _e.config;
2722
+ if (config && ((_g = (_f = this.apiSchema) === null || _f === void 0 ? void 0 : _f.defaultConfig) === null || _g === void 0 ? void 0 : _g.events.includes(event)) && this.checkApiRequestValidations(config)) {
2723
+ try {
2724
+ const responseData = yield makeRequest(config.method, config.url, config.headers, config.body);
2725
+ const apiResponseData = JSON.parse(String(responseData));
2726
+ const response = config.resultPath ? get(apiResponseData, config.resultPath) : apiResponseData;
2727
+ // this.apiResponseData = { response };
2728
+ responses.default = {
2729
+ response
2730
+ };
2731
+ } catch (e) {
2732
+ responses.default = {
2733
+ response: !isNil(config === null || config === void 0 ? void 0 : config.fallbackValue) ? config.fallbackValue : 'error'
2734
+ };
2735
+ }
2736
+ }
2737
+ if (((_h = this.apiSchema) === null || _h === void 0 ? void 0 : _h.configs) && Object.keys((_j = this.apiSchema) === null || _j === void 0 ? void 0 : _j.configs).some(key => {
2738
+ var _a, _b;
2739
+ return (_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.configs) === null || _b === void 0 ? void 0 : _b[key].events.includes(event);
2740
+ })) {
2741
+ if (this.apiSchema.configs) {
2742
+ /*
2743
+ @TODO handle promises with error
2744
+ */
2745
+ const result = yield Promise.all(Object.keys(this.apiSchema.configs).map(configKey => __awaiter(this, void 0, void 0, function* () {
2746
+ return new Promise(res => {
2747
+ var _a, _b, _c, _d;
2748
+ const config = (_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.configs) === null || _b === void 0 ? void 0 : _b[configKey].config;
2749
+ if (config && ((_d = (_c = this.apiSchema) === null || _c === void 0 ? void 0 : _c.configs) === null || _d === void 0 ? void 0 : _d[configKey].events.includes(event)) && this.checkApiRequestValidations(config)) {
2750
+ try {
2751
+ makeRequest(config.method, config.url, config.headers, config.body).then(responseData => {
2752
+ const apiResponseData = JSON.parse(String(responseData));
2753
+ const response = get(apiResponseData, config.resultPath || '');
2754
+ res({
2755
+ name: configKey,
2756
+ result: {
2757
+ response
2758
+ }
2759
+ });
2760
+ // responses.named![configKey] = { response };
2761
+ });
2762
+ } catch (e) {
2763
+ res({
2764
+ name: configKey,
2765
+ result: {
2766
+ response: !isNil(config.fallbackValue) ? config.fallbackValue : 'error'
2767
+ }
2768
+ });
2769
+ }
2770
+ }
2771
+ });
2772
+ })));
2773
+ result.forEach(({
2774
+ name,
2775
+ result
2776
+ }) => {
2777
+ if (responses.named) responses.named[name] = result;
2778
+ });
2779
+ }
2780
+ }
2781
+ this.api = responses;
2782
+ });
2783
+ }
2784
+ /**
2785
+ * Unsubscribes from all subject subscriptions associated with the field, cleaning up resources.
2786
+ *
2787
+ * @returns {void}
2788
+ */
2789
+ destroyField() {
2790
+ this.valueSubject$.unsubscribe();
2791
+ this.visibilitySubject$.unsubscribe();
2792
+ this.fieldStateSubscription$.unsubscribe();
2793
+ this.propsSubject$.unsubscribe();
2794
+ this.errorSubject$.unsubscribe();
2795
+ this.apiSubject$.unsubscribe();
2796
+ this.apiEventQueueSubject$.unsubscribe();
2797
+ }
2798
+ /**
2799
+ * Subscribes to changes in the field state and executes the provided callback function.
2800
+ *
2801
+ * @param {Function} callback - The callback function to be executed when the field state changes.
2802
+ * @returns {void}
2803
+ */
2804
+ subscribeState(callback) {
2805
+ this.fieldStateSubscription$ = this.fieldState$.pipe(debounceTime(100)).subscribe({
2806
+ next: callback
2807
+ });
2808
+ }
2809
+ /**
2810
+ * Subscribes to changes in the field value and executes the provided callback function.
2811
+ *
2812
+ * @param {Function} callback - The callback function to be executed when the field value changes.
2813
+ * @returns {void}
2814
+ */
2815
+ subscribeValue(callback) {
2816
+ this.valueSubject$.subscribe({
2817
+ next: callback
2818
+ });
2819
+ }
2820
+ }
2821
+
2822
+ const IVARPROPNAME = 'iVars';
2823
+ /**
2824
+ * Represents the core logic for managing a form, including field management, validation, and submission.
2825
+ */
2826
+ class FormCore {
2827
+ /**
2828
+ * Creates an instance of FormCore.
2829
+ *
2830
+ * @param {TFormEntry & Omit<IFormSchema, 'components'>} entry - Configuration options for the form.
2831
+ * @param {IFormSchema} entry.schema - The schema definition for the form.
2832
+ * @param {Record<string, unknown> | IFormSchema.initialValues} [entry.initialValues] - Initial values for the form fields.
2833
+ * @param {string} [entry.action] - The action attribute of the form.
2834
+ * @param {string} [entry.method] - The method attribute of the form.
2835
+ * @param {IFormSchema.iVars} [entry.iVars] - The internal variables of the form.
2836
+ * @param {(data: TFormValues) => void} [entry.onSubmit] - A callback function to handle form submission.
2837
+ * @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
2838
+ */
2839
+ constructor(entry) {
2840
+ var _a, _b, _c, _d;
2841
+ this.schema = entry.schema;
2842
+ this.fields = new Map();
2843
+ this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
2844
+ this.action = entry.action || ((_b = entry.schema) === null || _b === void 0 ? void 0 : _b.action);
2845
+ this.method = entry.method || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.method);
2846
+ this._iVars = entry.iVars || ((_d = entry.schema) === null || _d === void 0 ? void 0 : _d.iVars) || {};
2847
+ this.onSubmit = entry.onSubmit;
2848
+ this.mappers = entry.mappers;
2849
+ this.schema && FormCore.checkIndexes(this.schema.components);
2850
+ this.templateSubject$ = new Subject();
2851
+ this.submitSubject$ = new Subject();
2852
+ this.apiResponseSubject$ = new Subject();
2853
+ this.dataSubject$ = new Subject();
2854
+ this.dataCallbackSubscription$ = new Subscription();
2855
+ this.subscribedTemplates = [];
2856
+ this.schema && this.serializeStructure(this.schema.components);
2857
+ this.schema && this.subscribeTemplates();
2858
+ this.templateSubject$.subscribe(this.refreshTemplates.bind(this));
2859
+ this.templateSubject$.next({
2860
+ key: IVARPROPNAME,
2861
+ event: 'ON_IVARS'
2862
+ });
2863
+ this.apiResponseSubject$.subscribe(this.refreshApi.bind(this));
2864
+ entry.onData && this.subscribeData(entry.onData);
2865
+ /*
2866
+ mount events needs to occur on form level, only when all the fields are instantiated
2867
+ is it possible to apply all the side effects that occur globally, same effect occur
2868
+ onto refreshFields
2869
+ */
2870
+ this.fields.forEach((field, key) => {
2871
+ field.emitEvents({
2872
+ event: 'ON_FIELD_MOUNT'
2873
+ });
2874
+ this.refreshTemplates({
2875
+ key,
2876
+ event: 'ON_FIELDS'
2877
+ });
2878
+ });
2879
+ }
2880
+ /**
2881
+ * Retrieves the internal variables (iVars) of the form.
2882
+ *
2883
+ * @returns {Record<string, unknown>} - The internal variables of the form.
2884
+ */
2885
+ get iVars() {
2886
+ return this._iVars;
2887
+ }
2888
+ /**
2889
+ * Sets the internal variables (iVars) of the form and notifies subscribers about the change.
2890
+ *
2891
+ * @param {Record<string, unknown>} payload - The new internal variables to be set.
2892
+ */
2893
+ set iVars(payload) {
2894
+ this._iVars = payload;
2895
+ this.templateSubject$.next({
2896
+ key: IVARPROPNAME,
2897
+ event: 'ON_IVARS'
2898
+ });
2899
+ }
2900
+ /**
2901
+ * Checks if the form is valid by validating all form fields.
2902
+ *
2903
+ * @returns {boolean} True if the form is valid; otherwise, false.
2904
+ */
2905
+ get isValid() {
2906
+ for (const [, field] of this.fields) {
2907
+ if (!field.valid) return false;
2908
+ }
2909
+ return true;
2910
+ }
2911
+ /**
2912
+ * Subscribes to templates for dynamic updates.
2913
+ */
2914
+ subscribeTemplates() {
2915
+ /*
2916
+ @TODO fix removal of templates of removed fields, they are kept
2917
+ tried: this.subscribedTemplates = [] and only stores the last one..
2918
+ */
2919
+ this.fields.forEach(({
2920
+ component,
2921
+ props,
2922
+ name,
2923
+ validations,
2924
+ visibilityConditions,
2925
+ resetValues,
2926
+ errorMessages,
2927
+ apiSchema,
2928
+ metadata
2929
+ }, key) => {
2930
+ const template = {
2931
+ component,
2932
+ props,
2933
+ name,
2934
+ validations,
2935
+ visibilityConditions,
2936
+ resetValues,
2937
+ errorMessages,
2938
+ apiSchema,
2939
+ metadata
2940
+ };
2941
+ traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
2942
+ });
2943
+ // console.log(subscribedProps);
2944
+ }
2945
+ /**
2946
+ *
2947
+ * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
2948
+ */
2949
+ subscribeData(callback) {
2950
+ this.dataCallbackSubscription$ = this.dataSubject$.pipe(debounceTime(100), map(({
2951
+ key
2952
+ }) => ({
2953
+ field: key,
2954
+ data: this.getFormValues()
2955
+ }))).subscribe({
2956
+ next: callback
2957
+ });
2958
+ }
2959
+ /**
2960
+ * Gets the value of a property from a field.
2961
+ *
2962
+ * @param {object} options - Options for getting the value.
2963
+ * @param {string} options.key - The key of the field.
2964
+ * @param {string} options.property - The property to retrieve.
2965
+ * @param {string[]} options.path - The path to the property if it's nested.
2966
+ * @returns {unknown | undefined} The value of the property, or undefined if the field doesn't exist.
2967
+ */
2968
+ getValue({
2969
+ key,
2970
+ property,
2971
+ path
2972
+ }) {
2973
+ if (key === IVARPROPNAME) {
2974
+ const value = get(this.iVars, [property, ...path]);
2975
+ return value;
2976
+ }
2977
+ if (!this.fields.has(key)) return console.warn(`failed to get value from ${key}`);
2978
+ return path.length > 0 ? get(this.fields.get(key)[property], path) : this.fields.get(key)[property];
2979
+ }
2980
+ /**
2981
+ * Sets the value of a property in a field.
2982
+ *
2983
+ * @param {object} options - Options for setting the value.
2984
+ * @param {string} options.key - The key of the field.
2985
+ * @param {string} options.property - The property to set.
2986
+ * @param {string[]} options.path - The path to the property if it's nested.
2987
+ * @param {unknown} options.originKey - field that called templating
2988
+ * @param {unknown} options.value - The value to set.
2989
+ * @param {TMutationEvents} options.event - Internal Event for template Handling.
2990
+ */
2991
+ setValue({
2992
+ key,
2993
+ property,
2994
+ path,
2995
+ originKey,
2996
+ value
2997
+ }) {
2998
+ const field = this.fields.get(key);
2999
+ if (!field) {
3000
+ console.warn(`failed to update field ${key}`);
3001
+ return;
3002
+ }
3003
+ if (path.length > 0) {
3004
+ /*
3005
+ property value is only allowed to be templated if the value isn't
3006
+ changed by itself, else a black hole opens
3007
+ previously using event !== 'ON_VALUE' on the condition
3008
+ now using key !== originKey, check if any recursion error occurs
3009
+ **/
3010
+ if (property === 'props' && path[0] === field.valuePropName && key !== originKey) {
3011
+ // field.value = value;
3012
+ field.emitValue({
3013
+ event: 'ON_FIELD_CHANGE',
3014
+ value
3015
+ });
3016
+ return;
3017
+ }
3018
+ const fieldProp = field[property];
3019
+ let propState;
3020
+ if (Array.isArray(fieldProp)) {
3021
+ propState = [...fieldProp];
3022
+ } else if (typeof fieldProp === 'object' && !isNil(fieldProp)) {
3023
+ propState = Object.assign({}, fieldProp);
3024
+ } else {
3025
+ console.warn(`invalid template property, skipping evaluation of ${field.name} with ${fieldProp}`);
3026
+ return;
3027
+ }
3028
+ set(propState, path, value);
3029
+ field[property] = propState;
3030
+ return;
3031
+ }
3032
+ field[property] = value;
3033
+ return;
3034
+ }
3035
+ /**
3036
+ * Extracts parameters from an expression.
3037
+ *
3038
+ * @param {string} expression - The expression containing parameters.
3039
+ * @returns {string[]} An array of extracted parameters.
3040
+ */
3041
+ extractParams(expression) {
3042
+ const regex = /\${(.*?)}/g;
3043
+ const extractedValues = [];
3044
+ let match;
3045
+ while (!isNil(match = regex.exec(expression))) {
3046
+ extractedValues.push(match[1]);
3047
+ }
3048
+ const operatorRegex = /\s*(\|\||&&|!)\s*/g;
3049
+ const splittedString = extractedValues.map(el => el.split(operatorRegex));
3050
+ const result = splittedString.map(splittedStringVal => {
3051
+ // console.log(splittedStringVal)
3052
+ return splittedStringVal.filter(Boolean).reduce((acc, curr) => {
3053
+ if (curr.match(/^\|\||&&|!$/)) {
3054
+ return `${acc}${curr}`;
3055
+ }
3056
+ let value;
3057
+ // check if element is in dot notation to get from field instances
3058
+ const element = curr.split('.');
3059
+ const currElementContent = element.length > 1 ? this.getValue({
3060
+ key: element[0],
3061
+ property: element[1],
3062
+ path: element.slice(2)
3063
+ }) : element;
3064
+ let currValue;
3065
+ // if any parsable content was passed to the conditions, parse them
3066
+ // required to be able to apply conditions
3067
+ try {
3068
+ currValue = JSON.parse(currElementContent);
3069
+ } catch (e) {
3070
+ currValue = currElementContent;
3071
+ }
3072
+ switch (typeof currValue) {
3073
+ case 'string':
3074
+ value = `\`${currValue}\``;
3075
+ break;
3076
+ case 'boolean':
3077
+ case 'undefined':
3078
+ case 'number':
3079
+ value = currValue;
3080
+ break;
3081
+ case 'object':
3082
+ if (currValue === null) {
3083
+ value = null;
3084
+ }
3085
+ value = JSON.stringify(currValue);
3086
+ break;
3087
+ default:
3088
+ value = currValue;
3089
+ }
3090
+ return `${acc}${value}`;
3091
+ }, '');
3092
+ });
3093
+ return result.map(el => {
3094
+ try {
3095
+ // console.log(el);
3096
+ return new Function(`return ${el}`)();
3097
+ } catch (e) {
3098
+ console.log(e);
3099
+ return 'lil error here.. :(';
3100
+ }
3101
+ });
3102
+ }
3103
+ /**
3104
+ * Replaces expressions marked by ${...} in the expression string with the provided values.
3105
+ *
3106
+ * @param {string} expression - The expression string containing the marked expressions.
3107
+ * @param {string[]} values - The values to be inserted into the marked expressions.
3108
+ * @returns {string} The expression string with the replacements made.
3109
+ */
3110
+ replaceExpression(expression, values) {
3111
+ const regex = /\${(.*?)}/g;
3112
+ return expression.replace(regex, () => values.shift() || '');
3113
+ }
3114
+ /**
3115
+ * Checks if an expression string contains string concatenation within a marked expression.
3116
+ *
3117
+ * @param {string} expression - The expression string to be checked.
3118
+ * @returns {boolean} True if the expression contains string concatenation, otherwise false.
3119
+ */
3120
+ hasStringConcatenation(expression) {
3121
+ const regex = /^\${[^${}]*}$/;
3122
+ return !regex.test(expression);
3123
+ }
3124
+ /**
3125
+ * Refreshes templates with updated values.
3126
+ *
3127
+ * @param {object} options - Options for refreshing templates.
3128
+ * @param {string} options.key - The key of the field triggering the update.
3129
+ * @param {TMutationEvents} options.event - Internal event descriptor to handle templating.
3130
+ */
3131
+ refreshTemplates({
3132
+ key,
3133
+ event
3134
+ }) {
3135
+ this.subscribedTemplates.forEach(({
3136
+ destinationKey,
3137
+ destinationPath,
3138
+ destinationProperty,
3139
+ originExpression,
3140
+ originFieldKeys
3141
+ }) => {
3142
+ if (originFieldKeys.includes(key)) {
3143
+ const originExpressions = this.extractParams(originExpression);
3144
+ let originValue;
3145
+ if (this.hasStringConcatenation(originExpression)) {
3146
+ originValue = this.replaceExpression(originExpression, [...originExpressions]);
3147
+ } else {
3148
+ originValue = originExpressions === null || originExpressions === void 0 ? void 0 : originExpressions[0];
3149
+ }
3150
+ const destinationValue = this.getValue({
3151
+ key: destinationKey,
3152
+ property: destinationProperty,
3153
+ path: destinationPath
3154
+ });
3155
+ if (!isEqual(destinationValue, originValue)) {
3156
+ this.setValue({
3157
+ key: destinationKey,
3158
+ property: destinationProperty,
3159
+ path: destinationPath,
3160
+ originKey: key,
3161
+ value: originValue,
3162
+ event
3163
+ });
3164
+ }
3165
+ }
3166
+ });
3167
+ }
3168
+ /**
3169
+ * Refreshes api observed fields.
3170
+ *
3171
+ * @param {object} options - Options for refreshing api.
3172
+ * @param {string} options.key - The key of the field triggering the update.
3173
+ */
3174
+ refreshApi({
3175
+ key
3176
+ }) {
3177
+ /*
3178
+ global api notifications needs to have field dependency array
3179
+ in order to be reliable, disabled for now
3180
+ */
3181
+ return key;
3182
+ // const emmittedFields: string[] = [];
3183
+ // this.subscribedTemplates.forEach((template) => {
3184
+ // if (
3185
+ // template.originFieldKeys.includes(key) &&
3186
+ // template.originPropertyKeys.includes('api') &&
3187
+ // !emmittedFields.includes(template.destinationKey)
3188
+ // ) {
3189
+ // emmittedFields.push(template.destinationKey);
3190
+ // this.fields
3191
+ // .get(template.destinationKey)
3192
+ // ?.emitEvents({ event: 'ON_API_RESPONSE' });
3193
+ // }
3194
+ // });
3195
+ }
3196
+ /**
3197
+ * Validates visibility conditions for a given event and updates field visibility accordingly.
3198
+ *
3199
+ * @param {object} options - Options for validating visibility.
3200
+ * @param {TEvents} options.event - The event triggering visibility validation.
3201
+ * @param {string} options.key - The key of the field.
3202
+ */
3203
+ validateVisibility({
3204
+ event,
3205
+ key
3206
+ }) {
3207
+ const field = this.fields.get(key);
3208
+ const structVisibility = field === null || field === void 0 ? void 0 : field.visibilityConditions;
3209
+ if (!structVisibility || !(structVisibility === null || structVisibility === void 0 ? void 0 : structVisibility.some(config => config.events.includes(event)))) return;
3210
+ structVisibility.forEach(structElement => {
3211
+ if (!structElement.events.includes(event)) return;
3212
+ Object.keys(structElement.validations).forEach(validationKey => {
3213
+ const error = validations[validationKey](field.value, structElement.validations);
3214
+ if (Array.isArray(structElement.fields)) {
3215
+ structElement.fields.forEach(fieldKey => {
3216
+ if (!this.fields.has(fieldKey)) console.warn(`failed to update visibility onto field ${fieldKey}`);else this.fields.get(fieldKey).visibility = error;
3217
+ });
3218
+ } else if (structElement.fields) {
3219
+ if (!this.fields.has(structElement.fields)) console.warn(`failed to update visibility onto field ${structElement.fields}`);else this.fields.get(structElement.fields).visibility = error;
3220
+ }
3221
+ });
3222
+ });
3223
+ }
3224
+ /**
3225
+ * Resets field values based on reset conditions defined in the schema.
3226
+ *
3227
+ * @param {object} options - Options for resetting field values.
3228
+ * @param {TEvents} options.event - The event triggering the reset.
3229
+ * @param {string} options.key - The key of the field.
3230
+ */
3231
+ resetValue({
3232
+ event,
3233
+ key
3234
+ }) {
3235
+ const field = this.fields.get(key);
3236
+ const structResetValue = field === null || field === void 0 ? void 0 : field.resetValues;
3237
+ if (!structResetValue || !(structResetValue === null || structResetValue === void 0 ? void 0 : structResetValue.some(config => config.events.includes(event)))) return;
3238
+ structResetValue.forEach(structElement => {
3239
+ if (!structElement.events.includes(event)) return;
3240
+ Object.keys(structElement.validations).forEach(validationKey => {
3241
+ const error = validations[validationKey](field.value, structElement.validations);
3242
+ if (!error) {
3243
+ if (Array.isArray(structElement.fields)) {
3244
+ structElement.fields.forEach((fieldKey, index) => {
3245
+ const resettledValue = Array.isArray(structElement.resettledValue) ? structElement.resettledValue[index] : structElement.resettledValue;
3246
+ if (!this.fields.has(fieldKey)) console.warn(`failed to reset value onto field ${fieldKey}`);else this.fields.get(fieldKey).emitValue({
3247
+ value: resettledValue,
3248
+ event: 'ON_FIELD_CHANGE'
3249
+ });
3250
+ });
3251
+ } else if (structElement.fields) {
3252
+ if (!this.fields.has(structElement.fields)) console.warn(`failed to reset value onto field ${structElement.fields}`);else this.fields.get(structElement.fields).emitValue({
3253
+ value: structElement.resettledValue,
3254
+ event: 'ON_FIELD_CHANGE'
3255
+ });
3256
+ }
3257
+ }
3258
+ });
3259
+ });
3260
+ }
3261
+ /**
3262
+ * Serializes the schema structure to create form fields.
3263
+ *
3264
+ * @param {IComponentSchema[]} [struct] - The schema structure to serialize.
3265
+ * @param {string} [path] - The path of the parent component.
3266
+ */
3267
+ serializeStructure(struct, path) {
3268
+ if (!struct) return;
3269
+ struct.forEach(structElement => {
3270
+ var _a, _b;
3271
+ const currField = this.fields.get(structElement.name);
3272
+ if (!currField) {
3273
+ const mapper = this.mappers.find(mapEl => mapEl.componentName === structElement.component);
3274
+ if (!mapper) throw new Error(`mapper not found for ${structElement.component}, add it to the mappers configuration`);
3275
+ this.fields.set(structElement.name, new FormField({
3276
+ schemaComponent: structElement,
3277
+ mapper,
3278
+ path,
3279
+ children: structElement.children ? structElement.children.map(el => el.name) : [],
3280
+ validateVisibility: this.validateVisibility.bind(this),
3281
+ resetValue: this.resetValue.bind(this),
3282
+ initialValue: (_a = this.initialValues) === null || _a === void 0 ? void 0 : _a[structElement.name],
3283
+ templateSubject$: this.templateSubject$,
3284
+ apiResponseSubject$: this.apiResponseSubject$,
3285
+ dataSubject$: this.dataSubject$
3286
+ }));
3287
+ } else {
3288
+ currField.children = ((_b = structElement === null || structElement === void 0 ? void 0 : structElement.children) === null || _b === void 0 ? void 0 : _b.map(el => el.name)) || (currField === null || currField === void 0 ? void 0 : currField.children) || [];
3289
+ currField.path = path;
3290
+ currField.templateSubject$ = this.templateSubject$;
3291
+ }
3292
+ if (structElement.children) {
3293
+ return this.serializeStructure(structElement.children, `${path ? `${path}.` : ``}${structElement.name}`);
3294
+ }
3295
+ });
3296
+ }
3297
+ /**
3298
+ * Refreshes form fields based on changes in the schema structure.
3299
+ *
3300
+ * @param {IComponentSchema[]} struct - The updated schema structure.
3301
+ */
3302
+ refreshFields(struct) {
3303
+ const prevKeys = Array.from(this.fields.keys());
3304
+ this.serializeStructure(struct);
3305
+ const keys = FormCore.checkIndexes(struct);
3306
+ this.fields.forEach((_, key) => {
3307
+ var _a;
3308
+ if (!keys.includes(key)) {
3309
+ (_a = this.fields.get(key)) === null || _a === void 0 ? void 0 : _a.destroyField();
3310
+ this.fields.delete(key);
3311
+ }
3312
+ });
3313
+ this.subscribeTemplates();
3314
+ this.fields.forEach((_, key) => {
3315
+ var _a;
3316
+ if (!prevKeys.includes(key)) {
3317
+ (_a = this.fields.get(key)) === null || _a === void 0 ? void 0 : _a.emitEvents({
3318
+ event: 'ON_FIELD_MOUNT'
3319
+ });
3320
+ }
3321
+ });
3322
+ this.subscribedTemplates.forEach(el => {
3323
+ el.originFieldKeys.forEach(field => {
3324
+ this.templateSubject$.next({
3325
+ key: field,
3326
+ event: 'ON_FIELDS'
3327
+ });
3328
+ });
3329
+ });
3330
+ }
3331
+ /**
3332
+ * Gets a form field by its key.
3333
+ *
3334
+ * @param {object} options - Options for getting the form field.
3335
+ * @param {string} options.key - The key of the form field.
3336
+ * @returns {IFormField | undefined} The form field, or undefined if not found.
3337
+ */
3338
+ getField({
3339
+ key
3340
+ }) {
3341
+ return this.fields.get(key);
3342
+ }
3343
+ /**
3344
+ * Prints the current values of all form fields.
3345
+ */
3346
+ printValues() {
3347
+ const values = {};
3348
+ this.fields.forEach((val, key) => {
3349
+ if (val.value) {
3350
+ values[key] = val.value;
3351
+ }
3352
+ });
3353
+ console.log(values);
3354
+ }
3355
+ /**
3356
+ * Gets the current values of all form fields.
3357
+ *
3358
+ * @returns {TFormValues} The current form values.
3359
+ */
3360
+ getFormValues() {
3361
+ const values = {};
3362
+ const erroredFields = [];
3363
+ this.fields.forEach((val, key) => {
3364
+ if (val.value) {
3365
+ values[key] = val.value;
3366
+ }
3367
+ if (!val.valid) {
3368
+ erroredFields.push(key);
3369
+ }
3370
+ });
3371
+ return {
3372
+ values,
3373
+ erroredFields,
3374
+ isValid: this.isValid
3375
+ };
3376
+ }
3377
+ /**
3378
+ * Submits the form by triggering form field events and invoking the onSubmit callback.
3379
+ */
3380
+ submit() {
3381
+ this.fields.forEach(field => {
3382
+ field.emitEvents({
3383
+ event: 'ON_FORM_SUBMIT'
3384
+ });
3385
+ });
3386
+ if (!this.isValid) return;
3387
+ const values = this.getFormValues();
3388
+ this.submitSubject$.next(values);
3389
+ this.onSubmit && this.onSubmit(values);
3390
+ }
3391
+ destroy() {
3392
+ this.submitSubject$.unsubscribe();
3393
+ this.templateSubject$.unsubscribe();
3394
+ this.apiResponseSubject$.unsubscribe();
3395
+ this.dataSubject$.unsubscribe();
3396
+ }
3397
+ }
3398
+ /**
3399
+ * Validates and collects the names of form fields in the provided schema structure.
3400
+ *
3401
+ * @param {IComponentSchema[]} [struct] - The schema structure of the form components.
3402
+ * @param {string[]} [indexes=[]] - An array to collect the names of the form fields.
3403
+ * @returns {string[]} - An array of form field names.
3404
+ * @throws {Error} - Throws an error if a field name matches the reserved name defined by `IVARPROPNAME`.
3405
+ * @private
3406
+ */
3407
+ FormCore.checkIndexes = (struct, indexes = []) => {
3408
+ if (!struct) return indexes;
3409
+ for (let i = 0; i < struct.length; i++) {
3410
+ const structElement = struct[i];
3411
+ if (structElement.name === IVARPROPNAME) {
3412
+ throw new Error(`reserved ${IVARPROPNAME} name for field names`);
3413
+ }
3414
+ indexes.push(structElement.name);
3415
+ if (structElement.children) {
3416
+ return FormCore.checkIndexes(structElement.children, indexes);
3417
+ }
3418
+ }
3419
+ return indexes;
3420
+ };
3421
+
3422
+ /**
3423
+ * Represents a group that manages multiple forms.
3424
+ */
3425
+ class FormGroup {
3426
+ /**
3427
+ * Creates an instance of FormGroup.
3428
+ */
3429
+ constructor() {
3430
+ this.forms = new Map();
3431
+ }
3432
+ /**
3433
+ * Adds a form instance to the form group.
3434
+ *
3435
+ * @param {object} options - Options for adding a form.
3436
+ * @param {string} options.key - The key associated with the form instance.
3437
+ * @param {TFormCore} options.formInstance - The instance of the form to add.
3438
+ */
3439
+ addForm({
3440
+ key,
3441
+ formInstance
3442
+ }) {
3443
+ this.checkIndexes({
3444
+ key
3445
+ });
3446
+ this.forms.set(key, formInstance);
3447
+ }
3448
+ /**
3449
+ * Retrieves a form instance from the form group.
3450
+ *
3451
+ * @param {object} options - Options for retrieving a form.
3452
+ * @param {string} options.key - The key associated with the form instance.
3453
+ * @returns {TFormCore | undefined} The instance of the form, if found; otherwise, undefined.
3454
+ */
3455
+ getForm({
3456
+ key
3457
+ }) {
3458
+ return this.forms.get(key);
3459
+ }
3460
+ /**
3461
+ * Removes a form instance from the form group.
3462
+ *
3463
+ * @param {object} options - Options for removing a form.
3464
+ * @param {string} options.key - The key associated with the form instance to remove.
3465
+ */
3466
+ removeForm({
3467
+ key
3468
+ }) {
3469
+ var _a;
3470
+ //@TODO logic to unsubscribe all form related
3471
+ (_a = this.forms.get(key)) === null || _a === void 0 ? void 0 : _a.destroy();
3472
+ this.forms.delete(key);
3473
+ }
3474
+ /**
3475
+ * Checks if the specified key already exists in the form group.
3476
+ *
3477
+ * @param {object} options - Options for checking the key.
3478
+ * @param {string} options.key - The key to check.
3479
+ * @throws {Error} Throws an error if the key already exists in the form group.
3480
+ */
3481
+ checkIndexes({
3482
+ key
3483
+ }) {
3484
+ if (this.forms.has(key)) {
3485
+ throw new Error(`duplicate index ${key} on form group`);
3486
+ }
3487
+ }
3488
+ /**
3489
+ * Prints the form group instance to the console.
3490
+ */
3491
+ printFormGroupInstance() {
3492
+ console.log(this.forms);
3493
+ }
3494
+ /**
3495
+ * Prototype submit function to multiple forms
3496
+ * @param {string[]} indexes form indexes to be submitted
3497
+ * @returns
3498
+ */
3499
+ submitMultipleFormsByIndex(indexes) {
3500
+ let isValid = true;
3501
+ let values = {};
3502
+ let erroredFields = [];
3503
+ indexes.forEach(index => {
3504
+ var _a;
3505
+ const res = (_a = this.forms.get(index)) === null || _a === void 0 ? void 0 : _a.getFormValues();
3506
+ isValid = isValid && ((res === null || res === void 0 ? void 0 : res.isValid) || false);
3507
+ values = Object.assign(Object.assign({}, values), (res === null || res === void 0 ? void 0 : res.values) || {});
3508
+ erroredFields = [...erroredFields, ...((res === null || res === void 0 ? void 0 : res.erroredFields) || [])];
3509
+ });
3510
+ return {
3511
+ erroredFields,
3512
+ isValid,
3513
+ values
3514
+ };
3515
+ }
3516
+ }
3517
+
3518
+ export { FormCore, FormField, FormGroup, TMutationEnum };