@flowerforce/flower-react 4.0.11-beta.1 → 4.0.11-beta.2
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 +76 -2
- package/dist/index.esm.js +77 -5
- package/dist/src/abacProvider/abacProvider.d.ts +18 -0
- package/dist/src/abacProvider/index.d.ts +1 -0
- package/dist/src/components/hooks/useInitDevtools.d.ts +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/types/Flower.d.ts +1 -1
- package/package.json +7 -7
package/dist/index.cjs.js
CHANGED
@@ -96,7 +96,7 @@ const useInitNodes = ({ one, nodes, name, startId, persist = false, initialData,
|
|
96
96
|
}, [dispatch, name, nodes, startId, initialData, initialState, persist]);
|
97
97
|
};
|
98
98
|
|
99
|
-
const useInitDevtools = ({ devtoolState, setWsDevtools, flowName
|
99
|
+
const useInitDevtools = ({ devtoolState, setWsDevtools, flowName }) => {
|
100
100
|
const { dispatch } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
|
101
101
|
React.useEffect(() => {
|
102
102
|
/* istanbul ignore next */
|
@@ -209,7 +209,10 @@ const useFlowerNavigateEvent = ({ current, flowName, isInitialized, wsDevtools,
|
|
209
209
|
if (!isInitialized)
|
210
210
|
return;
|
211
211
|
if (isDisabled) {
|
212
|
-
dispatch({
|
212
|
+
dispatch({
|
213
|
+
type: `${flowerCore.REDUCER_NAME.FLOWER_FLOW}/next`,
|
214
|
+
payload: { flowName, disabled: true }
|
215
|
+
});
|
213
216
|
// eslint-disable-next-line no-underscore-dangle, no-undef
|
214
217
|
/* istanbul ignore next */
|
215
218
|
if (wsDevtools &&
|
@@ -761,6 +764,75 @@ const createSliceWithFlower = (createSliceOptions) => {
|
|
761
764
|
return slice;
|
762
765
|
};
|
763
766
|
|
767
|
+
const AbacContext = React.createContext({
|
768
|
+
can: () => false,
|
769
|
+
allowedActions: () => [],
|
770
|
+
loaded: false,
|
771
|
+
rawRules: null
|
772
|
+
});
|
773
|
+
const AbacProvider = ({ children, rules, rulesPath, fetchOptions, denyByDefault = true }) => {
|
774
|
+
const [loaded, setLoaded] = React.useState(flowerCore.isAbacInitialized());
|
775
|
+
const [localRawRules, setLocalRawRules] = React.useState(flowerCore.getRawRules());
|
776
|
+
React.useEffect(() => {
|
777
|
+
if (rules) {
|
778
|
+
flowerCore.initAbac(rules);
|
779
|
+
setLocalRawRules(rules);
|
780
|
+
setLoaded(true);
|
781
|
+
return;
|
782
|
+
}
|
783
|
+
if (!rulesPath) {
|
784
|
+
setLoaded(false);
|
785
|
+
setLocalRawRules(null);
|
786
|
+
return;
|
787
|
+
}
|
788
|
+
let mounted = true;
|
789
|
+
const init = async () => {
|
790
|
+
try {
|
791
|
+
const res = await fetch(rulesPath, fetchOptions);
|
792
|
+
if (!res.ok)
|
793
|
+
throw new Error(`HTTP ${res.status}`);
|
794
|
+
const json = (await res.json());
|
795
|
+
if (!mounted)
|
796
|
+
return;
|
797
|
+
flowerCore.initAbac(json);
|
798
|
+
setLocalRawRules(json);
|
799
|
+
setLoaded(true);
|
800
|
+
}
|
801
|
+
catch (err) {
|
802
|
+
console.error('ABAC: failed to load rules from', rulesPath, err);
|
803
|
+
setLocalRawRules(null);
|
804
|
+
setLoaded(false);
|
805
|
+
}
|
806
|
+
};
|
807
|
+
init();
|
808
|
+
return () => {
|
809
|
+
mounted = false;
|
810
|
+
};
|
811
|
+
}, [rules, rulesPath, fetchOptions]);
|
812
|
+
const can = React.useCallback((ctx) => {
|
813
|
+
if (!flowerCore.isAbacInitialized())
|
814
|
+
return !denyByDefault;
|
815
|
+
const subject = flowerCore.getSubject();
|
816
|
+
return (flowerCore.getAbacEngine()?.decide({ subject: subject ?? {}, ...ctx }) === 'Permit');
|
817
|
+
}, []);
|
818
|
+
const allowedActions = React.useCallback((resource, actions = ['read', 'create', 'update', 'delete']) => {
|
819
|
+
if (!flowerCore.isAbacInitialized())
|
820
|
+
return denyByDefault ? [] : actions.slice();
|
821
|
+
const subject = flowerCore.getSubject();
|
822
|
+
return flowerCore.getAbacEngine()?.allowedActions({ subject: subject ?? {}, resource, environment: undefined }, actions);
|
823
|
+
}, []);
|
824
|
+
const value = React.useMemo(() => ({
|
825
|
+
can,
|
826
|
+
allowedActions,
|
827
|
+
loaded,
|
828
|
+
rawRules: localRawRules
|
829
|
+
}), [loaded, localRawRules]);
|
830
|
+
return React.createElement(AbacContext.Provider, { value: value }, children);
|
831
|
+
};
|
832
|
+
function useAbac() {
|
833
|
+
return React.useContext(AbacContext);
|
834
|
+
}
|
835
|
+
|
764
836
|
Object.defineProperty(exports, "createApi", {
|
765
837
|
enumerable: true,
|
766
838
|
get: function () { return flowerReactStore.createApi; }
|
@@ -769,6 +841,7 @@ Object.defineProperty(exports, "HistoryContextProvider", {
|
|
769
841
|
enumerable: true,
|
770
842
|
get: function () { return flowerReactHistoryContext.HistoryContextProvider; }
|
771
843
|
});
|
844
|
+
exports.AbacProvider = AbacProvider;
|
772
845
|
exports.Flower = Flower;
|
773
846
|
exports.FlowerAction = FlowerAction;
|
774
847
|
exports.FlowerFlow = FlowerFlow;
|
@@ -783,5 +856,6 @@ exports.createStoreWithFlower = createStoreWithFlower;
|
|
783
856
|
exports.flowerReducers = flowerReducers;
|
784
857
|
exports.makeSelectData = makeSelectData;
|
785
858
|
exports.reducerFlower = reducerFlower;
|
859
|
+
exports.useAbac = useAbac;
|
786
860
|
exports.useFlower = useFlower;
|
787
861
|
exports.useFlowerNavigate = useFlowerNavigate;
|
package/dist/index.esm.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
import React, { useEffect, memo, useRef, useState, useMemo, Children, useContext, useCallback } from 'react';
|
1
|
+
import React, { useEffect, memo, useRef, useState, useMemo, Children, useContext, useCallback, createContext } from 'react';
|
2
2
|
import _keyBy from 'lodash/keyBy';
|
3
|
-
import { FlowUtils, FlowerCoreBaseReducers, REDUCER_NAME, FlowerCoreStateSelectors, FlowerStateUtils, Emitter, devtoolState } from '@flowerforce/flower-core';
|
3
|
+
import { FlowUtils, FlowerCoreBaseReducers, REDUCER_NAME, FlowerCoreStateSelectors, FlowerStateUtils, Emitter, devtoolState, isAbacInitialized, getRawRules, initAbac, getSubject, getAbacEngine } from '@flowerforce/flower-core';
|
4
4
|
import { FlowerReactProvider, FlowerReactContext } from '@flowerforce/flower-react-context';
|
5
5
|
import _get from 'lodash/get';
|
6
6
|
import { reducerData, ReduxFlowerProvider, flowerDataActions, middlewares } from '@flowerforce/flower-react-store';
|
@@ -96,7 +96,7 @@ const useInitNodes = ({ one, nodes, name, startId, persist = false, initialData,
|
|
96
96
|
}, [dispatch, name, nodes, startId, initialData, initialState, persist]);
|
97
97
|
};
|
98
98
|
|
99
|
-
const useInitDevtools = ({ devtoolState, setWsDevtools, flowName
|
99
|
+
const useInitDevtools = ({ devtoolState, setWsDevtools, flowName }) => {
|
100
100
|
const { dispatch } = ReduxFlowerProvider.getReduxHooks();
|
101
101
|
useEffect(() => {
|
102
102
|
/* istanbul ignore next */
|
@@ -209,7 +209,10 @@ const useFlowerNavigateEvent = ({ current, flowName, isInitialized, wsDevtools,
|
|
209
209
|
if (!isInitialized)
|
210
210
|
return;
|
211
211
|
if (isDisabled) {
|
212
|
-
dispatch({
|
212
|
+
dispatch({
|
213
|
+
type: `${REDUCER_NAME.FLOWER_FLOW}/next`,
|
214
|
+
payload: { flowName, disabled: true }
|
215
|
+
});
|
213
216
|
// eslint-disable-next-line no-underscore-dangle, no-undef
|
214
217
|
/* istanbul ignore next */
|
215
218
|
if (wsDevtools &&
|
@@ -761,4 +764,73 @@ const createSliceWithFlower = (createSliceOptions) => {
|
|
761
764
|
return slice;
|
762
765
|
};
|
763
766
|
|
764
|
-
|
767
|
+
const AbacContext = createContext({
|
768
|
+
can: () => false,
|
769
|
+
allowedActions: () => [],
|
770
|
+
loaded: false,
|
771
|
+
rawRules: null
|
772
|
+
});
|
773
|
+
const AbacProvider = ({ children, rules, rulesPath, fetchOptions, denyByDefault = true }) => {
|
774
|
+
const [loaded, setLoaded] = useState(isAbacInitialized());
|
775
|
+
const [localRawRules, setLocalRawRules] = useState(getRawRules());
|
776
|
+
useEffect(() => {
|
777
|
+
if (rules) {
|
778
|
+
initAbac(rules);
|
779
|
+
setLocalRawRules(rules);
|
780
|
+
setLoaded(true);
|
781
|
+
return;
|
782
|
+
}
|
783
|
+
if (!rulesPath) {
|
784
|
+
setLoaded(false);
|
785
|
+
setLocalRawRules(null);
|
786
|
+
return;
|
787
|
+
}
|
788
|
+
let mounted = true;
|
789
|
+
const init = async () => {
|
790
|
+
try {
|
791
|
+
const res = await fetch(rulesPath, fetchOptions);
|
792
|
+
if (!res.ok)
|
793
|
+
throw new Error(`HTTP ${res.status}`);
|
794
|
+
const json = (await res.json());
|
795
|
+
if (!mounted)
|
796
|
+
return;
|
797
|
+
initAbac(json);
|
798
|
+
setLocalRawRules(json);
|
799
|
+
setLoaded(true);
|
800
|
+
}
|
801
|
+
catch (err) {
|
802
|
+
console.error('ABAC: failed to load rules from', rulesPath, err);
|
803
|
+
setLocalRawRules(null);
|
804
|
+
setLoaded(false);
|
805
|
+
}
|
806
|
+
};
|
807
|
+
init();
|
808
|
+
return () => {
|
809
|
+
mounted = false;
|
810
|
+
};
|
811
|
+
}, [rules, rulesPath, fetchOptions]);
|
812
|
+
const can = useCallback((ctx) => {
|
813
|
+
if (!isAbacInitialized())
|
814
|
+
return !denyByDefault;
|
815
|
+
const subject = getSubject();
|
816
|
+
return (getAbacEngine()?.decide({ subject: subject ?? {}, ...ctx }) === 'Permit');
|
817
|
+
}, []);
|
818
|
+
const allowedActions = useCallback((resource, actions = ['read', 'create', 'update', 'delete']) => {
|
819
|
+
if (!isAbacInitialized())
|
820
|
+
return denyByDefault ? [] : actions.slice();
|
821
|
+
const subject = getSubject();
|
822
|
+
return getAbacEngine()?.allowedActions({ subject: subject ?? {}, resource, environment: undefined }, actions);
|
823
|
+
}, []);
|
824
|
+
const value = useMemo(() => ({
|
825
|
+
can,
|
826
|
+
allowedActions,
|
827
|
+
loaded,
|
828
|
+
rawRules: localRawRules
|
829
|
+
}), [loaded, localRawRules]);
|
830
|
+
return React.createElement(AbacContext.Provider, { value: value }, children);
|
831
|
+
};
|
832
|
+
function useAbac() {
|
833
|
+
return useContext(AbacContext);
|
834
|
+
}
|
835
|
+
|
836
|
+
export { AbacProvider, Flower, FlowerAction, FlowerFlow, FlowerNavigate, FlowerNode, FlowerProvider, FlowerRoute, FlowerServer, FlowerStart, createSliceWithFlower, createStoreWithFlower, flowerReducers, makeSelectData, reducerFlower, useAbac, useFlower, useFlowerNavigate };
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { RuleInput, AbacCtx, Resource, Action } from '@flowerforce/flower-core';
|
3
|
+
type AbacProviderProps = {
|
4
|
+
children: React.ReactNode;
|
5
|
+
rules?: RuleInput[];
|
6
|
+
rulesPath?: string;
|
7
|
+
fetchOptions?: RequestInit;
|
8
|
+
denyByDefault?: boolean;
|
9
|
+
};
|
10
|
+
type AbacContextValue = {
|
11
|
+
can: (args: Omit<AbacCtx, 'subject'>) => boolean;
|
12
|
+
allowedActions: (resource: Resource | undefined, actions?: Action[]) => Action[] | undefined;
|
13
|
+
loaded: boolean;
|
14
|
+
rawRules?: RuleInput[] | null;
|
15
|
+
};
|
16
|
+
export declare const AbacProvider: React.FC<AbacProviderProps>;
|
17
|
+
export declare function useAbac(): AbacContextValue;
|
18
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './abacProvider';
|
@@ -1,2 +1,2 @@
|
|
1
1
|
import { UseInitDevtoolsProps } from './types';
|
2
|
-
export declare const useInitDevtools: ({ devtoolState, setWsDevtools, flowName
|
2
|
+
export declare const useInitDevtools: ({ devtoolState, setWsDevtools, flowName }: UseInitDevtoolsProps) => void;
|
package/dist/src/index.d.ts
CHANGED
@@ -2,5 +2,6 @@ export { Flower, FlowerAction, FlowerFlow, FlowerNode, FlowerRoute, FlowerNaviga
|
|
2
2
|
export * from './provider';
|
3
3
|
export { makeSelectData, reducerFlower, flowerReducers } from './features';
|
4
4
|
export type * from './types';
|
5
|
+
export * from './abacProvider';
|
5
6
|
export { createApi } from '@flowerforce/flower-react-store';
|
6
7
|
export { HistoryContextProvider } from '@flowerforce/flower-react-history-context';
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@flowerforce/flower-react",
|
3
|
-
"version": "4.0.11-beta.
|
3
|
+
"version": "4.0.11-beta.2",
|
4
4
|
"description": "FlowerJS components, hooks and utils for React.",
|
5
5
|
"repository": {
|
6
6
|
"type": "git",
|
@@ -26,8 +26,8 @@
|
|
26
26
|
"@reduxjs/toolkit": "^2.2.4",
|
27
27
|
"reselect": "^5.1.0",
|
28
28
|
"@testing-library/react": "15.0.7",
|
29
|
-
"@testing-library/jest-dom": "6.
|
30
|
-
"@flowerforce/flower-react-form": "4.0.11-beta.
|
29
|
+
"@testing-library/jest-dom": "6.8.0",
|
30
|
+
"@flowerforce/flower-react-form": "4.0.11-beta.1",
|
31
31
|
"@testing-library/user-event": "14.6.1",
|
32
32
|
"redux": "5.0.1"
|
33
33
|
},
|
@@ -41,10 +41,10 @@
|
|
41
41
|
"typescript": "^5.4.5"
|
42
42
|
},
|
43
43
|
"dependencies": {
|
44
|
-
"@flowerforce/flower-core": "4.0.1-beta.
|
45
|
-
"@flowerforce/flower-react-context": "4.0.1-beta.
|
46
|
-
"@flowerforce/flower-react-store": "4.0.1-beta.
|
47
|
-
"@flowerforce/flower-react-shared": "4.0.6-beta.
|
44
|
+
"@flowerforce/flower-core": "4.0.1-beta.10",
|
45
|
+
"@flowerforce/flower-react-context": "4.0.1-beta.10",
|
46
|
+
"@flowerforce/flower-react-store": "4.0.1-beta.10",
|
47
|
+
"@flowerforce/flower-react-shared": "4.0.6-beta.6",
|
48
48
|
"@flowerforce/flower-react-history-context": "4.0.0",
|
49
49
|
"lodash": "^4.17.21"
|
50
50
|
},
|