@flowerforce/flower-core 4.0.1-beta.1 → 4.0.1-beta.10

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/CHANGELOG.md CHANGED
@@ -1,3 +1,38 @@
1
+ ## 3.5.2 (2025-04-23)
2
+
3
+
4
+ ### 🩹 Fixes
5
+
6
+ - fix init nodes ([996d8af](https://github.com/flowerforce/flower/commit/996d8af))
7
+
8
+ - package lock ([a3bb210](https://github.com/flowerforce/flower/commit/a3bb210))
9
+
10
+ ## 3.5.1 (2025-04-19)
11
+
12
+
13
+ ### 🩹 Fixes
14
+
15
+ - remove empty object from init state ([8604346](https://github.com/flowerforce/flower/commit/8604346))
16
+
17
+ ## 3.5.0 (2025-04-19)
18
+
19
+
20
+ ### 🚀 Features
21
+
22
+ - remove data blank from init nodes ([#80](https://github.com/flowerforce/flower/pull/80))
23
+
24
+ ## 3.4.0 (2025-04-19)
25
+
26
+
27
+ ### 🚀 Features
28
+
29
+ - added remove value on hide element ([#68](https://github.com/flowerforce/flower/pull/68))
30
+
31
+
32
+ ### 🩹 Fixes
33
+
34
+ - avoid validate hidden field ([#67](https://github.com/flowerforce/flower/pull/67))
35
+
1
36
  ## 3.3.0 (2024-10-08)
2
37
 
3
38
 
package/dist/index.cjs.js CHANGED
@@ -62,6 +62,89 @@ const generateData = (data) => {
62
62
  };
63
63
  };
64
64
 
65
+ class AbacEngine {
66
+ constructor(rules = []) {
67
+ // compile string conditions once
68
+ this.rules = (rules || [])
69
+ .map((r) => {
70
+ let conditionFn = undefined;
71
+ if (r.condition) {
72
+ try {
73
+ // NOTE: uses new Function -> ensure rules.json is trusted by the app owner
74
+ conditionFn = new Function('subject', 'resource', 'action', 'environment', `return (${r.condition});`);
75
+ }
76
+ catch (err) {
77
+ console.error('ABAC: failed to compile rule condition', r.id, err);
78
+ }
79
+ }
80
+ return { ...r, conditionFn };
81
+ })
82
+ .sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
83
+ }
84
+ decide(ctx) {
85
+ let anyPermit = false;
86
+ for (const r of this.rules) {
87
+ const matchesAction = Array.isArray(r.action)
88
+ ? r.action.includes(ctx.action)
89
+ : r.action === ctx.action || r.action === '*';
90
+ if (!matchesAction)
91
+ continue;
92
+ let matches = true;
93
+ if (r.conditionFn) {
94
+ try {
95
+ matches = !!r.conditionFn(ctx.subject, ctx.resource, ctx.action, ctx.environment);
96
+ }
97
+ catch (err) {
98
+ matches = false;
99
+ console.error(`ABAC: rule ${r.id} threw while evaluating`, err);
100
+ }
101
+ }
102
+ if (!matches)
103
+ continue;
104
+ if (r.effect === 'Deny') {
105
+ // deny-overrides
106
+ return 'Deny';
107
+ }
108
+ if (r.effect === 'Permit') {
109
+ anyPermit = true;
110
+ }
111
+ }
112
+ return anyPermit ? 'Permit' : 'Deny';
113
+ }
114
+ // utility: checks multiple actions and returns those allowed
115
+ allowedActions(ctxBase, actions) {
116
+ return actions.filter((a) => this.decide({ ...ctxBase, action: a }) === 'Permit');
117
+ }
118
+ }
119
+
120
+ let engine = null;
121
+ let rawRules = null;
122
+ let currentSubject = null;
123
+ function initAbac(rules) {
124
+ engine = new AbacEngine(rules);
125
+ rawRules = rules;
126
+ }
127
+ function getAbacEngine() {
128
+ if (!engine)
129
+ console.warn('ABAC engine non inizializzato');
130
+ return engine;
131
+ }
132
+ function getRawRules() {
133
+ return rawRules;
134
+ }
135
+ function isAbacInitialized() {
136
+ return engine !== null;
137
+ }
138
+ function setSubject(subject) {
139
+ currentSubject = subject;
140
+ }
141
+ function getSubject() {
142
+ return currentSubject;
143
+ }
144
+ function clearSubject() {
145
+ currentSubject = null;
146
+ }
147
+
65
148
  const EMPTY_STRING_REGEXP = /^\s*$/;
66
149
  /**
67
150
  * Defines a utility object named rulesMatcherUtils, which contains various helper functions used for processing rules and data in a rule-matching context.
@@ -85,6 +168,11 @@ const rulesMatcherUtils = {
85
168
  if (!operators[op]) {
86
169
  throw new Error(`Error missing operator:${op}`);
87
170
  }
171
+ if (op === '$can') {
172
+ const subject = getSubject();
173
+ const valid = operators['$can'](subject ?? {}, _get(value, 'action'), _get(value, 'resource'));
174
+ return { valid, name: `${pathWithPrefix}___${name || op}` };
175
+ }
88
176
  const valid = operators[op] && operators[op](valueForKey, valueRef, opt, data);
89
177
  return { valid, name: `${pathWithPrefix}___${name || op}` };
90
178
  },
@@ -242,6 +330,11 @@ const rulesMatcherUtils = {
242
330
  * Defines a set of comparison operators used for matching rules against user input.
243
331
  */
244
332
  const operators = {
333
+ $can: (subject, action, resource) => getAbacEngine()?.decide({
334
+ subject: subject ?? {},
335
+ action,
336
+ resource
337
+ }) === 'Permit',
245
338
  $exists: (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
246
339
  $eq: (a, b) => a === b,
247
340
  $ne: (a, b) => a !== b,
@@ -304,9 +397,6 @@ const flattenRules = (ob) => {
304
397
  }
305
398
  return result;
306
399
  };
307
- const getRulesExists = (rules) => {
308
- return Object.keys(rules).length ? FlowUtils.mapEdge(rules) : undefined;
309
- };
310
400
  const FlowUtils = {
311
401
  generateRulesName: (nextRules) => {
312
402
  return nextRules.reduce((acc, inc) => {
@@ -363,23 +453,6 @@ const FlowUtils = {
363
453
  return res;
364
454
  },
365
455
  makeRules: (rules) => Object.entries(rules).reduce((acc2, [k, v]) => [...acc2, { nodeId: k, rules: v }], []),
366
- // TODO: This function is strictly related to React nodes, could make sense to move it in the flower-react folder
367
- generateNodesForFlowerJson: (nodes) => nodes
368
- .filter((e) => !!_get(e, 'props.id'))
369
- .map((e) => {
370
- const rules = FlowUtils.makeRules(e.props.to ?? {});
371
- const nextRules = getRulesExists(rules);
372
- const children = e.props.data?.children;
373
- return {
374
- nodeId: e.props.id,
375
- nodeType: e.type.displayName || e.props.as || 'FlowerNode',
376
- nodeTitle: _get(e.props, 'data.title'),
377
- children,
378
- nextRules,
379
- retain: e.props.retain,
380
- disabled: e.props.disabled
381
- };
382
- }),
383
456
  allEqual: (arr, arr2) => arr.length === arr2.length && arr.every((v) => arr2.includes(v)),
384
457
  findValidRule: (nextRules, value, prefix) => find(nextRules, (rule) => {
385
458
  // fix per evitare di entrare in un nodo senza regole, ma con un name,
@@ -650,7 +723,7 @@ const FlowerCoreBaseReducers = {
650
723
  payload: { name: flowName, node: nextNumberNode }
651
724
  });
652
725
  },
653
- prev: (state, { payload }) => {
726
+ back: (state, { payload }) => {
654
727
  const { name, flowName } = payload;
655
728
  FlowerCoreBaseReducers.historyPop(state, {
656
729
  type: 'historyPop',
@@ -843,6 +916,7 @@ const FlowerCoreStateSelectors = {
843
916
  ...FlowerCoreStateDataSelectors
844
917
  };
845
918
 
919
+ exports.AbacEngine = AbacEngine;
846
920
  exports.CoreUtils = CoreUtils;
847
921
  exports.DataUtils = DataUtils;
848
922
  exports.Emitter = Emitter;
@@ -854,8 +928,15 @@ exports.FlowerCoreStateBaseSelectors = FlowerCoreStateBaseSelectors;
854
928
  exports.FlowerCoreStateDataSelectors = FlowerCoreStateDataSelectors;
855
929
  exports.FlowerCoreStateSelectors = FlowerCoreStateSelectors;
856
930
  exports.FlowerStateUtils = FlowerStateUtils;
931
+ exports.clearSubject = clearSubject;
857
932
  exports.devtoolState = devtoolState;
858
933
  exports.flattenRules = flattenRules;
859
934
  exports.generateData = generateData;
935
+ exports.getAbacEngine = getAbacEngine;
936
+ exports.getRawRules = getRawRules;
937
+ exports.getSubject = getSubject;
938
+ exports.initAbac = initAbac;
939
+ exports.isAbacInitialized = isAbacInitialized;
860
940
  exports.rulesMatcher = rulesMatcher;
861
941
  exports.rulesMatcherUtils = rulesMatcherUtils;
942
+ exports.setSubject = setSubject;
package/dist/index.esm.js CHANGED
@@ -60,6 +60,89 @@ const generateData = (data) => {
60
60
  };
61
61
  };
62
62
 
63
+ class AbacEngine {
64
+ constructor(rules = []) {
65
+ // compile string conditions once
66
+ this.rules = (rules || [])
67
+ .map((r) => {
68
+ let conditionFn = undefined;
69
+ if (r.condition) {
70
+ try {
71
+ // NOTE: uses new Function -> ensure rules.json is trusted by the app owner
72
+ conditionFn = new Function('subject', 'resource', 'action', 'environment', `return (${r.condition});`);
73
+ }
74
+ catch (err) {
75
+ console.error('ABAC: failed to compile rule condition', r.id, err);
76
+ }
77
+ }
78
+ return { ...r, conditionFn };
79
+ })
80
+ .sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
81
+ }
82
+ decide(ctx) {
83
+ let anyPermit = false;
84
+ for (const r of this.rules) {
85
+ const matchesAction = Array.isArray(r.action)
86
+ ? r.action.includes(ctx.action)
87
+ : r.action === ctx.action || r.action === '*';
88
+ if (!matchesAction)
89
+ continue;
90
+ let matches = true;
91
+ if (r.conditionFn) {
92
+ try {
93
+ matches = !!r.conditionFn(ctx.subject, ctx.resource, ctx.action, ctx.environment);
94
+ }
95
+ catch (err) {
96
+ matches = false;
97
+ console.error(`ABAC: rule ${r.id} threw while evaluating`, err);
98
+ }
99
+ }
100
+ if (!matches)
101
+ continue;
102
+ if (r.effect === 'Deny') {
103
+ // deny-overrides
104
+ return 'Deny';
105
+ }
106
+ if (r.effect === 'Permit') {
107
+ anyPermit = true;
108
+ }
109
+ }
110
+ return anyPermit ? 'Permit' : 'Deny';
111
+ }
112
+ // utility: checks multiple actions and returns those allowed
113
+ allowedActions(ctxBase, actions) {
114
+ return actions.filter((a) => this.decide({ ...ctxBase, action: a }) === 'Permit');
115
+ }
116
+ }
117
+
118
+ let engine = null;
119
+ let rawRules = null;
120
+ let currentSubject = null;
121
+ function initAbac(rules) {
122
+ engine = new AbacEngine(rules);
123
+ rawRules = rules;
124
+ }
125
+ function getAbacEngine() {
126
+ if (!engine)
127
+ console.warn('ABAC engine non inizializzato');
128
+ return engine;
129
+ }
130
+ function getRawRules() {
131
+ return rawRules;
132
+ }
133
+ function isAbacInitialized() {
134
+ return engine !== null;
135
+ }
136
+ function setSubject(subject) {
137
+ currentSubject = subject;
138
+ }
139
+ function getSubject() {
140
+ return currentSubject;
141
+ }
142
+ function clearSubject() {
143
+ currentSubject = null;
144
+ }
145
+
63
146
  const EMPTY_STRING_REGEXP = /^\s*$/;
64
147
  /**
65
148
  * Defines a utility object named rulesMatcherUtils, which contains various helper functions used for processing rules and data in a rule-matching context.
@@ -83,6 +166,11 @@ const rulesMatcherUtils = {
83
166
  if (!operators[op]) {
84
167
  throw new Error(`Error missing operator:${op}`);
85
168
  }
169
+ if (op === '$can') {
170
+ const subject = getSubject();
171
+ const valid = operators['$can'](subject ?? {}, _get(value, 'action'), _get(value, 'resource'));
172
+ return { valid, name: `${pathWithPrefix}___${name || op}` };
173
+ }
86
174
  const valid = operators[op] && operators[op](valueForKey, valueRef, opt, data);
87
175
  return { valid, name: `${pathWithPrefix}___${name || op}` };
88
176
  },
@@ -240,6 +328,11 @@ const rulesMatcherUtils = {
240
328
  * Defines a set of comparison operators used for matching rules against user input.
241
329
  */
242
330
  const operators = {
331
+ $can: (subject, action, resource) => getAbacEngine()?.decide({
332
+ subject: subject ?? {},
333
+ action,
334
+ resource
335
+ }) === 'Permit',
243
336
  $exists: (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
244
337
  $eq: (a, b) => a === b,
245
338
  $ne: (a, b) => a !== b,
@@ -302,9 +395,6 @@ const flattenRules = (ob) => {
302
395
  }
303
396
  return result;
304
397
  };
305
- const getRulesExists = (rules) => {
306
- return Object.keys(rules).length ? FlowUtils.mapEdge(rules) : undefined;
307
- };
308
398
  const FlowUtils = {
309
399
  generateRulesName: (nextRules) => {
310
400
  return nextRules.reduce((acc, inc) => {
@@ -361,23 +451,6 @@ const FlowUtils = {
361
451
  return res;
362
452
  },
363
453
  makeRules: (rules) => Object.entries(rules).reduce((acc2, [k, v]) => [...acc2, { nodeId: k, rules: v }], []),
364
- // TODO: This function is strictly related to React nodes, could make sense to move it in the flower-react folder
365
- generateNodesForFlowerJson: (nodes) => nodes
366
- .filter((e) => !!_get(e, 'props.id'))
367
- .map((e) => {
368
- const rules = FlowUtils.makeRules(e.props.to ?? {});
369
- const nextRules = getRulesExists(rules);
370
- const children = e.props.data?.children;
371
- return {
372
- nodeId: e.props.id,
373
- nodeType: e.type.displayName || e.props.as || 'FlowerNode',
374
- nodeTitle: _get(e.props, 'data.title'),
375
- children,
376
- nextRules,
377
- retain: e.props.retain,
378
- disabled: e.props.disabled
379
- };
380
- }),
381
454
  allEqual: (arr, arr2) => arr.length === arr2.length && arr.every((v) => arr2.includes(v)),
382
455
  findValidRule: (nextRules, value, prefix) => find(nextRules, (rule) => {
383
456
  // fix per evitare di entrare in un nodo senza regole, ma con un name,
@@ -648,7 +721,7 @@ const FlowerCoreBaseReducers = {
648
721
  payload: { name: flowName, node: nextNumberNode }
649
722
  });
650
723
  },
651
- prev: (state, { payload }) => {
724
+ back: (state, { payload }) => {
652
725
  const { name, flowName } = payload;
653
726
  FlowerCoreBaseReducers.historyPop(state, {
654
727
  type: 'historyPop',
@@ -841,4 +914,4 @@ const FlowerCoreStateSelectors = {
841
914
  ...FlowerCoreStateDataSelectors
842
915
  };
843
916
 
844
- export { CoreUtils, DataUtils, Emitter, FlowUtils, FlowerCoreBaseReducers, FlowerCoreDataReducers, FlowerCoreReducers, FlowerCoreStateBaseSelectors, FlowerCoreStateDataSelectors, FlowerCoreStateSelectors, FlowerStateUtils, REDUCER_NAME, devtoolState, flattenRules, generateData, rulesMatcher, rulesMatcherUtils };
917
+ export { AbacEngine, CoreUtils, DataUtils, Emitter, FlowUtils, FlowerCoreBaseReducers, FlowerCoreDataReducers, FlowerCoreReducers, FlowerCoreStateBaseSelectors, FlowerCoreStateDataSelectors, FlowerCoreStateSelectors, FlowerStateUtils, REDUCER_NAME, clearSubject, devtoolState, flattenRules, generateData, getAbacEngine, getRawRules, getSubject, initAbac, isAbacInitialized, rulesMatcher, rulesMatcherUtils, setSubject };
@@ -0,0 +1,32 @@
1
+ export type Action = string;
2
+ export type Subject = Record<string, any>;
3
+ export type Resource = Record<string, any>;
4
+ export type Environment = Record<string, any>;
5
+ export type AbacCtx = {
6
+ subject: Subject;
7
+ resource?: Resource;
8
+ action: Action;
9
+ environment?: Environment;
10
+ };
11
+ export type RuleInput = {
12
+ id: string;
13
+ description?: string;
14
+ effect: 'Permit' | 'Deny';
15
+ priority?: number;
16
+ action?: string | string[];
17
+ /**
18
+ * condition: string JS expression that returns boolean.
19
+ * It can reference `subject`, `resource`, `action`, `environment`.
20
+ * Example: "action === 'read' && (!resource.confidential || resource.ownerID === subject.id)"
21
+ */
22
+ condition?: string;
23
+ };
24
+ export type CompiledRule = RuleInput & {
25
+ conditionFn?: (subject: Subject, resource?: Resource, action?: Action, environment?: Environment) => boolean;
26
+ };
27
+ export declare class AbacEngine {
28
+ private rules;
29
+ constructor(rules?: RuleInput[]);
30
+ decide(ctx: AbacCtx): 'Permit' | 'Deny';
31
+ allowedActions(ctxBase: Omit<AbacCtx, 'action'>, actions: Action[]): string[];
32
+ }
@@ -0,0 +1,8 @@
1
+ import { AbacEngine, RuleInput, Subject } from './abacEngine';
2
+ export declare function initAbac(rules: RuleInput[]): void;
3
+ export declare function getAbacEngine(): AbacEngine | null;
4
+ export declare function getRawRules(): RuleInput[] | null;
5
+ export declare function isAbacInitialized(): boolean;
6
+ export declare function setSubject(subject: Subject): void;
7
+ export declare function getSubject(): Subject | null;
8
+ export declare function clearSubject(): void;
@@ -0,0 +1,2 @@
1
+ export * from './abacEngine';
2
+ export * from './abacSingleton';
@@ -3,4 +3,5 @@ export * from './constants';
3
3
  export * from './utils';
4
4
  export * from './rules-matcher';
5
5
  export * from './state-manager';
6
+ export * from './ABAC';
6
7
  export type * from './interfaces';
@@ -1,5 +1,6 @@
1
1
  export declare enum RulesOperators {
2
2
  $exists = "$exists",
3
+ $can = "$can",
3
4
  $eq = "$eq",
4
5
  $ne = "$ne",
5
6
  $gt = "$gt",
@@ -26,6 +27,15 @@ export type Edge<T = object> = {
26
27
  rules: RulesObject<T>;
27
28
  };
28
29
  };
30
+ export type NodeConfig = {
31
+ nodeId: string | undefined;
32
+ nodeType: string;
33
+ nodeTitle: string;
34
+ children: Node['children'];
35
+ nextRules: ReturnType<MapEdge> | undefined;
36
+ retain: boolean;
37
+ disabled: boolean;
38
+ };
29
39
  export type Node = {
30
40
  nodeId: string | undefined;
31
41
  nodeType: string;
@@ -93,22 +103,12 @@ export type MapEdge<K = RulesByNodeId<any>, T = K[]> = (nextNode: T) => Array<K>
93
103
  export type MakeRules<T extends Record<string, any> = {
94
104
  rules: RulesObject<any> | object;
95
105
  }> = (rules: T) => Array<RulesByNodeId<T>>;
96
- export type GetRulesExists = (rules: RulesByNodeId<any>[]) => ReturnType<MapEdge> | undefined;
97
- export type GenerateNodesForFlowerJson = (nodes: Node[], edges?: Edge[]) => {
98
- nodeId: string | undefined;
99
- nodeType: string;
100
- nodeTitle: string;
101
- children: Node['children'];
102
- nextRules: ReturnType<GetRulesExists>;
103
- retain: boolean;
104
- disabled: boolean;
105
- }[];
106
106
  export type HasNode = (state: Record<string, any>, name: string, node: string) => boolean;
107
- export type MakeObjectRules = (nodes: Node[]) => {
107
+ export type MakeObjectRules = (nodes: NodeConfig[]) => {
108
108
  [x: string]: Node['nextRules'];
109
109
  };
110
- export type GenerateNodes = (nodes: Node[]) => {
111
- [x: string]: Omit<Node, 'nextRules'>;
110
+ export type GenerateNodes = (nodes: NodeConfig[]) => {
111
+ [x: string]: Partial<Node>;
112
112
  };
113
113
  export type MapKeysDeepLodash = (obj: Record<string, any>, cb: (...args: any) => any, isRecursive?: boolean) => Record<string, any>;
114
114
  export type GenerateRulesName = (nextRules: RulesWithName[]) => {
@@ -176,13 +176,6 @@ export interface FlowUtilitiesFunctions {
176
176
  * @returns
177
177
  */
178
178
  makeRules: MakeRules;
179
- /**
180
- * Generates nodes for a flower JSON structure, extracting rules and other properties.
181
- * @param nodes
182
- *
183
- * @returns
184
- */
185
- generateNodesForFlowerJson: GenerateNodesForFlowerJson;
186
179
  /**
187
180
  * Checks if two arrays are equal in length and have the same elements.
188
181
  * @param arr
@@ -1,11 +1,11 @@
1
- import { Node } from './CoreInterface';
1
+ import { NodeConfig } from './CoreInterface';
2
2
  import { Flower } from './Store';
3
3
  export type ActionWithPayload<T> = {
4
4
  type: string;
5
5
  payload: T;
6
6
  };
7
7
  type ReducerFunctionSign<T extends object, R> = (state: Record<string, Flower<T>>, action: ActionWithPayload<R>) => Record<string, Flower<T>> | void;
8
- export type CoreReducersFunctions<T extends Record<string, any> = Record<string, Flower<Record<string, any>>>> = {
8
+ export type CoreReducersFunctions<T extends Record<string, any> = Record<FlowCaseReducersNames, Flower<Record<string, any>>>> = {
9
9
  /**
10
10
  * @param state
11
11
  * @param action
@@ -126,7 +126,7 @@ export type CoreReducersFunctions<T extends Record<string, any> = Record<string,
126
126
  name: string;
127
127
  startId: string;
128
128
  persist: boolean;
129
- nodes: Node[];
129
+ nodes: NodeConfig[];
130
130
  initialState: {
131
131
  startId?: string;
132
132
  current?: string;
@@ -197,7 +197,7 @@ export type CoreReducersFunctions<T extends Record<string, any> = Record<string,
197
197
  *
198
198
  * @returns state
199
199
  */
200
- prev: ReducerFunctionSign<T, {
200
+ back: ReducerFunctionSign<T, {
201
201
  name?: string;
202
202
  flowName?: string;
203
203
  }>;
@@ -235,10 +235,11 @@ export type CoreReducersFunctions<T extends Record<string, any> = Record<string,
235
235
  */
236
236
  >;
237
237
  };
238
+ export type FlowCaseReducersNames = 'historyAdd' | 'historyPrevToNode' | 'forceAddHistory' | 'historyPop' | 'restoreHistory' | 'replaceNode' | 'forceResetHistory' | 'destroy' | 'initNodes' | 'setCurrentNode' | 'node' | 'prevToNode' | 'next' | 'back' | 'restart' | 'reset';
238
239
  type DataReducerFunctionSign<T extends object, R = object> = (state: Record<string, T>, action: ActionWithPayload<{
239
240
  rootName: string;
240
241
  } & R>) => Record<string, T> | void;
241
- export type DataReducersFunctions<T extends Record<string, any> = Record<string, Record<string, any>>> = {
242
+ export type DataReducersFunctions<T extends Record<string, any> = Record<DataCaseReducersNames, Record<string, any>>> = {
242
243
  /**
243
244
  * @param state
244
245
  * @param action
@@ -403,4 +404,5 @@ export type DataReducersFunctions<T extends Record<string, any> = Record<string,
403
404
  initialData: Record<string, any>;
404
405
  }>;
405
406
  };
407
+ export type DataCaseReducersNames = 'setFormSubmitted' | 'addCustomDataErrors' | 'addDataErrors' | 'fieldDirty' | 'fieldTouch' | 'fieldFocus' | 'removeDataErrors' | 'addData' | 'addDataByPath' | 'replaceData' | 'unsetData' | 'setIsDataValidating' | 'resetData' | 'initData';
406
408
  export {};
@@ -161,7 +161,18 @@ export interface RulesMatcherUtils {
161
161
  getKeys: <T extends Record<string, any>>(rules?: T, options?: Record<string, any>) => string[] | null;
162
162
  }
163
163
  export type OperatorsFunction = (a: any, b: any, opt?: any, data?: any) => boolean;
164
+ export type DecisionFunction = (subject: Record<string, any>, action: string, resource: Record<string, any>) => boolean;
164
165
  export type Operators = {
166
+ /**
167
+ * @param subject
168
+ * @param action
169
+ * @param resource
170
+ *
171
+ * Checks if a operation is denied or permitted based on ABAC rules.
172
+ *
173
+ * @returns
174
+ */
175
+ $can: DecisionFunction;
165
176
  /**
166
177
  * @param a
167
178
  * @param b
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flower-core",
3
- "version": "4.0.1-beta.1",
3
+ "version": "4.0.1-beta.10",
4
4
  "description": "Core functions for flowerJS",
5
5
  "repository": {
6
6
  "type": "git",