@webiny/app-admin 6.0.0 → 6.1.0-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/base/ui/Brand.d.ts +6 -6
- package/base/ui/Dashboard.d.ts +6 -6
- package/base/ui/FileManager.d.ts +6 -6
- package/base/ui/Layout.d.ts +6 -6
- package/base/ui/LoginScreen.d.ts +12 -12
- package/base/ui/Logo.d.ts +6 -6
- package/base/ui/Navigation.d.ts +6 -6
- package/base/ui/NotFound.d.ts +6 -6
- package/base/ui/TenantSelector.d.ts +6 -6
- package/base/ui/UserMenu/UserMenu.d.ts +6 -6
- package/base/ui/UserMenu/UserMenuHandle.d.ts +6 -6
- package/base/ui/UserMenu/UserMenuItem.d.ts +12 -12
- package/base/ui/UserMenu/UserMenuLink.d.ts +12 -12
- package/base/ui/UserMenu/UserMenuSeparator.d.ts +6 -6
- package/base/ui/UserMenu.d.ts +12 -12
- package/components/Dialogs/Dialog.d.ts +1 -0
- package/components/Dialogs/Dialog.js +1 -0
- package/components/Dialogs/Dialog.js.map +1 -1
- package/components/Dialogs/DialogsContext.d.ts +4 -3
- package/components/Dialogs/DialogsContext.js +2 -0
- package/components/Dialogs/DialogsContext.js.map +1 -1
- package/components/IconPicker/IconRenderer.d.ts +6 -6
- package/components/LexicalEditor/LexicalLinkForm.js +3 -1
- package/components/LexicalEditor/LexicalLinkForm.js.map +1 -1
- package/components/OverlayLayout/components/OverlayRoot.js +3 -0
- package/components/OverlayLayout/components/OverlayRoot.js.map +1 -1
- package/components/Permissions/StyledComponents.js +9 -3
- package/components/Permissions/StyledComponents.js.map +1 -1
- package/components/SimpleUI/InputField.js +16 -39
- package/components/SimpleUI/InputField.js.map +1 -1
- package/components/index.d.ts +1 -4
- package/components/index.js +1 -4
- package/components/index.js.map +1 -1
- package/exports/admin/build-params.d.ts +1 -0
- package/exports/admin/build-params.js +1 -0
- package/exports/admin/build-params.js.map +1 -1
- package/exports/admin/ui.d.ts +1 -0
- package/exports/admin/ui.js +1 -0
- package/exports/admin/ui.js.map +1 -1
- package/exports/admin.d.ts +1 -0
- package/exports/admin.js +1 -0
- package/exports/admin.js.map +1 -1
- package/features/buildParams/feature.d.ts +1 -1
- package/features/buildParams/feature.js +4 -0
- package/features/buildParams/feature.js.map +1 -1
- package/hooks/index.d.ts +1 -0
- package/hooks/index.js +1 -0
- package/hooks/index.js.map +1 -1
- package/hooks/useHotkeys.d.ts +9 -0
- package/hooks/useHotkeys.js +99 -0
- package/hooks/useHotkeys.js.map +1 -0
- package/index.d.ts +1 -0
- package/index.js +2 -1
- package/index.js.map +1 -1
- package/package.json +30 -34
- package/permissions/createHasPermission.d.ts +1 -1
- package/permissions/createHasPermission.js +20 -5
- package/permissions/createHasPermission.js.map +1 -1
- package/permissions/createHasPermission.test.d.ts +1 -0
- package/permissions/createHasPermission.test.js +206 -0
- package/permissions/createHasPermission.test.js.map +1 -0
- package/permissions/types.d.ts +24 -4
- package/permissions/types.js.map +1 -1
- package/presentation/buildParams/useBuildParams.d.ts +1 -0
- package/presentation/buildParams/useBuildParams.js +7 -0
- package/presentation/buildParams/useBuildParams.js.map +1 -0
- package/presentation/wcp/WcpProvider.js +8 -1
- package/presentation/wcp/WcpProvider.js.map +1 -1
- package/components/FloatingPanel.d.ts +0 -13
- package/components/FloatingPanel.js +0 -94
- package/components/FloatingPanel.js.map +0 -1
- package/components/NavigationPrompt.d.ts +0 -27
- package/components/NavigationPrompt.js +0 -39
- package/components/NavigationPrompt.js.map +0 -1
- package/components/StateInspector.d.ts +0 -8
- package/components/StateInspector.js +0 -36
- package/components/StateInspector.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useEffect","useRef","isHotkey","state","listenerAttached","zIndex","handlers","triggerHotkeys","e","keys","key","registerZIndex","Object","length","Error","document","body","addEventListener","unregisterZIndex","Math","max","map","Number","removeEventListener","useHotkeys","props","disabled","prevPropsRef","firstRenderRef","current","assign"],"sources":["useHotkeys.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport isHotkey from \"is-hotkey\";\n\ntype HookProps = {\n disabled?: boolean;\n zIndex: number;\n keys?: { [key: string]: (e: KeyboardEvent) => void };\n};\n\ntype State = {\n listenerAttached: boolean;\n zIndex: null | number;\n handlers: { [zIndex: number]: { [key: string]: (e: KeyboardEvent) => void } };\n};\n\nconst state: State = {\n listenerAttached: false,\n zIndex: null,\n handlers: {}\n};\n\nfunction triggerHotkeys(e: KeyboardEvent) {\n if (state.zIndex === null) {\n return;\n }\n\n const keys = state.handlers[state.zIndex];\n for (const key in keys) {\n if (isHotkey(key, e)) {\n keys[key](e);\n break;\n }\n }\n}\n\nfunction registerZIndex({ zIndex, keys }: HookProps) {\n if (state.zIndex === null || state.zIndex < zIndex) {\n state.zIndex = zIndex;\n }\n\n if (!state.handlers[zIndex]) {\n state.handlers[zIndex] = {};\n }\n\n if (!keys || Object.keys(keys).length === 0) {\n return;\n }\n\n for (const key in keys) {\n if (key in state.handlers[zIndex]) {\n throw Error(`Shortcut \"${key}\" already registered on zIndex ${zIndex}.`);\n }\n state.handlers[zIndex][key] = keys[key];\n }\n\n if (!state.listenerAttached) {\n document.body.addEventListener(\"keydown\", triggerHotkeys);\n state.listenerAttached = true;\n }\n}\n\nfunction unregisterZIndex({ zIndex, keys }: HookProps) {\n if (state.handlers && state.handlers[zIndex]) {\n for (const key in keys) {\n delete state.handlers[zIndex][key];\n }\n\n if (Object.keys(state.handlers[zIndex]).length === 0) {\n delete state.handlers[zIndex];\n }\n }\n\n if (Object.keys(state.handlers).length > 0) {\n state.zIndex = Math.max(...Object.keys(state.handlers).map(Number));\n } else {\n state.zIndex = null;\n\n if (state.listenerAttached) {\n document.body.removeEventListener(\"keydown\", triggerHotkeys);\n state.listenerAttached = false;\n }\n }\n}\n\nexport function useHotkeys(props: HookProps) {\n const { disabled, zIndex, keys } = props;\n\n const prevPropsRef = useRef<HookProps | undefined>();\n const firstRenderRef = useRef(true);\n\n useEffect(function () {\n if (firstRenderRef.current || prevPropsRef.current?.disabled !== disabled) {\n firstRenderRef.current = false;\n if (disabled) {\n unregisterZIndex(props);\n } else {\n registerZIndex(props);\n }\n }\n\n if (!disabled && typeof keys === \"object\") {\n Object.assign(state.handlers[zIndex], keys);\n }\n prevPropsRef.current = { ...props };\n });\n\n useEffect(function () {\n return function () {\n if (!disabled) {\n unregisterZIndex(props);\n }\n };\n }, []);\n}\n"],"mappings":"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","ignoreList":[]}
|
package/index.d.ts
CHANGED
|
@@ -32,5 +32,6 @@ export { useWcp } from "./presentation/wcp/useWcp.js";
|
|
|
32
32
|
export { useTenantContext } from "./presentation/tenancy/useTenantContext.js";
|
|
33
33
|
export { useIdentity } from "./presentation/security/hooks/useIdentity.js";
|
|
34
34
|
export { useAuthentication } from "./presentation/security/hooks/useAuthentication.js";
|
|
35
|
+
export { useBuildParams } from "./presentation/buildParams/useBuildParams.js";
|
|
35
36
|
export { useSecurity } from "./presentation/security/hooks/useSecurity.js";
|
|
36
37
|
export * from "@webiny/app/renderApp.js";
|
package/index.js
CHANGED
|
@@ -30,7 +30,6 @@ export { SystemInstallerProvider } from "./presentation/installation/components/
|
|
|
30
30
|
|
|
31
31
|
// Feature types
|
|
32
32
|
|
|
33
|
-
// Features
|
|
34
33
|
export { BuildParamsFeature } from "./features/buildParams/feature.js";
|
|
35
34
|
|
|
36
35
|
// Hooks
|
|
@@ -39,6 +38,8 @@ export { useWcp } from "./presentation/wcp/useWcp.js";
|
|
|
39
38
|
export { useTenantContext } from "./presentation/tenancy/useTenantContext.js";
|
|
40
39
|
export { useIdentity } from "./presentation/security/hooks/useIdentity.js";
|
|
41
40
|
export { useAuthentication } from "./presentation/security/hooks/useAuthentication.js";
|
|
41
|
+
export { useBuildParams } from "./presentation/buildParams/useBuildParams.js";
|
|
42
|
+
|
|
42
43
|
// Legacy hook for easier migration
|
|
43
44
|
export { useSecurity } from "./presentation/security/hooks/useSecurity.js";
|
|
44
45
|
export * from "@webiny/app/renderApp.js";
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Admin","HasPermission","SecureRoute","FileManager","FileManagerRenderer","SystemInstallerProvider","BuildParamsFeature","useWcp","useTenantContext","useIdentity","useAuthentication","useSecurity"],"sources":["index.ts"],"sourcesContent":["export * from \"@webiny/app\";\nexport type { HigherOrderComponent, ProviderProps, ComposeProps } from \"@webiny/app\";\n// UI components\nexport * from \"./base/ui/Tags.js\";\nexport * from \"./base/ui/Layout.js\";\nexport * from \"./base/ui/TenantSelector.js\";\nexport type { LayoutProps } from \"./base/ui/Layout.js\";\nexport * from \"./base/ui/Navigation.js\";\nexport * from \"./base/ui/Brand.js\";\nexport * from \"./base/ui/Logo.js\";\nexport * from \"./base/ui/UserMenu.js\";\nexport * from \"./base/ui/LoginScreen.js\";\nexport * from \"./base/ui/CenteredView.js\";\nexport * from \"./base/ui/Dashboard.js\";\nexport * from \"./base/ui/NotFound.js\";\n\n// Base admin app\nexport { Admin } from \"./base/Admin.js\";\nexport * from \"./config/AdminConfig.js\";\n\nexport type { AdminProps } from \"./base/Admin.js\";\n\n// Plugins\nexport * from \"./base/plugins/AddGraphQLQuerySelection.js\";\n\n// Permissions\nexport * from \"./permissions/index.js\";\n\n// Components\nexport * from \"./components/index.js\";\nexport type { RichTextValueWithHtml } from \"./components/index.js\";\nexport { HasPermission } from \"./presentation/security/components/HasPermission.js\";\nexport { SecureRoute } from \"./presentation/security/components/SecureRoute.js\";\n\nexport { FileManager, FileManagerRenderer } from \"./base/ui/FileManager.js\";\nexport type {\n FileManagerProps,\n FileManagerRendererProps,\n FileManagerFileItem,\n FileManagerOnChange\n} from \"./base/ui/FileManager.js\";\n\nexport { SystemInstallerProvider } from \"./presentation/installation/components/SystemInstaller/index.js\";\n\n// Feature types\nexport type { AaclPermission } from \"./features/wcp/types.js\";\nexport type { Tenant } from \"./features/tenancy/types.js\";\n\
|
|
1
|
+
{"version":3,"names":["Admin","HasPermission","SecureRoute","FileManager","FileManagerRenderer","SystemInstallerProvider","BuildParamsFeature","useWcp","useTenantContext","useIdentity","useAuthentication","useBuildParams","useSecurity"],"sources":["index.ts"],"sourcesContent":["export * from \"@webiny/app\";\nexport type { HigherOrderComponent, ProviderProps, ComposeProps } from \"@webiny/app\";\n// UI components\nexport * from \"./base/ui/Tags.js\";\nexport * from \"./base/ui/Layout.js\";\nexport * from \"./base/ui/TenantSelector.js\";\nexport type { LayoutProps } from \"./base/ui/Layout.js\";\nexport * from \"./base/ui/Navigation.js\";\nexport * from \"./base/ui/Brand.js\";\nexport * from \"./base/ui/Logo.js\";\nexport * from \"./base/ui/UserMenu.js\";\nexport * from \"./base/ui/LoginScreen.js\";\nexport * from \"./base/ui/CenteredView.js\";\nexport * from \"./base/ui/Dashboard.js\";\nexport * from \"./base/ui/NotFound.js\";\n\n// Base admin app\nexport { Admin } from \"./base/Admin.js\";\nexport * from \"./config/AdminConfig.js\";\n\nexport type { AdminProps } from \"./base/Admin.js\";\n\n// Plugins\nexport * from \"./base/plugins/AddGraphQLQuerySelection.js\";\n\n// Permissions\nexport * from \"./permissions/index.js\";\n\n// Components\nexport * from \"./components/index.js\";\nexport type { RichTextValueWithHtml } from \"./components/index.js\";\nexport { HasPermission } from \"./presentation/security/components/HasPermission.js\";\nexport { SecureRoute } from \"./presentation/security/components/SecureRoute.js\";\n\nexport { FileManager, FileManagerRenderer } from \"./base/ui/FileManager.js\";\nexport type {\n FileManagerProps,\n FileManagerRendererProps,\n FileManagerFileItem,\n FileManagerOnChange\n} from \"./base/ui/FileManager.js\";\n\nexport { SystemInstallerProvider } from \"./presentation/installation/components/SystemInstaller/index.js\";\n\n// Feature types\nexport type { AaclPermission } from \"./features/wcp/types.js\";\nexport type { Tenant } from \"./features/tenancy/types.js\";\n\nexport { BuildParamsFeature } from \"./features/buildParams/feature.js\";\n\n// Hooks\nexport * from \"./hooks/index.js\";\nexport { useWcp } from \"./presentation/wcp/useWcp.js\";\nexport { useTenantContext } from \"./presentation/tenancy/useTenantContext.js\";\nexport { useIdentity } from \"./presentation/security/hooks/useIdentity.js\";\nexport { useAuthentication } from \"./presentation/security/hooks/useAuthentication.js\";\nexport { useBuildParams } from \"./presentation/buildParams/useBuildParams.js\";\n\n// Legacy hook for easier migration\nexport { useSecurity } from \"./presentation/security/hooks/useSecurity.js\";\n\nexport * from \"@webiny/app/renderApp.js\";\n"],"mappings":"AAAA,cAAc,aAAa;AAE3B;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,SAASA,KAAK;AACd;AAIA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA,SAASC,aAAa;AACtB,SAASC,WAAW;AAEpB,SAASC,WAAW,EAAEC,mBAAmB;AAQzC,SAASC,uBAAuB;;AAEhC;;AAIA,SAASC,kBAAkB;;AAE3B;AACA;AACA,SAASC,MAAM;AACf,SAASC,gBAAgB;AACzB,SAASC,WAAW;AACpB,SAASC,iBAAiB;AAC1B,SAASC,cAAc;;AAEvB;AACA,SAASC,WAAW;AAEpB,cAAc,0BAA0B","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/app-admin",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.1.0-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"description": "A collection of plugins that together form a complete admin interface, customizable and extensible with Webiny apps and plugins.",
|
|
@@ -13,53 +13,49 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@apollo/react-components": "3.1.5",
|
|
15
15
|
"@apollo/react-hooks": "3.1.5",
|
|
16
|
-
"@emotion/
|
|
17
|
-
"@emotion/
|
|
18
|
-
"@
|
|
19
|
-
"@
|
|
20
|
-
"@svgr/webpack": "
|
|
21
|
-
"@types/mime": "2.0.3",
|
|
16
|
+
"@emotion/css": "11.13.5",
|
|
17
|
+
"@emotion/react": "11.14.0",
|
|
18
|
+
"@emotion/styled": "11.14.1",
|
|
19
|
+
"@iconify/json": "2.2.455",
|
|
20
|
+
"@svgr/webpack": "8.1.0",
|
|
22
21
|
"@types/react": "18.2.79",
|
|
23
|
-
"@webiny/admin-ui": "6.0.
|
|
24
|
-
"@webiny/app": "6.0.
|
|
22
|
+
"@webiny/admin-ui": "6.1.0-beta.1",
|
|
23
|
+
"@webiny/app": "6.1.0-beta.1",
|
|
25
24
|
"@webiny/di": "0.2.3",
|
|
26
|
-
"@webiny/feature": "6.0.
|
|
27
|
-
"@webiny/form": "6.0.
|
|
28
|
-
"@webiny/icons": "6.0.
|
|
29
|
-
"@webiny/lexical-converter": "6.0.
|
|
30
|
-
"@webiny/lexical-editor": "6.0.
|
|
31
|
-
"@webiny/lexical-theme": "6.0.
|
|
32
|
-
"@webiny/plugins": "6.0.
|
|
33
|
-
"@webiny/react-composition": "6.0.
|
|
34
|
-
"@webiny/react-properties": "6.0.
|
|
35
|
-
"@webiny/telemetry": "6.0.
|
|
36
|
-
"@webiny/ui": "6.0.
|
|
37
|
-
"@webiny/utils": "6.0.
|
|
38
|
-
"@webiny/validation": "6.0.
|
|
39
|
-
"@webiny/wcp": "6.0.
|
|
25
|
+
"@webiny/feature": "6.1.0-beta.1",
|
|
26
|
+
"@webiny/form": "6.1.0-beta.1",
|
|
27
|
+
"@webiny/icons": "6.1.0-beta.1",
|
|
28
|
+
"@webiny/lexical-converter": "6.1.0-beta.1",
|
|
29
|
+
"@webiny/lexical-editor": "6.1.0-beta.1",
|
|
30
|
+
"@webiny/lexical-theme": "6.1.0-beta.1",
|
|
31
|
+
"@webiny/plugins": "6.1.0-beta.1",
|
|
32
|
+
"@webiny/react-composition": "6.1.0-beta.1",
|
|
33
|
+
"@webiny/react-properties": "6.1.0-beta.1",
|
|
34
|
+
"@webiny/telemetry": "6.1.0-beta.1",
|
|
35
|
+
"@webiny/ui": "6.1.0-beta.1",
|
|
36
|
+
"@webiny/utils": "6.1.0-beta.1",
|
|
37
|
+
"@webiny/validation": "6.1.0-beta.1",
|
|
38
|
+
"@webiny/wcp": "6.1.0-beta.1",
|
|
40
39
|
"apollo-cache": "1.3.5",
|
|
41
40
|
"apollo-client": "2.6.10",
|
|
42
41
|
"apollo-link": "1.2.14",
|
|
43
42
|
"apollo-link-context": "1.0.20",
|
|
44
43
|
"apollo-utilities": "1.3.4",
|
|
45
44
|
"classnames": "2.5.1",
|
|
46
|
-
"
|
|
47
|
-
"graphql": "16.13.1",
|
|
45
|
+
"graphql": "16.13.2",
|
|
48
46
|
"graphql-tag": "2.12.6",
|
|
49
47
|
"history": "5.3.0",
|
|
50
48
|
"is-hotkey": "0.2.0",
|
|
51
49
|
"lodash": "4.17.23",
|
|
52
|
-
"markdown-to-jsx": "9.7.
|
|
50
|
+
"markdown-to-jsx": "9.7.13",
|
|
53
51
|
"minimatch": "10.2.4",
|
|
54
52
|
"mobx": "6.15.0",
|
|
55
|
-
"mobx-react-lite": "
|
|
53
|
+
"mobx-react-lite": "4.1.1",
|
|
56
54
|
"monaco-editor": "0.53.0",
|
|
57
55
|
"prop-types": "15.8.1",
|
|
58
56
|
"react": "18.2.0",
|
|
59
57
|
"react-dom": "18.2.0",
|
|
60
|
-
"react-
|
|
61
|
-
"react-resizable": "3.1.3",
|
|
62
|
-
"react-resizable-panels": "4.7.2",
|
|
58
|
+
"react-resizable-panels": "4.7.6",
|
|
63
59
|
"react-transition-group": "4.4.5",
|
|
64
60
|
"react-virtualized": "9.22.6",
|
|
65
61
|
"reset-css": "5.0.2",
|
|
@@ -68,7 +64,7 @@
|
|
|
68
64
|
},
|
|
69
65
|
"devDependencies": {
|
|
70
66
|
"@emotion/babel-plugin": "11.13.5",
|
|
71
|
-
"@testing-library/react": "
|
|
67
|
+
"@testing-library/react": "16.3.2",
|
|
72
68
|
"@types/bytes": "3.1.5",
|
|
73
69
|
"@types/graphlib": "2.1.12",
|
|
74
70
|
"@types/is-hotkey": "0.1.10",
|
|
@@ -76,10 +72,10 @@
|
|
|
76
72
|
"@types/react-transition-group": "4.4.12",
|
|
77
73
|
"@types/store": "2.0.5",
|
|
78
74
|
"@types/tinycolor2": "1.4.6",
|
|
79
|
-
"@webiny/build-tools": "6.0.
|
|
75
|
+
"@webiny/build-tools": "6.1.0-beta.1",
|
|
80
76
|
"rimraf": "6.1.3",
|
|
81
77
|
"typescript": "5.9.3",
|
|
82
|
-
"vitest": "4.
|
|
78
|
+
"vitest": "4.1.2"
|
|
83
79
|
},
|
|
84
80
|
"publishConfig": {
|
|
85
81
|
"access": "public",
|
|
@@ -102,5 +98,5 @@
|
|
|
102
98
|
]
|
|
103
99
|
}
|
|
104
100
|
},
|
|
105
|
-
"gitHead": "
|
|
101
|
+
"gitHead": "3c1293ba17a14f239fb1cbf5d80cd66846849309"
|
|
106
102
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import type {
|
|
2
|
+
import type { HasPermissionProps, PermissionSchemaConfig } from "./types.js";
|
|
3
3
|
export declare function createHasPermission<const S extends PermissionSchemaConfig>(schema: S): React.FC<HasPermissionProps<S>>;
|
|
@@ -16,17 +16,32 @@ export function createHasPermission(schema) {
|
|
|
16
16
|
}) {
|
|
17
17
|
const permissions = usePermissions();
|
|
18
18
|
const action = props.action;
|
|
19
|
+
const someActions = props.someActions;
|
|
20
|
+
const allActions = props.allActions;
|
|
19
21
|
const entities = props.entity ? [props.entity] : props.any ?? props.all ?? [];
|
|
20
22
|
const requireAll = !!props.all;
|
|
21
|
-
const
|
|
22
|
-
if (!
|
|
23
|
+
const checkAction = (entityId, singleAction) => {
|
|
24
|
+
if (!singleAction) {
|
|
23
25
|
return permissions.canAccess(entityId);
|
|
24
26
|
}
|
|
25
|
-
const method = BUILT_IN_ACTIONS[
|
|
26
|
-
if (method) {
|
|
27
|
+
const method = BUILT_IN_ACTIONS[singleAction];
|
|
28
|
+
if (method && typeof permissions[method] === "function") {
|
|
27
29
|
return permissions[method](entityId);
|
|
28
30
|
}
|
|
29
|
-
|
|
31
|
+
/**
|
|
32
|
+
* // TODO cant be typed properly because of the dynamic nature of custom actions.
|
|
33
|
+
*/
|
|
34
|
+
// @ts-expect-error
|
|
35
|
+
return permissions.canAction(singleAction, entityId);
|
|
36
|
+
};
|
|
37
|
+
const check = entityId => {
|
|
38
|
+
if (allActions) {
|
|
39
|
+
return allActions.every(act => checkAction(entityId, act));
|
|
40
|
+
}
|
|
41
|
+
if (someActions) {
|
|
42
|
+
return someActions.some(act => checkAction(entityId, act));
|
|
43
|
+
}
|
|
44
|
+
return checkAction(entityId, action);
|
|
30
45
|
};
|
|
31
46
|
const allowed = requireAll ? entities.every(check) : entities.some(check);
|
|
32
47
|
return allowed ? /*#__PURE__*/React.createElement(React.Fragment, null, children) : null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","createUsePermissions","BUILT_IN_ACTIONS","read","create","edit","delete","publish","unpublish","createHasPermission","schema","usePermissions","HasPermission","children","props","permissions","action","entities","entity","any","all","requireAll","
|
|
1
|
+
{"version":3,"names":["React","createUsePermissions","BUILT_IN_ACTIONS","read","create","edit","delete","publish","unpublish","createHasPermission","schema","usePermissions","HasPermission","children","props","permissions","action","someActions","allActions","entities","entity","any","all","requireAll","checkAction","entityId","singleAction","canAccess","method","canAction","check","every","act","some","allowed","createElement","Fragment"],"sources":["createHasPermission.tsx"],"sourcesContent":["import React from \"react\";\nimport { createUsePermissions } from \"./usePermissions.js\";\nimport type { HasPermissionProps, PermissionSchemaConfig } from \"./types.js\";\n\nconst BUILT_IN_ACTIONS: Record<string, string> = {\n read: \"canRead\",\n create: \"canCreate\",\n edit: \"canEdit\",\n delete: \"canDelete\",\n publish: \"canPublish\",\n unpublish: \"canUnpublish\"\n};\n\nexport function createHasPermission<const S extends PermissionSchemaConfig>(\n schema: S\n): React.FC<HasPermissionProps<S>> {\n const usePermissions = createUsePermissions(schema);\n\n return function HasPermission({ children, ...props }) {\n const permissions = usePermissions();\n\n const action = props.action as string | undefined;\n const someActions = props.someActions as string[] | undefined;\n const allActions = props.allActions as string[] | undefined;\n const entities: string[] = props.entity ? [props.entity] : (props.any ?? props.all ?? []);\n const requireAll = !!props.all;\n\n const checkAction = (entityId: string, singleAction: string | undefined): boolean => {\n if (!singleAction) {\n return permissions.canAccess(entityId);\n }\n const method = BUILT_IN_ACTIONS[singleAction] as keyof typeof permissions;\n if (method && typeof permissions[method] === \"function\") {\n return permissions[method](entityId);\n }\n /**\n * // TODO cant be typed properly because of the dynamic nature of custom actions.\n */\n // @ts-expect-error\n return permissions.canAction(singleAction, entityId);\n };\n\n const check = (entityId: string): boolean => {\n if (allActions) {\n return allActions.every(act => checkAction(entityId, act));\n }\n if (someActions) {\n return someActions.some(act => checkAction(entityId, act));\n }\n return checkAction(entityId, action);\n };\n\n const allowed = requireAll ? entities.every(check) : entities.some(check);\n\n return allowed ? <>{children}</> : null;\n };\n}\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,oBAAoB;AAG7B,MAAMC,gBAAwC,GAAG;EAC7CC,IAAI,EAAE,SAAS;EACfC,MAAM,EAAE,WAAW;EACnBC,IAAI,EAAE,SAAS;EACfC,MAAM,EAAE,WAAW;EACnBC,OAAO,EAAE,YAAY;EACrBC,SAAS,EAAE;AACf,CAAC;AAED,OAAO,SAASC,mBAAmBA,CAC/BC,MAAS,EACsB;EAC/B,MAAMC,cAAc,GAAGV,oBAAoB,CAACS,MAAM,CAAC;EAEnD,OAAO,SAASE,aAAaA,CAAC;IAAEC,QAAQ;IAAE,GAAGC;EAAM,CAAC,EAAE;IAClD,MAAMC,WAAW,GAAGJ,cAAc,CAAC,CAAC;IAEpC,MAAMK,MAAM,GAAGF,KAAK,CAACE,MAA4B;IACjD,MAAMC,WAAW,GAAGH,KAAK,CAACG,WAAmC;IAC7D,MAAMC,UAAU,GAAGJ,KAAK,CAACI,UAAkC;IAC3D,MAAMC,QAAkB,GAAGL,KAAK,CAACM,MAAM,GAAG,CAACN,KAAK,CAACM,MAAM,CAAC,GAAIN,KAAK,CAACO,GAAG,IAAIP,KAAK,CAACQ,GAAG,IAAI,EAAG;IACzF,MAAMC,UAAU,GAAG,CAAC,CAACT,KAAK,CAACQ,GAAG;IAE9B,MAAME,WAAW,GAAGA,CAACC,QAAgB,EAAEC,YAAgC,KAAc;MACjF,IAAI,CAACA,YAAY,EAAE;QACf,OAAOX,WAAW,CAACY,SAAS,CAACF,QAAQ,CAAC;MAC1C;MACA,MAAMG,MAAM,GAAG1B,gBAAgB,CAACwB,YAAY,CAA6B;MACzE,IAAIE,MAAM,IAAI,OAAOb,WAAW,CAACa,MAAM,CAAC,KAAK,UAAU,EAAE;QACrD,OAAOb,WAAW,CAACa,MAAM,CAAC,CAACH,QAAQ,CAAC;MACxC;MACA;AACZ;AACA;MACY;MACA,OAAOV,WAAW,CAACc,SAAS,CAACH,YAAY,EAAED,QAAQ,CAAC;IACxD,CAAC;IAED,MAAMK,KAAK,GAAIL,QAAgB,IAAc;MACzC,IAAIP,UAAU,EAAE;QACZ,OAAOA,UAAU,CAACa,KAAK,CAACC,GAAG,IAAIR,WAAW,CAACC,QAAQ,EAAEO,GAAG,CAAC,CAAC;MAC9D;MACA,IAAIf,WAAW,EAAE;QACb,OAAOA,WAAW,CAACgB,IAAI,CAACD,GAAG,IAAIR,WAAW,CAACC,QAAQ,EAAEO,GAAG,CAAC,CAAC;MAC9D;MACA,OAAOR,WAAW,CAACC,QAAQ,EAAET,MAAM,CAAC;IACxC,CAAC;IAED,MAAMkB,OAAO,GAAGX,UAAU,GAAGJ,QAAQ,CAACY,KAAK,CAACD,KAAK,CAAC,GAAGX,QAAQ,CAACc,IAAI,CAACH,KAAK,CAAC;IAEzE,OAAOI,OAAO,gBAAGlC,KAAA,CAAAmC,aAAA,CAAAnC,KAAA,CAAAoC,QAAA,QAAGvB,QAAW,CAAC,GAAG,IAAI;EAC3C,CAAC;AACL","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { render, screen, cleanup } from "@testing-library/react";
|
|
4
|
+
import { createHasPermission } from "./createHasPermission.js";
|
|
5
|
+
import { createPermissionSchema } from "./createPermissionSchema.js";
|
|
6
|
+
import { Identity } from "../domain/Identity.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Mutable ref updated before each test so the mock always reflects the current identity.
|
|
10
|
+
* Using a ref (not a plain variable) keeps the mock factory closure stable across tests.
|
|
11
|
+
*/
|
|
12
|
+
const identityRef = {
|
|
13
|
+
current: Identity.createAnonymous()
|
|
14
|
+
};
|
|
15
|
+
vi.mock("~/presentation/security/hooks/useIdentity.js", () => ({
|
|
16
|
+
useIdentity: () => ({
|
|
17
|
+
identity: identityRef.current,
|
|
18
|
+
isAuthenticated: true
|
|
19
|
+
})
|
|
20
|
+
}));
|
|
21
|
+
const TEST_SCHEMA = createPermissionSchema({
|
|
22
|
+
prefix: "test",
|
|
23
|
+
fullAccess: true,
|
|
24
|
+
entities: [{
|
|
25
|
+
id: "page",
|
|
26
|
+
title: "Page",
|
|
27
|
+
permission: "test.page",
|
|
28
|
+
scopes: ["full"],
|
|
29
|
+
actions: [{
|
|
30
|
+
name: "rwd"
|
|
31
|
+
}, {
|
|
32
|
+
name: "pw"
|
|
33
|
+
}]
|
|
34
|
+
}]
|
|
35
|
+
});
|
|
36
|
+
const HasPermission = createHasPermission(TEST_SCHEMA);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Each call increments the counter so every test gets a fresh identity id,
|
|
40
|
+
* avoiding hits on the module-level permission cache in usePermissions.ts.
|
|
41
|
+
*/
|
|
42
|
+
let idCounter = 0;
|
|
43
|
+
function makeIdentity(permissions) {
|
|
44
|
+
return Identity.createAuthenticated({
|
|
45
|
+
id: `test-identity-${++idCounter}`,
|
|
46
|
+
displayName: "Test User",
|
|
47
|
+
type: "admin",
|
|
48
|
+
roles: [],
|
|
49
|
+
teams: [],
|
|
50
|
+
permissions,
|
|
51
|
+
profile: {
|
|
52
|
+
external: false
|
|
53
|
+
},
|
|
54
|
+
currentTenant: {
|
|
55
|
+
id: "root",
|
|
56
|
+
name: "Root"
|
|
57
|
+
},
|
|
58
|
+
defaultTenant: {
|
|
59
|
+
id: "root",
|
|
60
|
+
name: "Root"
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const CHILD_TEXT = "protected content";
|
|
65
|
+
const withPermissions = (permissions, jsx) => {
|
|
66
|
+
identityRef.current = makeIdentity(permissions);
|
|
67
|
+
return render(jsx);
|
|
68
|
+
};
|
|
69
|
+
afterEach(() => {
|
|
70
|
+
cleanup();
|
|
71
|
+
});
|
|
72
|
+
describe("createHasPermission", () => {
|
|
73
|
+
describe("single action", () => {
|
|
74
|
+
it("renders children when the user has the required action", () => {
|
|
75
|
+
withPermissions([{
|
|
76
|
+
name: "test.page",
|
|
77
|
+
pw: "p"
|
|
78
|
+
}], /*#__PURE__*/React.createElement(HasPermission, {
|
|
79
|
+
entity: "page",
|
|
80
|
+
action: "publish"
|
|
81
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
82
|
+
expect(screen.getByText(CHILD_TEXT)).toBeTruthy();
|
|
83
|
+
});
|
|
84
|
+
it("renders nothing when the user lacks the required action", () => {
|
|
85
|
+
withPermissions([{
|
|
86
|
+
name: "test.page",
|
|
87
|
+
pw: "u"
|
|
88
|
+
}], /*#__PURE__*/React.createElement(HasPermission, {
|
|
89
|
+
entity: "page",
|
|
90
|
+
action: "publish"
|
|
91
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
92
|
+
expect(screen.queryByText(CHILD_TEXT)).toBeNull();
|
|
93
|
+
});
|
|
94
|
+
it("renders nothing when the user has no permissions at all", () => {
|
|
95
|
+
withPermissions([], /*#__PURE__*/React.createElement(HasPermission, {
|
|
96
|
+
entity: "page",
|
|
97
|
+
action: "publish"
|
|
98
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
99
|
+
expect(screen.queryByText(CHILD_TEXT)).toBeNull();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe("someActions (OR semantics)", () => {
|
|
103
|
+
it("renders children when the user has all actions in the array", () => {
|
|
104
|
+
withPermissions([{
|
|
105
|
+
name: "test.page",
|
|
106
|
+
pw: "pu"
|
|
107
|
+
}], /*#__PURE__*/React.createElement(HasPermission, {
|
|
108
|
+
entity: "page",
|
|
109
|
+
someActions: ["publish", "unpublish"]
|
|
110
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
111
|
+
expect(screen.getByText(CHILD_TEXT)).toBeTruthy();
|
|
112
|
+
});
|
|
113
|
+
it("renders children when the user has only one of the actions", () => {
|
|
114
|
+
withPermissions([{
|
|
115
|
+
name: "test.page",
|
|
116
|
+
pw: "p"
|
|
117
|
+
}], /*#__PURE__*/React.createElement(HasPermission, {
|
|
118
|
+
entity: "page",
|
|
119
|
+
someActions: ["publish", "unpublish"]
|
|
120
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
121
|
+
expect(screen.getByText(CHILD_TEXT)).toBeTruthy();
|
|
122
|
+
});
|
|
123
|
+
it("renders children when only the last action in the array is allowed", () => {
|
|
124
|
+
withPermissions([{
|
|
125
|
+
name: "test.page",
|
|
126
|
+
rwd: "rw"
|
|
127
|
+
}], /*#__PURE__*/React.createElement(HasPermission, {
|
|
128
|
+
entity: "page",
|
|
129
|
+
someActions: ["publish", "unpublish", "edit"]
|
|
130
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
131
|
+
expect(screen.getByText(CHILD_TEXT)).toBeTruthy();
|
|
132
|
+
});
|
|
133
|
+
it("renders nothing when the user has none of the actions", () => {
|
|
134
|
+
withPermissions([{
|
|
135
|
+
name: "test.page",
|
|
136
|
+
rwd: "r"
|
|
137
|
+
}], /*#__PURE__*/React.createElement(HasPermission, {
|
|
138
|
+
entity: "page",
|
|
139
|
+
someActions: ["publish", "unpublish"]
|
|
140
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
141
|
+
expect(screen.queryByText(CHILD_TEXT)).toBeNull();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe("allActions (AND semantics)", () => {
|
|
145
|
+
it("renders children when the user has all actions in the array", () => {
|
|
146
|
+
withPermissions([{
|
|
147
|
+
name: "test.page",
|
|
148
|
+
pw: "pu"
|
|
149
|
+
}], /*#__PURE__*/React.createElement(HasPermission, {
|
|
150
|
+
entity: "page",
|
|
151
|
+
allActions: ["publish", "unpublish"]
|
|
152
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
153
|
+
expect(screen.getByText(CHILD_TEXT)).toBeTruthy();
|
|
154
|
+
});
|
|
155
|
+
it("renders nothing when the user has only one of the required actions", () => {
|
|
156
|
+
withPermissions([{
|
|
157
|
+
name: "test.page",
|
|
158
|
+
pw: "p"
|
|
159
|
+
}], /*#__PURE__*/React.createElement(HasPermission, {
|
|
160
|
+
entity: "page",
|
|
161
|
+
allActions: ["publish", "unpublish"]
|
|
162
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
163
|
+
expect(screen.queryByText(CHILD_TEXT)).toBeNull();
|
|
164
|
+
});
|
|
165
|
+
it("renders nothing when the user has none of the required actions", () => {
|
|
166
|
+
withPermissions([{
|
|
167
|
+
name: "test.page",
|
|
168
|
+
rwd: "r"
|
|
169
|
+
}], /*#__PURE__*/React.createElement(HasPermission, {
|
|
170
|
+
entity: "page",
|
|
171
|
+
allActions: ["publish", "unpublish"]
|
|
172
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
173
|
+
expect(screen.queryByText(CHILD_TEXT)).toBeNull();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
describe("no action (entity access only)", () => {
|
|
177
|
+
it("renders children when the user has any permission for the entity", () => {
|
|
178
|
+
withPermissions([{
|
|
179
|
+
name: "test.page",
|
|
180
|
+
rwd: "r"
|
|
181
|
+
}], /*#__PURE__*/React.createElement(HasPermission, {
|
|
182
|
+
entity: "page"
|
|
183
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
184
|
+
expect(screen.getByText(CHILD_TEXT)).toBeTruthy();
|
|
185
|
+
});
|
|
186
|
+
it("renders nothing when the user has no permission for the entity", () => {
|
|
187
|
+
withPermissions([], /*#__PURE__*/React.createElement(HasPermission, {
|
|
188
|
+
entity: "page"
|
|
189
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
190
|
+
expect(screen.queryByText(CHILD_TEXT)).toBeNull();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
describe("full access", () => {
|
|
194
|
+
it("renders children regardless of the action when the user has full access", () => {
|
|
195
|
+
withPermissions([{
|
|
196
|
+
name: "test.*"
|
|
197
|
+
}], /*#__PURE__*/React.createElement(HasPermission, {
|
|
198
|
+
entity: "page",
|
|
199
|
+
allActions: ["publish", "unpublish"]
|
|
200
|
+
}, /*#__PURE__*/React.createElement("span", null, CHILD_TEXT)));
|
|
201
|
+
expect(screen.getByText(CHILD_TEXT)).toBeTruthy();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
//# sourceMappingURL=createHasPermission.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["describe","it","expect","vi","afterEach","React","render","screen","cleanup","createHasPermission","createPermissionSchema","Identity","identityRef","current","createAnonymous","mock","useIdentity","identity","isAuthenticated","TEST_SCHEMA","prefix","fullAccess","entities","id","title","permission","scopes","actions","name","HasPermission","idCounter","makeIdentity","permissions","createAuthenticated","displayName","type","roles","teams","profile","external","currentTenant","defaultTenant","CHILD_TEXT","withPermissions","jsx","pw","createElement","entity","action","getByText","toBeTruthy","queryByText","toBeNull","someActions","rwd","allActions"],"sources":["createHasPermission.test.tsx"],"sourcesContent":["import { describe, it, expect, vi, afterEach } from \"vitest\";\nimport React from \"react\";\nimport { render, screen, cleanup } from \"@testing-library/react\";\nimport { createHasPermission } from \"./createHasPermission.js\";\nimport { createPermissionSchema } from \"./createPermissionSchema.js\";\nimport { Identity } from \"~/domain/Identity.js\";\n\n/**\n * Mutable ref updated before each test so the mock always reflects the current identity.\n * Using a ref (not a plain variable) keeps the mock factory closure stable across tests.\n */\nconst identityRef = { current: Identity.createAnonymous() };\n\nvi.mock(\"~/presentation/security/hooks/useIdentity.js\", () => ({\n useIdentity: () => ({ identity: identityRef.current, isAuthenticated: true })\n}));\n\nconst TEST_SCHEMA = createPermissionSchema({\n prefix: \"test\",\n fullAccess: true,\n entities: [\n {\n id: \"page\",\n title: \"Page\",\n permission: \"test.page\",\n scopes: [\"full\"],\n actions: [{ name: \"rwd\" }, { name: \"pw\" }]\n }\n ]\n} as const);\n\nconst HasPermission = createHasPermission(TEST_SCHEMA);\n\n/**\n * Each call increments the counter so every test gets a fresh identity id,\n * avoiding hits on the module-level permission cache in usePermissions.ts.\n */\nlet idCounter = 0;\n\nfunction makeIdentity(permissions: Array<{ name: string; [key: string]: any }>) {\n return Identity.createAuthenticated({\n id: `test-identity-${++idCounter}`,\n displayName: \"Test User\",\n type: \"admin\",\n roles: [],\n teams: [],\n permissions,\n profile: { external: false },\n currentTenant: { id: \"root\", name: \"Root\" },\n defaultTenant: { id: \"root\", name: \"Root\" }\n });\n}\n\nconst CHILD_TEXT = \"protected content\";\n\nconst withPermissions = (\n permissions: Array<{ name: string; [key: string]: any }>,\n jsx: React.ReactElement\n) => {\n identityRef.current = makeIdentity(permissions);\n return render(jsx);\n};\n\nafterEach(() => {\n cleanup();\n});\n\ndescribe(\"createHasPermission\", () => {\n describe(\"single action\", () => {\n it(\"renders children when the user has the required action\", () => {\n withPermissions(\n [{ name: \"test.page\", pw: \"p\" }],\n <HasPermission entity={\"page\"} action={\"publish\"}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.getByText(CHILD_TEXT)).toBeTruthy();\n });\n\n it(\"renders nothing when the user lacks the required action\", () => {\n withPermissions(\n [{ name: \"test.page\", pw: \"u\" }],\n <HasPermission entity={\"page\"} action={\"publish\"}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.queryByText(CHILD_TEXT)).toBeNull();\n });\n\n it(\"renders nothing when the user has no permissions at all\", () => {\n withPermissions(\n [],\n <HasPermission entity={\"page\"} action={\"publish\"}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.queryByText(CHILD_TEXT)).toBeNull();\n });\n });\n\n describe(\"someActions (OR semantics)\", () => {\n it(\"renders children when the user has all actions in the array\", () => {\n withPermissions(\n [{ name: \"test.page\", pw: \"pu\" }],\n <HasPermission entity={\"page\"} someActions={[\"publish\", \"unpublish\"]}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.getByText(CHILD_TEXT)).toBeTruthy();\n });\n\n it(\"renders children when the user has only one of the actions\", () => {\n withPermissions(\n [{ name: \"test.page\", pw: \"p\" }],\n <HasPermission entity={\"page\"} someActions={[\"publish\", \"unpublish\"]}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.getByText(CHILD_TEXT)).toBeTruthy();\n });\n\n it(\"renders children when only the last action in the array is allowed\", () => {\n withPermissions(\n [{ name: \"test.page\", rwd: \"rw\" }],\n <HasPermission entity={\"page\"} someActions={[\"publish\", \"unpublish\", \"edit\"]}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.getByText(CHILD_TEXT)).toBeTruthy();\n });\n\n it(\"renders nothing when the user has none of the actions\", () => {\n withPermissions(\n [{ name: \"test.page\", rwd: \"r\" }],\n <HasPermission entity={\"page\"} someActions={[\"publish\", \"unpublish\"]}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.queryByText(CHILD_TEXT)).toBeNull();\n });\n });\n\n describe(\"allActions (AND semantics)\", () => {\n it(\"renders children when the user has all actions in the array\", () => {\n withPermissions(\n [{ name: \"test.page\", pw: \"pu\" }],\n <HasPermission entity={\"page\"} allActions={[\"publish\", \"unpublish\"]}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.getByText(CHILD_TEXT)).toBeTruthy();\n });\n\n it(\"renders nothing when the user has only one of the required actions\", () => {\n withPermissions(\n [{ name: \"test.page\", pw: \"p\" }],\n <HasPermission entity={\"page\"} allActions={[\"publish\", \"unpublish\"]}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.queryByText(CHILD_TEXT)).toBeNull();\n });\n\n it(\"renders nothing when the user has none of the required actions\", () => {\n withPermissions(\n [{ name: \"test.page\", rwd: \"r\" }],\n <HasPermission entity={\"page\"} allActions={[\"publish\", \"unpublish\"]}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.queryByText(CHILD_TEXT)).toBeNull();\n });\n });\n\n describe(\"no action (entity access only)\", () => {\n it(\"renders children when the user has any permission for the entity\", () => {\n withPermissions(\n [{ name: \"test.page\", rwd: \"r\" }],\n <HasPermission entity={\"page\"}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.getByText(CHILD_TEXT)).toBeTruthy();\n });\n\n it(\"renders nothing when the user has no permission for the entity\", () => {\n withPermissions(\n [],\n <HasPermission entity={\"page\"}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.queryByText(CHILD_TEXT)).toBeNull();\n });\n });\n\n describe(\"full access\", () => {\n it(\"renders children regardless of the action when the user has full access\", () => {\n withPermissions(\n [{ name: \"test.*\" }],\n <HasPermission entity={\"page\"} allActions={[\"publish\", \"unpublish\"]}>\n <span>{CHILD_TEXT}</span>\n </HasPermission>\n );\n expect(screen.getByText(CHILD_TEXT)).toBeTruthy();\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
package/permissions/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
|
+
import type { NonEmptyArray } from "@webiny/app/types.js";
|
|
2
3
|
/**
|
|
3
4
|
* A single permission object from the API.
|
|
4
5
|
*/
|
|
@@ -192,31 +193,50 @@ export interface UsePermissionsResultUntyped {
|
|
|
192
193
|
* Built-in actions are always available; custom action names from the schema are inferred automatically.
|
|
193
194
|
*/
|
|
194
195
|
export type HasPermissionAction<S extends PermissionSchemaConfig> = "read" | "create" | "edit" | "delete" | "publish" | "unpublish" | CustomActionNames<S>;
|
|
196
|
+
/**
|
|
197
|
+
* Action constraint for `HasPermission`.
|
|
198
|
+
*
|
|
199
|
+
* Exactly one of `action`, `someActions`, or `allActions` may be provided:
|
|
200
|
+
* - `action` — single action; grants access if it passes.
|
|
201
|
+
* - `someActions` — array; grants access if ANY action passes (OR).
|
|
202
|
+
* - `allActions` — array; grants access only if ALL actions pass (AND).
|
|
203
|
+
*/
|
|
204
|
+
type ActionConstraint<S extends PermissionSchemaConfig> = {
|
|
205
|
+
action?: HasPermissionAction<S>;
|
|
206
|
+
someActions?: never;
|
|
207
|
+
allActions?: never;
|
|
208
|
+
} | {
|
|
209
|
+
action?: never;
|
|
210
|
+
someActions: NonEmptyArray<HasPermissionAction<S>>;
|
|
211
|
+
allActions?: never;
|
|
212
|
+
} | {
|
|
213
|
+
action?: never;
|
|
214
|
+
someActions?: never;
|
|
215
|
+
allActions: NonEmptyArray<HasPermissionAction<S>>;
|
|
216
|
+
};
|
|
195
217
|
/**
|
|
196
218
|
* Props for a schema-bound `HasPermission` component created via `createHasPermission`.
|
|
197
219
|
*
|
|
198
220
|
* Exactly one of `entity`, `any`, or `all` must be provided.
|
|
221
|
+
* Optionally combine with `action`, `someActions`, or `allActions`.
|
|
199
222
|
*/
|
|
200
|
-
export type HasPermissionProps<S extends PermissionSchemaConfig> = SingleEntityProps<S> | AnyEntitiesProps<S> | AllEntitiesProps<S>;
|
|
223
|
+
export type HasPermissionProps<S extends PermissionSchemaConfig> = (SingleEntityProps<S> | AnyEntitiesProps<S> | AllEntitiesProps<S>) & ActionConstraint<S>;
|
|
201
224
|
interface SingleEntityProps<S extends PermissionSchemaConfig> {
|
|
202
225
|
entity: AllEntityIds<S>;
|
|
203
226
|
any?: never;
|
|
204
227
|
all?: never;
|
|
205
|
-
action?: HasPermissionAction<S>;
|
|
206
228
|
children: React.ReactNode;
|
|
207
229
|
}
|
|
208
230
|
interface AnyEntitiesProps<S extends PermissionSchemaConfig> {
|
|
209
231
|
entity?: never;
|
|
210
232
|
any: AllEntityIds<S>[];
|
|
211
233
|
all?: never;
|
|
212
|
-
action?: HasPermissionAction<S>;
|
|
213
234
|
children: React.ReactNode;
|
|
214
235
|
}
|
|
215
236
|
interface AllEntitiesProps<S extends PermissionSchemaConfig> {
|
|
216
237
|
entity?: never;
|
|
217
238
|
any?: never;
|
|
218
239
|
all: AllEntityIds<S>[];
|
|
219
|
-
action?: HasPermissionAction<S>;
|
|
220
240
|
children: React.ReactNode;
|
|
221
241
|
}
|
|
222
242
|
type UsePermissionsResultTyped<S extends PermissionSchemaConfig> = {
|
package/permissions/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import type React from \"react\";\n\n/**\n * A single permission object from the API.\n */\nexport interface Permission {\n name: string;\n [key: string]: any;\n}\n\n/**\n * An action definition on an entity.\n *\n * Built-in actions:\n * - `{ name: \"rwd\" }` — read/write/delete single-select (serialized as joined string, e.g. \"rw\")\n * - `{ name: \"pw\" }` — publish/unpublish multi-select (serialized as joined string, e.g. \"pu\")\n *\n * Custom actions:\n * - `{ name: \"install\", label: \"Install\" }` — boolean flag (serialized as `install: true`)\n */\nexport interface ActionDefinition {\n /** Key on the permission object (e.g. \"rwd\", \"pw\", \"install\") */\n name: string;\n /** Display label for the UI. Required for custom actions; ignored for built-in \"rwd\"/\"pw\". */\n label?: string;\n}\n\n/**\n * Defines an entity within a permission schema.\n */\nexport interface EntityDefinition {\n /** Unique ID, used for form field naming: ${id}AccessScope, ${id}RWD, etc. */\n id: string;\n /** Display title for the UI renderer (e.g. \"Files\", \"Settings\") */\n title?: string;\n /** Permission name emitted for this entity (e.g. \"fm.file\") */\n permission: string;\n /** Available access scopes */\n scopes: (\"full\" | \"own\")[];\n /** Action definitions for this entity */\n actions?: ActionDefinition[];\n /** Dependency on another entity */\n dependsOn?: {\n /** ID of parent entity */\n entity: string;\n /** Required action character (e.g. \"r\") */\n requires: string;\n };\n}\n\n/**\n * Configuration for creating a permission schema.\n */\nexport interface PermissionSchemaConfig {\n /** Permission prefix — used to filter permissions from the array */\n prefix: string;\n /**\n * Full access configuration.\n * - `true` — emits `{ name: \"${prefix}.*\" }`.\n * - `{ ...extras }` — emits `{ name: \"${prefix}.*\", ...extras }`.\n */\n fullAccess: boolean | { [key: string]: any };\n /**\n * Read-only access configuration. When defined, the schema supports a read-only tier.\n * - `true` — emits `{ name: \"${prefix}.*\", rwd: \"r\" }`.\n * - `Permission[]` — emits the array as-is.\n */\n readOnlyAccess?: boolean | Permission[];\n /** Entity definitions (optional — simple apps have none) */\n entities?: EntityDefinition[];\n}\n\n/**\n * A compiled permission schema returned by `createPermissionSchema`.\n */\nexport interface PermissionSchema {\n prefix: string;\n /**\n * Full access configuration.\n * - `true` — emits `{ name: \"${prefix}.*\" }`.\n * - `{ ...extras }` — emits `{ name: \"${prefix}.*\", ...extras }`.\n */\n fullAccess: boolean | { [key: string]: any };\n /**\n * Read-only access configuration. When defined, the schema supports a read-only tier.\n * - `true` — emits `{ name: \"${prefix}.*\", rwd: \"r\" }`.\n * - `Permission[]` — emits the array as-is.\n */\n readOnlyAccess?: boolean | Permission[];\n entities?: EntityDefinition[];\n}\n\n/**\n * Options passed to the `usePermissionForm` hook.\n */\nexport interface UsePermissionFormOptions {\n value: Permission[];\n onChange: (value: Permission[]) => void;\n /** Merge extra fields into deserialized form data (for CMS endpoints, resource scopes, etc.) */\n deserialize?: (permissions: Permission[]) => Record<string, any>;\n /** Transform or extend the core-serialized permissions (for CMS endpoints, resource scopes, etc.) */\n serialize?: (formData: Record<string, any>, corePermissions: Permission[]) => Permission[];\n}\n\n/**\n * Return value of `usePermissionForm`.\n */\nexport interface UsePermissionFormResult {\n formData: Record<string, any>;\n onFormChange: (data: Record<string, any>) => void;\n}\n\n/**\n * Configuration for a permission renderer registered via AdminConfig.\n *\n * Either `schema` or `element` must be provided:\n * - `schema`: uses the built-in PermissionRenderer for auto-generated UI.\n * - `element`: uses a custom React element for full control.\n */\nexport type PermissionRendererConfig = PermissionRendererConfigBase &\n (\n | { schema: PermissionSchema; element?: never }\n | { schema?: never; element: React.ReactElement }\n );\n\ninterface PermissionRendererConfigBase {\n name: string;\n title: string;\n description?: string;\n icon?: React.ReactElement;\n system?: boolean;\n}\n\n/**\n * Item that may have an owner (used for own-scope permission checks).\n */\nexport interface OwnableItem {\n createdBy?: { id: string } | null;\n}\n\n/**\n * Extract the union of entity definitions from a schema config type.\n */\ntype EntitiesOf<S extends PermissionSchemaConfig> = S extends {\n entities: ReadonlyArray<infer E extends EntityDefinition>;\n}\n ? E\n : never;\n\n/**\n * Extract entity IDs whose actions array contains an action with the given name.\n */\ntype EntityIdWithAction<S extends PermissionSchemaConfig, A extends string> = {\n [K in EntitiesOf<S> as K extends { actions: ReadonlyArray<infer Act> }\n ? Act extends { name: A }\n ? K[\"id\"]\n : never\n : never]: never;\n} extends infer M\n ? keyof M & string\n : never;\n\n/**\n * Entity IDs that have the \"rwd\" action.\n */\nexport type RwdEntityId<S extends PermissionSchemaConfig> = EntityIdWithAction<S, \"rwd\">;\n\n/**\n * Entity IDs that have the \"pw\" action.\n */\nexport type PwEntityId<S extends PermissionSchemaConfig> = EntityIdWithAction<S, \"pw\">;\n\n/**\n * All entity IDs in the schema.\n */\nexport type AllEntityIds<S extends PermissionSchemaConfig> = EntitiesOf<S>[\"id\"];\n\n/**\n * Custom (non-builtin) action names across all entities.\n */\nexport type CustomActionNames<S extends PermissionSchemaConfig> = Exclude<\n EntitiesOf<S> extends { actions: ReadonlyArray<infer Act extends ActionDefinition> }\n ? Act[\"name\"]\n : never,\n \"rwd\" | \"pw\"\n>;\n\n/**\n * The return type of `usePermissions(schema)`.\n *\n * When the schema has literal entity types, methods are narrowed to only accept valid entity IDs.\n * When the schema is dynamically typed, all methods accept `string`.\n */\nexport type UsePermissionsResult<S extends PermissionSchemaConfig> =\n string extends AllEntityIds<S> ? UsePermissionsResultUntyped : UsePermissionsResultTyped<S>;\n\nexport interface UsePermissionsResultUntyped {\n canAccess: (entityId: string) => boolean;\n canRead: (entityId: string) => boolean;\n canCreate: (entityId: string) => boolean;\n canEdit: (entityId: string, item?: OwnableItem) => boolean;\n canDelete: (entityId: string, item?: OwnableItem) => boolean;\n canPublish: (entityId: string) => boolean;\n canUnpublish: (entityId: string) => boolean;\n canAction: (action: string, entityId: string) => boolean;\n}\n\n/**\n * Action values accepted by `HasPermission`.\n *\n * Built-in actions are always available; custom action names from the schema are inferred automatically.\n */\nexport type HasPermissionAction<S extends PermissionSchemaConfig> =\n | \"read\"\n | \"create\"\n | \"edit\"\n | \"delete\"\n | \"publish\"\n | \"unpublish\"\n | CustomActionNames<S>;\n\n/**\n * Props for a schema-bound `HasPermission` component created via `createHasPermission`.\n *\n * Exactly one of `entity`, `any`, or `all` must be provided.\n */\nexport type HasPermissionProps<S extends PermissionSchemaConfig> =\n | SingleEntityProps<S>\n | AnyEntitiesProps<S>\n | AllEntitiesProps<S>;\n\ninterface SingleEntityProps<S extends PermissionSchemaConfig> {\n entity: AllEntityIds<S>;\n any?: never;\n all?: never;\n action?: HasPermissionAction<S>;\n children: React.ReactNode;\n}\n\ninterface AnyEntitiesProps<S extends PermissionSchemaConfig> {\n entity?: never;\n any: AllEntityIds<S>[];\n all?: never;\n action?: HasPermissionAction<S>;\n children: React.ReactNode;\n}\n\ninterface AllEntitiesProps<S extends PermissionSchemaConfig> {\n entity?: never;\n any?: never;\n all: AllEntityIds<S>[];\n action?: HasPermissionAction<S>;\n children: React.ReactNode;\n}\n\ntype UsePermissionsResultTyped<S extends PermissionSchemaConfig> = {\n canAccess: (entityId: AllEntityIds<S>) => boolean;\n canAction: (\n action: CustomActionNames<S> extends never ? string : CustomActionNames<S>,\n entityId: AllEntityIds<S>\n ) => boolean;\n} & ([RwdEntityId<S>] extends [never]\n ? {\n canRead: (entityId: string) => boolean;\n canCreate: (entityId: string) => boolean;\n canEdit: (entityId: string, item?: OwnableItem) => boolean;\n canDelete: (entityId: string, item?: OwnableItem) => boolean;\n }\n : {\n canRead: (entityId: RwdEntityId<S>) => boolean;\n canCreate: (entityId: RwdEntityId<S>) => boolean;\n canEdit: (entityId: RwdEntityId<S>, item?: OwnableItem) => boolean;\n canDelete: (entityId: RwdEntityId<S>, item?: OwnableItem) => boolean;\n }) &\n ([PwEntityId<S>] extends [never]\n ? {\n canPublish: (entityId: string) => boolean;\n canUnpublish: (entityId: string) => boolean;\n }\n : {\n canPublish: (entityId: PwEntityId<S>) => boolean;\n canUnpublish: (entityId: PwEntityId<S>) => boolean;\n });\n"],"mappings":"","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import type React from \"react\";\nimport type { NonEmptyArray } from \"@webiny/app/types.js\";\n\n/**\n * A single permission object from the API.\n */\nexport interface Permission {\n name: string;\n [key: string]: any;\n}\n\n/**\n * An action definition on an entity.\n *\n * Built-in actions:\n * - `{ name: \"rwd\" }` — read/write/delete single-select (serialized as joined string, e.g. \"rw\")\n * - `{ name: \"pw\" }` — publish/unpublish multi-select (serialized as joined string, e.g. \"pu\")\n *\n * Custom actions:\n * - `{ name: \"install\", label: \"Install\" }` — boolean flag (serialized as `install: true`)\n */\nexport interface ActionDefinition {\n /** Key on the permission object (e.g. \"rwd\", \"pw\", \"install\") */\n name: string;\n /** Display label for the UI. Required for custom actions; ignored for built-in \"rwd\"/\"pw\". */\n label?: string;\n}\n\n/**\n * Defines an entity within a permission schema.\n */\nexport interface EntityDefinition {\n /** Unique ID, used for form field naming: ${id}AccessScope, ${id}RWD, etc. */\n id: string;\n /** Display title for the UI renderer (e.g. \"Files\", \"Settings\") */\n title?: string;\n /** Permission name emitted for this entity (e.g. \"fm.file\") */\n permission: string;\n /** Available access scopes */\n scopes: (\"full\" | \"own\")[];\n /** Action definitions for this entity */\n actions?: ActionDefinition[];\n /** Dependency on another entity */\n dependsOn?: {\n /** ID of parent entity */\n entity: string;\n /** Required action character (e.g. \"r\") */\n requires: string;\n };\n}\n\n/**\n * Configuration for creating a permission schema.\n */\nexport interface PermissionSchemaConfig {\n /** Permission prefix — used to filter permissions from the array */\n prefix: string;\n /**\n * Full access configuration.\n * - `true` — emits `{ name: \"${prefix}.*\" }`.\n * - `{ ...extras }` — emits `{ name: \"${prefix}.*\", ...extras }`.\n */\n fullAccess: boolean | { [key: string]: any };\n /**\n * Read-only access configuration. When defined, the schema supports a read-only tier.\n * - `true` — emits `{ name: \"${prefix}.*\", rwd: \"r\" }`.\n * - `Permission[]` — emits the array as-is.\n */\n readOnlyAccess?: boolean | Permission[];\n /** Entity definitions (optional — simple apps have none) */\n entities?: EntityDefinition[];\n}\n\n/**\n * A compiled permission schema returned by `createPermissionSchema`.\n */\nexport interface PermissionSchema {\n prefix: string;\n /**\n * Full access configuration.\n * - `true` — emits `{ name: \"${prefix}.*\" }`.\n * - `{ ...extras }` — emits `{ name: \"${prefix}.*\", ...extras }`.\n */\n fullAccess: boolean | { [key: string]: any };\n /**\n * Read-only access configuration. When defined, the schema supports a read-only tier.\n * - `true` — emits `{ name: \"${prefix}.*\", rwd: \"r\" }`.\n * - `Permission[]` — emits the array as-is.\n */\n readOnlyAccess?: boolean | Permission[];\n entities?: EntityDefinition[];\n}\n\n/**\n * Options passed to the `usePermissionForm` hook.\n */\nexport interface UsePermissionFormOptions {\n value: Permission[];\n onChange: (value: Permission[]) => void;\n /** Merge extra fields into deserialized form data (for CMS endpoints, resource scopes, etc.) */\n deserialize?: (permissions: Permission[]) => Record<string, any>;\n /** Transform or extend the core-serialized permissions (for CMS endpoints, resource scopes, etc.) */\n serialize?: (formData: Record<string, any>, corePermissions: Permission[]) => Permission[];\n}\n\n/**\n * Return value of `usePermissionForm`.\n */\nexport interface UsePermissionFormResult {\n formData: Record<string, any>;\n onFormChange: (data: Record<string, any>) => void;\n}\n\n/**\n * Configuration for a permission renderer registered via AdminConfig.\n *\n * Either `schema` or `element` must be provided:\n * - `schema`: uses the built-in PermissionRenderer for auto-generated UI.\n * - `element`: uses a custom React element for full control.\n */\nexport type PermissionRendererConfig = PermissionRendererConfigBase &\n (\n | { schema: PermissionSchema; element?: never }\n | { schema?: never; element: React.ReactElement }\n );\n\ninterface PermissionRendererConfigBase {\n name: string;\n title: string;\n description?: string;\n icon?: React.ReactElement;\n system?: boolean;\n}\n\n/**\n * Item that may have an owner (used for own-scope permission checks).\n */\nexport interface OwnableItem {\n createdBy?: { id: string } | null;\n}\n\n/**\n * Extract the union of entity definitions from a schema config type.\n */\ntype EntitiesOf<S extends PermissionSchemaConfig> = S extends {\n entities: ReadonlyArray<infer E extends EntityDefinition>;\n}\n ? E\n : never;\n\n/**\n * Extract entity IDs whose actions array contains an action with the given name.\n */\ntype EntityIdWithAction<S extends PermissionSchemaConfig, A extends string> = {\n [K in EntitiesOf<S> as K extends { actions: ReadonlyArray<infer Act> }\n ? Act extends { name: A }\n ? K[\"id\"]\n : never\n : never]: never;\n} extends infer M\n ? keyof M & string\n : never;\n\n/**\n * Entity IDs that have the \"rwd\" action.\n */\nexport type RwdEntityId<S extends PermissionSchemaConfig> = EntityIdWithAction<S, \"rwd\">;\n\n/**\n * Entity IDs that have the \"pw\" action.\n */\nexport type PwEntityId<S extends PermissionSchemaConfig> = EntityIdWithAction<S, \"pw\">;\n\n/**\n * All entity IDs in the schema.\n */\nexport type AllEntityIds<S extends PermissionSchemaConfig> = EntitiesOf<S>[\"id\"];\n\n/**\n * Custom (non-builtin) action names across all entities.\n */\nexport type CustomActionNames<S extends PermissionSchemaConfig> = Exclude<\n EntitiesOf<S> extends { actions: ReadonlyArray<infer Act extends ActionDefinition> }\n ? Act[\"name\"]\n : never,\n \"rwd\" | \"pw\"\n>;\n\n/**\n * The return type of `usePermissions(schema)`.\n *\n * When the schema has literal entity types, methods are narrowed to only accept valid entity IDs.\n * When the schema is dynamically typed, all methods accept `string`.\n */\nexport type UsePermissionsResult<S extends PermissionSchemaConfig> =\n string extends AllEntityIds<S> ? UsePermissionsResultUntyped : UsePermissionsResultTyped<S>;\n\nexport interface UsePermissionsResultUntyped {\n canAccess: (entityId: string) => boolean;\n canRead: (entityId: string) => boolean;\n canCreate: (entityId: string) => boolean;\n canEdit: (entityId: string, item?: OwnableItem) => boolean;\n canDelete: (entityId: string, item?: OwnableItem) => boolean;\n canPublish: (entityId: string) => boolean;\n canUnpublish: (entityId: string) => boolean;\n canAction: (action: string, entityId: string) => boolean;\n}\n\n/**\n * Action values accepted by `HasPermission`.\n *\n * Built-in actions are always available; custom action names from the schema are inferred automatically.\n */\nexport type HasPermissionAction<S extends PermissionSchemaConfig> =\n | \"read\"\n | \"create\"\n | \"edit\"\n | \"delete\"\n | \"publish\"\n | \"unpublish\"\n | CustomActionNames<S>;\n\n/**\n * Action constraint for `HasPermission`.\n *\n * Exactly one of `action`, `someActions`, or `allActions` may be provided:\n * - `action` — single action; grants access if it passes.\n * - `someActions` — array; grants access if ANY action passes (OR).\n * - `allActions` — array; grants access only if ALL actions pass (AND).\n */\ntype ActionConstraint<S extends PermissionSchemaConfig> =\n | { action?: HasPermissionAction<S>; someActions?: never; allActions?: never }\n | { action?: never; someActions: NonEmptyArray<HasPermissionAction<S>>; allActions?: never }\n | { action?: never; someActions?: never; allActions: NonEmptyArray<HasPermissionAction<S>> };\n\n/**\n * Props for a schema-bound `HasPermission` component created via `createHasPermission`.\n *\n * Exactly one of `entity`, `any`, or `all` must be provided.\n * Optionally combine with `action`, `someActions`, or `allActions`.\n */\nexport type HasPermissionProps<S extends PermissionSchemaConfig> = (\n | SingleEntityProps<S>\n | AnyEntitiesProps<S>\n | AllEntitiesProps<S>\n) &\n ActionConstraint<S>;\n\ninterface SingleEntityProps<S extends PermissionSchemaConfig> {\n entity: AllEntityIds<S>;\n any?: never;\n all?: never;\n children: React.ReactNode;\n}\n\ninterface AnyEntitiesProps<S extends PermissionSchemaConfig> {\n entity?: never;\n any: AllEntityIds<S>[];\n all?: never;\n children: React.ReactNode;\n}\n\ninterface AllEntitiesProps<S extends PermissionSchemaConfig> {\n entity?: never;\n any?: never;\n all: AllEntityIds<S>[];\n children: React.ReactNode;\n}\n\ntype UsePermissionsResultTyped<S extends PermissionSchemaConfig> = {\n canAccess: (entityId: AllEntityIds<S>) => boolean;\n canAction: (\n action: CustomActionNames<S> extends never ? string : CustomActionNames<S>,\n entityId: AllEntityIds<S>\n ) => boolean;\n} & ([RwdEntityId<S>] extends [never]\n ? {\n canRead: (entityId: string) => boolean;\n canCreate: (entityId: string) => boolean;\n canEdit: (entityId: string, item?: OwnableItem) => boolean;\n canDelete: (entityId: string, item?: OwnableItem) => boolean;\n }\n : {\n canRead: (entityId: RwdEntityId<S>) => boolean;\n canCreate: (entityId: RwdEntityId<S>) => boolean;\n canEdit: (entityId: RwdEntityId<S>, item?: OwnableItem) => boolean;\n canDelete: (entityId: RwdEntityId<S>, item?: OwnableItem) => boolean;\n }) &\n ([PwEntityId<S>] extends [never]\n ? {\n canPublish: (entityId: string) => boolean;\n canUnpublish: (entityId: string) => boolean;\n }\n : {\n canPublish: (entityId: PwEntityId<S>) => boolean;\n canUnpublish: (entityId: PwEntityId<S>) => boolean;\n });\n"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useBuildParams: () => import("../../features/buildParams/abstractions").IBuildParams;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useFeature","BuildParamsFeature","useBuildParams"],"sources":["useBuildParams.ts"],"sourcesContent":["import { useFeature } from \"@webiny/app\";\nimport { BuildParamsFeature } from \"~/features/buildParams/feature.js\";\n\nexport const useBuildParams = () => {\n return useFeature(BuildParamsFeature);\n};\n"],"mappings":"AAAA,SAASA,UAAU,QAAQ,aAAa;AACxC,SAASC,kBAAkB;AAE3B,OAAO,MAAMC,cAAc,GAAGA,CAAA,KAAM;EAChC,OAAOF,UAAU,CAACC,kBAAkB,CAAC;AACzC,CAAC","ignoreList":[]}
|