@webiny/app-admin 6.0.0 → 6.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/base/ui/Brand.d.ts +6 -6
  2. package/base/ui/Dashboard.d.ts +6 -6
  3. package/base/ui/FileManager.d.ts +6 -6
  4. package/base/ui/Layout.d.ts +6 -6
  5. package/base/ui/LoginScreen.d.ts +12 -12
  6. package/base/ui/Logo.d.ts +6 -6
  7. package/base/ui/Navigation.d.ts +6 -6
  8. package/base/ui/NotFound.d.ts +6 -6
  9. package/base/ui/TenantSelector.d.ts +6 -6
  10. package/base/ui/UserMenu/UserMenu.d.ts +6 -6
  11. package/base/ui/UserMenu/UserMenuHandle.d.ts +6 -6
  12. package/base/ui/UserMenu/UserMenuItem.d.ts +12 -12
  13. package/base/ui/UserMenu/UserMenuLink.d.ts +12 -12
  14. package/base/ui/UserMenu/UserMenuSeparator.d.ts +6 -6
  15. package/base/ui/UserMenu.d.ts +12 -12
  16. package/components/Dialogs/Dialog.d.ts +1 -0
  17. package/components/Dialogs/Dialog.js +1 -0
  18. package/components/Dialogs/Dialog.js.map +1 -1
  19. package/components/Dialogs/DialogsContext.d.ts +4 -3
  20. package/components/Dialogs/DialogsContext.js +2 -0
  21. package/components/Dialogs/DialogsContext.js.map +1 -1
  22. package/components/IconPicker/IconRenderer.d.ts +6 -6
  23. package/components/LexicalEditor/LexicalLinkForm.js +3 -1
  24. package/components/LexicalEditor/LexicalLinkForm.js.map +1 -1
  25. package/components/OverlayLayout/components/OverlayRoot.js +3 -0
  26. package/components/OverlayLayout/components/OverlayRoot.js.map +1 -1
  27. package/components/Permissions/StyledComponents.js +9 -3
  28. package/components/Permissions/StyledComponents.js.map +1 -1
  29. package/components/SimpleUI/InputField.js +16 -39
  30. package/components/SimpleUI/InputField.js.map +1 -1
  31. package/components/index.d.ts +1 -4
  32. package/components/index.js +1 -4
  33. package/components/index.js.map +1 -1
  34. package/exports/admin/build-params.d.ts +1 -0
  35. package/exports/admin/build-params.js +1 -0
  36. package/exports/admin/build-params.js.map +1 -1
  37. package/exports/admin/ui.d.ts +1 -0
  38. package/exports/admin/ui.js +1 -0
  39. package/exports/admin/ui.js.map +1 -1
  40. package/exports/admin.d.ts +1 -0
  41. package/exports/admin.js +1 -0
  42. package/exports/admin.js.map +1 -1
  43. package/features/buildParams/feature.d.ts +1 -1
  44. package/features/buildParams/feature.js +4 -0
  45. package/features/buildParams/feature.js.map +1 -1
  46. package/hooks/index.d.ts +1 -0
  47. package/hooks/index.js +1 -0
  48. package/hooks/index.js.map +1 -1
  49. package/hooks/useHotkeys.d.ts +9 -0
  50. package/hooks/useHotkeys.js +99 -0
  51. package/hooks/useHotkeys.js.map +1 -0
  52. package/index.d.ts +1 -0
  53. package/index.js +2 -1
  54. package/index.js.map +1 -1
  55. package/package.json +30 -34
  56. package/permissions/createHasPermission.d.ts +1 -1
  57. package/permissions/createHasPermission.js +20 -5
  58. package/permissions/createHasPermission.js.map +1 -1
  59. package/permissions/createHasPermission.test.d.ts +1 -0
  60. package/permissions/createHasPermission.test.js +206 -0
  61. package/permissions/createHasPermission.test.js.map +1 -0
  62. package/permissions/types.d.ts +24 -4
  63. package/permissions/types.js.map +1 -1
  64. package/presentation/buildParams/useBuildParams.d.ts +1 -0
  65. package/presentation/buildParams/useBuildParams.js +7 -0
  66. package/presentation/buildParams/useBuildParams.js.map +1 -0
  67. package/presentation/wcp/WcpProvider.js +8 -1
  68. package/presentation/wcp/WcpProvider.js.map +1 -1
  69. package/components/FloatingPanel.d.ts +0 -13
  70. package/components/FloatingPanel.js +0 -94
  71. package/components/FloatingPanel.js.map +0 -1
  72. package/components/NavigationPrompt.d.ts +0 -27
  73. package/components/NavigationPrompt.js +0 -39
  74. package/components/NavigationPrompt.js.map +0 -1
  75. package/components/StateInspector.d.ts +0 -8
  76. package/components/StateInspector.js +0 -36
  77. 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\n// Features\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\";\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;AACA,SAASC,kBAAkB;;AAE3B;AACA;AACA,SAASC,MAAM;AACf,SAASC,gBAAgB;AACzB,SAASC,WAAW;AACpB,SAASC,iBAAiB;AAC1B;AACA,SAASC,WAAW;AAEpB,cAAc,0BAA0B","ignoreList":[]}
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.0",
3
+ "version": "6.1.0-beta.0",
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/react": "11.10.8",
17
- "@emotion/styled": "11.10.6",
18
- "@iconify/json": "2.2.448",
19
- "@monaco-editor/react": "4.7.0",
20
- "@svgr/webpack": "6.5.1",
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.0",
24
- "@webiny/app": "6.0.0",
22
+ "@webiny/admin-ui": "6.1.0-beta.0",
23
+ "@webiny/app": "6.1.0-beta.0",
25
24
  "@webiny/di": "0.2.3",
