@bolttech/form-engine-core 0.0.1-beta.8 → 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,43 +143,35 @@ 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
  }
@@ -681,16 +684,9 @@ const currency = (value, masks) => {
681
684
  if (integerPart === '') {
682
685
  integerPart = '0';
683
686
  }
684
- console.log('Before', {
685
- convertedValue
686
- });
687
687
  if (align === 'right' && String(value).endsWith(' ')) {
688
688
  convertedValue = convertedValue.slice(0, -1);
689
689
  }
690
- console.log('After', {
691
- value,
692
- convertedValue
693
- });
694
690
  let newRawValue = integerPart;
695
691
  let decimalPart = convertedValue.slice(convertedValue.length - precision);
696
692
  if (precision > 0) {
@@ -729,10 +725,11 @@ const custom = (value, masks) => {
729
725
  if (!masks.custom || !value) return value;
730
726
  let mask = '';
731
727
  let index = 0;
728
+ const convertedValue = value.replace(/[^\w\s]/gi, '');
732
729
  for (let i = 0; i < masks.custom.length; i++) {
733
730
  if (masks.custom[i] === '#') {
734
- if (index < value.length) {
735
- mask += value[index];
731
+ if (index < convertedValue.length) {
732
+ mask += convertedValue[index];
736
733
  index++;
737
734
  } else {
738
735
  break;
@@ -1234,6 +1231,44 @@ const between = (value, validations) => {
1234
1231
  const num = Number(value);
1235
1232
  return !Number.isNaN(num) && !(+num >= validations.between.start && +num <= validations.between.end);
1236
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
+ };
1237
1272
  /**
1238
1273
  * Validates if a value contains sequential numbers.
1239
1274
  *
@@ -1928,41 +1963,44 @@ const conditions = (value, validations) => {
1928
1963
  };
1929
1964
 
1930
1965
  /**
1931
- * 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.
1932
1969
  *
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`.
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`.
1936
1975
  *
1937
1976
  * @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
- * };
1977
+ * // Returns false
1978
+ * invalidStringDate("25/07/1997", "DDMMYYYY");
1945
1979
  *
1946
- * const result1 = betweenDates('2023-06-01', validations);
1947
- * console.log(result1); // false (date is within the range)
1980
+ * @example
1981
+ * // Returns false
1982
+ * invalidStringDate("1997-07-25", "YYYYMMDD");
1948
1983
  *
1949
- * const result2 = betweenDates('2024-01-01', validations);
1950
- * console.log(result2); // true (date is outside the range)
1951
- * ```
1984
+ * @example
1985
+ * // Returns true, as the format does not match
1986
+ * invalidStringDate("1997/25/07", "MMDDYYYY");
1952
1987
  */
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
- }
1988
+ const invalidStringDate = (value, format) => {
1989
+ if (!value.includes('/') && !value.includes('-') || !format) {
1990
+ return true;
1964
1991
  }
1965
- 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);
1966
2004
  };
1967
2005
  /**
1968
2006
  * @internal
@@ -2015,6 +2053,44 @@ const dateRearrangeMapper = {
2015
2053
  MMDDYYYY: value => value,
2016
2054
  timestamp: value => new Date(value).toString()
2017
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
+ };
2018
2094
  /**
2019
2095
  * @function date
2020
2096
  * Validates a date value based on various date conditions and intervals.
@@ -2048,12 +2124,13 @@ const dateRearrangeMapper = {
2048
2124
  * ```
2049
2125
  */
2050
2126
  const date = (value, validations) => {
2051
- 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;
2052
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)) {
2053
2129
  return false;
2054
2130
  }
2055
2131
  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());
2132
+ const mappedValue = dateRearrangeMapper[(_e = validations.date) === null || _e === void 0 ? void 0 : _e.origin.format](originValue).toString();
2133
+ let originDate = new Date(mappedValue);
2057
2134
  let targetDate = new Date();
2058
2135
  let target = new Date();
2059
2136
  if ((_g = (_f = validations.date) === null || _f === void 0 ? void 0 : _f.target) === null || _g === void 0 ? void 0 : _g.format) {
@@ -2072,6 +2149,9 @@ const date = (value, validations) => {
2072
2149
  if (validations.date.onlyValidDate && (!(targetDate instanceof Date && isFinite(targetDate)) || !(targetDate instanceof Date && isFinite(originDate)) || originValue.length < 8)) {
2073
2150
  return true;
2074
2151
  }
2152
+ if (invalidStringDate(mappedValue, (_l = validations.date) === null || _l === void 0 ? void 0 : _l.origin.format)) {
2153
+ return false;
2154
+ }
2075
2155
  const originTimestamp = originDate.getTime();
2076
2156
  const targetTimestamp = targetDate.getTime();
2077
2157
  const operationsMapper = {
@@ -2082,7 +2162,7 @@ const date = (value, validations) => {
2082
2162
  '===': originTimestamp === targetTimestamp,
2083
2163
  '!==': originTimestamp !== targetTimestamp
2084
2164
  };
2085
- 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];
2086
2166
  };
2087
2167
  /**
2088
2168
  * @function validDate
@@ -2111,11 +2191,74 @@ const validDate = (value, validations) => {
2111
2191
  const month = parseInt(dateParts[0], 10) - 1; // Month is zero-based
2112
2192
  const day = parseInt(dateParts[1], 10);
2113
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
+ }
2114
2207
  // Check if the date is valid
2115
2208
  const isValidDate = date.getFullYear() === year && date.getMonth() === month && date.getDate() === day;
2116
2209
  return !isValidDate;
2117
2210
  };
2118
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
+
2119
2262
  /**
2120
2263
  * @internal
2121
2264
  * An object mapping validation keys to their respective validation functions.
@@ -2129,6 +2272,8 @@ const validDate = (value, validations) => {
2129
2272
  const validations$1 = {
2130
2273
  max,
2131
2274
  min,
2275
+ lessThan,
2276
+ greaterThan,
2132
2277
  length,
2133
2278
  regex,
2134
2279
  url,
@@ -2150,7 +2295,6 @@ const validations$1 = {
2150
2295
  notEmpty,
2151
2296
  bool,
2152
2297
  exists,
2153
- greaterThan: () => true,
2154
2298
  isNumber,
2155
2299
  conditions,
2156
2300
  validDate,
@@ -2174,20 +2318,26 @@ const validations$1 = {
2174
2318
  * const results = run('test@example.com', handlers);
2175
2319
  * console.log(results); // [false, false, true] (value fails 'max', passes 'required', passes 'email')
2176
2320
  */
2177
- const run = (value, handlers) => {
2321
+ function run(value, handlers) {
2178
2322
  const runner = [];
2179
2323
  Object.keys(handlers).forEach(rule => {
2180
- runner.push(validations$1[rule](value, {
2181
- [rule]: handlers[rule]
2182
- }));
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);
2183
2333
  });
2184
2334
  return runner;
2185
- };
2335
+ }
2186
2336
  /**
2187
2337
  * Validates that a value meets multiple validation rules.
2188
2338
  *
2189
2339
  * @param {number | string | boolean} value - The value to be validated.
2190
- * @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.
2191
2341
  * @returns {boolean} - Returns `true` if the value meets the specified multiple validation rules, otherwise `false`.
2192
2342
  *
2193
2343
  * @example
@@ -2212,20 +2362,22 @@ const run = (value, handlers) => {
2212
2362
  * console.log(result2); // false
2213
2363
  * ```
2214
2364
  */
2215
- const multipleValidations = (value, validations) => {
2216
- if (!validations.multipleValidations) return false;
2217
- 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);
2218
2368
  const rulesMapper = {
2219
2369
  AND: () => runner.every(validation => validation),
2220
2370
  OR: () => runner.some(validation => validation),
2221
2371
  NOT: () => !runner.every(validation => validation)
2222
2372
  };
2223
- return rulesMapper[validations.multipleValidations.rule]();
2373
+ return rulesMapper[methods.multipleValidations.rule]();
2224
2374
  };
2225
2375
 
2226
2376
  const validations = {
2227
2377
  max,
2228
2378
  min,
2379
+ lessThan,
2380
+ greaterThan,
2229
2381
  length,
2230
2382
  regex,
2231
2383
  url,
@@ -2247,7 +2399,6 @@ const validations = {
2247
2399
  notEmpty,
2248
2400
  bool,
2249
2401
  exists,
2250
- greaterThan: () => true,
2251
2402
  isNumber,
2252
2403
  conditions,
2253
2404
  multipleValidations,
@@ -2256,8 +2407,52 @@ const validations = {
2256
2407
  validDate
2257
2408
  };
2258
2409
 
2259
- const DEFAULT_API_DEBOUNCE_TIME = 1000;
2260
- 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
+ }
2261
2456
 
2262
2457
  /**
2263
2458
  * Represents a form field with observables for managing form state, validations, and API requests.
@@ -2290,25 +2485,27 @@ class FormField {
2290
2485
  mapper
2291
2486
  }) {
2292
2487
  var _a, _b, _c, _d, _e, _f, _g;
2488
+ this.valueSubscription$ = new Subscription();
2293
2489
  this.fieldStateSubscription$ = new Subscription();
2294
- this.originalSchema = schemaComponent;
2490
+ this.originalSchema = cloneDeep(schemaComponent);
2295
2491
  this.config = {
2296
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,
2297
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
2298
2494
  };
2299
2495
  this.name = schemaComponent.name;
2496
+ this.nameToSubmit = schemaComponent.nameToSubmit;
2300
2497
  this.component = schemaComponent.component;
2301
2498
  this.path = path;
2302
2499
  this.children = children;
2303
2500
  this.validations = schemaComponent.validations;
2304
- this.errorMessages = schemaComponent.errorMessages;
2501
+ this.errorMessages = (_a = schemaComponent.validations) === null || _a === void 0 ? void 0 : _a.messages;
2305
2502
  this.visibilityConditions = schemaComponent.visibilityConditions;
2306
2503
  this.resetValues = schemaComponent.resetValues;
2307
2504
  this.apiSchema = schemaComponent.api;
2308
2505
  this.formatters = schemaComponent.formatters;
2309
2506
  this.masks = schemaComponent.masks;
2310
2507
  if (mapper.valueChangeEvent) this.valueChangeEvent = mapper.valueChangeEvent;
2311
- 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;
2312
2509
  this.mapper = mapper;
2313
2510
  this.validateVisibility = validateVisibility;
2314
2511
  this.resetValue = resetValue;
@@ -2316,10 +2513,6 @@ class FormField {
2316
2513
  this.fieldEventSubject$ = fieldEventSubject$;
2317
2514
  this.dataSubject$ = dataSubject$;
2318
2515
  this._props = schemaComponent.props || {};
2319
- this._value = '';
2320
- this._stateValue = ((_b = this.mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) ? {
2321
- [this.mapper.events.setValue]: ''
2322
- } : {};
2323
2516
  this._metadata = '';
2324
2517
  this.errorsString = '';
2325
2518
  this.errorsList = [];
@@ -2339,7 +2532,9 @@ class FormField {
2339
2532
  };
2340
2533
  this._errors = {};
2341
2534
  this._valid = false;
2535
+ this._mounted = true;
2342
2536
  this.initializeObservers();
2537
+ this.value = this.initialValue || '';
2343
2538
  }
2344
2539
  /**
2345
2540
  * method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
@@ -2347,25 +2542,25 @@ class FormField {
2347
2542
  initializeObservers() {
2348
2543
  var _a;
2349
2544
  if (!this.valueSubject$ || this.valueSubject$.closed) {
2350
- this.valueSubject$ = new Subject();
2545
+ this.valueSubject$ = new SafeSubject(() => this._mounted);
2351
2546
  }
2352
2547
  if (!this.errorSubject$ || this.errorSubject$.closed) {
2353
- this.errorSubject$ = new Subject();
2548
+ this.errorSubject$ = new SafeSubject(() => this._mounted);
2354
2549
  }
2355
2550
  if (!this.visibilitySubject$ || this.visibilitySubject$.closed) {
2356
- this.visibilitySubject$ = new Subject();
2551
+ this.visibilitySubject$ = new SafeSubject(() => this._mounted);
2357
2552
  }
2358
2553
  if (!this.apiSubject$ || this.apiSubject$.closed) {
2359
- this.apiSubject$ = new Subject();
2554
+ this.apiSubject$ = new SafeSubject(() => this._mounted);
2360
2555
  }
2361
2556
  if (!this.propsSubject$ || this.propsSubject$.closed) {
2362
- this.propsSubject$ = new Subject();
2557
+ this.propsSubject$ = new SafeSubject(() => this._mounted);
2363
2558
  }
2364
2559
  if (!this.fieldStateSubscription$ || this.fieldStateSubscription$.closed) {
2365
2560
  this.fieldStateSubscription$ = new Subscription();
2366
2561
  }
2367
2562
  if (!this.apiEventQueueSubject$ || this.apiEventQueueSubject$.closed) {
2368
- this.apiEventQueueSubject$ = new Subject();
2563
+ this.apiEventQueueSubject$ = new SafeSubject(() => this._mounted);
2369
2564
  }
2370
2565
  this.fieldState$ = combineLatest({
2371
2566
  visibility: this.visibilitySubject$.pipe(startWith(this._visibility)),
@@ -2379,9 +2574,6 @@ class FormField {
2379
2574
  }) => event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultAPIdebounceTimeMS))), filter(() => this.apiSubject$ && !this.apiSubject$.closed)).subscribe(payload => {
2380
2575
  this.apiRequest(payload);
2381
2576
  });
2382
- if (!isNil(this.initialValue)) {
2383
- this.value = this.initialValue;
2384
- }
2385
2577
  }
2386
2578
  /**
2387
2579
  * Retrieves the properties associated with the form field.
@@ -2401,6 +2593,7 @@ class FormField {
2401
2593
  this._props = props;
2402
2594
  this.propsSubject$.next(this.props);
2403
2595
  this.templateSubject$.next({
2596
+ scope: 'fields',
2404
2597
  key: this.name,
2405
2598
  event: 'ON_PROPS'
2406
2599
  });
@@ -2464,6 +2657,7 @@ class FormField {
2464
2657
  }
2465
2658
  this.stateValue && this.valueSubject$.next(this.stateValue);
2466
2659
  this.templateSubject$.next({
2660
+ scope: 'fields',
2467
2661
  key: this.name,
2468
2662
  event: 'ON_VALUE'
2469
2663
  });
@@ -2484,8 +2678,26 @@ class FormField {
2484
2678
  set visibility(visible) {
2485
2679
  if (typeof visible === 'undefined' || visible === this.visibility) return;
2486
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
+ }
2487
2698
  this.visibilitySubject$.next(this.visibility);
2488
2699
  this.templateSubject$.next({
2700
+ scope: 'fields',
2489
2701
  key: this.name,
2490
2702
  event: 'ON_VISIBILITY'
2491
2703
  });
@@ -2518,12 +2730,13 @@ class FormField {
2518
2730
  this.errorsList = Object.values(this.errors);
2519
2731
  this.errorsString = this.errorsList.join(', ');
2520
2732
  /**
2521
- * 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
2522
2734
  */
2523
2735
  ((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setErrorMessage) && this.errorSubject$.next({
2524
2736
  [this.mapper.events.setErrorMessage]: this.errorsString
2525
2737
  });
2526
2738
  this.templateSubject$.next({
2739
+ scope: 'fields',
2527
2740
  key: this.name,
2528
2741
  event: 'ON_PROPS'
2529
2742
  });
@@ -2546,6 +2759,7 @@ class FormField {
2546
2759
  this._api = response;
2547
2760
  this.apiSubject$.next(this.api);
2548
2761
  this.templateSubject$.next({
2762
+ scope: 'fields',
2549
2763
  key: this.name,
2550
2764
  event: 'ON_API'
2551
2765
  });
@@ -2569,6 +2783,7 @@ class FormField {
2569
2783
  this.initializeObservers();
2570
2784
  this.subscribeValue(valueSubscription);
2571
2785
  this.subscribeState(propsSubscription);
2786
+ this._mounted = true;
2572
2787
  }
2573
2788
  /**
2574
2789
  * Sets the value of the form field and emits associated events.
@@ -2625,49 +2840,43 @@ class FormField {
2625
2840
  setFieldValidity({
2626
2841
  event
2627
2842
  }) {
2628
- var _a;
2629
- if (!this.validations) {
2843
+ var _a, _b, _c, _d;
2844
+ if (!this.validations || !this.visibility) {
2845
+ this.errors = {};
2630
2846
  this._valid = true;
2631
2847
  return;
2632
2848
  }
2633
- /*
2634
- @TODO evaluate if _valid flag needs to be updated on all events, this condition saves resources,
2635
- currently form submition needs to be controlled with form instance submit property function that
2636
- will evaluate if all fields are valid regardless the events that triggers error messages
2637
- */
2638
- if (!this.validations.events.includes(event) && event !== 'ON_FORM_SUBMIT') return;
2639
2849
  let valid = true;
2640
- const errors = Object.assign({}, this.errors);
2641
- 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;
2642
2852
  schemaValidations && Object.keys(schemaValidations).forEach(validationKey => {
2643
- var _a, _b;
2644
- const error = validations[validationKey](this.value, schemaValidations);
2853
+ const error = handleValidation(this.value, schemaValidations, validations, validationKey);
2645
2854
  // setting valid flag
2646
2855
  valid = !error && valid;
2647
2856
  // setting error messages
2648
- if (((_a = this.validations) === null || _a === void 0 ? void 0 : _a.events.includes(event)) || event === 'ON_FORM_SUBMIT') {
2649
- if (error && ((_b = this.errorMessages) === null || _b === void 0 ? void 0 : _b[validationKey])) {
2650
- errors[validationKey] = this.errorMessages[validationKey];
2651
- } else {
2652
- 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;
2653
2863
  }
2864
+ } else {
2865
+ delete errors[validationKey];
2654
2866
  }
2655
2867
  });
2656
2868
  this._valid = valid;
2657
- this.errors = errors;
2658
- }
2659
- /**
2660
- * WIP expensive function to get updated field validity on each event
2661
- */
2662
- updateValidityFlag() {
2663
- var _a;
2664
- let valid = true;
2665
- const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.config;
2666
- schemaValidations && Object.keys(schemaValidations).forEach(validationKey => {
2667
- const error = validations[validationKey](this.value, schemaValidations);
2668
- valid = !error && valid;
2669
- });
2670
- 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
+ }
2671
2880
  }
2672
2881
  /**
2673
2882
  * Formats the field value using the specified formatters, if available.
@@ -2701,7 +2910,7 @@ class FormField {
2701
2910
  let valid = true;
2702
2911
  const preConditions = config.preConditions;
2703
2912
  preConditions && Object.keys(preConditions).forEach(validationKey => {
2704
- const error = validations[validationKey](this.value, preConditions);
2913
+ const error = handleValidation(this.value, preConditions, validations, validationKey);
2705
2914
  valid = valid && !error;
2706
2915
  });
2707
2916
  if (config.blockRequestWhenInvalid) {
@@ -2720,6 +2929,17 @@ class FormField {
2720
2929
  }) {
2721
2930
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
2722
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
+ });
2723
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 => {
2724
2944
  var _a, _b;
2725
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);
@@ -2730,19 +2950,10 @@ class FormField {
2730
2950
  };
2731
2951
  const config = (_e = this.apiSchema.defaultConfig) === null || _e === void 0 ? void 0 : _e.config;
2732
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)) {
2733
- try {
2734
- const responseData = yield makeRequest(config.method, config.url, config.headers, config.body);
2735
- const apiResponseData = JSON.parse(String(responseData));
2736
- const response = config.resultPath ? get(apiResponseData, config.resultPath) : apiResponseData;
2737
- // this.apiResponseData = { response };
2738
- responses.default = {
2739
- response
2740
- };
2741
- } catch (e) {
2742
- responses.default = {
2743
- response: !isNil(config === null || config === void 0 ? void 0 : config.fallbackValue) ? config.fallbackValue : 'error'
2744
- };
2745
- }
2953
+ const response = yield configRequest(config);
2954
+ responses.default = {
2955
+ response
2956
+ };
2746
2957
  }
2747
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 => {
2748
2959
  var _a, _b;
@@ -2753,38 +2964,21 @@ class FormField {
2753
2964
  @TODO handle promises with error
2754
2965
  */
2755
2966
  const result = yield Promise.all(Object.keys(this.apiSchema.configs).map(configKey => __awaiter(this, void 0, void 0, function* () {
2756
- return new Promise(res => {
2757
- var _a, _b, _c, _d;
2758
- const config = (_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.configs) === null || _b === void 0 ? void 0 : _b[configKey].config;
2759
- 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)) {
2760
- try {
2761
- makeRequest(config.method, config.url, config.headers, config.body).then(responseData => {
2762
- const apiResponseData = JSON.parse(String(responseData));
2763
- const response = get(apiResponseData, config.resultPath || '');
2764
- res({
2765
- name: configKey,
2766
- result: {
2767
- response
2768
- }
2769
- });
2770
- // responses.named![configKey] = { response };
2771
- });
2772
- } catch (e) {
2773
- res({
2774
- name: configKey,
2775
- result: {
2776
- response: !isNil(config.fallbackValue) ? config.fallbackValue : 'error'
2777
- }
2778
- });
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
2779
2975
  }
2780
- }
2781
- });
2976
+ };
2977
+ }
2978
+ return null;
2782
2979
  })));
