@flowerforce/flower-core 3.1.1 → 3.1.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/dist/index.cjs.js CHANGED
@@ -23,864 +23,840 @@ var flat = require('flat');
23
23
  const Emitter = new tinyEmitter.TinyEmitter();
24
24
 
25
25
  const EMPTY_STRING_REGEXP = /^\s*$/;
26
+ /**
27
+ * Defines a utility object named rulesMatcherUtils, which contains various helper functions used for processing rules and data in a rule-matching context.
28
+ */
26
29
  const rulesMatcherUtils = {
27
- isNumber: el => {
28
- const num = String(el);
29
- return !!num.match(/(^-?|^\d+\.)\d+$/);
30
- },
31
- rule: (val, data, options) => {
32
- const {
33
- prefix
34
- } = options || {};
35
- const path = Object.keys(val)[0];
36
- const valueBlock = val;
37
- const pathWithPrefix = rulesMatcherUtils.getPath(path, prefix);
38
- const valueForKey = _get(data, pathWithPrefix, undefined);
39
- const {
40
- name
41
- } = _get(valueBlock, [path], {}) || {};
42
- const {
43
- op,
44
- value,
45
- opt
46
- } = rulesMatcherUtils.getDefaultRule(valueBlock[path]);
47
- const valueRef = value && String(value).indexOf('$ref:') === 0 ? _get(data, rulesMatcherUtils.getPath(value.replace('$ref:', ''), prefix), undefined) : value;
48
- if (!operators[op]) {
49
- throw new Error(`Error missing operator:${op}`);
50
- }
51
- const valid = operators[op] && operators[op](valueForKey, valueRef, opt, data);
52
- return {
53
- valid,
54
- name: `${pathWithPrefix}___${name || op}`
55
- };
56
- },
57
- getKey: (block, keys, options) => {
58
- if (Object.prototype.hasOwnProperty.call(block, '$and')) {
59
- return block.$and.map(item => rulesMatcherUtils.getKey(item, keys, options));
60
- }
61
- if (Object.prototype.hasOwnProperty.call(block, '$or')) {
62
- return block.$or.map(item => rulesMatcherUtils.getKey(item, keys, options));
63
- }
64
- const {
65
- prefix
66
- } = options || {};
67
- const path = Object.keys(block)[0];
68
- const valueBlock = block;
69
- const res = rulesMatcherUtils.getPath(path, prefix);
70
- const {
71
- value
72
- } = rulesMatcherUtils.getDefaultRule(valueBlock[path]);
73
- if (value && String(value).indexOf('$ref:') === 0) {
74
- keys[rulesMatcherUtils.getPath(value.replace('$ref:', ''), prefix)] = true;
75
- }
76
- return keys[res] = true;
77
- },
78
- isDate: v => v instanceof Date,
79
- isDefined: v => v !== null && v !== undefined,
80
- isObject: v => v === Object(v),
81
- isFunction: v => typeof v === 'function',
82
- isString: v => typeof v === 'string',
83
- getDefaultStringValue: value => {
84
- switch (value) {
85
- case '$required':
86
- return {
87
- op: '$exists',
88
- value: true
89
- };
90
- case '$exists':
91
- return {
92
- op: '$exists',
93
- value: true
94
- };
95
- default:
96
- return {
97
- op: '$eq',
98
- value
99
- };
100
- }
101
- },
102
- getTypeOf: value => Array.isArray(value) ? 'array' : rulesMatcherUtils.isNumber(value) ? 'number' : value === null ? null : typeof value,
103
- getDefaultRule: value => {
104
- const valueType = rulesMatcherUtils.getTypeOf(value);
105
- switch (valueType) {
106
- case 'number':
107
- return {
108
- op: '$eq',
109
- value
110
- };
111
- case 'string':
112
- return rulesMatcherUtils.getDefaultStringValue(value);
113
- case 'boolean':
114
- return {
115
- op: '$exists',
116
- value
117
- };
118
- case 'array':
119
- return {
120
- op: '$in',
121
- value
122
- };
123
- case 'object':
124
- return {
125
- ...value,
126
- op: value.op || Object.keys(value)[0],
127
- value: value.value || value[Object.keys(value)[0]]
128
- };
129
- default:
130
- return {
131
- op: '$eq',
132
- value
133
- };
134
- }
135
- },
136
- isEmpty: value => {
137
- if (!rulesMatcherUtils.isDefined(value)) {
138
- return true;
139
- }
140
- if (rulesMatcherUtils.isFunction(value)) {
141
- return false;
142
- }
143
- if (rulesMatcherUtils.isString(value)) {
144
- return EMPTY_STRING_REGEXP.test(value);
145
- }
146
- if (Array.isArray(value)) {
147
- return value.length === 0;
148
- }
149
- if (rulesMatcherUtils.isDate(value)) {
150
- return false;
151
- }
152
- let attr;
153
- if (rulesMatcherUtils.isObject(value)) {
154
- for (attr in value) {
30
+ isNumber: (el) => {
31
+ const num = String(el);
32
+ return !!num.match(/(^-?|^\d+\.)\d+$/);
33
+ },
34
+ rule: (val, data, options) => {
35
+ const { prefix } = options || {};
36
+ const path = Object.keys(val)[0];
37
+ const valueBlock = val;
38
+ const pathWithPrefix = rulesMatcherUtils.getPath(path, prefix);
39
+ const valueForKey = _get(data, pathWithPrefix, undefined);
40
+ const { name } = _get(valueBlock, [path], {}) || {};
41
+ const { op, value, opt } = rulesMatcherUtils.getDefaultRule(valueBlock[path]);
42
+ const valueRef = value && String(value).indexOf('$ref:') === 0
43
+ ? _get(data, rulesMatcherUtils.getPath(value.replace('$ref:', ''), prefix), undefined)
44
+ : value;
45
+ if (!operators[op]) {
46
+ throw new Error(`Error missing operator:${op}`);
47
+ }
48
+ const valid = operators[op] && operators[op](valueForKey, valueRef, opt, data);
49
+ return { valid, name: `${pathWithPrefix}___${name || op}` };
50
+ },
51
+ getKey: (block, keys, options) => {
52
+ if (Object.prototype.hasOwnProperty.call(block, '$and')) {
53
+ return block.$and.map((item) => rulesMatcherUtils.getKey(item, keys, options));
54
+ }
55
+ if (Object.prototype.hasOwnProperty.call(block, '$or')) {
56
+ return block.$or.map((item) => rulesMatcherUtils.getKey(item, keys, options));
57
+ }
58
+ const { prefix } = options || {};
59
+ const path = Object.keys(block)[0];
60
+ const valueBlock = block;
61
+ const res = rulesMatcherUtils.getPath(path, prefix);
62
+ const { value } = rulesMatcherUtils.getDefaultRule(valueBlock[path]);
63
+ if (value && String(value).indexOf('$ref:') === 0) {
64
+ keys[rulesMatcherUtils.getPath(value.replace('$ref:', ''), prefix)] = true;
65
+ }
66
+ return (keys[res] = true);
67
+ },
68
+ isDate: (v) => v instanceof Date,
69
+ isDefined: (v) => v !== null && v !== undefined,
70
+ isObject: (v) => v === Object(v),
71
+ isFunction: (v) => typeof v === 'function',
72
+ isString: (v) => typeof v === 'string',
73
+ getDefaultStringValue: (value) => {
74
+ switch (value) {
75
+ case '$required':
76
+ return { op: '$exists', value: true };
77
+ case '$exists':
78
+ return { op: '$exists', value: true };
79
+ default:
80
+ return { op: '$eq', value };
81
+ }
82
+ },
83
+ getTypeOf: (value) => Array.isArray(value)
84
+ ? 'array'
85
+ : rulesMatcherUtils.isNumber(value)
86
+ ? 'number'
87
+ : value === null
88
+ ? null
89
+ : typeof value,
90
+ getDefaultRule: (value) => {
91
+ const valueType = rulesMatcherUtils.getTypeOf(value);
92
+ switch (valueType) {
93
+ case 'number':
94
+ return { op: '$eq', value };
95
+ case 'string':
96
+ return rulesMatcherUtils.getDefaultStringValue(value);
97
+ case 'boolean':
98
+ return { op: '$exists', value };
99
+ case 'array':
100
+ return { op: '$in', value };
101
+ case 'object':
102
+ return {
103
+ ...value,
104
+ op: value.op || Object.keys(value)[0],
105
+ value: value.value || value[Object.keys(value)[0]]
106
+ };
107
+ default:
108
+ return { op: '$eq', value };
109
+ }
110
+ },
111
+ isEmpty: (value) => {
112
+ // Null and undefined are empty
113
+ if (!rulesMatcherUtils.isDefined(value)) {
114
+ return true;
115
+ }
116
+ // functions are non empty
117
+ if (rulesMatcherUtils.isFunction(value)) {
118
+ return false;
119
+ }
120
+ /* if (isBool(value)) {
121
+ return false;
122
+ }
123
+ */
124
+ // Whitespace only strings are empty
125
+ if (rulesMatcherUtils.isString(value)) {
126
+ return EMPTY_STRING_REGEXP.test(value);
127
+ }
128
+ // For arrays we use the length property
129
+ if (Array.isArray(value)) {
130
+ return value.length === 0;
131
+ }
132
+ // Dates have no attributes but aren't empty
133
+ if (rulesMatcherUtils.isDate(value)) {
134
+ return false;
135
+ }
136
+ // If we find at least one property we consider it non empty
137
+ let attr;
138
+ if (rulesMatcherUtils.isObject(value)) {
139
+ for (attr in value) {
140
+ return false;
141
+ }
142
+ return true;
143
+ }
155
144
  return false;
156
- }
157
- return true;
158
- }
159
- return false;
160
- },
161
- forceArray: a => Array.isArray(a) ? a : [a],
162
- getPath: (path, prefix) => {
163
- if (path.indexOf('^') === 0) {
164
- return _trimStart(path, '^');
165
- }
166
- if (path.indexOf('$') === 0) {
167
- return path;
168
- }
169
- return prefix ? `${prefix}.${path}` : path;
170
- },
171
- forceNumber: el => {
172
- if (Array.isArray(el)) {
173
- return el.length;
174
- }
175
- if (rulesMatcherUtils.isNumber(String(el))) {
176
- return parseFloat(String(el));
177
- }
178
- return 0;
179
- },
180
- checkRule: (block, data, options) => {
181
- if (!Array.isArray(block) && block && Object.prototype.hasOwnProperty.call(block, '$and')) {
182
- if (block && block['$and'] && !block['$and'].length) return true;
183
- return block['$and'].every(item => rulesMatcherUtils.checkRule(item, data, options));
184
- }
185
- if (!Array.isArray(block) && block && Object.prototype.hasOwnProperty.call(block, '$or')) {
186
- if (block && block['$or'] && !block['$or'].length) return true;
187
- return block['$or'].some(item => rulesMatcherUtils.checkRule(item, data, options));
145
+ },
146
+ forceArray: (a) => (Array.isArray(a) ? a : [a]),
147
+ getPath: (path, prefix) => {
148
+ if (path.indexOf('^') === 0) {
149
+ return _trimStart(path, '^');
150
+ }
151
+ // da verificare se è ancora utilizzato
152
+ if (path.indexOf('$') === 0) {
153
+ return path;
154
+ }
155
+ return prefix ? `${prefix}.${path}` : path;
156
+ },
157
+ // TODO BUG NUMERI CON LETTERE 1asdas o solo
158
+ forceNumber: (el) => {
159
+ if (Array.isArray(el)) {
160
+ return el.length;
161
+ }
162
+ if (rulesMatcherUtils.isNumber(String(el))) {
163
+ return parseFloat(String(el));
164
+ }
165
+ // fix perchè un valore false < 1 è true, quindi sbagliato, mentre un valore undefined < 1 è false
166
+ return 0;
167
+ },
168
+ checkRule: (block, data, options) => {
169
+ if (!Array.isArray(block) &&
170
+ block &&
171
+ Object.prototype.hasOwnProperty.call(block, '$and')) {
172
+ if (block && block['$and'] && !block['$and'].length)
173
+ return true;
174
+ return block['$and'].every((item) => rulesMatcherUtils.checkRule(item, data, options));
175
+ }
176
+ if (!Array.isArray(block) &&
177
+ block &&
178
+ Object.prototype.hasOwnProperty.call(block, '$or')) {
179
+ if (block && block['$or'] && !block['$or'].length)
180
+ return true;
181
+ return block['$or'].some((item) => rulesMatcherUtils.checkRule(item, data, options));
182
+ }
183
+ const res = rulesMatcherUtils.rule(block, data, options);
184
+ return res.valid;
185
+ },
186
+ getKeys: (rules, options) => {
187
+ if (!rules)
188
+ return null;
189
+ if (typeof rules == 'function')
190
+ return [];
191
+ if (!rulesMatcherUtils
192
+ .forceArray(rules)
193
+ .every((r) => rulesMatcherUtils.isObject(r)))
194
+ return null;
195
+ const keys = {};
196
+ const conditions = Array.isArray(rules) ? { $and: rules } : rules;
197
+ rulesMatcherUtils.getKey(conditions, keys, options ?? {});
198
+ return Object.keys(keys);
188
199
  }
189
- const res = rulesMatcherUtils.rule(block, data, options);
190
- return res.valid;
191
- },
192
- getKeys: (rules, options) => {
193
- if (!rules) return null;
194
- if (typeof rules == 'function') return [];
195
- if (!rulesMatcherUtils.forceArray(rules).every(r => rulesMatcherUtils.isObject(r))) return null;
196
- const keys = {};
197
- const conditions = Array.isArray(rules) ? {
198
- $and: rules
199
- } : rules;
200
- rulesMatcherUtils.getKey(conditions, keys, options ?? {});
201
- return Object.keys(keys);
202
- }
203
200
  };
201
+ /**
202
+ * Defines a set of comparison operators used for matching rules against user input.
203
+ */
204
204
  const operators = {
205
- $exists: (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
206
- $eq: (a, b) => a === b,
207
- $ne: (a, b) => a !== b,
208
- $gt: (a, b) => rulesMatcherUtils.forceNumber(a) > parseFloat(b),
209
- $gte: (a, b) => rulesMatcherUtils.forceNumber(a) >= parseFloat(b),
210
- $lt: (a, b) => rulesMatcherUtils.forceNumber(a) < parseFloat(b),
211
- $lte: (a, b) => rulesMatcherUtils.forceNumber(a) <= parseFloat(b),
212
- $strGt: (a, b) => String(a || '').length > parseFloat(b),
213
- $strGte: (a, b) => String(a || '').length >= parseFloat(b),
214
- $strLt: (a, b) => String(a || '').length < parseFloat(b),
215
- $strLte: (a, b) => String(a || '').length <= parseFloat(b),
216
- $in: (a, b) => rulesMatcherUtils.forceArray(b).some(c => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
217
- $nin: (a, b) => !rulesMatcherUtils.forceArray(b).some(c => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
218
- $all: (a, b) => rulesMatcherUtils.forceArray(b).every(c => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
219
- $regex: (a, b, opt) => rulesMatcherUtils.forceArray(b).some(c => c instanceof RegExp ? c.test(a) : new RegExp(c, opt).test(a))
205
+ $exists: (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
206
+ $eq: (a, b) => a === b,
207
+ $ne: (a, b) => a !== b,
208
+ $gt: (a, b) => rulesMatcherUtils.forceNumber(a) > parseFloat(b),
209
+ $gte: (a, b) => rulesMatcherUtils.forceNumber(a) >= parseFloat(b),
210
+ $lt: (a, b) => rulesMatcherUtils.forceNumber(a) < parseFloat(b),
211
+ $lte: (a, b) => rulesMatcherUtils.forceNumber(a) <= parseFloat(b),
212
+ $strGt: (a, b) => String(a || '').length > parseFloat(b),
213
+ $strGte: (a, b) => String(a || '').length >= parseFloat(b),
214
+ $strLt: (a, b) => String(a || '').length < parseFloat(b),
215
+ $strLte: (a, b) => String(a || '').length <= parseFloat(b),
216
+ $in: (a, b) => rulesMatcherUtils
217
+ .forceArray(b)
218
+ .some((c) => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
219
+ $nin: (a, b) => !rulesMatcherUtils
220
+ .forceArray(b)
221
+ .some((c) => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
222
+ $all: (a, b) => rulesMatcherUtils
223
+ .forceArray(b)
224
+ .every((c) => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
225
+ $regex: (a, b, opt) => rulesMatcherUtils
226
+ .forceArray(b)
227
+ .some((c) => c instanceof RegExp ? c.test(a) : new RegExp(c, opt).test(a))
220
228
  };
221
229
 
222
230
  const rulesMatcher = (rules, formValue = {}, apply = true, options) => {
223
- if (!rules) return [apply];
224
- if (typeof rules === 'function') {
225
- return [rules(formValue) === apply];
226
- }
227
- const conditions = Array.isArray(rules) ? {
228
- $and: rules
229
- } : rules;
230
- const valid = rulesMatcherUtils.checkRule(conditions, formValue, options ?? {});
231
- return [valid === apply];
231
+ if (!rules)
232
+ return [apply];
233
+ // if (typeof rules !== 'object' && !Array.isArray(rules)) {
234
+ // throw new Error('Rules accept only array or object');
235
+ // }
236
+ if (typeof rules === 'function') {
237
+ return [rules(formValue) === apply];
238
+ }
239
+ const conditions = Array.isArray(rules)
240
+ ? { $and: rules }
241
+ : rules;
242
+ const valid = rulesMatcherUtils.checkRule(conditions, formValue, options ?? {});
243
+ return [valid === apply];
232
244
  };
233
245
  const MatchRules = {
234
- rulesMatcher,
235
- utils: rulesMatcherUtils
246
+ rulesMatcher,
247
+ utils: rulesMatcherUtils
236
248
  };
237
249
 
238
- const flattenRules = ob => {
239
- const result = {};
240
- for (const i in ob) {
241
- if (ob[i] === null) {
242
- result[i] = null;
243
- }
244
- if (typeof ob[i] === 'object' && !Array.isArray(ob[i]) || Array.isArray(ob[i]) && typeof ob[i][0] === 'object') {
245
- const temp = flattenRules(ob[i]);
246
- for (const j in temp) {
247
- result[i + '*' + j] = temp[j];
248
- }
249
- } else {
250
- result[i] = ob[i];
250
+ /* eslint-disable no-useless-escape */
251
+ // TODO align this set of functions to selectors and reducers functions
252
+ const flattenRules = (ob) => {
253
+ const result = {};
254
+ for (const i in ob) {
255
+ if (ob[i] === null) {
256
+ result[i] = null;
257
+ }
258
+ if ((typeof ob[i] === 'object' && !Array.isArray(ob[i])) ||
259
+ (Array.isArray(ob[i]) && typeof ob[i][0] === 'object')) {
260
+ const temp = flattenRules(ob[i]);
261
+ for (const j in temp) {
262
+ result[i + '*' + j] = temp[j];
263
+ }
264
+ }
265
+ else {
266
+ result[i] = ob[i];
267
+ }
251
268
  }
252
- }
253
- return result;
269
+ return result;
254
270
  };
255
- const getRulesExists = rules => {
256
- return Object.keys(rules).length ? CoreUtils.mapEdge(rules) : undefined;
271
+ // export const searchEmptyKeyRecursively = <T extends object>(
272
+ // _key: "$and" | "$or",
273
+ // obj: T
274
+ // ) => {
275
+ // if (
276
+ // isEmpty(obj) ||
277
+ // typeof obj !== "object" ||
278
+ // Object.keys(obj).length === 0
279
+ // ) {
280
+ // return true;
281
+ // }
282
+ // if (Object.keys(obj).includes(_key)) {
283
+ // if (obj[_key] && obj[_key].length === 0) return true;
284
+ // return Object.keys(obj).map((key) =>
285
+ // searchEmptyKeyRecursively(_key, obj[key])
286
+ // );
287
+ // }
288
+ // return Object.keys(obj)
289
+ // .map((key) => searchEmptyKeyRecursively(_key, obj[key]))
290
+ // .every((v) => v === true);
291
+ // };
292
+ const getRulesExists = (rules) => {
293
+ return Object.keys(rules).length ? CoreUtils.mapEdge(rules) : undefined;
257
294
  };
295
+ /**
296
+ * Defines a collection of utility functions for processing rules, nodes and graph-like structures
297
+ */
258
298
  const CoreUtils = {
259
- generateRulesName: nextRules => {
260
- const a = nextRules.reduce((acc, inc) => {
261
- const n = typeof inc.rules === 'string' ? inc.rules || '__ERROR_NAME__' : inc.rules?.name || '__ERROR_NAME__';
262
- return {
263
- ...acc,
264
- [n]: inc.nodeId
265
- };
266
- }, {});
267
- return a;
268
- },
269
- mapKeysDeepLodash: (obj, cb, isRecursive) => {
270
- if (!obj && !isRecursive) {
271
- return {};
272
- }
273
- if (!isRecursive) {
274
- if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') {
275
- return {};
276
- }
277
- }
278
- if (Array.isArray(obj)) {
279
- return obj.map(item => CoreUtils.mapKeysDeepLodash(item, cb, true));
280
- }
281
- if (!isPlainObject(obj)) {
282
- return obj;
283
- }
284
- const result = mapKeys(obj, cb);
285
- return mapValues(result, value => CoreUtils.mapKeysDeepLodash(value, cb, true));
286
- },
287
- generateNodes: nodes => keyBy(nodes.map(s => omit(s, 'nextRules')), 'nodeId'),
288
- makeObjectRules: nodes => nodes.reduce((acc, inc) => ({
289
- ...acc,
290
- [inc.nodeId]: inc.nextRules
291
- }), {}),
292
- hasNode: (state, name, node) => has(state, [name, 'nodes', node]),
293
- isEmptyRules: rules => {
294
- if (isEmpty(rules)) return true;
295
- if (isEmpty(_get(rules, 'rules'))) return true;
296
- if (Object.keys(flattenRules(rules)).every(key => key.endsWith('$and') || key.endsWith('$or'))) {
297
- return true;
298
- }
299
- return false;
300
- },
301
- mapEdge: nextNode => {
302
- const res = nextNode.sort((a, b) => {
303
- const x = CoreUtils.isEmptyRules(a.rules);
304
- const y = CoreUtils.isEmptyRules(b.rules);
305
- return Number(x) - Number(y);
306
- });
307
- return res;
308
- },
309
- makeRules: rules => Object.entries(rules).reduce((acc2, [k, v]) => [...acc2, {
310
- nodeId: k,
311
- rules: v
312
- }], []),
313
- generateNodesForFlowerJson: nodes => nodes.filter(e => !!_get(e, 'props.id')).map(e => {
314
- const rules = CoreUtils.makeRules(e.props.to ?? {});
315
- const nextRules = getRulesExists(rules);
316
- const children = e.props.data?.children;
317
- return {
318
- nodeId: e.props.id,
319
- nodeType: e.type.displayName || e.props.as || 'FlowerNode',
320
- nodeTitle: _get(e.props, 'data.title'),
321
- children,
322
- nextRules,
323
- retain: e.props.retain,
324
- disabled: e.props.disabled
325
- };
326
- }),
327
- cleanPath: (name, char = '^') => _trimStart(name, char),
328
- getPath: idValue => {
329
- if (!idValue) {
330
- return {
331
- path: []
332
- };
333
- }
334
- if (idValue === '*') {
335
- return {
336
- path: '*'
337
- };
338
- }
339
- if (idValue.indexOf('^') === 0) {
340
- const [flowNameFromPath, ...rest] = CoreUtils.cleanPath(idValue).split('.');
341
- return {
342
- flowNameFromPath,
343
- path: rest
344
- };
345
- }
346
- return {
347
- path: idValue.split('.')
348
- };
349
- },
350
- allEqual: (arr, arr2) => arr.length === arr2.length && arr.every(v => arr2.includes(v)),
351
- findValidRule: (nextRules, value, prefix) => find(nextRules, rule => {
352
- if (typeof rule.rules === 'string') {
353
- return false;
354
- }
355
- if (typeof rule.rules === 'function') {
356
- return rule.rules(value);
357
- }
358
- if (rule.rules === null) {
359
- return true;
360
- }
361
- if (typeof rule.rules?.rules === 'undefined') {
362
- return false;
363
- }
364
- if (typeof rule.rules.rules === 'string') {
365
- return false;
366
- }
367
- const [valid] = MatchRules.rulesMatcher(rule.rules.rules, value, true, {
368
- prefix
369
- });
370
- return valid;
371
- })
299
+ generateRulesName: (nextRules) => {
300
+ const a = nextRules.reduce((acc, inc) => {
301
+ const n = typeof inc.rules === 'string'
302
+ ? inc.rules || '__ERROR_NAME__'
303
+ : inc.rules?.name || '__ERROR_NAME__';
304
+ return {
305
+ ...acc,
306
+ [n]: inc.nodeId
307
+ };
308
+ }, {});
309
+ return a;
310
+ },
311
+ mapKeysDeepLodash: (obj, cb, isRecursive) => {
312
+ /* istanbul ignore next */
313
+ if (!obj && !isRecursive) {
314
+ return {};
315
+ }
316
+ if (!isRecursive) {
317
+ /* istanbul ignore next */
318
+ if (typeof obj === 'string' ||
319
+ typeof obj === 'number' ||
320
+ typeof obj === 'boolean') {
321
+ return {};
322
+ }
323
+ }
324
+ if (Array.isArray(obj)) {
325
+ return obj.map((item) => CoreUtils.mapKeysDeepLodash(item, cb, true));
326
+ }
327
+ if (!isPlainObject(obj)) {
328
+ return obj;
329
+ }
330
+ const result = mapKeys(obj, cb);
331
+ return mapValues(result, (value) => CoreUtils.mapKeysDeepLodash(value, cb, true));
332
+ },
333
+ generateNodes: (nodes) => keyBy(nodes.map((s) => omit(s, 'nextRules')), 'nodeId'),
334
+ makeObjectRules: (nodes) => nodes.reduce((acc, inc) => ({ ...acc, [inc.nodeId]: inc.nextRules }), {}),
335
+ hasNode: (state, name, node) => has(state, [name, 'nodes', node]),
336
+ isEmptyRules: (rules) => {
337
+ if (isEmpty(rules))
338
+ return true;
339
+ if (isEmpty(_get(rules, 'rules')))
340
+ return true;
341
+ if (Object.keys(flattenRules(rules)).every((key) => key.endsWith('$and') || key.endsWith('$or'))) {
342
+ return true;
343
+ }
344
+ return false;
345
+ },
346
+ mapEdge: (nextNode) => {
347
+ const res = nextNode.sort((a, b) => {
348
+ const x = CoreUtils.isEmptyRules(a.rules);
349
+ const y = CoreUtils.isEmptyRules(b.rules);
350
+ return Number(x) - Number(y);
351
+ });
352
+ return res;
353
+ },
354
+ makeRules: (rules) => Object.entries(rules).reduce((acc2, [k, v]) => [...acc2, { nodeId: k, rules: v }], []),
355
+ generateNodesForFlowerJson: (nodes) => nodes
356
+ .filter((e) => !!_get(e, 'props.id'))
357
+ .map((e) => {
358
+ const rules = CoreUtils.makeRules(e.props.to ?? {});
359
+ const nextRules = getRulesExists(rules);
360
+ const children = e.props.data?.children;
361
+ return {
362
+ nodeId: e.props.id,
363
+ nodeType: e.type.displayName || e.props.as || 'FlowerNode',
364
+ nodeTitle: _get(e.props, 'data.title'),
365
+ children,
366
+ nextRules,
367
+ retain: e.props.retain,
368
+ disabled: e.props.disabled
369
+ };
370
+ }),
371
+ cleanPath: (name, char = '^') => _trimStart(name, char),
372
+ getPath: (idValue) => {
373
+ if (!idValue) {
374
+ return {
375
+ path: []
376
+ };
377
+ }
378
+ if (idValue === '*') {
379
+ return {
380
+ path: '*'
381
+ };
382
+ }
383
+ if (idValue.indexOf('^') === 0) {
384
+ const [flowNameFromPath, ...rest] = CoreUtils.cleanPath(idValue).split('.');
385
+ return {
386
+ flowNameFromPath,
387
+ path: rest
388
+ };
389
+ }
390
+ return {
391
+ path: idValue.split('.')
392
+ };
393
+ },
394
+ allEqual: (arr, arr2) => arr.length === arr2.length && arr.every((v) => arr2.includes(v)),
395
+ findValidRule: (nextRules, value, prefix) => find(nextRules, (rule) => {
396
+ // fix per evitare di entrare in un nodo senza regole, ma con un name,
397
+ // invocando un next() senza paramentri
398
+ if (typeof rule.rules === 'string') {
399
+ return false;
400
+ }
401
+ if (typeof rule.rules === 'function') {
402
+ return rule.rules(value);
403
+ }
404
+ if (rule.rules === null) {
405
+ return true;
406
+ }
407
+ if (typeof rule.rules?.rules === 'undefined') {
408
+ return false;
409
+ }
410
+ if (typeof rule.rules.rules === 'string') {
411
+ return false;
412
+ }
413
+ const [valid] = MatchRules.rulesMatcher(rule.rules.rules, value, true, {
414
+ prefix
415
+ });
416
+ return valid;
417
+ })
372
418
  };
373
419
 
374
420
  const FlowerStateUtils = {
375
- getAllData: state => state && Object.entries(state ?? {}).reduce((acc, [k, v]) => ({
376
- ...acc,
377
- [k]: v.data
378
- }), {}),
379
- selectFlowerFormNode: (name, id) => state => _get(state, [name, 'form', id]),
380
- makeSelectCurrentNextRules: name => state => {
381
- const nextRules = _get(state, [name, 'nextRules']);
382
- const currentNodeId = FlowerStateUtils.makeSelectCurrentNodeId(name)(state);
383
- return _get(nextRules, [currentNodeId]);
384
- },
385
- makeSelectCurrentNodeId: name => state => {
386
- const subState = _get(state, [name]);
387
- const startId = _get(state, ['startId']);
388
- return _get(subState, ['current']) || startId;
389
- },
390
- makeSelectNodeErrors: (name, currentNodeId) => state => {
391
- const form = FlowerStateUtils.selectFlowerFormNode(name, currentNodeId)(state);
392
- return {
393
- touched: form?.touched || false,
394
- errors: form?.errors,
395
- isValidating: form?.isValidating,
396
- isValid: form && form.errors ? Object.values(form.errors).flat().length === 0 : true
397
- };
398
- }
421
+ getAllData: (state) => state &&
422
+ Object.entries(state ?? {}).reduce((acc, [k, v]) => ({ ...acc, [k]: v.data }), {}),
423
+ selectFlowerFormNode: (name, id) => (state) => _get(state, [name, 'form', id]),
424
+ makeSelectCurrentNextRules: (name) => (state) => {
425
+ const nextRules = _get(state, [name, 'nextRules']);
426
+ const currentNodeId = FlowerStateUtils.makeSelectCurrentNodeId(name)(state);
427
+ return _get(nextRules, [currentNodeId]);
428
+ },
429
+ makeSelectCurrentNodeId: (name) => (state) => {
430
+ const subState = _get(state, [name]);
431
+ const startId = _get(state, ['startId']);
432
+ return _get(subState, ['current']) || startId;
433
+ },
434
+ makeSelectNodeErrors: (name, currentNodeId) => (state) => {
435
+ const form = FlowerStateUtils.selectFlowerFormNode(name, currentNodeId)(state);
436
+ return {
437
+ touched: form?.touched || false,
438
+ errors: form?.errors,
439
+ isValidating: form?.isValidating,
440
+ isValid: form && form.errors
441
+ ? Object.values(form.errors).flat().length === 0
442
+ : true
443
+ };
444
+ }
399
445
  };
400
446
 
401
- const {
402
- generateNodes,
403
- hasNode,
404
- makeObjectRules,
405
- generateRulesName,
406
- findValidRule
407
- } = CoreUtils;
447
+ const { generateNodes, hasNode, makeObjectRules, generateRulesName, findValidRule } = CoreUtils;
448
+ /**
449
+ * These functions are Redux reducers used in a Flux architecture for managing state transitions and updates in a Flower application.
450
+ */
408
451
  const FlowerCoreReducers = {
409
- historyAdd: (state, {
410
- payload
411
- }) => {
412
- if (hasNode(state, payload.name, payload.node)) {
413
- state[payload.name].history.push(payload.node);
414
- _set(state, [payload.name, 'current'], payload.node);
415
- }
416
- return state;
417
- },
418
- historyPrevToNode: (state, {
419
- payload
420
- }) => {
421
- const history = _get(state[typeof payload === 'string' ? payload : payload.name], ['history'], []);
422
- const lastIndex = lastIndexOf(history, typeof payload === 'string' ? payload : payload.node);
423
- if (lastIndex >= 0) {
424
- const newHistory = _slice(history, 0, lastIndex + 1);
425
- _set(state, [typeof payload === 'string' ? payload : payload.name, 'history'], newHistory);
426
- _set(state, [typeof payload === 'string' ? payload : payload.name, 'current'], typeof payload === 'string' ? payload : payload.node);
427
- }
428
- return state;
429
- },
430
- setFormTouched: (state, {
431
- payload
432
- }) => {
433
- if (!_get(state, [typeof payload === 'string' ? payload : payload.flowName, 'nodes', typeof payload === 'string' ? payload : payload.currentNode])) {
434
- return state;
435
- }
436
- _set(state, [typeof payload === 'string' ? payload : payload.flowName, 'form', typeof payload === 'string' ? payload : payload.currentNode, 'touched'], true);
437
- return state;
438
- },
439
- forceAddHistory: (state, {
440
- payload
441
- }) => {
442
- const {
443
- name,
444
- flowName,
445
- history
446
- } = payload;
447
- const key = name || flowName || '' || '';
448
- const resultHistory = history.slice(1, -1);
449
- state[key].history.push(...resultHistory);
450
- return state;
451
- },
452
- historyPop: (state, {
453
- payload
454
- }) => {
455
- const container = _get(state, [payload.name]);
456
- const startId = _get(container, ['startId']);
457
- const history = _get(container, ['history']);
458
- const originHistory = [..._get(container, ['history'], [])];
459
- if (originHistory.length < 2) {
460
- _set(state, [payload.name, 'current'], startId);
461
- _set(state, [payload.name, 'history'], [startId]);
462
- return state;
463
- }
464
- history.pop();
465
- const total = originHistory.length - 2;
466
- for (let index = total; index > 0; index--) {
467
- const curr = originHistory[index];
468
- const nodeType = _get(container, ['nodes', curr, 'nodeType']);
469
- const nodeDisabled = _get(container, ['nodes', curr, 'disabled']);
470
- if (nodeDisabled || nodeType === 'FlowerAction' || nodeType === 'FlowerServer' || !nodeType) {
452
+ historyAdd: (state, { payload }) => {
453
+ if (hasNode(state, payload.name, payload.node)) {
454
+ state[payload.name].history.push(payload.node);
455
+ _set(state, [payload.name, 'current'], payload.node);
456
+ }
457
+ return state;
458
+ },
459
+ historyPrevToNode: (state, { payload }) => {
460
+ const history = _get(state[typeof payload === 'string' ? payload : payload.name], ['history'], []);
461
+ const lastIndex = lastIndexOf(history, typeof payload === 'string' ? payload : payload.node);
462
+ // se passo un nodo che non esiste
463
+ if (lastIndex >= 0) {
464
+ const newHistory = _slice(history, 0, lastIndex + 1);
465
+ _set(state, [typeof payload === 'string' ? payload : payload.name, 'history'], newHistory);
466
+ _set(state, [typeof payload === 'string' ? payload : payload.name, 'current'], typeof payload === 'string' ? payload : payload.node);
467
+ }
468
+ return state;
469
+ },
470
+ setFormTouched: (state, { payload }) => {
471
+ if (!_get(state, [
472
+ typeof payload === 'string' ? payload : payload.flowName,
473
+ 'nodes',
474
+ typeof payload === 'string' ? payload : payload.currentNode
475
+ ])) {
476
+ return state;
477
+ }
478
+ _set(state, [
479
+ typeof payload === 'string' ? payload : payload.flowName,
480
+ 'form',
481
+ typeof payload === 'string' ? payload : payload.currentNode,
482
+ 'touched'
483
+ ], true);
484
+ return state;
485
+ },
486
+ // TODO check internal logic and use case
487
+ /* istanbul ignore next */
488
+ forceAddHistory: (state, { payload }) => {
489
+ const { name, flowName, history } = payload;
490
+ const key = name || flowName || '' || '';
491
+ const resultHistory = history.slice(1, -1);
492
+ state[key].history.push(...resultHistory);
493
+ return state;
494
+ },
495
+ historyPop: (state, { payload }) => {
496
+ const container = _get(state, [payload.name]);
497
+ const startId = _get(container, ['startId']);
498
+ const history = _get(container, ['history']);
499
+ const originHistory = [..._get(container, ['history'], [])];
500
+ if (originHistory.length < 2) {
501
+ _set(state, [payload.name, 'current'], startId);
502
+ _set(state, [payload.name, 'history'], [startId]);
503
+ return state;
504
+ }
505
+ // elimino lo node corrente
471
506
  history.pop();
472
- } else {
473
- break;
474
- }
475
- }
476
- const lastId = _last(history);
477
- _set(state, [payload.name, 'current'], lastId);
478
- return state;
479
- },
480
- restoreHistory: (state, {
481
- payload
482
- }) => {
483
- const startId = _get(state, [payload.name, 'startId']);
484
- _set(state, [payload.name, 'current'], startId);
485
- _set(state, [payload.name, 'history'], [startId]);
486
- return state;
487
- },
488
- replaceNode: (state, {
489
- payload
490
- }) => {
491
- const {
492
- name,
493
- flowName,
494
- node
495
- } = payload;
496
- if (hasNode(state, name || flowName || '', node)) {
497
- _set(state, [name || flowName || '', 'current'], node);
498
- _set(state, [name || flowName || '', 'history'], [node]);
499
- }
500
- return state;
501
- },
502
- initializeFromNode: (state, {
503
- payload
504
- }) => {
505
- const {
506
- name,
507
- flowName,
508
- node
509
- } = payload;
510
- if (hasNode(state, name || flowName || '', node)) {
511
- _set(state, [name || flowName || '', 'startId'], node);
512
- _set(state, [name || flowName || '', 'current'], node);
513
- _set(state, [name || flowName || '', 'history'], [node]);
514
- }
515
- return state;
516
- },
517
- forceResetHistory: (state, {
518
- payload
519
- }) => {
520
- const {
521
- name,
522
- flowName
523
- } = payload;
524
- if (!name && !flowName) return state;
525
- _set(state, [name || flowName || '', 'history'], []);
526
- return state;
527
- },
528
- destroy: (state, {
529
- payload
530
- }) => {
531
- _set(state, [payload.name], {});
532
- },
533
- initNodes: (state, {
534
- payload
535
- }) => {
536
- if (payload.persist && _get(state, [payload.name, 'persist'])) return;
537
- const startId = payload.startId || _get(payload, 'nodes.0.nodeId');
538
- if (!startId) {
539
- console.warn('Flower is empty');
540
- return;
541
- }
542
- _set(state, payload.name, {
543
- persist: payload.persist,
544
- startId,
545
- current: startId,
546
- history: [startId],
547
- nodes: generateNodes(payload.nodes),
548
- nextRules: makeObjectRules(payload.nodes),
549
- data: payload.initialData
550
- });
551
- },
552
- setCurrentNode: (state, {
553
- payload
554
- }) => {
555
- if (hasNode(state, payload.name, payload.node)) {
556
- const startId = _get(state, [payload.name, 'startId']);
557
- if (payload.node === startId) {
507
+ const total = originHistory.length - 2;
508
+ // eslint-disable-next-line no-plusplus
509
+ for (let index = total; index > 0; index--) {
510
+ const curr = originHistory[index];
511
+ const nodeType = _get(container, ['nodes', curr, 'nodeType']);
512
+ const nodeDisabled = _get(container, ['nodes', curr, 'disabled']);
513
+ if (nodeDisabled ||
514
+ nodeType === 'FlowerAction' ||
515
+ nodeType === 'FlowerServer' ||
516
+ !nodeType) {
517
+ history.pop();
518
+ }
519
+ else {
520
+ break;
521
+ }
522
+ }
523
+ const lastId = _last(history);
524
+ _set(state, [payload.name, 'current'], lastId);
525
+ return state;
526
+ },
527
+ restoreHistory: (state, { payload }) => {
528
+ const startId = _get(state, [payload.name, 'startId']);
558
529
  _set(state, [payload.name, 'current'], startId);
559
530
  _set(state, [payload.name, 'history'], [startId]);
560
- } else {
561
- _set(state, [payload.name, 'current'], payload.node);
562
- }
563
- }
564
- },
565
- formAddErrors: (state, {
566
- payload
567
- }) => {
568
- _set(state, [payload.name, 'form', payload.currentNode, 'errors', payload.id], payload.errors);
569
- },
570
- formRemoveErrors: (state, {
571
- payload
572
- }) => {
573
- _unset(state, [payload.name, 'form', payload.currentNode, 'errors', payload.id]);
574
- _unset(state, [payload.name, 'form', payload.currentNode, 'isValidating']);
575
- },
576
- addData: (state, {
577
- payload
578
- }) => {
579
- const prevData = _get(state, [payload.flowName, 'data']);
580
- _set(state, [payload.flowName, 'data'], {
581
- ...prevData,
582
- ...payload.value
583
- });
584
- },
585
- addDataByPath: (state, {
586
- payload
587
- }) => {
588
- if (payload.id && payload.id.length) {
589
- _set(state, [payload.flowName, 'data', ...payload.id], payload.value);
590
- }
591
- },
592
- replaceData: (state, {
593
- payload
594
- }) => {
595
- _set(state, [payload.flowName, 'data'], payload.value);
596
- },
597
- unsetData: (state, {
598
- payload
599
- }) => {
600
- _unset(state, [payload.flowName, 'data', ...payload.id]);
601
- },
602
- setFormIsValidating: (state, {
603
- payload
604
- }) => {
605
- _set(state, [payload.name, 'form', payload.currentNode, 'isValidating'], payload.isValidating);
606
- },
607
- node: (state, {
608
- payload
609
- }) => {
610
- const {
611
- name,
612
- history
613
- } = payload;
614
- const node = payload.nodeId || payload.node || '';
615
- const flowName = name || payload.flowName || '';
616
- const startNode = _get(state, [payload.name, 'startId']);
617
- const currentNodeId = _get(state, [payload.name, 'current'], startNode);
618
- FlowerCoreReducers.setFormTouched(state, {
619
- type: 'setFormTouched',
620
- payload: {
621
- flowName,
622
- currentNode: currentNodeId
623
- }
624
- });
625
- if (global.window && _get(global.window, '__FLOWER_DEVTOOLS__') && history) {
626
- FlowerCoreReducers.forceAddHistory(state, {
627
- type: 'forceAddHistory',
628
- payload: {
629
- name,
630
- flowName,
631
- history
632
- }
633
- });
634
- }
635
- FlowerCoreReducers.historyAdd(state, {
636
- type: 'historyAdd',
637
- payload: {
638
- name: name || flowName || '',
639
- node
640
- }
641
- });
642
- },
643
- prevToNode: (state, {
644
- payload
645
- }) => {
646
- const {
647
- node,
648
- name,
649
- flowName
650
- } = payload;
651
- FlowerCoreReducers.historyPrevToNode(state, {
652
- type: 'historyPrevToNode',
653
- payload: {
654
- name: name || flowName || '',
655
- node
656
- }
657
- });
658
- },
659
- next: (state, {
660
- payload
661
- }) => {
662
- const {
663
- name,
664
- data = {},
665
- route
666
- } = payload;
667
- const flowName = name || payload.flowName || '';
668
- const currentNodeId = FlowerStateUtils.makeSelectCurrentNodeId(flowName)(state);
669
- const currentNextRules = FlowerStateUtils.makeSelectCurrentNextRules(flowName)(state);
670
- const form = FlowerStateUtils.makeSelectNodeErrors(flowName, currentNodeId)(state);
671
- const clonedData = _cloneDeep(FlowerStateUtils.getAllData(state));
672
- const stateWithNodeData = {
673
- $in: data,
674
- $form: form,
675
- ...clonedData
676
- };
677
- FlowerCoreReducers.setFormTouched(state, {
678
- type: 'setFormTouched',
679
- payload: {
680
- flowName,
681
- currentNode: currentNodeId
682
- }
683
- });
684
- if (!currentNextRules) {
685
- return;
686
- }
687
- if (route) {
688
- const rulesByName = generateRulesName(currentNextRules);
689
- if (!rulesByName[route]) {
690
- return;
691
- }
692
- FlowerCoreReducers.historyAdd(state, {
693
- type: 'historyAdd',
694
- payload: {
695
- name: flowName,
696
- node: rulesByName[route]
697
- }
698
- });
699
- return;
700
- }
701
- const validRule = findValidRule(currentNextRules, stateWithNodeData, flowName);
702
- const nextNumberNode = _get(validRule, 'nodeId');
703
- if (!nextNumberNode) {
704
- return;
531
+ return state;
532
+ },
533
+ replaceNode: (state, { payload }) => {
534
+ const { name, flowName, node } = payload;
535
+ // non ancora implementanto nell'hook useFlower
536
+ /* istanbul ignore next */
537
+ if (hasNode(state, name || flowName || '', node)) {
538
+ _set(state, [name || flowName || '', 'current'], node);
539
+ _set(state, [name || flowName || '', 'history'], [node]);
540
+ }
541
+ /* istanbul ignore next */
542
+ return state;
543
+ },
544
+ /* istanbul ignore next */
545
+ initializeFromNode: (state, { payload }) => {
546
+ const { name, flowName, node } = payload;
547
+ if (hasNode(state, name || flowName || '', node)) {
548
+ _set(state, [name || flowName || '', 'startId'], node);
549
+ _set(state, [name || flowName || '', 'current'], node);
550
+ _set(state, [name || flowName || '', 'history'], [node]);
551
+ }
552
+ return state;
553
+ },
554
+ /* istanbul ignore next */
555
+ forceResetHistory: (state, { payload }) => {
556
+ const { name, flowName } = payload;
557
+ if (!name && !flowName)
558
+ return state;
559
+ _set(state, [name || flowName || '', 'history'], []);
560
+ return state;
561
+ },
562
+ destroy: (state, { payload }) => {
563
+ _set(state, [payload.name], {});
564
+ },
565
+ initNodes: (state, { payload }) => {
566
+ if (payload.persist && _get(state, [payload.name, 'persist']))
567
+ return;
568
+ const startId = payload.startId ||
569
+ _get(payload, 'initialState.startId') ||
570
+ _get(payload, 'nodes.0.nodeId');
571
+ // TODO non verificato, controllo precendente che non lo permette
572
+ /* istanbul ignore next */
573
+ if (!startId) {
574
+ // eslint-disable-next-line no-console
575
+ console.warn('Flower is empty');
576
+ return;
577
+ }
578
+ const current = _get(payload, 'initialState.current') || startId;
579
+ const history = _get(payload, 'initialState.history') || [startId];
580
+ _set(state, payload.name, {
581
+ persist: payload.persist,
582
+ startId,
583
+ current,
584
+ history,
585
+ nodes: generateNodes(payload.nodes),
586
+ nextRules: makeObjectRules(payload.nodes),
587
+ data: payload.initialData
588
+ });
589
+ },
590
+ // TODO usato solo da flower su vscode
591
+ setCurrentNode: /* istanbul ignore next */ (state, { payload }) => {
592
+ /* istanbul ignore next */
593
+ if (hasNode(state, payload.name, payload.node)) {
594
+ const startId = _get(state, [payload.name, 'startId']);
595
+ if (payload.node === startId) {
596
+ _set(state, [payload.name, 'current'], startId);
597
+ _set(state, [payload.name, 'history'], [startId]);
598
+ }
599
+ else {
600
+ _set(state, [payload.name, 'current'], payload.node);
601
+ }
602
+ }
603
+ },
604
+ formAddErrors: (state, { payload }) => {
605
+ _set(state, [payload.name, 'form', payload.currentNode, 'errors', payload.id], payload.errors);
606
+ },
607
+ formRemoveErrors: (state, { payload }) => {
608
+ _unset(state, [
609
+ payload.name,
610
+ 'form',
611
+ payload.currentNode,
612
+ 'errors',
613
+ payload.id
614
+ ]);
615
+ _unset(state, [payload.name, 'form', payload.currentNode, 'isValidating']);
616
+ },
617
+ addData: (state, { payload }) => {
618
+ const prevData = _get(state, [payload.flowName, 'data']);
619
+ _set(state, [payload.flowName, 'data'], { ...prevData, ...payload.value });
620
+ },
621
+ addDataByPath: (state, { payload }) => {
622
+ if (payload.id && payload.id.length) {
623
+ _set(state, [payload.flowName, 'data', ...payload.id], payload.value);
624
+ }
625
+ },
626
+ // TODO usato al momento solo il devtool
627
+ replaceData: /* istanbul ignore next */ (state, { payload }) => {
628
+ /* istanbul ignore next */
629
+ _set(state, [payload.flowName, 'data'], payload.value);
630
+ },
631
+ unsetData: (state, { payload }) => {
632
+ _unset(state, [payload.flowName, 'data', ...payload.id]);
633
+ },
634
+ setFormIsValidating: (state, { payload }) => {
635
+ _set(state, [payload.name, 'form', payload.currentNode, 'isValidating'], payload.isValidating);
636
+ },
637
+ node: (state, { payload }) => {
638
+ const { name, history } = payload;
639
+ const node = payload.nodeId || payload.node || '';
640
+ const flowName = name || payload.flowName || '';
641
+ const startNode = _get(state, [payload.name, 'startId']);
642
+ const currentNodeId = _get(state, [payload.name, 'current'], startNode);
643
+ FlowerCoreReducers.setFormTouched(state, {
644
+ type: 'setFormTouched',
645
+ payload: { flowName, currentNode: currentNodeId }
646
+ });
647
+ /* istanbul ignore next */
648
+ // eslint-disable-next-line no-underscore-dangle
649
+ if (devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__') && history) {
650
+ FlowerCoreReducers.forceAddHistory(state, {
651
+ type: 'forceAddHistory',
652
+ payload: {
653
+ name,
654
+ flowName,
655
+ history
656
+ }
657
+ });
658
+ }
659
+ FlowerCoreReducers.historyAdd(state, {
660
+ type: 'historyAdd',
661
+ payload: { name: name || flowName || '', node }
662
+ });
663
+ },
664
+ prevToNode: (state, { payload }) => {
665
+ const { node, name, flowName } = payload;
666
+ FlowerCoreReducers.historyPrevToNode(state, {
667
+ type: 'historyPrevToNode',
668
+ payload: { name: name || flowName || '', node }
669
+ });
670
+ },
671
+ next: (state, { payload }) => {
672
+ const { name, data = {}, route } = payload;
673
+ const flowName = name || payload.flowName || '';
674
+ const currentNodeId = FlowerStateUtils.makeSelectCurrentNodeId(flowName)(state);
675
+ const currentNextRules = FlowerStateUtils.makeSelectCurrentNextRules(flowName)(state);
676
+ const form = FlowerStateUtils.makeSelectNodeErrors(flowName, currentNodeId)(state);
677
+ const clonedData = _cloneDeep(FlowerStateUtils.getAllData(state));
678
+ const stateWithNodeData = {
679
+ $in: data,
680
+ $form: form,
681
+ ...clonedData
682
+ };
683
+ FlowerCoreReducers.setFormTouched(state, {
684
+ type: 'setFormTouched',
685
+ payload: { flowName, currentNode: currentNodeId }
686
+ });
687
+ if (!currentNextRules) {
688
+ return;
689
+ }
690
+ if (route) {
691
+ const rulesByName = generateRulesName(currentNextRules);
692
+ if (!rulesByName[route]) {
693
+ return;
694
+ }
695
+ FlowerCoreReducers.historyAdd(state, {
696
+ type: 'historyAdd',
697
+ payload: { name: flowName, node: rulesByName[route] }
698
+ });
699
+ return;
700
+ }
701
+ const validRule = findValidRule(currentNextRules, stateWithNodeData, flowName);
702
+ const nextNumberNode = _get(validRule, 'nodeId');
703
+ if (!nextNumberNode) {
704
+ return;
705
+ }
706
+ FlowerCoreReducers.historyAdd(state, {
707
+ type: 'historyAdd',
708
+ payload: { name: flowName, node: nextNumberNode }
709
+ });
710
+ },
711
+ prev: (state, { payload }) => {
712
+ const { name, flowName } = payload;
713
+ FlowerCoreReducers.historyPop(state, {
714
+ type: 'historyPop',
715
+ payload: { name: name || flowName || '' }
716
+ });
717
+ },
718
+ restart: (state, { payload }) => {
719
+ const { name, flowName } = payload;
720
+ FlowerCoreReducers.restoreHistory(state, {
721
+ type: 'restoreHistory',
722
+ payload: { name: name || flowName || '' }
723
+ });
724
+ },
725
+ reset: (state, { payload }) => {
726
+ const { name, flowName, initialData } = payload;
727
+ FlowerCoreReducers.restoreHistory(state, {
728
+ type: 'restoreHistory',
729
+ payload: { name: name || flowName || '' }
730
+ });
731
+ _set(state, [name || flowName || '', 'form'], {});
732
+ _set(state, [name || flowName || '', 'data'], initialData);
705
733
  }
706
- FlowerCoreReducers.historyAdd(state, {
707
- type: 'historyAdd',
708
- payload: {
709
- name: flowName,
710
- node: nextNumberNode
711
- }
712
- });
713
- },
714
- prev: (state, {
715
- payload
716
- }) => {
717
- const {
718
- name,
719
- flowName
720
- } = payload;
721
- FlowerCoreReducers.historyPop(state, {
722
- type: 'historyPop',
723
- payload: {
724
- name: name || flowName || ''
725
- }
726
- });
727
- },
728
- restart: (state, {
729
- payload
730
- }) => {
731
- const {
732
- name,
733
- flowName
734
- } = payload;
735
- FlowerCoreReducers.restoreHistory(state, {
736
- type: 'restoreHistory',
737
- payload: {
738
- name: name || flowName || ''
739
- }
740
- });
741
- },
742
- reset: (state, {
743
- payload
744
- }) => {
745
- const {
746
- name,
747
- flowName,
748
- initialData
749
- } = payload;
750
- FlowerCoreReducers.restoreHistory(state, {
751
- type: 'restoreHistory',
752
- payload: {
753
- name: name || flowName || ''
754
- }
755
- });
756
- _set(state, [name || flowName || '', 'form'], {});
757
- _set(state, [name || flowName || '', 'data'], initialData);
758
- }
759
734
  };
760
735
 
761
736
  const FlowerCoreStateSelectors = {
762
- selectGlobal: state => state && state.flower,
763
- selectFlower: name => state => _get(state, [name]),
764
- selectFlowerFormNode: id => state => _get(state, ['form', id]),
765
- selectFlowerHistory: flower => _get(flower, ['history'], []),
766
- makeSelectNodesIds: flower => _get(flower, ['nodes']),
767
- makeSelectStartNodeId: flower => _get(flower, ['startId']),
768
- getDataByFlow: flower => _get(flower, ['data']) ?? {},
769
- getDataFromState: id => data => id === '*' ? data : _get(data, id),
770
- makeSelectNodeFormTouched: form => form && form.touched,
771
- makeSelectCurrentNodeId: (flower, startNodeId) => _get(flower, ['current']) || startNodeId,
772
- makeSelectCurrentNodeDisabled: (nodes, current) => !!_get(nodes, [current, 'disabled']),
773
- makeSelectPrevNodeRetain: (nodes, history, current) => {
774
- if (!nodes) return;
775
- const prevFlowerNode = [...history].reverse().find(el => {
776
- const {
777
- nodeType,
778
- retain
779
- } = nodes[el];
780
- return nodeType === 'FlowerNode' || retain;
781
- });
782
- if (nodes[current].nodeType === 'FlowerNode' || nodes[current].retain) return;
783
- if (!prevFlowerNode) return;
784
- if (nodes[prevFlowerNode] && nodes[prevFlowerNode].disabled) return;
785
- return nodes[prevFlowerNode] && nodes[prevFlowerNode].retain && prevFlowerNode;
786
- },
787
- makeSelectNodeErrors: form => {
788
- return {
789
- touched: form?.touched || false,
790
- errors: form?.errors,
791
- isValidating: form?.isValidating,
792
- isValid: form && form.errors ? Object.values(form.errors).flat().length === 0 : true
793
- };
794
- },
795
- makeSelectFieldError: (name, id, validate) => data => {
796
- if (!validate || !data) return [];
797
- const errors = validate.filter(rule => {
798
- if (!rule) return true;
799
- if (!rule.rules) return true;
800
- const transformSelf = CoreUtils.mapKeysDeepLodash(rule.rules, (v, key) => key === '$self' ? id : key);
801
- const [hasError] = MatchRules.rulesMatcher(transformSelf, data, false, {
802
- prefix: name
803
- });
804
- return hasError;
805
- });
806
- const result = errors.map(r => r && r.message || 'error');
807
- return result.length === 0 ? [] : result;
808
- },
809
- selectorRulesDisabled: (id, rules, keys, flowName, value) => (data, form) => {
810
- const newState = {
811
- ...data,
812
- ...value,
813
- $form: form
814
- };
815
- const state = Object.assign(newState, id ? {
816
- $self: _get(newState, [flowName, ...id.split('.')])
817
- } : {});
818
- if (!rules) return false;
819
- if (typeof rules === 'function') {
820
- return !rules(state);
737
+ selectGlobal: (state) => state && state.flower,
738
+ selectFlower: (name) => (state) => _get(state, [name]),
739
+ selectFlowerFormNode: (id) => (state) => _get(state, ['form', id]),
740
+ selectFlowerHistory: (flower) => _get(flower, ['history'], []),
741
+ makeSelectNodesIds: (flower) => _get(flower, ['nodes']),
742
+ makeSelectStartNodeId: (flower) => _get(flower, ['startId']),
743
+ getDataByFlow: (flower) => _get(flower, ['data']) ?? {},
744
+ getDataFromState: (id) => (data) => (id === '*' ? data : _get(data, id)),
745
+ makeSelectNodeFormTouched: (form) => form && form.touched,
746
+ makeSelectCurrentNodeId: (flower, startNodeId) => _get(flower, ['current']) || startNodeId,
747
+ makeSelectCurrentNodeDisabled: (nodes, current) => !!_get(nodes, [current, 'disabled']),
748
+ makeSelectPrevNodeRetain: (nodes, history, current) => {
749
+ if (!nodes)
750
+ return;
751
+ const prevFlowerNode = [...history].reverse().find((el) => {
752
+ const { nodeType, retain } = nodes[el];
753
+ return nodeType === 'FlowerNode' || retain;
754
+ });
755
+ // eslint-disable-next-line consistent-return
756
+ if (nodes[current].nodeType === 'FlowerNode' || nodes[current].retain)
757
+ return;
758
+ if (!prevFlowerNode)
759
+ return;
760
+ if (nodes[prevFlowerNode] && nodes[prevFlowerNode].disabled)
761
+ return;
762
+ // eslint-disable-next-line consistent-return
763
+ return (nodes[prevFlowerNode] && nodes[prevFlowerNode].retain && prevFlowerNode);
764
+ },
765
+ makeSelectNodeErrors: (form) => {
766
+ return {
767
+ touched: form?.touched || false,
768
+ errors: form?.errors,
769
+ isValidating: form?.isValidating,
770
+ isValid: form && form.errors
771
+ ? Object.values(form.errors).flat().length === 0
772
+ : true
773
+ };
774
+ },
775
+ makeSelectFieldError: (name, id, validate) => (data) => {
776
+ if (!validate || !data)
777
+ return [];
778
+ const errors = validate.filter((rule) => {
779
+ if (!rule)
780
+ return true;
781
+ if (!rule.rules)
782
+ return true;
783
+ const transformSelf = CoreUtils.mapKeysDeepLodash(rule.rules, (v, key) => key === '$self' ? id : key);
784
+ const [hasError] = MatchRules.rulesMatcher(transformSelf, data, false, {
785
+ prefix: name
786
+ });
787
+ return hasError;
788
+ });
789
+ const result = errors.map((r) => (r && r.message) || 'error');
790
+ return result.length === 0 ? [] : result;
791
+ },
792
+ selectorRulesDisabled: (id, rules, keys, flowName, value) => (data, form) => {
793
+ const newState = { ...data, ...value, $form: form };
794
+ const state = Object.assign(newState, id ? { $self: _get(newState, [flowName, ...id.split('.')]) } : {});
795
+ if (!rules)
796
+ return false;
797
+ if (typeof rules === 'function') {
798
+ return !rules(state);
799
+ }
800
+ if (!keys)
801
+ return false;
802
+ const res = keys.reduce((acc, inc) => {
803
+ const k = inc;
804
+ return Object.assign(acc, { [k]: _get(state, k) });
805
+ }, {});
806
+ const [disabled] = MatchRules.rulesMatcher(rules, { ...flat.unflatten(res) }, false, { prefix: flowName });
807
+ return disabled;
821
808
  }
822
- if (!keys) return false;
823
- const res = keys.reduce((acc, inc) => {
824
- const k = inc;
825
- return Object.assign(acc, {
826
- [k]: _get(state, k)
827
- });
828
- }, {});
829
- const [disabled] = MatchRules.rulesMatcher(rules, {
830
- ...flat.unflatten(res)
831
- }, false, {
832
- prefix: flowName
833
- });
834
- return disabled;
835
- }
836
809
  };
837
810
 
838
811
  exports.RulesModes = void 0;
839
812
  (function (RulesModes) {
840
- RulesModes["$and"] = "$and";
841
- RulesModes["$or"] = "$or";
813
+ RulesModes["$and"] = "$and";
814
+ RulesModes["$or"] = "$or";
842
815
  })(exports.RulesModes || (exports.RulesModes = {}));
843
816
  var RulesOperators;
844
817
  (function (RulesOperators) {
845
- RulesOperators["$exist"] = "$exist";
846
- RulesOperators["$eq"] = "$eq";
847
- RulesOperators["$ne"] = "$ne";
848
- RulesOperators["$gt"] = "$gt";
849
- RulesOperators["$gte"] = "$gte";
850
- RulesOperators["$lt"] = "$lt";
851
- RulesOperators["$lte"] = "$lte";
852
- RulesOperators["$strGt"] = "$strGt";
853
- RulesOperators["$strGte"] = "$strGte";
854
- RulesOperators["$strLt"] = "$strLt";
855
- RulesOperators["$strLte"] = "$strLte";
856
- RulesOperators["$in"] = "$in";
857
- RulesOperators["$nin"] = "$nin";
858
- RulesOperators["$all"] = "$all";
859
- RulesOperators["$regex"] = "$regex";
818
+ RulesOperators["$exist"] = "$exist";
819
+ RulesOperators["$eq"] = "$eq";
820
+ RulesOperators["$ne"] = "$ne";
821
+ RulesOperators["$gt"] = "$gt";
822
+ RulesOperators["$gte"] = "$gte";
823
+ RulesOperators["$lt"] = "$lt";
824
+ RulesOperators["$lte"] = "$lte";
825
+ RulesOperators["$strGt"] = "$strGt";
826
+ RulesOperators["$strGte"] = "$strGte";
827
+ RulesOperators["$strLt"] = "$strLt";
828
+ RulesOperators["$strLte"] = "$strLte";
829
+ RulesOperators["$in"] = "$in";
830
+ RulesOperators["$nin"] = "$nin";
831
+ RulesOperators["$all"] = "$all";
832
+ RulesOperators["$regex"] = "$regex";
860
833
  })(RulesOperators || (RulesOperators = {}));
861
834
 
862
835
  exports.RulesOperators = void 0;
863
836
  (function (RulesOperators) {
864
- RulesOperators["$exist"] = "$exist";
865
- RulesOperators["$eq"] = "$eq";
866
- RulesOperators["$ne"] = "$ne";
867
- RulesOperators["$gt"] = "$gt";
868
- RulesOperators["$gte"] = "$gte";
869
- RulesOperators["$lt"] = "$lt";
870
- RulesOperators["$lte"] = "$lte";
871
- RulesOperators["$strGt"] = "$strGt";
872
- RulesOperators["$strGte"] = "$strGte";
873
- RulesOperators["$strLt"] = "$strLt";
874
- RulesOperators["$strLte"] = "$strLte";
875
- RulesOperators["$in"] = "$in";
876
- RulesOperators["$nin"] = "$nin";
877
- RulesOperators["$all"] = "$all";
878
- RulesOperators["$regex"] = "$regex";
837
+ RulesOperators["$exist"] = "$exist";
838
+ RulesOperators["$eq"] = "$eq";
839
+ RulesOperators["$ne"] = "$ne";
840
+ RulesOperators["$gt"] = "$gt";
841
+ RulesOperators["$gte"] = "$gte";
842
+ RulesOperators["$lt"] = "$lt";
843
+ RulesOperators["$lte"] = "$lte";
844
+ RulesOperators["$strGt"] = "$strGt";
845
+ RulesOperators["$strGte"] = "$strGte";
846
+ RulesOperators["$strLt"] = "$strLt";
847
+ RulesOperators["$strLte"] = "$strLte";
848
+ RulesOperators["$in"] = "$in";
849
+ RulesOperators["$nin"] = "$nin";
850
+ RulesOperators["$all"] = "$all";
851
+ RulesOperators["$regex"] = "$regex";
879
852
  })(exports.RulesOperators || (exports.RulesOperators = {}));
880
853
 
854
+ const devtoolState = {};
855
+
881
856
  exports.CoreUtils = CoreUtils;
882
857
  exports.Emitter = Emitter;
883
858
  exports.FlowerCoreReducers = FlowerCoreReducers;
884
859
  exports.FlowerStateUtils = FlowerStateUtils;
885
860
  exports.MatchRules = MatchRules;
886
861
  exports.Selectors = FlowerCoreStateSelectors;
862
+ exports.devtoolState = devtoolState;