@flowerforce/flower-core 3.0.0

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