2783
- result.forEach(({
2784
- name,
2785
- result
2786
- }) => {
2787
- if (responses.named) responses.named[name] = result;
2980
+ result.forEach(payload => {
2981
+ if (payload && responses.named) responses.named[payload.name] = payload.result;
2788
2982
  });
2789
2983
  }
2790
2984
  }
@@ -2797,7 +2991,8 @@ class FormField {
2797
2991
  * @returns {void}
2798
2992
  */
2799
2993
  destroyField() {
2800
- this.valueSubject$.unsubscribe();
2994
+ this._mounted = false;
2995
+ this.valueSubscription$.unsubscribe();
2801
2996
  this.visibilitySubject$.unsubscribe();
2802
2997
  this.fieldStateSubscription$.unsubscribe();
2803
2998
  this.propsSubject$.unsubscribe();
@@ -2823,13 +3018,12 @@ class FormField {
2823
3018
  * @returns {void}
2824
3019
  */
2825
3020
  subscribeValue(callback) {
2826
- this.valueSubject$.subscribe({
3021
+ this.valueSubscription$ = this.valueSubject$.subscribe({
2827
3022
  next: callback
2828
3023
  });
2829
3024
  }
2830
3025
  }
2831
3026
 
2832
- const IVARPROPNAME = 'iVars';
2833
3027
  /**
2834
3028
  * Represents the core logic for managing a form, including field management, validation, and submission.
2835
3029
  */
@@ -2843,38 +3037,39 @@ class FormCore {
2843
3037
  * @param {string} [entry.action] - The action attribute of the form.
2844
3038
  * @param {string} [entry.method] - The method attribute of the form.
2845
3039
  * @param {IFormSchema.iVars} [entry.iVars] - The internal variables of the form.
2846
- * @param {(data: TFormValues) => void} [entry.onSubmit] - A callback function to handle form submission.
2847
3040
  * @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
2848
3041
  */
2849
3042
  constructor(entry) {
2850
- 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();
2851
3046
  this.schema = entry.schema;
2852
3047
  this.fields = new Map();
2853
3048
  this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
2854
3049
  this.action = entry.action || ((_b = entry.schema) === null || _b === void 0 ? void 0 : _b.action);
2855
3050
  this.method = entry.method || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.method);
2856
3051
  this._iVars = entry.iVars || ((_d = entry.schema) === null || _d === void 0 ? void 0 : _d.iVars) || {};
2857
- this.onSubmit = entry.onSubmit;
2858
3052
  this.config = {
2859
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,
2860
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
2861
3055
  };
2862
- 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
+ });
2863
3059
  this.schema && FormCore.checkIndexes(this.schema.components);
2864
3060
  this.templateSubject$ = new Subject();
2865
3061
  this.submitSubject$ = new Subject();
2866
3062
  this.fieldEventSubject$ = new Subject();
2867
3063
  this.dataSubject$ = new Subject();
2868
- this.dataCallbackSubscription$ = new Subscription();
3064
+ this.mountSubject$ = new Subject();
2869
3065
  this.subscribedTemplates = [];
2870
3066
  this.schema && this.serializeStructure(this.schema.components);
2871
3067
  this.schema && this.subscribeTemplates();
2872
- this.templateSubject$.subscribe(this.refreshTemplates.bind(this));
3068
+ this.templateSubscription$ = this.templateSubject$.subscribe(this.refreshTemplates.bind(this));
2873
3069
  this.templateSubject$.next({
2874
- key: IVARPROPNAME,
3070
+ scope: 'iVars',
2875
3071
  event: 'ON_IVARS'
2876
3072
  });
2877
- entry.onData && this.subscribeData(entry.onData);
2878
3073
  /*
2879
3074
  mount events needs to occur on form level, only when all the fields are instantiated
2880
3075
  is it possible to apply all the side effects that occur globally, same effect occur
@@ -2885,6 +3080,7 @@ class FormCore {
2885
3080
  event: 'ON_FIELD_MOUNT'
2886
3081
  });
2887
3082
  this.refreshTemplates({
3083
+ scope: 'fields',
2888
3084
  key,
2889
3085
  event: 'ON_FIELDS'
2890
3086
  });
@@ -2906,7 +3102,7 @@ class FormCore {
2906
3102
  set iVars(payload) {
2907
3103
  this._iVars = payload;
2908
3104
  this.templateSubject$.next({
2909
- key: IVARPROPNAME,
3105
+ scope: 'iVars',
2910
3106
  event: 'ON_IVARS'
2911
3107
  });
2912
3108
  }
@@ -2934,7 +3130,6 @@ class FormCore {
2934
3130
  validations,
2935
3131
  visibilityConditions,
2936
3132
  resetValues,
2937
- errorMessages,
2938
3133
  api
2939
3134
  }
2940
3135
  }, key) => {
@@ -2945,26 +3140,11 @@ class FormCore {
2945
3140
  validations,
2946
3141
  visibilityConditions,
2947
3142
  resetValues,
2948
- errorMessages,
2949
3143
  apiSchema: api
2950
3144
  };
2951
3145
  traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
2952
3146
  });
2953
3147
  }
2954
- /**
2955
- *
2956
- * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
2957
- */
2958
- subscribeData(callback) {
2959
- this.dataCallbackSubscription$ = this.dataSubject$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS), map(({
2960
- key
2961
- }) => ({
2962
- field: key,
2963
- data: this.getFormValues()
2964
- }))).subscribe({
2965
- next: callback
2966
- });
2967
- }
2968
3148
  /**
2969
3149
  * Gets the value of a property from a field.
2970
3150
  *
@@ -2975,20 +3155,27 @@ class FormCore {
2975
3155
  * @returns {unknown | undefined} The value of the property, or undefined if the field doesn't exist.
2976
3156
  */
2977
3157
  getValue({
3158
+ scope,
2978
3159
  key,
2979
3160
  property,
2980
3161
  path
2981
3162
  }) {
2982
- if (key === IVARPROPNAME) {
2983
- const value = get(this.iVars, [property, ...path]);
2984
- return value;
2985
- }
2986
- const field = this.fields.get(key);
2987
- if (!field) return console.warn(`failed to get value from ${key}`);
2988
- if (property === 'props' && path[0] === field.valuePropName) {
2989
- 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
+ }
2990
3178
  }
2991
- return path.length > 0 ? get(field[property], path) : field[property];
2992
3179
  }