26
- "@webiny/feature": "6.0.0",
27
- "@webiny/form": "6.0.0",
28
- "@webiny/icons": "6.0.0",
29
- "@webiny/lexical-converter": "6.0.0",
30
- "@webiny/lexical-editor": "6.0.0",
31
- "@webiny/lexical-theme": "6.0.0",
32
- "@webiny/plugins": "6.0.0",
33
- "@webiny/react-composition": "6.0.0",
34
- "@webiny/react-properties": "6.0.0",
35
- "@webiny/telemetry": "6.0.0",
36
- "@webiny/ui": "6.0.0",
37
- "@webiny/utils": "6.0.0",
38
- "@webiny/validation": "6.0.0",
39
- "@webiny/wcp": "6.0.0",
25
+ "@webiny/feature": "6.1.0-beta.0",
26
+ "@webiny/form": "6.1.0-beta.0",
27
+ "@webiny/icons": "6.1.0-beta.0",
28
+ "@webiny/lexical-converter": "6.1.0-beta.0",
29
+ "@webiny/lexical-editor": "6.1.0-beta.0",
30
+ "@webiny/lexical-theme": "6.1.0-beta.0",
31
+ "@webiny/plugins": "6.1.0-beta.0",
32
+ "@webiny/react-composition": "6.1.0-beta.0",
33
+ "@webiny/react-properties": "6.1.0-beta.0",
34
+ "@webiny/telemetry": "6.1.0-beta.0",
35
+ "@webiny/ui": "6.1.0-beta.0",
36
+ "@webiny/utils": "6.1.0-beta.0",
37
+ "@webiny/validation": "6.1.0-beta.0",
38
+ "@webiny/wcp": "6.1.0-beta.0",
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
- "emotion": "10.0.27",
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.8",
50
+ "markdown-to-jsx": "9.7.13",
53
51
  "minimatch": "10.2.4",
54
52
  "mobx": "6.15.0",
55
- "mobx-react-lite": "3.4.3",
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-draggable": "4.5.0",
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": "15.0.7",
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.0",
75
+ "@webiny/build-tools": "6.1.0-beta.0",
80
76
  "rimraf": "6.1.3",
81
77
  "typescript": "5.9.3",
82
- "vitest": "4.0.18"
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": "9c6892640a45679ff521e25cd6587dff57393a2e"
101
+ "gitHead": "a3bd3695c66c79238e380d7360d9731b5fcf9c87"
106
102
  }
@@ -1,3 +1,3 @@
1
1
  import React from "react";
2
- import type { PermissionSchemaConfig, HasPermissionProps } from "./types.js";
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 check = entityId => {
22
- if (!action) {
23
+ const checkAction = (entityId, singleAction) => {
24
+ if (!singleAction) {
23
25
  return permissions.canAccess(entityId);
24
26
  }
25
- const method = BUILT_IN_ACTIONS[action];
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
- return permissions.canAction(action, entityId);
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","check","entityId","canAccess","method","canAction","allowed","every","some","createElement","Fragment"],"sources":["createHasPermission.tsx"],"sourcesContent":["import React from \"react\";\nimport { createUsePermissions } from \"./usePermissions.js\";\nimport type { PermissionSchemaConfig, HasPermissionProps } 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 entities: string[] = props.entity ? [props.entity] : (props.any ?? props.all ?? []);\n const requireAll = !!props.all;\n\n const check = (entityId: string): boolean => {\n if (!action) {\n return permissions.canAccess(entityId as any);\n }\n const method = BUILT_IN_ACTIONS[action];\n if (method) {\n return (permissions as any)[method](entityId);\n }\n return permissions.canAction(action as any, entityId as any);\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,QAAkB,GAAGH,KAAK,CAACI,MAAM,GAAG,CAACJ,KAAK,CAACI,MAAM,CAAC,GAAIJ,KAAK,CAACK,GAAG,IAAIL,KAAK,CAACM,GAAG,IAAI,EAAG;IACzF,MAAMC,UAAU,GAAG,CAAC,CAACP,KAAK,CAACM,GAAG;IAE9B,MAAME,KAAK,GAAIC,QAAgB,IAAc;MACzC,IAAI,CAACP,MAAM,EAAE;QACT,OAAOD,WAAW,CAACS,SAAS,CAACD,QAAe,CAAC;MACjD;MACA,MAAME,MAAM,GAAGvB,gBAAgB,CAACc,MAAM,CAAC;MACvC,IAAIS,MAAM,EAAE;QACR,OAAQV,WAAW,CAASU,MAAM,CAAC,CAACF,QAAQ,CAAC;MACjD;MACA,OAAOR,WAAW,CAACW,SAAS,CAACV,MAAM,EAASO,QAAe,CAAC;IAChE,CAAC;IAED,MAAMI,OAAO,GAAGN,UAAU,GAAGJ,QAAQ,CAACW,KAAK,CAACN,KAAK,CAAC,GAAGL,QAAQ,CAACY,IAAI,CAACP,KAAK,CAAC;IAEzE,OAAOK,OAAO,gBAAG3B,KAAA,CAAA8B,aAAA,CAAA9B,KAAA,CAAA+B,QAAA,QAAGlB,QAAW,CAAC,GAAG,IAAI;EAC3C,CAAC;AACL","ignoreList":[]}
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":[]}
@@ -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> = {
@@ -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,7 @@
1
+ import { useFeature } from "@webiny/app";
2
+ import { BuildParamsFeature } from "../../features/buildParams/feature.js";
3
+ export const useBuildParams = () => {
4
+ return useFeature(BuildParamsFeature);
5
+ };
6
+
7
+ //# sourceMappingURL=useBuildParams.js.map
@@ -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":[]}