@jmruthers/pace-core 0.5.189 → 0.5.191
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/core-usage-manifest.json +0 -4
- package/dist/{AuthService-B-cd2MA4.d.ts → AuthService-CbP_utw2.d.ts} +7 -3
- package/dist/{DataTable-IVYljGJ6.d.ts → DataTable-Be6dH_dR.d.ts} +1 -1
- package/dist/{DataTable-GUFUNZ3N.js → DataTable-WKRZD47S.js} +8 -8
- package/dist/{PublicPageProvider-B8HaLe69.d.ts → PublicPageProvider-ULXC_u6U.d.ts} +84 -25
- package/dist/{UnifiedAuthProvider-BG0AL5eE.d.ts → UnifiedAuthProvider-BYA9qB-o.d.ts} +4 -3
- package/dist/{UnifiedAuthProvider-643PUAIM.js → UnifiedAuthProvider-FTSG5XH7.js} +4 -2
- package/dist/{api-YP7XD5L6.js → api-IHKALJZD.js} +4 -2
- package/dist/{chunk-VGZZXKBR.js → chunk-6LTQQAT6.js} +351 -157
- package/dist/chunk-6LTQQAT6.js.map +1 -0
- package/dist/{chunk-MX64ZF6I.js → chunk-6TQDD426.js} +15 -15
- package/dist/chunk-6TQDD426.js.map +1 -0
- package/dist/{chunk-YHCN776L.js → chunk-G37KK66H.js} +2 -75
- package/dist/chunk-G37KK66H.js.map +1 -0
- package/dist/{chunk-THRPYOFK.js → chunk-HW3OVDUF.js} +5 -5
- package/dist/chunk-HW3OVDUF.js.map +1 -0
- package/dist/{chunk-F2IMUDXZ.js → chunk-I7PSE6JW.js} +75 -2
- package/dist/chunk-I7PSE6JW.js.map +1 -0
- package/dist/{chunk-IM4QE42D.js → chunk-LOMZXPSN.js} +141 -326
- package/dist/chunk-LOMZXPSN.js.map +1 -0
- package/dist/chunk-OETXORNB.js +614 -0
- package/dist/chunk-OETXORNB.js.map +1 -0
- package/dist/{chunk-HESYZWZW.js → chunk-QWWZ5CAQ.js} +2 -2
- package/dist/{chunk-HEHYGYOX.js → chunk-ROXMHMY2.js} +403 -46
- package/dist/chunk-ROXMHMY2.js.map +1 -0
- package/dist/{chunk-2UUZZJFT.js → chunk-ULHIJK66.js} +228 -177
- package/dist/{chunk-2UUZZJFT.js.map → chunk-ULHIJK66.js.map} +1 -1
- package/dist/{chunk-YGPFYGA6.js → chunk-VKB2CO4Z.js} +838 -503
- package/dist/chunk-VKB2CO4Z.js.map +1 -0
- package/dist/{chunk-3GOZZZYH.js → chunk-VRGWKHDB.js} +238 -301
- package/dist/chunk-VRGWKHDB.js.map +1 -0
- package/dist/{chunk-UCQSRW7Z.js → chunk-XNYQOL3Z.js} +431 -384
- package/dist/chunk-XNYQOL3Z.js.map +1 -0
- package/dist/{chunk-DDM4CCYT.js → chunk-XYXSXPUK.js} +79 -59
- package/dist/chunk-XYXSXPUK.js.map +1 -0
- package/dist/{chunk-SAUPYVLF.js → chunk-ZSAAAMVR.js} +1 -1
- package/dist/chunk-ZSAAAMVR.js.map +1 -0
- package/dist/components.d.ts +5 -6
- package/dist/components.js +19 -19
- package/dist/components.js.map +1 -1
- package/dist/{database.generated-DI89OQeI.d.ts → database.generated-CzIvgcPu.d.ts} +165 -201
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -2
- package/dist/{file-reference-D037xOFK.d.ts → file-reference-BavO2eQj.d.ts} +13 -10
- package/dist/hooks.d.ts +20 -15
- package/dist/hooks.js +14 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +17 -15
- package/dist/index.js +86 -81
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +3 -1
- package/dist/rbac/index.d.ts +77 -13
- package/dist/rbac/index.js +12 -9
- package/dist/{types-Bwgl--Xo.d.ts → types-CEpcvwwF.d.ts} +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-CTDELQ7H.d.ts → usePublicRouteParams-TZe0gy-4.d.ts} +17 -10
- package/dist/utils.d.ts +8 -8
- package/dist/utils.js +16 -16
- package/docs/README.md +2 -2
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +2 -2
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +2 -2
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +5 -5
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +2 -2
- package/docs/api/classes/SecureSupabaseClient.md +25 -20
- package/docs/api/classes/StorageUtils.md +7 -4
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.md +20 -6
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +62 -16
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +2 -2
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +26 -12
- package/docs/api/interfaces/FileUploadProps.md +30 -19
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
- package/docs/api/interfaces/NavigationContextType.md +9 -9
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +7 -7
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +8 -8
- package/docs/api/interfaces/PagePermissionContextType.md +8 -8
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +2 -2
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +3 -11
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
- package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +10 -10
- package/docs/api/interfaces/RouteConfig.md +10 -10
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +4 -4
- package/docs/api/interfaces/StorageFileInfo.md +7 -7
- package/docs/api/interfaces/StorageFileMetadata.md +25 -14
- package/docs/api/interfaces/StorageListOptions.md +22 -9
- package/docs/api/interfaces/StorageListResult.md +4 -4
- package/docs/api/interfaces/StorageUploadOptions.md +21 -8
- package/docs/api/interfaces/StorageUploadResult.md +6 -6
- package/docs/api/interfaces/StorageUrlOptions.md +19 -6
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +5 -5
- package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +165 -106
- package/docs/api-reference/components.md +15 -7
- package/docs/api-reference/providers.md +2 -2
- package/docs/api-reference/rpc-functions.md +1 -0
- package/docs/best-practices/README.md +1 -1
- package/docs/best-practices/deployment.md +8 -8
- package/docs/getting-started/examples/README.md +2 -2
- package/docs/getting-started/installation-guide.md +4 -4
- package/docs/getting-started/quick-start.md +3 -3
- package/docs/migration/MIGRATION_GUIDE.md +3 -3
- package/docs/migration/README.md +18 -0
- package/docs/migration/database-changes-december-2025.md +767 -0
- package/docs/migration/person-scoped-profiles-migration-guide.md +472 -0
- package/docs/rbac/compliance/compliance-guide.md +2 -2
- package/docs/rbac/event-based-apps.md +2 -2
- package/docs/rbac/getting-started.md +2 -2
- package/docs/rbac/quick-start.md +2 -2
- package/docs/security/README.md +4 -4
- package/docs/standards/07-rbac-and-rls-standard.md +430 -7
- package/docs/troubleshooting/README.md +2 -2
- package/docs/troubleshooting/migration.md +3 -3
- package/package.json +1 -3
- package/scripts/check-pace-core-compliance.cjs +1 -1
- package/scripts/check-pace-core-compliance.js +1 -1
- package/src/__tests__/fixtures/supabase.ts +301 -0
- package/src/__tests__/public-recipe-view.test.ts +19 -19
- package/src/__tests__/rls-policies.test.ts +210 -74
- package/src/components/AddressField/AddressField.test.tsx +42 -0
- package/src/components/AddressField/AddressField.tsx +71 -60
- package/src/components/AddressField/README.md +7 -6
- package/src/components/Alert/Alert.test.tsx +50 -10
- package/src/components/Alert/Alert.tsx +5 -3
- package/src/components/Avatar/Avatar.test.tsx +95 -43
- package/src/components/Avatar/Avatar.tsx +16 -16
- package/src/components/Button/Button.test.tsx +2 -1
- package/src/components/Button/Button.tsx +3 -3
- package/src/components/Calendar/Calendar.test.tsx +53 -37
- package/src/components/Calendar/Calendar.tsx +409 -82
- package/src/components/Card/Card.test.tsx +7 -4
- package/src/components/Card/Card.tsx +3 -6
- package/src/components/Checkbox/Checkbox.tsx +2 -2
- package/src/components/DataTable/components/ActionButtons.tsx +5 -5
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
- package/src/components/DataTable/components/ColumnFilter.tsx +1 -1
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +3 -3
- package/src/components/DataTable/components/DataTableBody.tsx +12 -12
- package/src/components/DataTable/components/DataTableCore.tsx +3 -3
- package/src/components/DataTable/components/DataTableToolbar.tsx +5 -5
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +3 -3
- package/src/components/DataTable/components/EditableRow.tsx +2 -2
- package/src/components/DataTable/components/EmptyState.tsx +3 -3
- package/src/components/DataTable/components/GroupHeader.tsx +2 -2
- package/src/components/DataTable/components/GroupingDropdown.tsx +1 -1
- package/src/components/DataTable/components/ImportModal.tsx +4 -4
- package/src/components/DataTable/components/LoadingState.tsx +1 -1
- package/src/components/DataTable/components/PaginationControls.tsx +11 -11
- package/src/components/DataTable/components/UnifiedTableBody.tsx +9 -9
- package/src/components/DataTable/components/ViewRowModal.tsx +2 -2
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +11 -37
- package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +157 -0
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +2 -1
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +128 -0
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +19 -0
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +51 -0
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +84 -0
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +14 -0
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +136 -0
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +16 -0
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +18 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +28 -7
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +30 -1
- package/src/components/DataTable/utils/hierarchicalUtils.ts +38 -10
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -3
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +4 -4
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/EventSelector/EventSelector.tsx +7 -7
- package/src/components/FileDisplay/FileDisplay.tsx +291 -179
- package/src/components/FileUpload/FileUpload.tsx +7 -4
- package/src/components/Header/Header.test.tsx +28 -0
- package/src/components/Header/Header.tsx +22 -9
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -2
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +19 -14
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +5 -5
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +127 -1
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +42 -22
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +4 -0
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +16 -6
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +37 -3
- package/src/components/PaceAppLayout/test-setup.tsx +1 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +66 -45
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +6 -4
- package/src/components/Progress/Progress.test.tsx +18 -19
- package/src/components/Progress/Progress.tsx +31 -32
- package/src/components/PublicLayout/PublicLayout.test.tsx +6 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +5 -3
- package/src/components/Select/Select.test.tsx +4 -1
- package/src/components/Select/Select.tsx +65 -20
- package/src/components/Switch/Switch.test.tsx +2 -1
- package/src/components/Switch/Switch.tsx +1 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.test.tsx +8 -2
- package/src/components/UserMenu/UserMenu.tsx +3 -3
- package/src/eslint-rules/pace-core-compliance.cjs +0 -2
- package/src/eslint-rules/pace-core-compliance.js +0 -2
- package/src/hooks/__tests__/hooks.integration.test.tsx +4 -1
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +76 -5
- package/src/hooks/__tests__/useDataTableState.test.ts +76 -0
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +25 -69
- package/src/hooks/__tests__/useFileUrlCache.test.ts +129 -0
- package/src/hooks/__tests__/usePreventTabReload.test.ts +88 -0
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +1 -1
- package/src/hooks/__tests__/usePublicEvent.test.ts +608 -0
- package/src/hooks/__tests__/useQueryCache.test.ts +144 -0
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +67 -24
- package/src/hooks/index.ts +1 -1
- package/src/hooks/public/usePublicEvent.ts +10 -10
- package/src/hooks/public/usePublicFileDisplay.ts +173 -87
- package/src/hooks/useAppConfig.ts +24 -5
- package/src/hooks/useFileDisplay.ts +298 -36
- package/src/hooks/useFileReference.ts +56 -11
- package/src/hooks/useFileUrl.ts +1 -1
- package/src/hooks/useInactivityTracker.ts +16 -7
- package/src/hooks/usePermissionCache.test.ts +85 -8
- package/src/hooks/useQueryCache.ts +27 -6
- package/src/hooks/useSecureDataAccess.test.ts +87 -42
- package/src/hooks/useSecureDataAccess.ts +95 -48
- package/src/providers/__tests__/OrganisationProvider.test.tsx +27 -21
- package/src/providers/services/EventServiceProvider.tsx +37 -17
- package/src/providers/services/InactivityServiceProvider.tsx +4 -4
- package/src/providers/services/OrganisationServiceProvider.tsx +8 -1
- package/src/providers/services/UnifiedAuthProvider.tsx +115 -29
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +451 -0
- package/src/rbac/__tests__/engine.comprehensive.test.ts +12 -0
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +8 -0
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +4 -0
- package/src/rbac/api.ts +240 -36
- package/src/rbac/cache-invalidation.ts +21 -7
- package/src/rbac/compliance/quick-fix-suggestions.ts +1 -1
- package/src/rbac/components/NavigationGuard.tsx +23 -63
- package/src/rbac/components/NavigationProvider.test.tsx +52 -23
- package/src/rbac/components/NavigationProvider.tsx +13 -11
- package/src/rbac/components/PagePermissionGuard.tsx +77 -203
- package/src/rbac/components/PagePermissionProvider.tsx +13 -11
- package/src/rbac/components/PermissionEnforcer.tsx +24 -62
- package/src/rbac/components/RoleBasedRouter.tsx +14 -12
- package/src/rbac/components/SecureDataProvider.tsx +13 -11
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +104 -41
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +49 -12
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +22 -1
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +161 -82
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +22 -1
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +77 -30
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +39 -5
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +47 -4
- package/src/rbac/engine.ts +4 -2
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +144 -52
- package/src/rbac/hooks/index.ts +3 -0
- package/src/rbac/hooks/useCan.test.ts +101 -53
- package/src/rbac/hooks/usePermissions.ts +108 -41
- package/src/rbac/hooks/useRBAC.test.ts +11 -3
- package/src/rbac/hooks/useRBAC.ts +83 -40
- package/src/rbac/hooks/useResolvedScope.test.ts +189 -63
- package/src/rbac/hooks/useResolvedScope.ts +128 -70
- package/src/rbac/hooks/useSecureSupabase.ts +36 -19
- package/src/rbac/hooks/useSuperAdminBypass.ts +126 -0
- package/src/rbac/request-deduplication.ts +1 -1
- package/src/rbac/secureClient.ts +72 -12
- package/src/rbac/security.ts +29 -23
- package/src/rbac/types.ts +10 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +150 -0
- package/src/rbac/utils/__tests__/deep-equal.test.ts +53 -0
- package/src/rbac/utils/__tests__/eventContext.test.ts +8 -3
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +74 -12
- package/src/rbac/utils/contextValidator.ts +288 -0
- package/src/rbac/utils/eventContext.ts +52 -3
- package/src/services/AuthService.ts +37 -8
- package/src/services/EventService.ts +165 -21
- package/src/services/OrganisationService.ts +125 -137
- package/src/services/__tests__/EventService.test.ts +26 -21
- package/src/services/__tests__/OrganisationService.pagination.test.ts +34 -8
- package/src/services/__tests__/OrganisationService.test.ts +218 -86
- package/src/types/database.generated.ts +166 -201
- package/src/types/file-reference.ts +13 -10
- package/src/types/supabase.ts +2 -2
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +3 -2
- package/src/utils/app/appNameResolver.test.ts +346 -73
- package/src/utils/context/superAdminOverride.ts +58 -0
- package/src/utils/file-reference/index.ts +65 -37
- package/src/utils/google-places/googlePlacesUtils.test.ts +98 -0
- package/src/utils/google-places/googlePlacesUtils.ts +1 -1
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +83 -0
- package/src/utils/google-places/types.ts +1 -1
- package/src/utils/request-deduplication.ts +4 -4
- package/src/utils/security/secureDataAccess.test.ts +1 -1
- package/src/utils/security/secureDataAccess.ts +7 -4
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/helpers.test.ts +1 -1
- package/src/utils/storage/helpers.ts +38 -19
- package/src/utils/storage/types.ts +15 -8
- package/src/utils/validation/__tests__/csrf.test.ts +105 -0
- package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +92 -0
- package/src/vite-env.d.ts +2 -2
- package/dist/chunk-3GOZZZYH.js.map +0 -1
- package/dist/chunk-DDM4CCYT.js.map +0 -1
- package/dist/chunk-E7UAOUMY.js +0 -75
- package/dist/chunk-E7UAOUMY.js.map +0 -1
- package/dist/chunk-F2IMUDXZ.js.map +0 -1
- package/dist/chunk-HEHYGYOX.js.map +0 -1
- package/dist/chunk-IM4QE42D.js.map +0 -1
- package/dist/chunk-MX64ZF6I.js.map +0 -1
- package/dist/chunk-SAUPYVLF.js.map +0 -1
- package/dist/chunk-THRPYOFK.js.map +0 -1
- package/dist/chunk-UCQSRW7Z.js.map +0 -1
- package/dist/chunk-VGZZXKBR.js.map +0 -1
- package/dist/chunk-YGPFYGA6.js.map +0 -1
- package/dist/chunk-YHCN776L.js.map +0 -1
- /package/dist/{DataTable-GUFUNZ3N.js.map → DataTable-WKRZD47S.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-643PUAIM.js.map → UnifiedAuthProvider-FTSG5XH7.js.map} +0 -0
- /package/dist/{api-YP7XD5L6.js.map → api-IHKALJZD.js.map} +0 -0
- /package/dist/{chunk-HESYZWZW.js.map → chunk-QWWZ5CAQ.js.map} +0 -0
|
@@ -22,6 +22,8 @@ const mockUseUnifiedAuthFn = vi.fn(() => ({
|
|
|
22
22
|
selectedEvent: null,
|
|
23
23
|
selectedOrganisationId: 'org-123',
|
|
24
24
|
selectedEventId: undefined,
|
|
25
|
+
appId: 'app-123',
|
|
26
|
+
appName: 'test-app',
|
|
25
27
|
supabase: {
|
|
26
28
|
from: vi.fn(() => ({
|
|
27
29
|
select: vi.fn(() => ({
|
|
@@ -41,17 +43,36 @@ vi.mock('../../hooks', () => ({
|
|
|
41
43
|
useCan: vi.fn()
|
|
42
44
|
}));
|
|
43
45
|
|
|
46
|
+
// Mock useResolvedScope hook
|
|
47
|
+
vi.mock('../../hooks/useResolvedScope', () => ({
|
|
48
|
+
useResolvedScope: vi.fn()
|
|
49
|
+
}));
|
|
50
|
+
|
|
44
51
|
// Mock the app name resolver
|
|
45
|
-
vi.mock('../../../utils/appNameResolver', () => ({
|
|
52
|
+
vi.mock('../../../utils/app/appNameResolver', () => ({
|
|
46
53
|
getCurrentAppName: () => 'test-app'
|
|
47
54
|
}));
|
|
48
55
|
|
|
56
|
+
import { useResolvedScope } from '../../hooks/useResolvedScope';
|
|
57
|
+
|
|
49
58
|
describe('PagePermissionGuard Simplified Tests', () => {
|
|
50
59
|
const mockUseCan = vi.mocked(useCan);
|
|
60
|
+
const mockUseResolvedScope = vi.mocked(useResolvedScope);
|
|
51
61
|
|
|
52
62
|
beforeEach(() => {
|
|
53
63
|
vi.clearAllMocks();
|
|
54
64
|
|
|
65
|
+
// Mock useResolvedScope to return resolved scope immediately
|
|
66
|
+
mockUseResolvedScope.mockReturnValue({
|
|
67
|
+
resolvedScope: {
|
|
68
|
+
organisationId: 'org-123',
|
|
69
|
+
eventId: undefined,
|
|
70
|
+
appId: 'app-123'
|
|
71
|
+
},
|
|
72
|
+
isLoading: false,
|
|
73
|
+
error: null
|
|
74
|
+
});
|
|
75
|
+
|
|
55
76
|
// Set up default mock behavior
|
|
56
77
|
mockUseCan.mockReturnValue({
|
|
57
78
|
can: false,
|
|
@@ -31,12 +31,18 @@ vi.mock('../../utils/eventContext', () => ({
|
|
|
31
31
|
createScopeFromEvent: vi.fn()
|
|
32
32
|
}));
|
|
33
33
|
|
|
34
|
+
// Mock useResolvedScope hook
|
|
35
|
+
vi.mock('../../hooks/useResolvedScope', () => ({
|
|
36
|
+
useResolvedScope: vi.fn()
|
|
37
|
+
}));
|
|
38
|
+
|
|
34
39
|
// Mock the app name resolver
|
|
35
40
|
vi.mock('../../../utils/app/appNameResolver', () => ({
|
|
36
41
|
getCurrentAppName: vi.fn()
|
|
37
42
|
}));
|
|
38
43
|
|
|
39
44
|
import { createScopeFromEvent } from '../../utils/eventContext';
|
|
45
|
+
import { useResolvedScope } from '../../hooks/useResolvedScope';
|
|
40
46
|
import { getCurrentAppName } from '../../../utils/app/appNameResolver';
|
|
41
47
|
|
|
42
48
|
// Mock data
|
|
@@ -71,23 +77,28 @@ describe('PagePermissionGuard Component', () => {
|
|
|
71
77
|
const mockUseCan = vi.mocked(useCan);
|
|
72
78
|
const mockCreateScopeFromEvent = vi.mocked(createScopeFromEvent);
|
|
73
79
|
const mockGetCurrentAppName = vi.mocked(getCurrentAppName);
|
|
80
|
+
const mockUseResolvedScope = vi.mocked(useResolvedScope);
|
|
74
81
|
|
|
75
82
|
beforeEach(() => {
|
|
76
83
|
vi.clearAllMocks();
|
|
77
84
|
|
|
78
85
|
// Default mock implementations
|
|
86
|
+
// For org-required apps: selectedOrganisation is available
|
|
87
|
+
// For event-required apps: selectedOrganisation is null, org derived from event
|
|
79
88
|
mockUseUnifiedAuthFn.mockReturnValue({
|
|
80
89
|
user: mockUser,
|
|
81
|
-
selectedOrganisation: { id: 'org-123' },
|
|
82
|
-
selectedEvent: { event_id: 'event-123' },
|
|
90
|
+
selectedOrganisation: { id: 'org-123' }, // Available for org-required apps, null for event-required
|
|
91
|
+
selectedEvent: { event_id: 'event-123', organisation_id: 'org-123' },
|
|
83
92
|
appId: 'app-123', // Required for scope resolution
|
|
93
|
+
appName: 'test-app',
|
|
94
|
+
appConfig: { requires_event: false }, // Default to org-required for tests
|
|
84
95
|
supabase: {
|
|
85
96
|
from: vi.fn().mockReturnValue({
|
|
86
97
|
select: vi.fn().mockReturnValue({
|
|
87
98
|
eq: vi.fn().mockReturnValue({
|
|
88
99
|
eq: vi.fn().mockReturnValue({
|
|
89
100
|
single: vi.fn().mockResolvedValue({
|
|
90
|
-
data: { id: 'app-123', name: 'test-app', is_active: true },
|
|
101
|
+
data: { id: 'app-123', name: 'test-app', is_active: true, requires_event: false },
|
|
91
102
|
error: null
|
|
92
103
|
})
|
|
93
104
|
})
|
|
@@ -99,6 +110,17 @@ describe('PagePermissionGuard Component', () => {
|
|
|
99
110
|
|
|
100
111
|
mockGetCurrentAppName.mockReturnValue('test-app');
|
|
101
112
|
|
|
113
|
+
// Mock useResolvedScope to return resolved scope immediately
|
|
114
|
+
mockUseResolvedScope.mockReturnValue({
|
|
115
|
+
resolvedScope: {
|
|
116
|
+
organisationId: 'org-123',
|
|
117
|
+
eventId: 'event-123',
|
|
118
|
+
appId: 'app-123'
|
|
119
|
+
},
|
|
120
|
+
isLoading: false,
|
|
121
|
+
error: null
|
|
122
|
+
});
|
|
123
|
+
|
|
102
124
|
mockUseCan.mockReturnValue({
|
|
103
125
|
can: true,
|
|
104
126
|
isLoading: false,
|
|
@@ -238,14 +260,15 @@ describe('PagePermissionGuard Component', () => {
|
|
|
238
260
|
|
|
239
261
|
expect(mockUseCan).toHaveBeenCalledWith(
|
|
240
262
|
'user-123',
|
|
241
|
-
|
|
263
|
+
{
|
|
242
264
|
organisationId: 'org-123',
|
|
243
265
|
eventId: 'event-123',
|
|
244
266
|
appId: 'app-123'
|
|
245
|
-
}
|
|
267
|
+
},
|
|
246
268
|
'read:page.dashboard',
|
|
247
269
|
'dashboard',
|
|
248
|
-
true
|
|
270
|
+
true,
|
|
271
|
+
'test-app'
|
|
249
272
|
);
|
|
250
273
|
});
|
|
251
274
|
|
|
@@ -274,14 +297,15 @@ describe('PagePermissionGuard Component', () => {
|
|
|
274
297
|
|
|
275
298
|
expect(mockUseCan).toHaveBeenCalledWith(
|
|
276
299
|
'user-123',
|
|
277
|
-
|
|
300
|
+
{
|
|
278
301
|
organisationId: 'org-123',
|
|
279
302
|
eventId: 'event-123',
|
|
280
303
|
appId: 'app-123'
|
|
281
|
-
}
|
|
304
|
+
},
|
|
282
305
|
'read:page.dashboard',
|
|
283
306
|
customPageId,
|
|
284
|
-
true
|
|
307
|
+
true,
|
|
308
|
+
'test-app'
|
|
285
309
|
);
|
|
286
310
|
});
|
|
287
311
|
|
|
@@ -312,14 +336,15 @@ describe('PagePermissionGuard Component', () => {
|
|
|
312
336
|
|
|
313
337
|
expect(mockUseCan).toHaveBeenCalledWith(
|
|
314
338
|
'user-123',
|
|
315
|
-
|
|
339
|
+
{
|
|
316
340
|
organisationId: 'org-123',
|
|
317
341
|
eventId: 'event-123',
|
|
318
342
|
appId: 'app-123'
|
|
319
|
-
}
|
|
343
|
+
},
|
|
320
344
|
`${operation}:page.dashboard`,
|
|
321
345
|
'dashboard',
|
|
322
|
-
true
|
|
346
|
+
true,
|
|
347
|
+
'test-app'
|
|
323
348
|
);
|
|
324
349
|
|
|
325
350
|
unmount();
|
|
@@ -353,8 +378,8 @@ describe('PagePermissionGuard Component', () => {
|
|
|
353
378
|
|
|
354
379
|
describe('App ID Resolution', () => {
|
|
355
380
|
it('resolves app ID from database', async () => {
|
|
356
|
-
//
|
|
357
|
-
// The component uses appId from context
|
|
381
|
+
// useResolvedScope will call getCurrentAppName to resolve app config
|
|
382
|
+
// The component uses appId from context when available
|
|
358
383
|
mockUseCan.mockReturnValue({
|
|
359
384
|
can: true,
|
|
360
385
|
isLoading: false,
|
|
@@ -374,9 +399,11 @@ describe('PagePermissionGuard Component', () => {
|
|
|
374
399
|
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
375
400
|
}, { interval: 10, timeout: 2000 });
|
|
376
401
|
|
|
377
|
-
//
|
|
378
|
-
// The component uses contextAppId
|
|
379
|
-
|
|
402
|
+
// useResolvedScope calls getCurrentAppName to resolve app config
|
|
403
|
+
// The component uses contextAppId when available, but useResolvedScope still needs app name
|
|
404
|
+
// So getCurrentAppName may be called by useResolvedScope
|
|
405
|
+
// The important thing is that the component works correctly
|
|
406
|
+
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
380
407
|
});
|
|
381
408
|
|
|
382
409
|
it('handles app resolution errors in test environment', async () => {
|
|
@@ -413,6 +440,19 @@ describe('PagePermissionGuard Component', () => {
|
|
|
413
440
|
it('handles app resolution errors in production', async () => {
|
|
414
441
|
mockGetCurrentAppName.mockReturnValue(null);
|
|
415
442
|
|
|
443
|
+
// When app resolution fails, useResolvedScope should return null scope or error
|
|
444
|
+
mockUseResolvedScope.mockReturnValue({
|
|
445
|
+
resolvedScope: null,
|
|
446
|
+
isLoading: true, // Still loading when app resolution fails
|
|
447
|
+
error: null
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
mockUseCan.mockReturnValue({
|
|
451
|
+
can: false,
|
|
452
|
+
isLoading: true,
|
|
453
|
+
error: null
|
|
454
|
+
});
|
|
455
|
+
|
|
416
456
|
// Mock import.meta.env.MODE for production using vi.stubEnv
|
|
417
457
|
vi.stubEnv('MODE', 'production');
|
|
418
458
|
|
|
@@ -426,9 +466,8 @@ describe('PagePermissionGuard Component', () => {
|
|
|
426
466
|
</PagePermissionGuard>
|
|
427
467
|
);
|
|
428
468
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}, { interval: 10 });
|
|
469
|
+
// Component should show loading when scope is still resolving
|
|
470
|
+
expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
|
|
432
471
|
|
|
433
472
|
// Restore environment
|
|
434
473
|
vi.unstubAllEnvs();
|
|
@@ -516,11 +555,13 @@ describe('PagePermissionGuard Component', () => {
|
|
|
516
555
|
customScope,
|
|
517
556
|
'read:page.dashboard',
|
|
518
557
|
'dashboard',
|
|
519
|
-
true
|
|
558
|
+
true,
|
|
559
|
+
'test-app'
|
|
520
560
|
);
|
|
521
561
|
});
|
|
522
562
|
|
|
523
|
-
it('resolves scope from organisation and event context', async () => {
|
|
563
|
+
it('resolves scope from organisation and event context (org-required app)', async () => {
|
|
564
|
+
// For org-required apps, organisation is primary, event is optional
|
|
524
565
|
mockUseCan.mockReturnValue({
|
|
525
566
|
can: true,
|
|
526
567
|
isLoading: false,
|
|
@@ -542,23 +583,26 @@ describe('PagePermissionGuard Component', () => {
|
|
|
542
583
|
|
|
543
584
|
expect(mockUseCan).toHaveBeenCalledWith(
|
|
544
585
|
'user-123',
|
|
545
|
-
|
|
586
|
+
{
|
|
546
587
|
organisationId: 'org-123',
|
|
547
588
|
eventId: 'event-123',
|
|
548
589
|
appId: 'app-123'
|
|
549
|
-
}
|
|
590
|
+
},
|
|
550
591
|
'read:page.dashboard',
|
|
551
592
|
'dashboard',
|
|
552
|
-
true
|
|
593
|
+
true,
|
|
594
|
+
'test-app'
|
|
553
595
|
);
|
|
554
596
|
});
|
|
555
597
|
|
|
556
|
-
it('resolves scope from organisation only', async () => {
|
|
598
|
+
it('resolves scope from organisation only (org-required app)', async () => {
|
|
599
|
+
// For org-required apps, organisation is primary context, event is optional
|
|
557
600
|
mockUseUnifiedAuthFn.mockReturnValue({
|
|
558
601
|
user: mockUser,
|
|
559
602
|
selectedOrganisation: { id: 'org-123' },
|
|
560
603
|
selectedEvent: null,
|
|
561
604
|
appId: 'app-123',
|
|
605
|
+
appName: 'test-app',
|
|
562
606
|
supabase: {
|
|
563
607
|
from: vi.fn().mockReturnValue({
|
|
564
608
|
select: vi.fn().mockReturnValue({
|
|
@@ -575,6 +619,16 @@ describe('PagePermissionGuard Component', () => {
|
|
|
575
619
|
} as any
|
|
576
620
|
});
|
|
577
621
|
|
|
622
|
+
mockUseResolvedScope.mockReturnValue({
|
|
623
|
+
resolvedScope: {
|
|
624
|
+
organisationId: 'org-123',
|
|
625
|
+
eventId: undefined,
|
|
626
|
+
appId: 'app-123'
|
|
627
|
+
},
|
|
628
|
+
isLoading: false,
|
|
629
|
+
error: null
|
|
630
|
+
});
|
|
631
|
+
|
|
578
632
|
mockUseCan.mockReturnValue({
|
|
579
633
|
can: true,
|
|
580
634
|
isLoading: false,
|
|
@@ -596,23 +650,26 @@ describe('PagePermissionGuard Component', () => {
|
|
|
596
650
|
|
|
597
651
|
expect(mockUseCan).toHaveBeenCalledWith(
|
|
598
652
|
'user-123',
|
|
599
|
-
|
|
653
|
+
{
|
|
600
654
|
organisationId: 'org-123',
|
|
601
655
|
eventId: undefined,
|
|
602
656
|
appId: 'app-123'
|
|
603
|
-
}
|
|
657
|
+
},
|
|
604
658
|
'read:page.dashboard',
|
|
605
659
|
'dashboard',
|
|
606
|
-
true
|
|
660
|
+
true,
|
|
661
|
+
'test-app'
|
|
607
662
|
);
|
|
608
663
|
});
|
|
609
664
|
|
|
610
|
-
it('resolves scope from event context when organisation not available', async () => {
|
|
665
|
+
it('resolves scope from event context when organisation not available (event-required app)', async () => {
|
|
666
|
+
// For event-required apps, selectedOrganisation is null, org is derived from event
|
|
611
667
|
mockUseUnifiedAuthFn.mockReturnValue({
|
|
612
668
|
user: mockUser,
|
|
613
|
-
selectedOrganisation: null,
|
|
669
|
+
selectedOrganisation: null, // Not available for event-required apps
|
|
614
670
|
selectedEvent: { event_id: 'event-123' },
|
|
615
671
|
appId: 'app-123',
|
|
672
|
+
appName: 'test-app',
|
|
616
673
|
supabase: {
|
|
617
674
|
from: vi.fn().mockReturnValue({
|
|
618
675
|
select: vi.fn().mockReturnValue({
|
|
@@ -629,10 +686,15 @@ describe('PagePermissionGuard Component', () => {
|
|
|
629
686
|
} as any
|
|
630
687
|
});
|
|
631
688
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
689
|
+
// Mock useResolvedScope to return scope resolved from event
|
|
690
|
+
mockUseResolvedScope.mockReturnValue({
|
|
691
|
+
resolvedScope: {
|
|
692
|
+
organisationId: 'resolved-org',
|
|
693
|
+
eventId: 'event-123',
|
|
694
|
+
appId: 'app-123'
|
|
695
|
+
},
|
|
696
|
+
isLoading: false,
|
|
697
|
+
error: null
|
|
636
698
|
});
|
|
637
699
|
|
|
638
700
|
mockUseCan.mockReturnValue({
|
|
@@ -654,20 +716,17 @@ describe('PagePermissionGuard Component', () => {
|
|
|
654
716
|
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
655
717
|
}, { interval: 10 });
|
|
656
718
|
|
|
657
|
-
expect(mockCreateScopeFromEvent).toHaveBeenCalledWith(
|
|
658
|
-
expect.any(Object),
|
|
659
|
-
'event-123'
|
|
660
|
-
);
|
|
661
719
|
expect(mockUseCan).toHaveBeenCalledWith(
|
|
662
720
|
'user-123',
|
|
663
|
-
|
|
721
|
+
{
|
|
664
722
|
organisationId: 'resolved-org',
|
|
665
723
|
eventId: 'event-123',
|
|
666
724
|
appId: 'app-123' // Component uses appId from context, not from resolved scope
|
|
667
|
-
}
|
|
725
|
+
},
|
|
668
726
|
'read:page.dashboard',
|
|
669
727
|
'dashboard',
|
|
670
|
-
true
|
|
728
|
+
true,
|
|
729
|
+
'test-app'
|
|
671
730
|
);
|
|
672
731
|
});
|
|
673
732
|
|
|
@@ -677,11 +736,23 @@ describe('PagePermissionGuard Component', () => {
|
|
|
677
736
|
selectedOrganisation: null,
|
|
678
737
|
selectedEvent: { event_id: 'event-123' },
|
|
679
738
|
appId: undefined, // Not available for error case
|
|
739
|
+
appName: 'test-app',
|
|
680
740
|
supabase: {} as any
|
|
681
741
|
});
|
|
682
742
|
|
|
683
743
|
const error = new Error('Could not resolve organisation from event');
|
|
684
|
-
|
|
744
|
+
mockUseResolvedScope.mockReturnValue({
|
|
745
|
+
resolvedScope: null,
|
|
746
|
+
isLoading: false,
|
|
747
|
+
error
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
// Mock useCan to return quickly so component can process error
|
|
751
|
+
mockUseCan.mockReturnValue({
|
|
752
|
+
can: false,
|
|
753
|
+
isLoading: false,
|
|
754
|
+
error: null
|
|
755
|
+
});
|
|
685
756
|
|
|
686
757
|
render(
|
|
687
758
|
<PagePermissionGuard
|
|
@@ -693,9 +764,10 @@ describe('PagePermissionGuard Component', () => {
|
|
|
693
764
|
</PagePermissionGuard>
|
|
694
765
|
);
|
|
695
766
|
|
|
767
|
+
// Component shows fallback when there's a scope error
|
|
696
768
|
await waitFor(() => {
|
|
697
|
-
expect(screen.
|
|
698
|
-
}, { interval: 10 });
|
|
769
|
+
expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
|
|
770
|
+
}, { interval: 10, timeout: 2000 });
|
|
699
771
|
});
|
|
700
772
|
|
|
701
773
|
it('handles missing context gracefully', async () => {
|
|
@@ -704,9 +776,16 @@ describe('PagePermissionGuard Component', () => {
|
|
|
704
776
|
selectedOrganisation: null,
|
|
705
777
|
selectedEvent: null,
|
|
706
778
|
appId: undefined,
|
|
779
|
+
appName: 'test-app',
|
|
707
780
|
supabase: null
|
|
708
781
|
});
|
|
709
782
|
|
|
783
|
+
mockUseResolvedScope.mockReturnValue({
|
|
784
|
+
resolvedScope: null,
|
|
785
|
+
isLoading: true,
|
|
786
|
+
error: null
|
|
787
|
+
});
|
|
788
|
+
|
|
710
789
|
render(
|
|
711
790
|
<PagePermissionGuard
|
|
712
791
|
pageName={mockPageName}
|
|
@@ -717,9 +796,7 @@ describe('PagePermissionGuard Component', () => {
|
|
|
717
796
|
</PagePermissionGuard>
|
|
718
797
|
);
|
|
719
798
|
|
|
720
|
-
|
|
721
|
-
expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
|
|
722
|
-
}, { interval: 10 });
|
|
799
|
+
expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
|
|
723
800
|
});
|
|
724
801
|
});
|
|
725
802
|
|
|
@@ -922,23 +999,18 @@ describe('PagePermissionGuard Component', () => {
|
|
|
922
999
|
it('handles missing user gracefully', async () => {
|
|
923
1000
|
mockUseUnifiedAuthFn.mockReturnValue({
|
|
924
1001
|
user: null,
|
|
925
|
-
selectedOrganisation:
|
|
926
|
-
selectedEvent:
|
|
927
|
-
appId:
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
})
|
|
938
|
-
})
|
|
939
|
-
})
|
|
940
|
-
})
|
|
941
|
-
} as any
|
|
1002
|
+
selectedOrganisation: null, // No org when user is missing
|
|
1003
|
+
selectedEvent: null, // No event when user is missing
|
|
1004
|
+
appId: undefined,
|
|
1005
|
+
appName: 'test-app',
|
|
1006
|
+
supabase: null
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
// When user is missing, scope resolution should return null
|
|
1010
|
+
mockUseResolvedScope.mockReturnValue({
|
|
1011
|
+
resolvedScope: null,
|
|
1012
|
+
isLoading: false,
|
|
1013
|
+
error: null
|
|
942
1014
|
});
|
|
943
1015
|
|
|
944
1016
|
mockUseCan.mockReturnValue({
|
|
@@ -957,21 +1029,8 @@ describe('PagePermissionGuard Component', () => {
|
|
|
957
1029
|
</PagePermissionGuard>
|
|
958
1030
|
);
|
|
959
1031
|
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
}, { interval: 10 });
|
|
963
|
-
|
|
964
|
-
expect(mockUseCan).toHaveBeenCalledWith(
|
|
965
|
-
'',
|
|
966
|
-
expect.objectContaining({
|
|
967
|
-
organisationId: undefined, // Changed from empty string - undefined is used when not resolved
|
|
968
|
-
eventId: undefined,
|
|
969
|
-
appId: undefined // appId is optional and should be undefined when not resolved
|
|
970
|
-
}),
|
|
971
|
-
'read:page.dashboard',
|
|
972
|
-
'dashboard',
|
|
973
|
-
true
|
|
974
|
-
);
|
|
1032
|
+
// Component shows loading when user is missing (no valid user to check permissions for)
|
|
1033
|
+
expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
|
|
975
1034
|
});
|
|
976
1035
|
|
|
977
1036
|
it('handles database errors during app resolution', async () => {
|
|
@@ -980,6 +1039,7 @@ describe('PagePermissionGuard Component', () => {
|
|
|
980
1039
|
selectedOrganisation: { id: 'org-123' },
|
|
981
1040
|
selectedEvent: { event_id: 'event-123' },
|
|
982
1041
|
appId: undefined, // Not resolved due to database error
|
|
1042
|
+
appName: 'test-app',
|
|
983
1043
|
supabase: {
|
|
984
1044
|
from: vi.fn().mockReturnValue({
|
|
985
1045
|
select: vi.fn().mockReturnValue({
|
|
@@ -996,6 +1056,24 @@ describe('PagePermissionGuard Component', () => {
|
|
|
996
1056
|
} as any
|
|
997
1057
|
});
|
|
998
1058
|
|
|
1059
|
+
// When database error occurs, useResolvedScope might still return a scope (with appId undefined)
|
|
1060
|
+
// or might return null. For PORTAL app, it can work without appId
|
|
1061
|
+
mockUseResolvedScope.mockReturnValue({
|
|
1062
|
+
resolvedScope: {
|
|
1063
|
+
organisationId: 'org-123',
|
|
1064
|
+
eventId: 'event-123',
|
|
1065
|
+
appId: undefined // App resolution failed
|
|
1066
|
+
},
|
|
1067
|
+
isLoading: false,
|
|
1068
|
+
error: null
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
mockUseCan.mockReturnValue({
|
|
1072
|
+
can: true, // Permission check can still succeed without appId for page permissions
|
|
1073
|
+
isLoading: false,
|
|
1074
|
+
error: null
|
|
1075
|
+
});
|
|
1076
|
+
|
|
999
1077
|
// Set NODE_ENV to production
|
|
1000
1078
|
const originalEnv = process.env.NODE_ENV;
|
|
1001
1079
|
process.env.NODE_ENV = 'production';
|
|
@@ -1010,9 +1088,10 @@ describe('PagePermissionGuard Component', () => {
|
|
|
1010
1088
|
</PagePermissionGuard>
|
|
1011
1089
|
);
|
|
1012
1090
|
|
|
1091
|
+
// Component can still work if scope is resolved (even without appId for page permissions)
|
|
1013
1092
|
await waitFor(() => {
|
|
1014
|
-
expect(screen.
|
|
1015
|
-
}, { interval: 10 });
|
|
1093
|
+
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
1094
|
+
}, { interval: 10, timeout: 2000 });
|
|
1016
1095
|
|
|
1017
1096
|
// Restore NODE_ENV
|
|
1018
1097
|
process.env.NODE_ENV = originalEnv;
|
|
@@ -22,6 +22,8 @@ const mockUseUnifiedAuthFn = vi.fn(() => ({
|
|
|
22
22
|
selectedEvent: null,
|
|
23
23
|
selectedOrganisationId: 'org-123',
|
|
24
24
|
selectedEventId: undefined,
|
|
25
|
+
appId: 'app-123',
|
|
26
|
+
appName: 'test-app',
|
|
25
27
|
supabase: {
|
|
26
28
|
from: vi.fn(() => ({
|
|
27
29
|
select: vi.fn(() => ({
|
|
@@ -41,17 +43,36 @@ vi.mock('../../hooks', () => ({
|
|
|
41
43
|
useCan: vi.fn()
|
|
42
44
|
}));
|
|
43
45
|
|
|
46
|
+
// Mock useResolvedScope hook
|
|
47
|
+
vi.mock('../../hooks/useResolvedScope', () => ({
|
|
48
|
+
useResolvedScope: vi.fn()
|
|
49
|
+
}));
|
|
50
|
+
|
|
44
51
|
// Mock the app name resolver
|
|
45
|
-
vi.mock('../../../utils/appNameResolver', () => ({
|
|
52
|
+
vi.mock('../../../utils/app/appNameResolver', () => ({
|
|
46
53
|
getCurrentAppName: () => 'test-app'
|
|
47
54
|
}));
|
|
48
55
|
|
|
56
|
+
import { useResolvedScope } from '../../hooks/useResolvedScope';
|
|
57
|
+
|
|
49
58
|
describe('PagePermissionGuard Verification', () => {
|
|
50
59
|
const mockUseCan = vi.mocked(useCan);
|
|
60
|
+
const mockUseResolvedScope = vi.mocked(useResolvedScope);
|
|
51
61
|
|
|
52
62
|
beforeEach(() => {
|
|
53
63
|
vi.clearAllMocks();
|
|
54
64
|
|
|
65
|
+
// Mock useResolvedScope to return resolved scope immediately
|
|
66
|
+
mockUseResolvedScope.mockReturnValue({
|
|
67
|
+
resolvedScope: {
|
|
68
|
+
organisationId: 'org-123',
|
|
69
|
+
eventId: undefined,
|
|
70
|
+
appId: 'app-123'
|
|
71
|
+
},
|
|
72
|
+
isLoading: false,
|
|
73
|
+
error: null
|
|
74
|
+
});
|
|
75
|
+
|
|
55
76
|
// Set up default mock behavior
|
|
56
77
|
mockUseCan.mockReturnValue({
|
|
57
78
|
can: true,
|