2993
3180
  /**
2994
3181
  * Sets the value of a property in a field.
@@ -3051,27 +3238,26 @@ class FormCore {
3051
3238
  * @returns {string[]} An array of extracted parameters.
3052
3239
  */
3053
3240
  extractParams(expression) {
3054
- const regex = /\${(.*?)}/g;
3055
3241
  const extractedValues = [];
3056
3242
  let match;
3057
- while (!isNil(match = regex.exec(expression))) {
3243
+ while (!isNil(match = TEMPLATE_REGEX_DELIMITATOR.exec(expression))) {
3058
3244
  extractedValues.push(match[1]);
3059
3245
  }
3060
- const operatorRegex = /\s*(\|\||&&|!)\s*/g;
3061
- const splittedString = extractedValues.map(el => el.split(operatorRegex));
3246
+ const splittedString = extractedValues.map(el => el.split(TEMPLATE_REGEX_OPERATOR_SPLITTER));
3062
3247
  const result = splittedString.map(splittedStringVal => {
3063
3248
  return splittedStringVal.filter(Boolean).reduce((acc, curr) => {
3064
- if (curr.match(/^\|\||&&|!$/)) {
3249
+ if (curr.match(TEMPLATE_REGEX_OPERATOR_MATCHER)) {
3065
3250
  return `${acc}${curr}`;
3066
3251
  }
3067
3252
  let value;
3068
3253
  // check if element is in dot notation to get from field instances
3069
3254
  const element = curr.split('.');
3070
3255
  const currElementContent = element.length > 1 ? this.getValue({
3071
- key: element[0],
3072
- property: element[1],
3073
- path: element.slice(2)
3074
- }) : element;
3256
+ scope: element[0],
3257
+ key: element[1],
3258
+ property: element[2],
3259
+ path: element.slice(3)
3260
+ }) : curr;
3075
3261
  let currValue;
3076
3262
  // if any parsable content was passed to the conditions, parse them
3077
3263
  // required to be able to apply conditions
@@ -3092,6 +3278,11 @@ class FormCore {
3092
3278
  case 'object':
3093
3279
  if (currValue === null) {
3094
3280
  value = null;
3281
+ break;
3282
+ }
3283
+ if (currValue instanceof Date) {
3284
+ value = `new Date(\`${currValue}\`)`;
3285
+ break;
3095
3286
  }
3096
3287
  value = JSON.stringify(currValue);
3097
3288
  break;
@@ -3118,8 +3309,8 @@ class FormCore {
3118
3309
  * @returns {string} The expression string with the replacements made.
3119
3310
  */
3120
3311
  replaceExpression(expression, values) {
3121
- const regex = /\${(.*?)}/g;
3122
- return expression.replace(regex, () => values.shift() || '');
3312
+ const regex = TEMPLATE_REGEX_DELIMITATOR;
3313
+ return expression.replace(regex, () => String(values.shift()) || '');
3123
3314
  }
3124
3315
  /**
3125
3316
  * Checks if an expression string contains string concatenation within a marked expression.
@@ -3128,8 +3319,7 @@ class FormCore {
3128
3319
  * @returns {boolean} True if the expression contains string concatenation, otherwise false.
3129
3320
  */
3130
3321
  hasStringConcatenation(expression) {
3131
- const regex = /^\${[^${}]*}$/;
3132
- return !regex.test(expression);
3322
+ return !TEMPLATE_REGEX_STRING_CONCATENATION_DETECTOR.test(expression);
3133
3323
  }
3134
3324
  /**
3135
3325
  * Refreshes templates with updated values.
@@ -3149,7 +3339,7 @@ class FormCore {
3149
3339
  originExpression,
3150
3340
  originFieldKeys
3151
3341
  }) => {
3152
- if (originFieldKeys.includes(key)) {
3342
+ if (!key || originFieldKeys.includes(key)) {
3153
3343
  const originExpressions = this.extractParams(originExpression);
3154
3344
  let originValue;
3155
3345
  if (this.hasStringConcatenation(originExpression)) {
@@ -3158,6 +3348,7 @@ class FormCore {
3158
3348
  originValue = originExpressions === null || originExpressions === void 0 ? void 0 : originExpressions[0];
3159
3349
  }
3160
3350
  const destinationValue = this.getValue({
3351
+ scope: 'fields',
3161
3352
  key: destinationKey,
3162
3353
  property: destinationProperty,
3163
3354
  path: destinationPath
@@ -3175,6 +3366,26 @@ class FormCore {
3175
3366
  }
3176
3367
  });
3177
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
+ }
3178
3389
  /**
3179
3390
  * Validates visibility conditions for a given event and updates field visibility accordingly.
3180
3391
  *
@@ -3188,21 +3399,44 @@ class FormCore {
3188
3399
  }) {
3189
3400
  const field = this.fields.get(key);
3190
3401
  const structVisibility = field === null || field === void 0 ? void 0 : field.visibilityConditions;
3191
- 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
+ }
3192
3405
  structVisibility.forEach(structElement => {
3193
3406
  if (!structElement.events.includes(event)) return;
3194
3407
  Object.keys(structElement.validations).forEach(validationKey => {
3195
- const error = validations[validationKey](field.value, structElement.validations);
3408
+ const error = handleValidation(field.value, structElement.validations, validations, validationKey);
3196
3409
  if (Array.isArray(structElement.fields)) {
3197
3410
  structElement.fields.forEach(fieldKey => {
3198
- 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));
3199
3412
  });
3200
3413
  } else if (structElement.fields) {
3201
- 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));
3202
3415
  }
3203
3416
  });
3204
3417
  });
3205
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
+ }
3206
3440
  /**
3207
3441
  * Resets field values based on reset conditions defined in the schema.
3208
3442
  *
@@ -3219,23 +3453,23 @@ class FormCore {
3219
3453
  if (!structResetValue || !(structResetValue === null || structResetValue === void 0 ? void 0 : structResetValue.some(config => config.events.includes(event)))) return;
3220
3454
  structResetValue.forEach(structElement => {
3221
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
+ }
3222
3469
  Object.keys(structElement.validations).forEach(validationKey => {
3223
- const error = validations[validationKey](field.value, structElement.validations);
3470
+ const error = handleValidation(field.value, structElement.validations, validations, validationKey);
3224
3471
  if (!error) {
3225
- if (Array.isArray(structElement.fields)) {
3226
- structElement.fields.forEach((fieldKey, index) => {
3227
- const resettledValue = Array.isArray(structElement.resettledValue) ? structElement.resettledValue[index] : structElement.resettledValue;
3228
- if (!this.fields.has(fieldKey)) console.warn(`failed to reset value onto field ${fieldKey}`);else this.fields.get(fieldKey).emitValue({
3229
- value: resettledValue,
3230
- event: 'ON_FIELD_CHANGE'
3231
- });
3232
- });
3233
- } else if (structElement.fields) {
3234
- if (!this.fields.has(structElement.fields)) console.warn(`failed to reset value onto field ${structElement.fields}`);else this.fields.get(structElement.fields).emitValue({
3235
- value: structElement.resettledValue,
3236
- event: 'ON_FIELD_CHANGE'
3237
- });
3238
- }
3472
+ handleFieldRestoration();
3239
3473
  }
3240
3474
  });
3241
3475
  });
@@ -3244,6 +3478,7 @@ class FormCore {
3244
3478
  * Adds a field onto the form instance regardless there is a schema or not
3245
3479
  *
3246
3480
  * @param fieldSchema
3481
+ * @param mapperElement
3247
3482
  */
3248
3483
  addField({
3249
3484
  fieldSchema,
@@ -3253,7 +3488,7 @@ class FormCore {
3253
3488
  if (this.fields.has(fieldSchema.name)) {
3254
3489
  throw new Error(`field name ${fieldSchema.name} already defined`);
3255
3490
  }
3256
- 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));
3257
3492
  if (!mapper) throw new Error(`mapper not found for ${fieldSchema.component}, add it to the mappers configuration`);
3258
3493
  this.fields.set(fieldSchema.name, new FormField({
3259
3494
  schemaComponent: fieldSchema,
@@ -3269,6 +3504,7 @@ class FormCore {
3269
3504
  }));
3270
3505
  this.subscribeTemplates();
3271
3506
  this.refreshTemplates({
3507
+ scope: 'fields',
3272
3508
  event: 'ON_FIELDS',
3273
3509
  key: fieldSchema.name
3274
3510
  });
@@ -3284,6 +3520,7 @@ class FormCore {
3284
3520
  this.fields.delete(key);
3285
3521
  this.subscribeTemplates();
3286
3522
  this.templateSubject$.next({
3523
+ scope: 'fields',
3287
3524
  key,
3288
3525
  event: 'ON_FIELDS'
3289
3526
  });
@@ -3300,7 +3537,12 @@ class FormCore {
3300
3537
  var _a, _b, _c;
3301
3538
  const currField = this.fields.get(structElement.name);
3302
3539
  if (!currField) {
3303
- 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
+ }
3304
3546
  if (!mapper) throw new Error(`mapper not found for ${structElement.component}, add it to the mappers configuration`);
3305
3547
  this.fields.set(structElement.name, new FormField({
3306
3548
  schemaComponent: structElement,
@@ -3318,10 +3560,11 @@ class FormCore {
3318
3560
  } else {
3319
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) || [];
3320
3562
  currField.path = path;
3563
+ currField.originalSchema = structElement;
3321
3564
  currField.templateSubject$ = this.templateSubject$;
3322
3565
  }
3323
3566
  if (structElement.children) {
3324
- return this.serializeStructure(structElement.children, `${path ? `${path}.` : ``}${structElement.name}`);
3567
+ this.serializeStructure(structElement.children, `${path ? `${path}.` : ``}${structElement.name}`);
3325
3568
  }
3326
3569
  });
3327
3570
  }
@@ -3353,6 +3596,7 @@ class FormCore {
3353
3596
  this.subscribedTemplates.forEach(el => {
3354
3597
  el.originFieldKeys.forEach(field => {
3355
3598
  this.templateSubject$.next({
3599
+ scope: 'fields',
3356
3600
  key: field,
3357
3601
  event: 'ON_FIELDS'
3358
3602
  });
@@ -3378,10 +3622,10 @@ class FormCore {
3378
3622
  const values = {};
3379
3623
  this.fields.forEach((val, key) => {
3380
3624
  if (val.value) {
3381
- values[key] = val.value;
3625
+ set(values, val.nameToSubmit || key, val.value);
3382
3626
  }
3383
3627
  });
3384
- console.log(values);
3628
+ console.table(values);
3385
3629
  }
3386
3630
  /**
3387
3631
  * Gets the current values of all form fields.
@@ -3393,7 +3637,7 @@ class FormCore {
3393
3637
  const erroredFields = [];
3394
3638
  this.fields.forEach((val, key) => {
3395
3639
  if (val.value) {
3396
- values[key] = val.value;
3640
+ set(values, val.nameToSubmit || key, val.value);
3397
3641
  }
3398
3642
  if (!val.valid) {
3399
3643
  erroredFields.push(key);
@@ -3413,6 +3657,45 @@ class FormCore {
3413
3657
  });
3414
3658
  return sub;
3415
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
+ }
3416
3699
  /**
3417
3700
  * Submits the form by triggering form field events and invoking the onSubmit callback.
3418
3701
  */
@@ -3425,11 +3708,10 @@ class FormCore {
3425
3708
  if (!this.isValid) return;
3426
3709
  const values = this.getFormValues();
3427
3710
  this.submitSubject$.next(values);
3428
- this.onSubmit && this.onSubmit(values);
3429
3711
  }
3430
3712
  destroy() {
3431
3713
  this.submitSubject$.unsubscribe();
3432
- this.templateSubject$.unsubscribe();
3714
+ this.templateSubscription$.unsubscribe();
3433
3715
  this.fieldEventSubject$.unsubscribe();
3434
3716
  this.dataSubject$.unsubscribe();
3435
3717
  this.fields.forEach(field => field.destroyField());
@@ -3446,15 +3728,19 @@ class FormCore {
3446
3728
  */
3447
3729
  FormCore.checkIndexes = (struct, indexes = []) => {
3448
3730
  if (!struct) return indexes;
3449
- for (let i = 0; i < struct.length; i++) {
3450
- const structElement = struct[i];
3451
- if (structElement.name === IVARPROPNAME) {
3452
- throw new Error(`reserved ${IVARPROPNAME} name for field names`);
3453
- }
3454
- indexes.push(structElement.name);
3455
- if (structElement.children) {
3456
- 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
+ }
3457
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)}`);
3458
3744
  }
3459
3745
  return indexes;
3460
3746
  };
@@ -3562,29 +3848,56 @@ class FormGroup {
3562
3848
  * Prints the form group instance to the console.
3563
3849
  */
3564
3850
  printFormGroupInstance() {
3565
- console.log(this.forms);
3851
+ console.table(this.forms);
3566
3852
  }
3567
3853
  /**
3568
3854
  * Prototype submit function to multiple forms
3569
3855
  * @param {string[]} indexes form indexes to be submitted
3570
3856
  * @returns
3571
3857
  */
3572
- submitMultipleFormsByIndex(indexes) {
3858
+ submitMultipleFormsByIndex(indexes, callback) {
3573
3859
  let isValid = true;
3574
3860
  let values = {};
3575
3861
  let erroredFields = [];
3576
3862
  indexes.forEach(index => {
3577
- var _a;
3578
- 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();
3579
3866
  isValid = isValid && ((res === null || res === void 0 ? void 0 : res.isValid) || false);
3580
3867
  values = Object.assign(Object.assign({}, values), (res === null || res === void 0 ? void 0 : res.values) || {});
3581
3868
  erroredFields = [...erroredFields, ...((res === null || res === void 0 ? void 0 : res.erroredFields) || [])];
3582
3869
  });
3583
- return {
3870
+ isValid && callback && callback({
3584
3871
  erroredFields,
3585
3872
  isValid,
3586
3873
  values
3587
- };
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;
3588
3901
  }
3589
3902
  }
3590
3903