@bolttech/form-engine-core 0.0.1-beta.9 → 0.0.2-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.
package/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
- import { Subscription, Subject, combineLatest, startWith, groupBy, mergeMap, debounceTime, filter, map } from 'rxjs';
1
+ import { Subject, Subscription, combineLatest, startWith, groupBy, mergeMap, debounceTime, filter, map } from 'rxjs';
2
2
  import creditCardType from 'credit-card-type';
3
- import { isNumber as isNumber$1, isNil, isEqual, get, set } from 'lodash';
3
+ import { isNumber as isNumber$1, isFunction, cloneDeep, isEqual, get, isNil, set } from 'lodash';
4
4
  import { getCurrencySymbol } from '@gaignoux/currency';
5
5
 
6
6
  var TMutationEnum;
@@ -44,6 +44,14 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
44
44
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
45
45
  };
46
46
 
47
+ const DEFAULT_API_DEBOUNCE_TIME = 1000;
48
+ const DEFAULT_STATE_REFRESH_TIME = 100;
49
+ const TEMPLATE_REGEX_STRING_CONCATENATION_DETECTOR = /^\$\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}$/;
50
+ const TEMPLATE_REGEX_DELIMITATOR = /\$\{((?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*)\}/g;
51
+ const TEMPLATE_REGEX_OPERATOR_SPLITTER = /\s*(\|\||&&|!)\s*/g;
52
+ const TEMPLATE_REGEX_OPERATOR_MATCHER = /^\|\||&&|!$/;
53
+ const TEMPLATE_AVALIABLE_SCOPES = ['fields', 'iVars'];
54
+
47
55
  /**
48
56
  * Makes an HTTP request using XMLHttpRequest.
49
57
  *
@@ -96,15 +104,18 @@ function makeRequest(method, url, headers, body) {
96
104
  * ```
97
105
  */
98
106
  function extractFieldKeys(expression) {
99
- const regex = /\${(.*?)}/g;
107
+ const regex = TEMPLATE_REGEX_DELIMITATOR;
100
108
  const extractedValues = [];
101
109
  let match;
102
110
  while ((match = regex.exec(expression)) !== null) {
103
111
  extractedValues.push(match[1]);
104
112
  }
113
+ const operatorRegex = TEMPLATE_REGEX_OPERATOR_SPLITTER;
114
+ const splittedString = extractedValues.map(el => el.split(operatorRegex).filter(item => !operatorRegex.test(item))).flat().filter(el => el.split('.').length > 1);
105
115
  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])))
116
+ originScopeKeys: Array.from(new Set(splittedString.map(el => el.split('.')[0]))),
117
+ originFieldKeys: Array.from(new Set(splittedString.map(el => el.split('.')[1]))),
118
+ originPropertyKeys: Array.from(new Set(splittedString.map(el => el.split('.')[2])))
108
119
  };
109
120
  }
110
121
  /**
@@ -132,95 +143,40 @@ function extractFieldKeys(expression) {
132
143
  * // expressions will contain an array of objects with origin expressions, field keys, and destination paths.
133
144
  * ```
134
145
  */
135
- function traverseObject(obj, path) {
146
+ function traverseObject(element, path) {
136
147
  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') {
148
+ if (Array.isArray(element)) {
149
+ element.forEach((item, index) => {
150
+ result.push(...traverseObject(item, `${path ? `${path}.` : ``}${index}`));
151
+ });
152
+ } else if (typeof element === 'object') {
153
+ for (const key in element) {
154
+ if (Object.prototype.hasOwnProperty.call(element, key)) {
155
+ const value = element[key];
159
156
  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
- }
157
+ }
158
+ }
159
+ } else if (typeof element === 'string') {
160
+ if (element.includes('${')) {
161
+ // const extractedPath = value.replace(/\$|{|}/g, '').split('.');
162
+ const destinationPath = (path ? path : '').split('.');
163
+ // if(destinationPath[0])
164
+ const originfieldKeys = extractFieldKeys(element);
165
+ if (!originfieldKeys.originScopeKeys.every(scopeKey => TEMPLATE_AVALIABLE_SCOPES.includes(scopeKey))) {
166
+ console.warn(`scope malformed on this expression: ${element}, ignoring..`);
167
+ } else {
168
+ result.push(Object.assign(Object.assign({
169
+ originExpression: element
170
+ }, extractFieldKeys(element)), {
171
+ destinationKey: destinationPath[0],
172
+ destinationProperty: destinationPath[1],
173
+ destinationPath: destinationPath.slice(2)
174
+ }));
172
175
  }
173
176
  }
174
177
  }
175
178
  return result;
176
179
  }
177
- /**
178
- * Recursively creates a nested object structure based on the given parts array,
179
- * setting the specified value at the appropriate nested level.
180
- *
181
- * @param {string[]} parts - Array of strings representing the keys for the nested object.
182
- * @param {Record<string, unknown>} subObject - The sub-object to be updated or used for recursion.
183
- * @param {unknown} value - The value to be set at the final nested level.
184
- * @returns {Record<string, unknown>} - The updated object with the new nested structure and value.
185
- */
186
- const getNewPart = (parts, subObject, value) => {
187
- const clonedParts = [...parts];
188
- clonedParts.splice(0, 1);
189
- const part = parts.length !== 1 ? getNewPart(clonedParts, subObject ? subObject[parts[0]] : {}, value) : value;
190
- return Object.assign(Object.assign({}, subObject), {
191
- [parts[0]]: part
192
- });
193
- };
194
- /**
195
- * Encapsulates in a given object, at a given path the provided value
196
- *
197
- * @example
198
- * ORIGINAL object
199
- *
200
- * encapsulateIn({maintain: 'spread_me'}, 'a.b.c','test')
201
- *
202
- * RESULT
203
- *
204
- * {
205
- * maintain: 'spread_me',
206
- * c: 'test
207
- * }
208
- * }
209
- *
210
- * @param origin - The original object where the new value will be appended
211
- * @param path - The path at which the new value will be placed
212
- * @param value - The new value
213
- * @returns One new object with the new value at the provided path merged with the given object
214
- */
215
- const encapsulateIn = (origin, path, value) => {
216
- const parts = path.split('.');
217
- if (parts.length === 1) {
218
- return Object.assign(Object.assign({}, origin), {
219
- [path]: value
220
- });
221
- }
222
- return getNewPart(parts, origin, value);
223
- };
224
180
 
225
181
  /**
226
182
  * Splits a string by inserting specified values at given positions.
@@ -769,10 +725,11 @@ const custom = (value, masks) => {
769
725
  if (!masks.custom || !value) return value;
770
726
  let mask = '';
771
727
  let index = 0;
728
+ const convertedValue = value.replace(/[^\w\s]/gi, '');
772
729
  for (let i = 0; i < masks.custom.length; i++) {
773
730
  if (masks.custom[i] === '#') {
774
- if (index < value.length) {
775
- mask += value[index];
731
+ if (index < convertedValue.length) {
732
+ mask += convertedValue[index];
776
733
  index++;
777
734
  } else {
778
735
  break;
@@ -1274,6 +1231,44 @@ const between = (value, validations) => {
1274
1231
  const num = Number(value);
1275
1232
  return !Number.isNaN(num) && !(+num >= validations.between.start && +num <= validations.between.end);
1276
1233
  };
1234
+ /**
1235
+ * Checks if a given value is less than a specified threshold.
1236
+ *
1237
+ * @param value - The value to be checked. This value will be converted to a number.
1238
+ * @param validations - An object containing validation methods, specifically the `lessThan` property which holds the threshold value.
1239
+ * @returns Returns `false` if the `lessThan` threshold is not provided or if the value is not a valid number. Otherwise, returns `true` if the value is greater than or equal to the `lessThan` threshold.
1240
+ *
1241
+ * @example
1242
+ * ```typescript
1243
+ * const validations = { lessThan: 10 };
1244
+ * console.log(lessThan(5, validations)); // true
1245
+ * console.log(lessThan(15, validations)); // false
1246
+ * ```
1247
+ */
1248
+ const lessThan = (value, validations) => {
1249
+ const number = Number(value);
1250
+ if (!validations.lessThan || Number.isNaN(number)) return false;
1251
+ return number >= validations.lessThan;
1252
+ };
1253
+ /**
1254
+ * Checks if a given value is greater than a specified threshold.
1255
+ *
1256
+ * @param value - The value to be checked. This value will be converted to a number.
1257
+ * @param validations - An object containing validation methods, specifically the `greaterThan` property which holds the threshold value.
1258
+ * @returns Returns `false` if the `greaterThan` threshold is not provided or if the value is not a valid number. Otherwise, returns `true` if the value is less than or equal to the `greaterThan` threshold.
1259
+ *
1260
+ * @example
1261
+ * ```typescript
1262
+ * const validations = { greaterThan: 10 };
1263
+ * console.log(greaterThan(15, validations)); // true
1264
+ * console.log(greaterThan(5, validations)); // false
1265
+ * ```
1266
+ */
1267
+ const greaterThan = (value, validations) => {
1268
+ const number = Number(value);
1269
+ if (!validations.greaterThan || Number.isNaN(number)) return false;
1270
+ return number <= validations.greaterThan;
1271
+ };
1277
1272
  /**
1278
1273
  * Validates if a value contains sequential numbers.
1279
1274
  *
@@ -1968,41 +1963,44 @@ const conditions = (value, validations) => {
1968
1963
  };
1969
1964
 
1970
1965
  /**
1971
- * Validates if a date value falls between two specified dates.
1966
+ * @internal
1967
+ * Validates if a date string matches a specific date format.
1968
+ * The function accepts strings with '/' or '-' separators and removes them before validating the format.
1972
1969
  *
1973
- * @param {string} value - The date value to be validated in string format.
1974
- * @param {TValidationMethods} validations - The validation methods object containing the betweenDates validation rules.
1975
- * @returns {boolean} - Returns `true` if the date value fails the betweenDates validation, otherwise `false`.
1970
+ * @param {string} value - The date string to be validated. It can contain '/' or '-' separators.
1971
+ * @param {string} format - The expected date format. It can be one of the following:
1972
+ * 'DDMMYYYY', 'YYYYMMDD', 'YYYYDDMM', 'MMDDYYYY',
1973
+ * 'DMYYYY', 'YYYYMD', 'YYYYDM', 'MDYYYY'.
1974
+ * @returns {boolean} - Returns `false` if the date string matches the specified format, otherwise `true`.
1976
1975
  *
1977
1976
  * @example
1978
- * ```typescript
1979
- * const validations = {
1980
- * betweenDates: [
1981
- * { origin: { value: '2023-01-01', format: 'yyyy-MM-dd' }, operator: '>=' },
1982
- * { origin: { value: '2023-12-31', format: 'yyyy-MM-dd' }, operator: '<=' }
1983
- * ]
1984
- * };
1977
+ * // Returns false
1978
+ * invalidStringDate("25/07/1997", "DDMMYYYY");
1985
1979
  *
1986
- * const result1 = betweenDates('2023-06-01', validations);
1987
- * console.log(result1); // false (date is within the range)
1980
+ * @example
1981
+ * // Returns false
1982
+ * invalidStringDate("1997-07-25", "YYYYMMDD");
1988
1983
  *
1989
- * const result2 = betweenDates('2024-01-01', validations);
1990
- * console.log(result2); // true (date is outside the range)
1991
- * ```
1984
+ * @example
1985
+ * // Returns true, as the format does not match
1986
+ * invalidStringDate("1997/25/07", "MMDDYYYY");
1992
1987
  */
1993
- const betweenDates = (value, validations) => {
1994
- var _a;
1995
- let fail = false;
1996
- if (((_a = validations.betweenDates) === null || _a === void 0 ? void 0 : _a.length) != 2) return false;
1997
- for (const validation of validations.betweenDates) {
1998
- if (date(value, {
1999
- date: validation
2000
- })) {
2001
- fail = true;
2002
- break;
2003
- }
1988
+ const invalidStringDate = (value, format) => {
1989
+ if (!value.includes('/') && !value.includes('-') || !format) {
1990
+ return true;
2004
1991
  }
2005
- return fail;
1992
+ const valueParts = value.replace(/[-/]/g, '');
1993
+ const dateMapper = {
1994
+ DDMMYYYY: /^(\d{2})(\d{2})(\d{4})$/,
1995
+ YYYYMMDD: /^(\d{4})(\d{2})(\d{2})$/,
1996
+ YYYYDDMM: /^(\d{4})(\d{2})(\d{2})$/,
1997
+ MMDDYYYY: /^(\d{2})(\d{2})(\d{4})$/,
1998
+ DMYYYY: /^(\d{1,2})(\d{1,2})(\d{4})$/,
1999
+ YYYYMD: /^(\d{4})(\d{1,2})(\d{1,2})$/,
2000
+ YYYYDM: /^(\d{4})(\d{1,2})(\d{1,2})$/,
2001
+ MDYYYY: /^(\d{1,2})(\d{1,2})(\d{4})$/
2002
+ };
2003
+ return !dateMapper[format].test(valueParts);
2006
2004
  };
2007
2005
  /**
2008
2006
  * @internal
@@ -2055,6 +2053,44 @@ const dateRearrangeMapper = {
2055
2053
  MMDDYYYY: value => value,
2056
2054
  timestamp: value => new Date(value).toString()
2057
2055
  };
2056
+ /**
2057
+ * @function betweenDates
2058
+ * Validates if a date value falls between two specified dates.
2059
+ *
2060
+ * @param {string} value - The date value to be validated in string format.
2061
+ * @param {TValidationMethods} validations - The validation methods object containing the betweenDates validation rules.
2062
+ * @returns {boolean} - Returns `true` if the date value fails the betweenDates validation, otherwise `false`.
2063
+ *
2064
+ * @example
2065
+ * ```typescript
2066
+ * const validations = {
2067
+ * betweenDates: [
2068
+ * { origin: { value: '2023-01-01', format: 'yyyy-MM-dd' }, operator: '>=' },
2069
+ * { origin: { value: '2023-12-31', format: 'yyyy-MM-dd' }, operator: '<=' }
2070
+ * ]
2071
+ * };
2072
+ *
2073
+ * const result1 = betweenDates('2023-06-01', validations);
2074
+ * console.log(result1); // false (date is within the range)
2075
+ *
2076
+ * const result2 = betweenDates('2024-01-01', validations);
2077
+ * console.log(result2); // true (date is outside the range)
2078
+ * ```
2079
+ */
2080
+ const betweenDates = (value, validations) => {
2081
+ var _a;
2082
+ let fail = false;
2083
+ if (((_a = validations.betweenDates) === null || _a === void 0 ? void 0 : _a.length) != 2) return false;
2084
+ for (const validation of validations.betweenDates) {
2085
+ if (date(value, {
2086
+ date: validation
2087
+ })) {
2088
+ fail = true;
2089
+ break;
2090
+ }
2091
+ }
2092
+ return fail;
2093
+ };
2058
2094
  /**
2059
2095
  * @function date
2060
2096
  * Validates a date value based on various date conditions and intervals.
@@ -2088,12 +2124,13 @@ const dateRearrangeMapper = {
2088
2124
  * ```
2089
2125
  */
2090
2126
  const date = (value, validations) => {
2091
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
2127
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
2092
2128
  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)) {
2093
2129
  return false;
2094
2130
  }
2095
2131
  const originValue = validations.date.origin.value || value;
2096
- let originDate = new Date(dateRearrangeMapper[(_e = validations.date) === null || _e === void 0 ? void 0 : _e.origin.format](originValue).toString());
2132
+ const mappedValue = dateRearrangeMapper[(_e = validations.date) === null || _e === void 0 ? void 0 : _e.origin.format](originValue).toString();
2133
+ let originDate = new Date(mappedValue);
2097
2134
  let targetDate = new Date();
2098
2135
  let target = new Date();
2099
2136
  if ((_g = (_f = validations.date) === null || _f === void 0 ? void 0 : _f.target) === null || _g === void 0 ? void 0 : _g.format) {
@@ -2112,6 +2149,9 @@ const date = (value, validations) => {
2112
2149
  if (validations.date.onlyValidDate && (!(targetDate instanceof Date && isFinite(targetDate)) || !(targetDate instanceof Date && isFinite(originDate)) || originValue.length < 8)) {
2113
2150
  return true;
2114
2151
  }
2152
+ if (invalidStringDate(mappedValue, (_l = validations.date) === null || _l === void 0 ? void 0 : _l.origin.format)) {
2153
+ return false;
2154
+ }
2115
2155
  const originTimestamp = originDate.getTime();
2116
2156
  const targetTimestamp = targetDate.getTime();
2117
2157
  const operationsMapper = {
@@ -2122,7 +2162,7 @@ const date = (value, validations) => {
2122
2162
  '===': originTimestamp === targetTimestamp,
2123
2163
  '!==': originTimestamp !== targetTimestamp
2124
2164
  };
2125
- return operationsMapper[(_l = validations.date) === null || _l === void 0 ? void 0 : _l.operator];
2165
+ return operationsMapper[(_m = validations.date) === null || _m === void 0 ? void 0 : _m.operator];
2126
2166
  };
2127
2167
  /**
2128
2168
  * @function validDate
@@ -2151,11 +2191,74 @@ const validDate = (value, validations) => {
2151
2191
  const month = parseInt(dateParts[0], 10) - 1; // Month is zero-based
2152
2192
  const day = parseInt(dateParts[1], 10);
2153
2193
  const date = new Date(year, month, day);
2194
+ /*
2195
+ * Motivation: due to the scenario in which a date field may be 'typeable',
2196
+ * we need to ensure that dates less than a thousand and such years are valid.
2197
+ * E.g.: user types 13-07-199 (without this condition, it becomes a valid date, as 199 is a valid year)
2198
+ *
2199
+ * Ps: 150 is a valid value for now, as we still cannot live more than that, from the
2200
+ * the moment we start to live longer, we can rethink this.
2201
+ */
2202
+ const today = new Date();
2203
+ today.setFullYear(today.getFullYear() - 150);
2204
+ if (date.getFullYear() < today.getFullYear()) {
2205
+ return true;
2206
+ }
2154
2207
  // Check if the date is valid
2155
2208
  const isValidDate = date.getFullYear() === year && date.getMonth() === month && date.getDate() === day;
2156
2209
  return !isValidDate;
2157
2210
  };
2158
2211
 
2212
+ /**
2213
+ * @internal
2214
+ * Runs a set of validation handlers against a given value.
2215
+ *
2216
+ * @param {unknown} value - The value to be validated.
2217
+ * @param {TValidationMethods} handlers - An object containing validation methods to be applied.
2218
+ * @param {TValidationHandler} validations - An object containing every validation methods to be executed.
2219
+ * @returns {boolean[]} - An array of boolean results for each validation method.
2220
+ *
2221
+ * @example
2222
+ * const handlers = {
2223
+ * max: { max: 10 },
2224
+ * required: true,
2225
+ * email: true
2226
+ * };
2227
+ * const results = run('test@example.com', handlers);
2228
+ * console.log(results); // [false, false, true] (value fails 'max', passes 'required', passes 'email')
2229
+ */
2230
+ function run$1(value, handlers, validations) {
2231
+ const runner = [];
2232
+ Object.keys(handlers).forEach(rule => {
2233
+ runner.push(validations[rule](value, {
2234
+ [rule]: handlers[rule]
2235
+ }));
2236
+ });
2237
+ return runner;
2238
+ }
2239
+ /**
2240
+ * Validates a given value based on specified validation methods inside a custom named validation.
2241
+ *
2242
+ * @param {unknown} value - The value to be validated.
2243
+ * @param {TValidationMethods} methods - The validation methods to be applied.
2244
+ * @param {TValidationHandler} validations - An object containing every validation methods to be executed.
2245
+ * @returns {boolean} - Returns true if any of the validation methods pass, otherwise false.
2246
+ *
2247
+ * @example
2248
+ * const value = 'example@example.com';
2249
+ * const methods = {
2250
+ * required: true,
2251
+ * email: true
2252
+ * };
2253
+ *
2254
+ * const isValid = validateValue(value, methods);
2255
+ * console.log(isValid); // Output: true
2256
+ */
2257
+ var namedRule = ((value, methods, validations) => {
2258
+ if (!methods) return false;
2259
+ return run$1(value, methods, validations).some(validation => validation);
2260
+ });
2261
+
2159
2262
  /**
2160
2263
  * @internal
2161
2264
  * An object mapping validation keys to their respective validation functions.
@@ -2169,6 +2272,8 @@ const validDate = (value, validations) => {
2169
2272
  const validations$1 = {
2170
2273
  max,
2171
2274
  min,
2275
+ lessThan,
2276
+ greaterThan,
2172
2277
  length,
2173
2278
  regex,
2174
2279
  url,
@@ -2190,7 +2295,6 @@ const validations$1 = {
2190
2295
  notEmpty,
2191
2296
  bool,
2192
2297
  exists,
2193
- greaterThan: () => true,
2194
2298
  isNumber,
2195
2299
  conditions,
2196
2300
  validDate,
@@ -2214,20 +2318,26 @@ const validations$1 = {
2214
2318
  * const results = run('test@example.com', handlers);
2215
2319
  * console.log(results); // [false, false, true] (value fails 'max', passes 'required', passes 'email')
2216
2320
  */
2217
- const run = (value, handlers) => {
2321
+ function run(value, handlers) {
2218
2322
  const runner = [];
2219
2323
  Object.keys(handlers).forEach(rule => {
2220
- runner.push(validations$1[rule](value, {
2221
- [rule]: handlers[rule]
2222
- }));
2324
+ let handler;
2325
+ if (isFunction(validations$1[rule])) {
2326
+ handler = validations$1[rule](value, {
2327
+ [rule]: handlers[rule]
2328
+ });
2329
+ } else {
2330
+ handler = namedRule(value, handlers[rule], validations$1);
2331
+ }
2332
+ runner.push(handler);
2223
2333
  });
2224
2334
  return runner;
2225
- };
2335
+ }
2226
2336
  /**
2227
2337
  * Validates that a value meets multiple validation rules.
2228
2338
  *
2229
2339
  * @param {number | string | boolean} value - The value to be validated.
2230
- * @param {TValidationMethods} validations - The validation methods object containing the multipleValidations rule set.
2340
+ * @param {TValidationMethods} methods - The validation methods object containing the multipleValidations rule set.
2231
2341
  * @returns {boolean} - Returns `true` if the value meets the specified multiple validation rules, otherwise `false`.
2232
2342
  *
2233
2343
  * @example
@@ -2252,20 +2362,22 @@ const run = (value, handlers) => {
2252
2362
  * console.log(result2); // false
2253
2363
  * ```
2254
2364
  */
2255
- const multipleValidations = (value, validations) => {
2256
- if (!validations.multipleValidations) return false;
2257
- const runner = run(value, validations.multipleValidations.validations);
2365
+ const multipleValidations = (value, methods) => {
2366
+ if (!methods.multipleValidations) return false;
2367
+ const runner = run(value, methods.multipleValidations.validations);
2258
2368
  const rulesMapper = {
2259
2369
  AND: () => runner.every(validation => validation),
2260
2370
  OR: () => runner.some(validation => validation),
2261
2371
  NOT: () => !runner.every(validation => validation)
2262
2372
  };
2263
- return rulesMapper[validations.multipleValidations.rule]();
2373
+ return rulesMapper[methods.multipleValidations.rule]();
2264
2374
  };
2265
2375
 
2266
2376
  const validations = {
2267
2377
  max,
2268
2378
  min,
2379
+ lessThan,
2380
+ greaterThan,
2269
2381
  length,
2270
2382
  regex,
2271
2383
  url,
@@ -2287,7 +2399,6 @@ const validations = {
2287
2399
  notEmpty,
2288
2400
  bool,
2289
2401
  exists,
2290
- greaterThan: () => true,
2291
2402
  isNumber,
2292
2403
  conditions,
2293
2404
  multipleValidations,
@@ -2296,8 +2407,52 @@ const validations = {
2296
2407
  validDate
2297
2408
  };
2298
2409
 
2299
- const DEFAULT_API_DEBOUNCE_TIME = 1000;
2300
- const DEFAULT_STATE_REFRESH_TIME = 100;
2410
+ /**
2411
+ * Custom RXJS Subject to gracefully handle errors on unsubscribed Subjects
2412
+ * that were unmounted due to adapter external handling such as visibility
2413
+ */
2414
+ class SafeSubject extends Subject {
2415
+ constructor(isMounted) {
2416
+ super();
2417
+ this.isMounted = isMounted;
2418
+ }
2419
+ next(value) {
2420
+ if (this.isMounted()) {
2421
+ super.next(value);
2422
+ }
2423
+ }
2424
+ }
2425
+
2426
+ /**
2427
+ * @internal
2428
+ * Handles the validation of a given value based on specified validation methods and rules.
2429
+ *
2430
+ * @param {string | number | boolean | unknown} value - The value to be validated.
2431
+ * @param {TSchemaValidation} validations - The schema validations to be applied.
2432
+ * @param {TValidationHandler} methods - The validation handler methods.
2433
+ * @param {keyof TValidationMethods} key - The specific key of the validation method to be used.
2434
+ * @returns {boolean} - Returns true if the value passes the validation, otherwise false.
2435
+ *
2436
+ * @example
2437
+ * const value = 'example@example.com';
2438
+ * const validations = {
2439
+ * required: true,
2440
+ * customName: { email: true }
2441
+ * };
2442
+ * const methods = {
2443
+ * email: (value) => /\S+@\S+\.\S+/.test(value)
2444
+ * };
2445
+ * const key = 'required';
2446
+ *
2447
+ * const isValid = handleValidation(value, validations, methods, key);
2448
+ * console.log(isValid); // Output: true
2449
+ */
2450
+ function handleValidation(value, validations, methods, key) {
2451
+ if (isFunction(methods[key])) {
2452
+ return methods[key](value, validations);
2453
+ }
2454
+ return namedRule(value, validations[key], methods);
2455
+ }
2301
2456
 
2302
2457
  /**
2303
2458
  * Represents a form field with observables for managing form state, validations, and API requests.
@@ -2330,8 +2485,9 @@ class FormField {
2330
2485
  mapper
2331
2486
  }) {
2332
2487
  var _a, _b, _c, _d, _e, _f, _g;
2488
+ this.valueSubscription$ = new Subscription();
2333
2489
  this.fieldStateSubscription$ = new Subscription();
2334
- this.originalSchema = schemaComponent;
2490
+ this.originalSchema = cloneDeep(schemaComponent);
2335
2491
  this.config = {
2336
2492
  defaultAPIdebounceTimeMS: Number(config === null || config === void 0 ? void 0 : config.defaultAPIdebounceTimeMS) ? Number(config === null || config === void 0 ? void 0 : config.defaultAPIdebounceTimeMS) : DEFAULT_API_DEBOUNCE_TIME,
2337
2493
  defaultStateRefreshTimeMS: Number(config === null || config === void 0 ? void 0 : config.defaultStateRefreshTimeMS) ? Number(config === null || config === void 0 ? void 0 : config.defaultStateRefreshTimeMS) : DEFAULT_STATE_REFRESH_TIME
@@ -2342,14 +2498,14 @@ class FormField {
2342
2498
  this.path = path;
2343
2499
  this.children = children;
2344
2500
  this.validations = schemaComponent.validations;
2345
- this.errorMessages = schemaComponent.errorMessages;
2501
+ this.errorMessages = (_a = schemaComponent.validations) === null || _a === void 0 ? void 0 : _a.messages;
2346
2502
  this.visibilityConditions = schemaComponent.visibilityConditions;
2347
2503
  this.resetValues = schemaComponent.resetValues;
2348
2504
  this.apiSchema = schemaComponent.api;
2349
2505
  this.formatters = schemaComponent.formatters;
2350
2506
  this.masks = schemaComponent.masks;
2351
2507
  if (mapper.valueChangeEvent) this.valueChangeEvent = mapper.valueChangeEvent;
2352
- if ((_a = mapper.events) === null || _a === void 0 ? void 0 : _a.setValue) this.valuePropName = mapper.events.setValue;
2508
+ if ((_b = mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) this.valuePropName = mapper.events.setValue;
2353
2509
  this.mapper = mapper;
2354
2510
  this.validateVisibility = validateVisibility;
2355
2511
  this.resetValue = resetValue;
@@ -2357,10 +2513,6 @@ class FormField {
2357
2513
  this.fieldEventSubject$ = fieldEventSubject$;
2358
2514
  this.dataSubject$ = dataSubject$;
2359
2515
  this._props = schemaComponent.props || {};
2360
- this._value = '';
2361
- this._stateValue = ((_b = this.mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) ? {
2362
- [this.mapper.events.setValue]: ''
2363
- } : {};
2364
2516
  this._metadata = '';
2365
2517
  this.errorsString = '';
2366
2518
  this.errorsList = [];
@@ -2380,7 +2532,9 @@ class FormField {
2380
2532
  };
2381
2533
  this._errors = {};
2382
2534
  this._valid = false;
2535
+ this._mounted = true;
2383
2536
  this.initializeObservers();
2537
+ this.value = this.initialValue || '';
2384
2538
  }
2385
2539
  /**
2386
2540
  * method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
@@ -2388,25 +2542,25 @@ class FormField {
2388
2542
  initializeObservers() {
2389
2543
  var _a;
2390
2544
  if (!this.valueSubject$ || this.valueSubject$.closed) {
2391
- this.valueSubject$ = new Subject();
2545
+ this.valueSubject$ = new SafeSubject(() => this._mounted);
2392
2546
  }
2393
2547
  if (!this.errorSubject$ || this.errorSubject$.closed) {
2394
- this.errorSubject$ = new Subject();
2548
+ this.errorSubject$ = new SafeSubject(() => this._mounted);
2395
2549
  }
2396
2550
  if (!this.visibilitySubject$ || this.visibilitySubject$.closed) {
2397
- this.visibilitySubject$ = new Subject();
2551
+ this.visibilitySubject$ = new SafeSubject(() => this._mounted);
2398
2552
  }
2399
2553
  if (!this.apiSubject$ || this.apiSubject$.closed) {
2400
- this.apiSubject$ = new Subject();
2554
+ this.apiSubject$ = new SafeSubject(() => this._mounted);
2401
2555
  }
2402
2556
  if (!this.propsSubject$ || this.propsSubject$.closed) {
2403
- this.propsSubject$ = new Subject();
2557
+ this.propsSubject$ = new SafeSubject(() => this._mounted);
2404
2558
  }
2405
2559
  if (!this.fieldStateSubscription$ || this.fieldStateSubscription$.closed) {
2406
2560
  this.fieldStateSubscription$ = new Subscription();
2407
2561
  }
2408
2562
  if (!this.apiEventQueueSubject$ || this.apiEventQueueSubject$.closed) {
2409
- this.apiEventQueueSubject$ = new Subject();
2563
+ this.apiEventQueueSubject$ = new SafeSubject(() => this._mounted);
2410
2564
  }
2411
2565
  this.fieldState$ = combineLatest({
2412
2566
  visibility: this.visibilitySubject$.pipe(startWith(this._visibility)),
@@ -2420,9 +2574,6 @@ class FormField {
2420
2574
  }) => event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultAPIdebounceTimeMS))), filter(() => this.apiSubject$ && !this.apiSubject$.closed)).subscribe(payload => {
2421
2575
  this.apiRequest(payload);
2422
2576
  });
2423
- if (!isNil(this.initialValue)) {
2424
- this.value = this.initialValue;
2425
- }
2426
2577
  }
2427
2578
  /**
2428
2579
  * Retrieves the properties associated with the form field.
@@ -2442,6 +2593,7 @@ class FormField {
2442
2593
  this._props = props;
2443
2594
  this.propsSubject$.next(this.props);
2444
2595
  this.templateSubject$.next({
2596
+ scope: 'fields',
2445
2597
  key: this.name,
2446
2598
  event: 'ON_PROPS'
2447
2599
  });
@@ -2505,6 +2657,7 @@ class FormField {
2505
2657
  }
2506
2658
  this.stateValue && this.valueSubject$.next(this.stateValue);
2507
2659
  this.templateSubject$.next({
2660
+ scope: 'fields',
2508
2661
  key: this.name,
2509
2662
  event: 'ON_VALUE'
2510
2663
  });
@@ -2525,8 +2678,26 @@ class FormField {
2525
2678
  set visibility(visible) {
2526
2679
  if (typeof visible === 'undefined' || visible === this.visibility) return;
2527
2680
  this._visibility = visible;
2681
+ /**
2682
+ * I was sure I would not require to gambiarra, but..
2683
+ * in order to ignore an hidden value on a form submit
2684
+ * or revalidate it when it comes back to visibility
2685
+ * I needed to...
2686
+ * I don't recommend setting private properties like this
2687
+ * this will force the field to be valid when it's hidden
2688
+ * and trigger the validation when it's visible
2689
+ */
2690
+ if (!this.visibility) {
2691
+ this.value = '';
2692
+ this._valid = true;
2693
+ } else {
2694
+ this.setFieldValidity({
2695
+ event: 'ON_FIELD_MOUNT'
2696
+ });
2697
+ }
2528
2698
  this.visibilitySubject$.next(this.visibility);
2529
2699
  this.templateSubject$.next({
2700
+ scope: 'fields',
2530
2701
  key: this.name,
2531
2702
  event: 'ON_VISIBILITY'
2532
2703
  });
@@ -2559,12 +2730,13 @@ class FormField {
2559
2730
  this.errorsList = Object.values(this.errors);
2560
2731
  this.errorsString = this.errorsList.join(', ');
2561
2732
  /**
2562
- * if any error receieves a list of errors, set a prop for it, currently only supporting a single string
2733
+ * if any error receives a list of errors, set a prop for it, currently only supporting a single string
2563
2734
  */
2564
2735
  ((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setErrorMessage) && this.errorSubject$.next({
2565
2736
  [this.mapper.events.setErrorMessage]: this.errorsString
2566
2737
  });
2567
2738
  this.templateSubject$.next({
2739
+ scope: 'fields',
2568
2740
  key: this.name,
2569
2741
  event: 'ON_PROPS'
2570
2742
  });
@@ -2587,6 +2759,7 @@ class FormField {
2587
2759
  this._api = response;
2588
2760
  this.apiSubject$.next(this.api);
2589
2761
  this.templateSubject$.next({
2762
+ scope: 'fields',
2590
2763
  key: this.name,
2591
2764
  event: 'ON_API'
2592
2765
  });
@@ -2610,6 +2783,7 @@ class FormField {
2610
2783
  this.initializeObservers();
2611
2784
  this.subscribeValue(valueSubscription);
2612
2785
  this.subscribeState(propsSubscription);
2786
+ this._mounted = true;
2613
2787
  }
2614
2788
  /**
2615
2789
  * Sets the value of the form field and emits associated events.
@@ -2666,54 +2840,43 @@ class FormField {
2666
2840
  setFieldValidity({
2667
2841
  event
2668
2842
  }) {
2669
- var _a;
2670
- if (!this.validations) {
2843
+ var _a, _b, _c, _d;
2844
+ if (!this.validations || !this.visibility) {
2845
+ this.errors = {};
2671
2846
  this._valid = true;
2672
2847
  return;
2673
2848
  }
2674
- /*
2675
- @TODO evaluate if _valid flag needs to be updated on all events, this condition saves resources,
2676
- currently form submition needs to be controlled with form instance submit property function that
2677
- will evaluate if all fields are valid regardless the events that triggers error messages
2678
- */
2679
- if (!this.validations.events.includes(event) && event !== 'ON_FORM_SUBMIT') return;
2680
2849
  let valid = true;
2681
- const errors = Object.assign({}, this.errors);
2682
- const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.config;
2850
+ const errors = {};
2851
+ const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.methods;
2683
2852
  schemaValidations && Object.keys(schemaValidations).forEach(validationKey => {
2684
- var _a;
2685
- const error = validations[validationKey](this.value, schemaValidations);
2853
+ const error = handleValidation(this.value, schemaValidations, validations, validationKey);
2686
2854
  // setting valid flag
2687
2855
  valid = !error && valid;
2688
2856
  // setting error messages
2689
- if (((_a = this.validations) === null || _a === void 0 ? void 0 : _a.events.includes(event)) || event === 'ON_FORM_SUBMIT') {
2690
- if (error && this.errorMessages) {
2691
- if (validationKey in this.errorMessages) {
2692
- const messages = this.errorMessages;
2693
- errors[validationKey] = messages[validationKey];
2694
- } else if ('default' in this.errorMessages) {
2695
- errors[validationKey] = this.errorMessages.default;
2696
- }
2697
- } else {
2698
- delete errors[validationKey];
2857
+ if (error && this.errorMessages) {
2858
+ if (validationKey in this.errorMessages) {
2859
+ const messages = this.errorMessages;
2860
+ errors[validationKey] = messages[validationKey];
2861
+ } else if ('default' in this.errorMessages) {
2862
+ errors[validationKey] = this.errorMessages.default;
2699
2863
  }
2864
+ } else {
2865
+ delete errors[validationKey];
2700
2866
  }
2701
2867
  });
2702
2868
  this._valid = valid;
2703
- this.errors = errors;
2704
- }
2705
- /**
2706
- * WIP expensive function to get updated field validity on each event
2707
- */
2708
- updateValidityFlag() {
2709
- var _a;
2710
- let valid = true;
2711
- const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.config;
2712
- schemaValidations && Object.keys(schemaValidations).forEach(validationKey => {
2713
- const error = validations[validationKey](this.value, schemaValidations);
2714
- valid = !error && valid;
2715
- });
2716
- this._valid = valid;
2869
+ if ((_c = (_b = this.validations) === null || _b === void 0 ? void 0 : _b.eventMessages) === null || _c === void 0 ? void 0 : _c[event]) {
2870
+ const eventMessages = {};
2871
+ (_d = this.validations.eventMessages[event]) === null || _d === void 0 ? void 0 : _d.forEach(method => {
2872
+ if (method in errors) {
2873
+ eventMessages[method] = errors[method];
2874
+ }
2875
+ });
2876
+ this.errors = eventMessages;
2877
+ } else if (event === 'ON_FORM_SUBMIT') {
2878
+ this.errors = errors;
2879
+ }
2717
2880
  }
2718
2881
  /**
2719
2882
  * Formats the field value using the specified formatters, if available.
@@ -2747,7 +2910,7 @@ class FormField {
2747
2910
  let valid = true;
2748
2911
  const preConditions = config.preConditions;
2749
2912
  preConditions && Object.keys(preConditions).forEach(validationKey => {
2750
- const error = validations[validationKey](this.value, preConditions);
2913
+ const error = handleValidation(this.value, preConditions, validations, validationKey);
2751
2914
  valid = valid && !error;
2752
2915
  });
2753
2916
  if (config.blockRequestWhenInvalid) {
@@ -2766,6 +2929,17 @@ class FormField {
2766
2929
  }) {
2767
2930
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
2768
2931
  return __awaiter(this, void 0, void 0, function* () {
2932
+ const configRequest = config => __awaiter(this, void 0, void 0, function* () {
2933
+ try {
2934
+ const responseData = yield makeRequest(config.method, config.url, config.headers, config.body);
2935
+ const apiResponseData = JSON.parse(String(responseData));
2936
+ const response = config.resultPath ? get(apiResponseData, config.resultPath) : apiResponseData;
2937
+ // this.apiResponseData = { response };
2938
+ return response;
2939
+ } catch (e) {
2940
+ return !isNil(config === null || config === void 0 ? void 0 : config.fallbackValue) ? config.fallbackValue : 'error';
2941
+ }
2942
+ });
2769
2943
  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 => {
2770
2944
  var _a, _b;
2771
2945
  return (_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.configs) === null || _b === void 0 ? void 0 : _b[key].events.includes(event);
@@ -2776,19 +2950,10 @@ class FormField {
2776
2950
  };
2777
2951
  const config = (_e = this.apiSchema.defaultConfig) === null || _e === void 0 ? void 0 : _e.config;
2778
2952
  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)) {
2779
- try {
2780
- const responseData = yield makeRequest(config.method, config.url, config.headers, config.body);
2781
- const apiResponseData = JSON.parse(String(responseData));
2782
- const response = config.resultPath ? get(apiResponseData, config.resultPath) : apiResponseData;
2783
- // this.apiResponseData = { response };
2784
- responses.default = {
2785
- response
2786
- };
2787
- } catch (e) {
2788
- responses.default = {
2789
- response: !isNil(config === null || config === void 0 ? void 0 : config.fallbackValue) ? config.fallbackValue : 'error'
2790
- };
2791
- }
2953
+ const response = yield configRequest(config);
2954
+ responses.default = {
2955
+ response
2956
+ };
2792
2957
  }
2793
2958
  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 => {
2794
2959
  var _a, _b;
@@ -2799,38 +2964,21 @@ class FormField {
2799
2964
  @TODO handle promises with error
2800
2965
  */
2801
2966
  const result = yield Promise.all(Object.keys(this.apiSchema.configs).map(configKey => __awaiter(this, void 0, void 0, function* () {
2802
- return new Promise(res => {
2803
- var _a, _b, _c, _d;
2804
- const config = (_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.configs) === null || _b === void 0 ? void 0 : _b[configKey].config;
2805
- 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)) {
2806
- try {
2807
- makeRequest(config.method, config.url, config.headers, config.body).then(responseData => {
2808
- const apiResponseData = JSON.parse(String(responseData));
2809
- const response = get(apiResponseData, config.resultPath || '');
2810
- res({
2811
- name: configKey,
2812
- result: {
2813
- response
2814
- }
2815
- });
2816
- // responses.named![configKey] = { response };
2817
- });
2818
- } catch (e) {
2819
- res({
2820
- name: configKey,
2821
- result: {
2822
- response: !isNil(config.fallbackValue) ? config.fallbackValue : 'error'
2823
- }
2824
- });
2967
+ var _k, _l, _m, _o;
2968
+ const config = (_l = (_k = this.apiSchema) === null || _k === void 0 ? void 0 : _k.configs) === null || _l === void 0 ? void 0 : _l[configKey].config;
2969
+ if (config && ((_o = (_m = this.apiSchema) === null || _m === void 0 ? void 0 : _m.configs) === null || _o === void 0 ? void 0 : _o[configKey].events.includes(event)) && this.checkApiRequestValidations(config)) {
2970
+ const response = yield configRequest(config);
2971
+ return {
2972
+ name: configKey,
2973
+ result: {
2974
+ response
2825
2975
  }
2826
- }
2827
- });
2976
+ };
2977
+ }
2978
+ return null;
2828
2979
  })));
2829
- result.forEach(({
2830
- name,
2831
- result
2832
- }) => {
2833
- if (responses.named) responses.named[name] = result;
2980
+ result.forEach(payload => {
2981
+ if (payload && responses.named) responses.named[payload.name] = payload.result;
2834
2982
  });
2835
2983
  }
2836
2984
  }
@@ -2843,7 +2991,8 @@ class FormField {
2843
2991
  * @returns {void}
2844
2992
  */
2845
2993
  destroyField() {
2846
- this.valueSubject$.unsubscribe();
2994
+ this._mounted = false;
2995
+ this.valueSubscription$.unsubscribe();
2847
2996
  this.visibilitySubject$.unsubscribe();
2848
2997
  this.fieldStateSubscription$.unsubscribe();
2849
2998
  this.propsSubject$.unsubscribe();
@@ -2869,13 +3018,12 @@ class FormField {
2869
3018
  * @returns {void}
2870
3019
  */
2871
3020
  subscribeValue(callback) {
2872
- this.valueSubject$.subscribe({
3021
+ this.valueSubscription$ = this.valueSubject$.subscribe({
2873
3022
  next: callback
2874
3023
  });
2875
3024
  }
2876
3025
  }
2877
3026
 
2878
- const IVARPROPNAME = 'iVars';
2879
3027
  /**
2880
3028
  * Represents the core logic for managing a form, including field management, validation, and submission.
2881
3029
  */
@@ -2889,38 +3037,39 @@ class FormCore {
2889
3037
  * @param {string} [entry.action] - The action attribute of the form.
2890
3038
  * @param {string} [entry.method] - The method attribute of the form.
2891
3039
  * @param {IFormSchema.iVars} [entry.iVars] - The internal variables of the form.
2892
- * @param {(data: TFormValues) => void} [entry.onSubmit] - A callback function to handle form submission.
2893
3040
  * @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
2894
3041
  */
2895
3042
  constructor(entry) {
2896
- var _a, _b, _c, _d, _e, _f, _g, _h;
3043
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
3044
+ this.templateSubscription$ = new Subscription();
3045
+ this.mappers = new Map();
2897
3046
  this.schema = entry.schema;
2898
3047
  this.fields = new Map();
2899
3048
  this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
2900
3049
  this.action = entry.action || ((_b = entry.schema) === null || _b === void 0 ? void 0 : _b.action);
2901
3050
  this.method = entry.method || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.method);
2902
3051
  this._iVars = entry.iVars || ((_d = entry.schema) === null || _d === void 0 ? void 0 : _d.iVars) || {};
2903
- this.onSubmit = entry.onSubmit;
2904
3052
  this.config = {
2905
3053
  defaultAPIdebounceTimeMS: Number((_e = entry.config) === null || _e === void 0 ? void 0 : _e.defaultAPIdebounceTimeMS) ? Number((_f = entry.config) === null || _f === void 0 ? void 0 : _f.defaultAPIdebounceTimeMS) : DEFAULT_API_DEBOUNCE_TIME,
2906
3054
  defaultStateRefreshTimeMS: Number((_g = entry.config) === null || _g === void 0 ? void 0 : _g.defaultStateRefreshTimeMS) ? Number((_h = entry.config) === null || _h === void 0 ? void 0 : _h.defaultStateRefreshTimeMS) : DEFAULT_STATE_REFRESH_TIME
2907
3055
  };
2908
- this.mappers = entry.mappers;
3056
+ (_j = entry.mappers) === null || _j === void 0 ? void 0 : _j.map(mapper => {
3057
+ this.mappers.set(mapper.componentName, mapper);
3058
+ });
2909
3059
  this.schema && FormCore.checkIndexes(this.schema.components);
2910
3060
  this.templateSubject$ = new Subject();
2911
3061
  this.submitSubject$ = new Subject();
2912
3062
  this.fieldEventSubject$ = new Subject();
2913
3063
  this.dataSubject$ = new Subject();
2914
- this.dataCallbackSubscription$ = new Subscription();
3064
+ this.mountSubject$ = new Subject();
2915
3065
  this.subscribedTemplates = [];
2916
3066
  this.schema && this.serializeStructure(this.schema.components);
2917
3067
  this.schema && this.subscribeTemplates();
2918
- this.templateSubject$.subscribe(this.refreshTemplates.bind(this));
3068
+ this.templateSubscription$ = this.templateSubject$.subscribe(this.refreshTemplates.bind(this));
2919
3069
  this.templateSubject$.next({
2920
- key: IVARPROPNAME,
3070
+ scope: 'iVars',
2921
3071
  event: 'ON_IVARS'
2922
3072
  });
2923
- entry.onData && this.subscribeData(entry.onData);
2924
3073
  /*
2925
3074
  mount events needs to occur on form level, only when all the fields are instantiated
2926
3075
  is it possible to apply all the side effects that occur globally, same effect occur
@@ -2931,6 +3080,7 @@ class FormCore {
2931
3080
  event: 'ON_FIELD_MOUNT'
2932
3081
  });
2933
3082
  this.refreshTemplates({
3083
+ scope: 'fields',
2934
3084
  key,
2935
3085
  event: 'ON_FIELDS'
2936
3086
  });
@@ -2952,7 +3102,7 @@ class FormCore {
2952
3102
  set iVars(payload) {
2953
3103
  this._iVars = payload;
2954
3104
  this.templateSubject$.next({
2955
- key: IVARPROPNAME,
3105
+ scope: 'iVars',
2956
3106
  event: 'ON_IVARS'
2957
3107
  });
2958
3108
  }
@@ -2980,7 +3130,6 @@ class FormCore {
2980
3130
  validations,
2981
3131
  visibilityConditions,
2982
3132
  resetValues,
2983
- errorMessages,
2984
3133
  api
2985
3134
  }
2986
3135
  }, key) => {
@@ -2991,26 +3140,11 @@ class FormCore {
2991
3140
  validations,
2992
3141
  visibilityConditions,
2993
3142
  resetValues,
2994
- errorMessages,
2995
3143
  apiSchema: api
2996
3144
  };
2997
3145
  traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
2998
3146
  });
2999
3147
  }
3000
- /**
3001
- *
3002
- * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
3003
- */
3004
- subscribeData(callback) {
3005
- this.dataCallbackSubscription$ = this.dataSubject$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS), map(({
3006
- key
3007
- }) => ({
3008
- field: key,
3009
- data: this.getFormValues()
3010
- }))).subscribe({
3011
- next: callback
3012
- });
3013
- }
3014
3148
  /**
3015
3149
  * Gets the value of a property from a field.
3016
3150
  *
@@ -3021,20 +3155,27 @@ class FormCore {
3021
3155
  * @returns {unknown | undefined} The value of the property, or undefined if the field doesn't exist.
3022
3156
  */
3023
3157
  getValue({
3158
+ scope,
3024
3159
  key,
3025
3160
  property,
3026
3161
  path
3027
3162
  }) {
3028
- if (key === IVARPROPNAME) {
3029
- const value = get(this.iVars, [property, ...path]);
3030
- return value;
3031
- }
3032
- const field = this.fields.get(key);
3033
- if (!field) return console.warn(`failed to get value from ${key}`);
3034
- if (property === 'props' && path[0] === field.valuePropName) {
3035
- return field.stateValue;
3163
+ switch (scope) {
3164
+ case 'iVars':
3165
+ {
3166
+ const value = get(this.iVars, [key, ...(property ? [property] : []), ...path]);
3167
+ return value;
3168
+ }
3169
+ case 'fields':
3170
+ {
3171
+ const field = this.fields.get(key);
3172
+ if (!field) return console.warn(`failed to get value from ${key}`);
3173
+ if (property === 'props' && path[0] === field.valuePropName) {
3174
+ return field.stateValue;
3175
+ }
3176
+ return path.length > 0 ? get(field[property], path) : field[property];
3177
+ }
3036
3178
  }
3037
- return path.length > 0 ? get(field[property], path) : field[property];
3038
3179
  }
3039
3180
  /**
3040
3181
  * Sets the value of a property in a field.
@@ -3097,27 +3238,26 @@ class FormCore {
3097
3238
  * @returns {string[]} An array of extracted parameters.
3098
3239
  */
3099
3240
  extractParams(expression) {
3100
- const regex = /\${(.*?)}/g;
3101
3241
  const extractedValues = [];
3102
3242
  let match;
3103
- while (!isNil(match = regex.exec(expression))) {
3243
+ while (!isNil(match = TEMPLATE_REGEX_DELIMITATOR.exec(expression))) {
3104
3244
  extractedValues.push(match[1]);
3105
3245
  }
3106
- const operatorRegex = /\s*(\|\||&&|!)\s*/g;
3107
- const splittedString = extractedValues.map(el => el.split(operatorRegex));
3246
+ const splittedString = extractedValues.map(el => el.split(TEMPLATE_REGEX_OPERATOR_SPLITTER));
3108
3247
  const result = splittedString.map(splittedStringVal => {
3109
3248
  return splittedStringVal.filter(Boolean).reduce((acc, curr) => {
3110
- if (curr.match(/^\|\||&&|!$/)) {
3249
+ if (curr.match(TEMPLATE_REGEX_OPERATOR_MATCHER)) {
3111
3250
  return `${acc}${curr}`;
3112
3251
  }
3113
3252
  let value;
3114
3253
  // check if element is in dot notation to get from field instances
3115
3254
  const element = curr.split('.');
3116
3255
  const currElementContent = element.length > 1 ? this.getValue({
3117
- key: element[0],
3118
- property: element[1],
3119
- path: element.slice(2)
3120
- }) : element;
3256
+ scope: element[0],
3257
+ key: element[1],
3258
+ property: element[2],
3259
+ path: element.slice(3)
3260
+ }) : curr;
3121
3261
  let currValue;
3122
3262
  // if any parsable content was passed to the conditions, parse them
3123
3263
  // required to be able to apply conditions
@@ -3138,6 +3278,11 @@ class FormCore {
3138
3278
  case 'object':
3139
3279
  if (currValue === null) {
3140
3280
  value = null;
3281
+ break;
3282
+ }
3283
+ if (currValue instanceof Date) {
3284
+ value = `new Date(\`${currValue}\`)`;
3285
+ break;
3141
3286
  }
3142
3287
  value = JSON.stringify(currValue);
3143
3288
  break;
@@ -3164,8 +3309,8 @@ class FormCore {
3164
3309
  * @returns {string} The expression string with the replacements made.
3165
3310
  */
3166
3311
  replaceExpression(expression, values) {
3167
- const regex = /\${(.*?)}/g;
3168
- return expression.replace(regex, () => values.shift() || '');
3312
+ const regex = TEMPLATE_REGEX_DELIMITATOR;
3313
+ return expression.replace(regex, () => String(values.shift()) || '');
3169
3314
  }
3170
3315
  /**
3171
3316
  * Checks if an expression string contains string concatenation within a marked expression.
@@ -3174,8 +3319,7 @@ class FormCore {
3174
3319
  * @returns {boolean} True if the expression contains string concatenation, otherwise false.
3175
3320
  */
3176
3321
  hasStringConcatenation(expression) {
3177
- const regex = /^\${[^${}]*}$/;
3178
- return !regex.test(expression);
3322
+ return !TEMPLATE_REGEX_STRING_CONCATENATION_DETECTOR.test(expression);
3179
3323
  }
3180
3324
  /**
3181
3325
  * Refreshes templates with updated values.
@@ -3195,7 +3339,7 @@ class FormCore {
3195
3339
  originExpression,
3196
3340
  originFieldKeys
3197
3341
  }) => {
3198
- if (originFieldKeys.includes(key)) {
3342
+ if (!key || originFieldKeys.includes(key)) {
3199
3343
  const originExpressions = this.extractParams(originExpression);
3200
3344
  let originValue;
3201
3345
  if (this.hasStringConcatenation(originExpression)) {
@@ -3204,6 +3348,7 @@ class FormCore {
3204
3348
  originValue = originExpressions === null || originExpressions === void 0 ? void 0 : originExpressions[0];
3205
3349
  }
3206
3350
  const destinationValue = this.getValue({
3351
+ scope: 'fields',
3207
3352
  key: destinationKey,
3208
3353
  property: destinationProperty,
3209
3354
  path: destinationPath
@@ -3221,6 +3366,26 @@ class FormCore {
3221
3366
  }
3222
3367
  });
3223
3368
  }
3369
+ /**
3370
+ * @internal
3371
+ * Update field visibility accordingly.
3372
+ *
3373
+ * @param {string} field - Field name to be updated.
3374
+ * @param {boolean} hasError - Condition to be used as visibility.
3375
+ * @param {boolean|undefined} showOnlyIfTrue - Flag to be considered when update field visibility. If it's true, then considered error, if it's false, always considered the opposite.
3376
+ */
3377
+ setFieldVisibility(field, hasError, showOnlyIfTrue) {
3378
+ if (!this.fields.has(field)) {
3379
+ /*
3380
+ * check with João the need for a global mount event for the form
3381
+ * to avoid not displaying this message in some cases (AsFormField)
3382
+ * suggestion: create subscriber to targetField.ON_FIELD_MOUNT
3383
+ */
3384
+ console.warn(`failed to update visibility onto field ${field}`);
3385
+ } else {
3386
+ this.fields.get(field).visibility = showOnlyIfTrue ? hasError : !hasError;
3387
+ }
3388
+ }
3224
3389
  /**
3225
3390
  * Validates visibility conditions for a given event and updates field visibility accordingly.
3226
3391
  *
@@ -3234,21 +3399,44 @@ class FormCore {
3234
3399
  }) {
3235
3400
  const field = this.fields.get(key);
3236
3401
  const structVisibility = field === null || field === void 0 ? void 0 : field.visibilityConditions;
3237
- if (!structVisibility || !(structVisibility === null || structVisibility === void 0 ? void 0 : structVisibility.some(config => config.events.includes(event)))) return;
3402
+ if (!structVisibility || !(structVisibility === null || structVisibility === void 0 ? void 0 : structVisibility.some(config => config.events.includes(event)))) {
3403
+ return;
3404
+ }
3238
3405
  structVisibility.forEach(structElement => {
3239
3406
  if (!structElement.events.includes(event)) return;
3240
3407
  Object.keys(structElement.validations).forEach(validationKey => {
3241
- const error = validations[validationKey](field.value, structElement.validations);
3408
+ const error = handleValidation(field.value, structElement.validations, validations, validationKey);
3242
3409
  if (Array.isArray(structElement.fields)) {
3243
3410
  structElement.fields.forEach(fieldKey => {
3244
- if (!this.fields.has(fieldKey)) console.warn(`failed to update visibility onto field ${fieldKey}`);else this.fields.get(fieldKey).visibility = error;
3411
+ this.setFieldVisibility(fieldKey, error, !!(field.value && structElement.showOnlyIfTrue));
3245
3412
  });
3246
3413
  } else if (structElement.fields) {
3247
- if (!this.fields.has(structElement.fields)) console.warn(`failed to update visibility onto field ${structElement.fields}`);else this.fields.get(structElement.fields).visibility = error;
3414
+ this.setFieldVisibility(structElement.fields, error, !!(field.value && structElement.showOnlyIfTrue));
3248
3415
  }
3249
3416
  });
3250
3417
  });
3251
3418
  }
3419
+ /**
3420
+ * @internal
3421
+ * Update field value and emit change and cleared event.
3422
+ *
3423
+ * @param {string} key - Field name to be updated.
3424
+ * @param {unknown} value - Value to be inserted into field.
3425
+ */
3426
+ setResetFieldValue(key, value) {
3427
+ if (!this.fields.has(key)) {
3428
+ console.warn(`failed to reset value onto field ${key}`);
3429
+ } else {
3430
+ const field = this.fields.get(key);
3431
+ field.emitValue({
3432
+ value: value,
3433
+ event: 'ON_FIELD_CHANGE'
3434
+ });
3435
+ field.emitEvents({
3436
+ event: 'ON_FIELD_CLEARED'
3437
+ });
3438
+ }
3439
+ }
3252
3440
  /**
3253
3441
  * Resets field values based on reset conditions defined in the schema.
3254
3442
  *
@@ -3265,23 +3453,23 @@ class FormCore {
3265
3453
  if (!structResetValue || !(structResetValue === null || structResetValue === void 0 ? void 0 : structResetValue.some(config => config.events.includes(event)))) return;
3266
3454
  structResetValue.forEach(structElement => {
3267
3455
  if (!structElement.events.includes(event)) return;
3456
+ const handleFieldRestoration = () => {
3457
+ if (Array.isArray(structElement.fields)) {
3458
+ structElement.fields.forEach((fieldKey, index) => {
3459
+ const resettledValue = Array.isArray(structElement.resettledValue) ? structElement.resettledValue[index] : structElement.resettledValue;
3460
+ this.setResetFieldValue(fieldKey, resettledValue);
3461
+ });
3462
+ } else if (structElement.fields) {
3463
+ this.setResetFieldValue(structElement.fields, structElement.resettledValue);
3464
+ }
3465
+ };
3466
+ if (!structElement.validations) {
3467
+ return handleFieldRestoration();
3468
+ }
3268
3469
  Object.keys(structElement.validations).forEach(validationKey => {
3269
- const error = validations[validationKey](field.value, structElement.validations);
3470
+ const error = handleValidation(field.value, structElement.validations, validations, validationKey);
3270
3471
  if (!error) {
3271
- if (Array.isArray(structElement.fields)) {
3272
- structElement.fields.forEach((fieldKey, index) => {
3273
- const resettledValue = Array.isArray(structElement.resettledValue) ? structElement.resettledValue[index] : structElement.resettledValue;
3274
- if (!this.fields.has(fieldKey)) console.warn(`failed to reset value onto field ${fieldKey}`);else this.fields.get(fieldKey).emitValue({
3275
- value: resettledValue,
3276
- event: 'ON_FIELD_CHANGE'
3277
- });
3278
- });
3279
- } else if (structElement.fields) {
3280
- if (!this.fields.has(structElement.fields)) console.warn(`failed to reset value onto field ${structElement.fields}`);else this.fields.get(structElement.fields).emitValue({
3281
- value: structElement.resettledValue,
3282
- event: 'ON_FIELD_CHANGE'
3283
- });
3284
- }
3472
+ handleFieldRestoration();
3285
3473
  }
3286
3474
  });
3287
3475
  });
@@ -3290,6 +3478,7 @@ class FormCore {
3290
3478
  * Adds a field onto the form instance regardless there is a schema or not
3291
3479
  *
3292
3480
  * @param fieldSchema
3481
+ * @param mapperElement
3293
3482
  */
3294
3483
  addField({
3295
3484
  fieldSchema,
@@ -3299,7 +3488,7 @@ class FormCore {
3299
3488
  if (this.fields.has(fieldSchema.name)) {
3300
3489
  throw new Error(`field name ${fieldSchema.name} already defined`);
3301
3490
  }
3302
- const mapper = mapperElement || ((_a = this.mappers) === null || _a === void 0 ? void 0 : _a.find(mapEl => mapEl.componentName === fieldSchema.component));
3491
+ const mapper = mapperElement || ((_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(fieldSchema.component));
3303
3492
  if (!mapper) throw new Error(`mapper not found for ${fieldSchema.component}, add it to the mappers configuration`);
3304
3493
  this.fields.set(fieldSchema.name, new FormField({
3305
3494
  schemaComponent: fieldSchema,
@@ -3315,6 +3504,7 @@ class FormCore {
3315
3504
  }));
3316
3505
  this.subscribeTemplates();
3317
3506
  this.refreshTemplates({
3507
+ scope: 'fields',
3318
3508
  event: 'ON_FIELDS',
3319
3509
  key: fieldSchema.name
3320
3510
  });
@@ -3330,6 +3520,7 @@ class FormCore {
3330
3520
  this.fields.delete(key);
3331
3521
  this.subscribeTemplates();
3332
3522
  this.templateSubject$.next({
3523
+ scope: 'fields',
3333
3524
  key,
3334
3525
  event: 'ON_FIELDS'
3335
3526
  });
@@ -3346,7 +3537,12 @@ class FormCore {
3346
3537
  var _a, _b, _c;
3347
3538
  const currField = this.fields.get(structElement.name);
3348
3539
  if (!currField) {
3349
- const mapper = (_a = this.mappers) === null || _a === void 0 ? void 0 : _a.find(mapEl => mapEl.componentName === structElement.component);
3540
+ let mapper;
3541
+ if (structElement === null || structElement === void 0 ? void 0 : structElement.mapper) {
3542
+ mapper = structElement === null || structElement === void 0 ? void 0 : structElement.mapper;
3543
+ } else {
3544
+ mapper = (_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(structElement.component);
3545
+ }
3350
3546
  if (!mapper) throw new Error(`mapper not found for ${structElement.component}, add it to the mappers configuration`);
3351
3547
  this.fields.set(structElement.name, new FormField({
3352
3548
  schemaComponent: structElement,
@@ -3364,10 +3560,11 @@ class FormCore {
3364
3560
  } else {
3365
3561
  currField.children = ((_c = structElement === null || structElement === void 0 ? void 0 : structElement.children) === null || _c === void 0 ? void 0 : _c.map(el => el.name)) || (currField === null || currField === void 0 ? void 0 : currField.children) || [];
3366
3562
  currField.path = path;
3563
+ currField.originalSchema = structElement;
3367
3564
  currField.templateSubject$ = this.templateSubject$;
3368
3565
  }
3369
3566
  if (structElement.children) {
3370
- return this.serializeStructure(structElement.children, `${path ? `${path}.` : ``}${structElement.name}`);
3567
+ this.serializeStructure(structElement.children, `${path ? `${path}.` : ``}${structElement.name}`);
3371
3568
  }
3372
3569
  });
3373
3570
  }
@@ -3399,6 +3596,7 @@ class FormCore {
3399
3596
  this.subscribedTemplates.forEach(el => {
3400
3597
  el.originFieldKeys.forEach(field => {
3401
3598
  this.templateSubject$.next({
3599
+ scope: 'fields',
3402
3600
  key: field,
3403
3601
  event: 'ON_FIELDS'
3404
3602
  });
@@ -3424,10 +3622,10 @@ class FormCore {
3424
3622
  const values = {};
3425
3623
  this.fields.forEach((val, key) => {
3426
3624
  if (val.value) {
3427
- values[key] = val.value;
3625
+ set(values, val.nameToSubmit || key, val.value);
3428
3626
  }
3429
3627
  });
3430
- console.log(values);
3628
+ console.table(values);
3431
3629
  }
3432
3630
  /**
3433
3631
  * Gets the current values of all form fields.
@@ -3435,11 +3633,11 @@ class FormCore {
3435
3633
  * @returns {TFormValues} The current form values.
3436
3634
  */
3437
3635
  getFormValues() {
3438
- let values = {};
3636
+ const values = {};
3439
3637
  const erroredFields = [];
3440
3638
  this.fields.forEach((val, key) => {
3441
3639
  if (val.value) {
3442
- values = Object.assign(Object.assign({}, values), encapsulateIn(values, val.nameToSubmit || key, val.value));
3640
+ set(values, val.nameToSubmit || key, val.value);
3443
3641
  }
3444
3642
  if (!val.valid) {
3445
3643
  erroredFields.push(key);
@@ -3459,6 +3657,45 @@ class FormCore {
3459
3657
  });
3460
3658
  return sub;
3461
3659
  }
3660
+ subscribeOnMount(callback) {
3661
+ const sub = this.mountSubject$.pipe(map(() => this.getFormValues())).subscribe({
3662
+ next: callback
3663
+ });
3664
+ return sub;
3665
+ }
3666
+ /**
3667
+ *
3668
+ * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
3669
+ */
3670
+ subscribeData(callback) {
3671
+ const sub = this.dataSubject$.pipe(groupBy(payload => payload.event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS))), map(({
3672
+ key
3673
+ }) => ({
3674
+ field: key,
3675
+ data: this.getFormValues()
3676
+ }))).subscribe({
3677
+ next: callback
3678
+ });
3679
+ return sub;
3680
+ }
3681
+ subscribeOnSubmit(callback) {
3682
+ const sub = this.submitSubject$.pipe(map(() => this.getFormValues())).subscribe({
3683
+ next: callback
3684
+ });
3685
+ return sub;
3686
+ }
3687
+ /**
3688
+ * Submits the form by triggering form field events and invoking the onSubmit callback.
3689
+ */
3690
+ mounted() {
3691
+ this.fields.forEach(field => {
3692
+ field.emitEvents({
3693
+ event: 'ON_FORM_MOUNT'
3694
+ });
3695
+ });
3696
+ const values = this.getFormValues();
3697
+ this.mountSubject$.next(values);
3698
+ }
3462
3699
  /**
3463
3700
  * Submits the form by triggering form field events and invoking the onSubmit callback.
3464
3701
  */
@@ -3471,11 +3708,10 @@ class FormCore {
3471
3708
  if (!this.isValid) return;
3472
3709
  const values = this.getFormValues();
3473
3710
  this.submitSubject$.next(values);
3474
- this.onSubmit && this.onSubmit(values);
3475
3711
  }
3476
3712
  destroy() {
3477
3713
  this.submitSubject$.unsubscribe();
3478
- this.templateSubject$.unsubscribe();
3714
+ this.templateSubscription$.unsubscribe();
3479
3715
  this.fieldEventSubject$.unsubscribe();
3480
3716
  this.dataSubject$.unsubscribe();
3481
3717
  this.fields.forEach(field => field.destroyField());
@@ -3492,15 +3728,19 @@ class FormCore {
3492
3728
  */
3493
3729
  FormCore.checkIndexes = (struct, indexes = []) => {
3494
3730
  if (!struct) return indexes;
3495
- for (let i = 0; i < struct.length; i++) {
3496
- const structElement = struct[i];
3497
- if (structElement.name === IVARPROPNAME) {
3498
- throw new Error(`reserved ${IVARPROPNAME} name for field names`);
3499
- }
3500
- indexes.push(structElement.name);
3501
- if (structElement.children) {
3502
- return FormCore.checkIndexes(structElement.children, indexes);
3731
+ const helper = (struct, indexes) => {
3732
+ for (let i = 0; i < struct.length; i++) {
3733
+ const structElement = struct[i];
3734
+ indexes.push(structElement.name);
3735
+ if (structElement.children) {
3736
+ helper(structElement.children, indexes);
3737
+ }
3503
3738
  }
3739
+ };
3740
+ helper(struct, indexes);
3741
+ const duppedIndexes = indexes.filter((item, index) => indexes.indexOf(item) !== index);
3742
+ if (duppedIndexes.length > 0) {
3743
+ throw new Error(`duplicated indexes found on schema: ${JSON.stringify(duppedIndexes)}`);
3504
3744
  }
3505
3745
  return indexes;
3506
3746
  };
@@ -3608,29 +3848,56 @@ class FormGroup {
3608
3848
  * Prints the form group instance to the console.
3609
3849
  */
3610
3850
  printFormGroupInstance() {
3611
- console.log(this.forms);
3851
+ console.table(this.forms);
3612
3852
  }
3613
3853
  /**
3614
3854
  * Prototype submit function to multiple forms
3615
3855
  * @param {string[]} indexes form indexes to be submitted
3616
3856
  * @returns
3617
3857
  */
3618
- submitMultipleFormsByIndex(indexes) {
3858
+ submitMultipleFormsByIndex(indexes, callback) {
3619
3859
  let isValid = true;
3620
3860
  let values = {};
3621
3861
  let erroredFields = [];
3622
3862
  indexes.forEach(index => {
3623
- var _a;
3624
- const res = (_a = this.forms.get(index)) === null || _a === void 0 ? void 0 : _a.getFormValues();
3863
+ var _a, _b;
3864
+ (_a = this.forms.get(index)) === null || _a === void 0 ? void 0 : _a.submit();
3865
+ const res = (_b = this.forms.get(index)) === null || _b === void 0 ? void 0 : _b.getFormValues();
3625
3866
  isValid = isValid && ((res === null || res === void 0 ? void 0 : res.isValid) || false);
3626
3867
  values = Object.assign(Object.assign({}, values), (res === null || res === void 0 ? void 0 : res.values) || {});
3627
3868
  erroredFields = [...erroredFields, ...((res === null || res === void 0 ? void 0 : res.erroredFields) || [])];
3628
3869
  });
3629
- return {
3870
+ isValid && callback && callback({
3630
3871
  erroredFields,
3631
3872
  isValid,
3632
3873
  values
3633
- };
3874
+ });
3875
+ }
3876
+ onDataSubscription({
3877
+ ids,
3878
+ callback
3879
+ }) {
3880
+ const subs = ids.reduce((acc, formId) => {
3881
+ var _a;
3882
+ // @TODO add config on debounceTime on this events
3883
+ const sub = (_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.dataSubject$.pipe(groupBy(payload => `${formId}.${payload.event}`), mergeMap(group$ => group$.pipe(debounceTime(100))), map(({
3884
+ key
3885
+ }) => {
3886
+ var _a;
3887
+ return {
3888
+ formField: key,
3889
+ values: (_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.getFormValues()
3890
+ };
3891
+ }));
3892
+ if (sub) {
3893
+ acc[formId] = sub;
3894
+ } else {
3895
+ console.warn(`failed to register form id ${formId}`);
3896
+ }
3897
+ return acc;
3898
+ }, {});
3899
+ const sub = combineLatest(subs).subscribe(callback);
3900
+ return sub;
3634
3901
  }
3635
3902
  }
3636
3903