@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
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Supabase Test Fixtures
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module TestFixtures/Supabase
|
|
5
|
+
* @since 1.0.0
|
|
6
|
+
*
|
|
7
|
+
* Shared Supabase client fixtures and mock data generators for tests.
|
|
8
|
+
* Reduces repeated mock setup across test files.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createMockSupabaseClient, createMockQueryBuilder } from '../helpers/supabaseMock';
|
|
12
|
+
import type { User, Session } from '@supabase/supabase-js';
|
|
13
|
+
import { TEST_FIXTURES } from './test-data';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a standard mock Supabase client with common RBAC mocks
|
|
17
|
+
*/
|
|
18
|
+
export function createTestSupabaseClient(overrides: {
|
|
19
|
+
user?: User;
|
|
20
|
+
session?: Session;
|
|
21
|
+
rpcResponses?: Record<string, any>;
|
|
22
|
+
tableData?: Record<string, any[]>;
|
|
23
|
+
} = {}) {
|
|
24
|
+
const mockUser = overrides.user || {
|
|
25
|
+
id: TEST_FIXTURES.users.basic.id,
|
|
26
|
+
email: TEST_FIXTURES.users.basic.email,
|
|
27
|
+
user_metadata: {},
|
|
28
|
+
app_metadata: {},
|
|
29
|
+
aud: 'authenticated',
|
|
30
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
31
|
+
} as User;
|
|
32
|
+
|
|
33
|
+
const mockSession = overrides.session || {
|
|
34
|
+
access_token: 'test-access-token',
|
|
35
|
+
refresh_token: 'test-refresh-token',
|
|
36
|
+
expires_at: Date.now() + 3600000,
|
|
37
|
+
expires_in: 3600,
|
|
38
|
+
token_type: 'bearer',
|
|
39
|
+
user: mockUser,
|
|
40
|
+
} as Session;
|
|
41
|
+
|
|
42
|
+
const client = createMockSupabaseClient();
|
|
43
|
+
if (!overrides.tableData) {
|
|
44
|
+
throw new Error('createTestSupabaseClient requires tableData overrides for tests');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// Setup auth mocks
|
|
49
|
+
vi.spyOn(client.auth, 'getSession').mockResolvedValue({
|
|
50
|
+
data: { session: mockSession },
|
|
51
|
+
error: null
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
vi.spyOn(client.auth, 'getUser').mockResolvedValue({
|
|
55
|
+
data: { user: mockUser },
|
|
56
|
+
error: null
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Setup default RPC responses
|
|
60
|
+
const defaultRpcResponses: Record<string, any> = {
|
|
61
|
+
rbac_page_access_check: { data: true, error: null },
|
|
62
|
+
rbac_permissions_get: {
|
|
63
|
+
data: [
|
|
64
|
+
{
|
|
65
|
+
permission_type: 'organisation_access',
|
|
66
|
+
role_name: 'member',
|
|
67
|
+
context_id: TEST_FIXTURES.organisations.basic.id
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
error: null
|
|
71
|
+
},
|
|
72
|
+
rbac_access_validate: {
|
|
73
|
+
data: [{
|
|
74
|
+
has_access: true,
|
|
75
|
+
access_level: 'organisation',
|
|
76
|
+
context_id: TEST_FIXTURES.organisations.basic.id
|
|
77
|
+
}],
|
|
78
|
+
error: null
|
|
79
|
+
},
|
|
80
|
+
...overrides.rpcResponses
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
client.rpc.mockImplementation((functionName: string, params?: any) => {
|
|
84
|
+
const response = defaultRpcResponses[functionName];
|
|
85
|
+
if (response) {
|
|
86
|
+
// If response is a function, call it with params
|
|
87
|
+
if (typeof response === 'function') {
|
|
88
|
+
return response(params);
|
|
89
|
+
}
|
|
90
|
+
return Promise.resolve(response);
|
|
91
|
+
}
|
|
92
|
+
return Promise.resolve({ data: null, error: null });
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Setup default table data
|
|
96
|
+
// CRITICAL: Capture organisations from overrides BEFORE any processing
|
|
97
|
+
// This ensures we use the exact organisations provided, not defaults
|
|
98
|
+
const organisationsData = overrides.tableData?.organisations
|
|
99
|
+
? [...overrides.tableData.organisations] // Create a copy to avoid mutations
|
|
100
|
+
: [TEST_FIXTURES.organisations.basic];
|
|
101
|
+
|
|
102
|
+
// Ensure all organisations have is_active: true (required by OrganisationService)
|
|
103
|
+
// But preserve all other properties including name
|
|
104
|
+
const processedOrganisations = organisationsData.map((org: any) => {
|
|
105
|
+
// If is_active is already set, return org as-is to preserve all properties including name
|
|
106
|
+
if (org.is_active !== undefined) {
|
|
107
|
+
return org;
|
|
108
|
+
}
|
|
109
|
+
// Only add is_active if missing, preserving all other properties
|
|
110
|
+
return {
|
|
111
|
+
...org, // Preserve ALL existing properties including name
|
|
112
|
+
is_active: true
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const defaultTableData: Record<string, any> = {
|
|
117
|
+
organisations: processedOrganisations, // Use processed organisations
|
|
118
|
+
event: overrides.tableData?.event ?? [TEST_FIXTURES.events.basic],
|
|
119
|
+
rbac_global_roles: overrides.tableData?.rbac_global_roles ?? [],
|
|
120
|
+
rbac_apps: overrides.tableData?.rbac_apps ?? [{ id: 'app-123', name: 'TEST_APP', is_active: true }],
|
|
121
|
+
rbac_app_pages: overrides.tableData?.rbac_app_pages ?? [{ id: 'page-123', page_name: 'dashboard', app_id: 'app-123' }],
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Apply any additional table data that wasn't explicitly handled above
|
|
125
|
+
if (overrides.tableData) {
|
|
126
|
+
for (const [key, value] of Object.entries(overrides.tableData)) {
|
|
127
|
+
if (!(key in defaultTableData)) {
|
|
128
|
+
defaultTableData[key] = value;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Mock from() to return a builder that resolves with table-specific data
|
|
134
|
+
// CRITICAL: Capture processedOrganisations in closure to ensure it's always used
|
|
135
|
+
const organisationsForMock = processedOrganisations;
|
|
136
|
+
|
|
137
|
+
// Override the from() mock completely - don't use the default one from createMockSupabaseClient
|
|
138
|
+
client.from = vi.fn().mockImplementation((table: string) => {
|
|
139
|
+
// For organisations table, always use the captured processedOrganisations
|
|
140
|
+
// This ensures we use the correct organisations even if defaultTableData is modified
|
|
141
|
+
let tableData: any;
|
|
142
|
+
if (table === 'organisations') {
|
|
143
|
+
tableData = organisationsForMock;
|
|
144
|
+
} else {
|
|
145
|
+
tableData = defaultTableData[table as keyof typeof defaultTableData];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Ensure data is always an array
|
|
149
|
+
const data = Array.isArray(tableData) ? tableData : (tableData ? [tableData] : []);
|
|
150
|
+
// Create response in the format Supabase returns: { data: [...], error: null }
|
|
151
|
+
const response = { data, error: null };
|
|
152
|
+
|
|
153
|
+
// Create a fresh builder for each call to ensure data is correct
|
|
154
|
+
// We'll completely override the builder to use our response
|
|
155
|
+
// IMPORTANT: All chainable methods must return the builder itself (not 'this')
|
|
156
|
+
const builder: any = {};
|
|
157
|
+
|
|
158
|
+
// Define chainable methods that return the builder
|
|
159
|
+
const chainableMethods = ['select', 'insert', 'update', 'delete', 'eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'like', 'ilike', 'in', 'not', 'is', 'or', 'and', 'filter', 'match', 'order', 'offset'];
|
|
160
|
+
chainableMethods.forEach(method => {
|
|
161
|
+
builder[method] = vi.fn().mockReturnValue(builder);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Methods that resolve with data
|
|
165
|
+
builder.limit = vi.fn().mockResolvedValue(response);
|
|
166
|
+
builder.single = vi.fn().mockResolvedValue({ data: data[0] || null, error: null });
|
|
167
|
+
builder.maybeSingle = vi.fn().mockResolvedValue({ data: data[0] || null, error: null });
|
|
168
|
+
|
|
169
|
+
// Thenable interface - CRITICAL: must resolve with the correct response
|
|
170
|
+
builder.then = vi.fn().mockImplementation((resolve, reject) => {
|
|
171
|
+
Promise.resolve(response).then(resolve, reject);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
builder.catch = vi.fn().mockImplementation((onRejected) => {
|
|
175
|
+
return Promise.resolve(response).catch(onRejected);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
builder.finally = vi.fn().mockImplementation((onFinally) => {
|
|
179
|
+
return Promise.resolve(response).finally(onFinally);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
builder.range = vi.fn().mockImplementation((min: number, max: number) => {
|
|
183
|
+
// Return a thenable builder that resolves with the sliced data
|
|
184
|
+
const rangedData = data.slice(min, max + 1);
|
|
185
|
+
const rangedResponse = { data: rangedData, error: null };
|
|
186
|
+
const rangedBuilder = Object.assign({}, builder);
|
|
187
|
+
rangedBuilder.then = vi.fn().mockImplementation((resolve, reject) => {
|
|
188
|
+
Promise.resolve(rangedResponse).then(resolve, reject);
|
|
189
|
+
});
|
|
190
|
+
rangedBuilder.catch = vi.fn().mockImplementation((onRejected) => {
|
|
191
|
+
return Promise.resolve(rangedResponse).catch(onRejected);
|
|
192
|
+
});
|
|
193
|
+
rangedBuilder.finally = vi.fn().mockImplementation((onFinally) => {
|
|
194
|
+
return Promise.resolve(rangedResponse).finally(onFinally);
|
|
195
|
+
});
|
|
196
|
+
// Ensure chainable methods still work
|
|
197
|
+
chainableMethods.forEach(method => {
|
|
198
|
+
rangedBuilder[method] = vi.fn().mockReturnValue(rangedBuilder);
|
|
199
|
+
});
|
|
200
|
+
return rangedBuilder;
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return builder;
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return client;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Creates mock user data
|
|
211
|
+
*/
|
|
212
|
+
export function createMockUser(overrides: Partial<User> = {}): User {
|
|
213
|
+
return {
|
|
214
|
+
id: TEST_FIXTURES.users.basic.id,
|
|
215
|
+
email: TEST_FIXTURES.users.basic.email,
|
|
216
|
+
user_metadata: {},
|
|
217
|
+
app_metadata: {},
|
|
218
|
+
aud: 'authenticated',
|
|
219
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
220
|
+
...overrides
|
|
221
|
+
} as User;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Creates mock session data
|
|
226
|
+
*/
|
|
227
|
+
export function createMockSession(user?: User, overrides: Partial<Session> = {}): Session {
|
|
228
|
+
const mockUser = user || createMockUser();
|
|
229
|
+
return {
|
|
230
|
+
access_token: 'test-access-token',
|
|
231
|
+
refresh_token: 'test-refresh-token',
|
|
232
|
+
expires_at: Date.now() + 3600000,
|
|
233
|
+
expires_in: 3600,
|
|
234
|
+
token_type: 'bearer',
|
|
235
|
+
user: mockUser,
|
|
236
|
+
...overrides
|
|
237
|
+
} as Session;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Creates mock organisation data
|
|
242
|
+
*/
|
|
243
|
+
export function createMockOrganisation(overrides: Partial<typeof TEST_FIXTURES.organisations.basic> & { is_active?: boolean; display_name?: string; name?: string } = {}) {
|
|
244
|
+
// Extract name from overrides if provided, otherwise use default
|
|
245
|
+
const orgName = overrides.name ?? TEST_FIXTURES.organisations.basic.name;
|
|
246
|
+
const displayName = overrides.display_name ?? orgName;
|
|
247
|
+
|
|
248
|
+
// Build the organisation object, ensuring overrides take precedence
|
|
249
|
+
return {
|
|
250
|
+
...TEST_FIXTURES.organisations.basic,
|
|
251
|
+
// Set required fields with defaults
|
|
252
|
+
is_active: true, // Required by OrganisationService - only active orgs are used
|
|
253
|
+
subscription_tier: 'basic',
|
|
254
|
+
settings: {},
|
|
255
|
+
parent_id: null,
|
|
256
|
+
created_at: '2023-01-01T00:00:00Z',
|
|
257
|
+
updated_at: '2023-01-01T00:00:00Z',
|
|
258
|
+
// Apply name and display_name (these should come before overrides to ensure they're set)
|
|
259
|
+
name: orgName, // Explicitly set name (used by UI components)
|
|
260
|
+
display_name: displayName, // display_name defaults to name
|
|
261
|
+
// Apply all overrides last to ensure they take precedence (including name if provided)
|
|
262
|
+
...overrides
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Creates mock event data
|
|
268
|
+
*/
|
|
269
|
+
export function createMockEvent(overrides: Partial<typeof TEST_FIXTURES.events.basic> = {}) {
|
|
270
|
+
return {
|
|
271
|
+
...TEST_FIXTURES.events.basic,
|
|
272
|
+
...overrides
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Creates a Supabase client configured for organisation-scoped data access tests
|
|
278
|
+
*/
|
|
279
|
+
export function createOrganisationScopedSupabaseClient(organisationId: string) {
|
|
280
|
+
return createTestSupabaseClient({
|
|
281
|
+
tableData: {
|
|
282
|
+
organisations: [createMockOrganisation({ id: organisationId })],
|
|
283
|
+
event: [createMockEvent({ organisation_id: organisationId })]
|
|
284
|
+
},
|
|
285
|
+
rpcResponses: {
|
|
286
|
+
rbac_permissions_get: {
|
|
287
|
+
data: [{
|
|
288
|
+
permission_type: 'organisation_access',
|
|
289
|
+
role_name: 'member',
|
|
290
|
+
context_id: organisationId
|
|
291
|
+
}],
|
|
292
|
+
error: null
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Re-export vi for convenience
|
|
299
|
+
import { vi } from 'vitest';
|
|
300
|
+
export { vi };
|
|
301
|
+
|
|
@@ -19,10 +19,10 @@ const TEST_TIMEOUT = 5000;
|
|
|
19
19
|
const PERFORMANCE_THRESHOLD = 500; // 500ms for public view queries
|
|
20
20
|
|
|
21
21
|
// Check if we're using real test-db (via environment variables)
|
|
22
|
-
const USE_REAL_DB = !!(process.env.SUPABASE_URL && process.env.
|
|
22
|
+
const USE_REAL_DB = !!(process.env.SUPABASE_URL && process.env.VITE_SUPABASE_PUBLISHABLE_KEY);
|
|
23
23
|
const TEST_SUPABASE_URL = process.env.SUPABASE_URL || process.env.VITE_SUPABASE_URL;
|
|
24
|
-
const
|
|
25
|
-
const TEST_SUPABASE_SERVICE_ROLE_KEY = process.env.
|
|
24
|
+
const TEST_SUPABASE_PUBLISHABLE_KEY = process.env.VITE_SUPABASE_PUBLISHABLE_KEY;
|
|
25
|
+
const TEST_SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
26
26
|
|
|
27
27
|
let anonClient: SupabaseClient<Database>;
|
|
28
28
|
let authenticatedClient: SupabaseClient<Database>;
|
|
@@ -43,14 +43,14 @@ const privateEvent = {
|
|
|
43
43
|
organisation_id: 'org-1' as any
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !
|
|
46
|
+
describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_PUBLISHABLE_KEY)('Public Recipe View - Anonymous Access', () => {
|
|
47
47
|
beforeEach(() => {
|
|
48
|
-
if (USE_REAL_DB && TEST_SUPABASE_URL &&
|
|
49
|
-
anonClient = createClient<Database>(TEST_SUPABASE_URL,
|
|
50
|
-
authenticatedClient = createClient<Database>(TEST_SUPABASE_URL, TEST_SUPABASE_SERVICE_ROLE_KEY ||
|
|
48
|
+
if (USE_REAL_DB && TEST_SUPABASE_URL && TEST_SUPABASE_PUBLISHABLE_KEY) {
|
|
49
|
+
anonClient = createClient<Database>(TEST_SUPABASE_URL, TEST_SUPABASE_PUBLISHABLE_KEY);
|
|
50
|
+
authenticatedClient = createClient<Database>(TEST_SUPABASE_URL, TEST_SUPABASE_SERVICE_ROLE_KEY || TEST_SUPABASE_PUBLISHABLE_KEY);
|
|
51
51
|
} else {
|
|
52
52
|
// This should not happen due to skipIf, but provide fallback
|
|
53
|
-
throw new Error('Test database credentials not available. Set SUPABASE_URL and
|
|
53
|
+
throw new Error('Test database credentials not available. Set SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY environment variables.');
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
|
|
@@ -58,7 +58,7 @@ describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_ANON_KEY)('
|
|
|
58
58
|
it('should allow anonymous access when event.public_readable = true', async () => {
|
|
59
59
|
const start = Date.now();
|
|
60
60
|
const { data, error } = await anonClient
|
|
61
|
-
.from('
|
|
61
|
+
.from('cake_public_recipe_details')
|
|
62
62
|
.select('*')
|
|
63
63
|
.eq('event_id', publicEvent.event_id);
|
|
64
64
|
const duration = Date.now() - start;
|
|
@@ -70,7 +70,7 @@ describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_ANON_KEY)('
|
|
|
70
70
|
|
|
71
71
|
it('should block anonymous access when event.public_readable = false', async () => {
|
|
72
72
|
const { data, error } = await anonClient
|
|
73
|
-
.from('
|
|
73
|
+
.from('cake_public_recipe_details')
|
|
74
74
|
.select('*')
|
|
75
75
|
.eq('event_id', privateEvent.event_id);
|
|
76
76
|
|
|
@@ -80,7 +80,7 @@ describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_ANON_KEY)('
|
|
|
80
80
|
|
|
81
81
|
it('should block anonymous access when event.is_visible = false', async () => {
|
|
82
82
|
const { data, error } = await anonClient
|
|
83
|
-
.from('
|
|
83
|
+
.from('cake_public_recipe_details')
|
|
84
84
|
.select('*')
|
|
85
85
|
.eq('event_id', 'hidden-event-1');
|
|
86
86
|
|
|
@@ -92,7 +92,7 @@ describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_ANON_KEY)('
|
|
|
92
92
|
describe('View Column Exposure', () => {
|
|
93
93
|
it('should expose only expected columns', async () => {
|
|
94
94
|
const { data, error } = await anonClient
|
|
95
|
-
.from('
|
|
95
|
+
.from('cake_public_recipe_details')
|
|
96
96
|
.select('*')
|
|
97
97
|
.eq('event_id', publicEvent.event_id)
|
|
98
98
|
.limit(1)
|
|
@@ -119,7 +119,7 @@ describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_ANON_KEY)('
|
|
|
119
119
|
it('should filter by meal type', async () => {
|
|
120
120
|
const start = Date.now();
|
|
121
121
|
const { data, error } = await anonClient
|
|
122
|
-
.from('
|
|
122
|
+
.from('cake_public_recipe_details')
|
|
123
123
|
.select('*')
|
|
124
124
|
.eq('event_id', publicEvent.event_id)
|
|
125
125
|
.eq('mealtype_name', 'Breakfast');
|
|
@@ -134,7 +134,7 @@ describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_ANON_KEY)('
|
|
|
134
134
|
// This test would verify that diet filters work correctly
|
|
135
135
|
// Adjust based on actual view structure
|
|
136
136
|
const { data, error } = await anonClient
|
|
137
|
-
.from('
|
|
137
|
+
.from('cake_public_recipe_details')
|
|
138
138
|
.select('*')
|
|
139
139
|
.eq('event_id', publicEvent.event_id)
|
|
140
140
|
.eq('dish_dietnote', 'Vegetarian');
|
|
@@ -148,7 +148,7 @@ describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_ANON_KEY)('
|
|
|
148
148
|
it('should complete view queries in < 500ms', async () => {
|
|
149
149
|
const start = Date.now();
|
|
150
150
|
await anonClient
|
|
151
|
-
.from('
|
|
151
|
+
.from('cake_public_recipe_details')
|
|
152
152
|
.select('*')
|
|
153
153
|
.eq('event_id', publicEvent.event_id)
|
|
154
154
|
.limit(100);
|
|
@@ -160,7 +160,7 @@ describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_ANON_KEY)('
|
|
|
160
160
|
it('should handle filtered queries efficiently', async () => {
|
|
161
161
|
const start = Date.now();
|
|
162
162
|
await anonClient
|
|
163
|
-
.from('
|
|
163
|
+
.from('cake_public_recipe_details')
|
|
164
164
|
.select('*')
|
|
165
165
|
.eq('event_id', publicEvent.event_id)
|
|
166
166
|
.eq('mealtype_name', 'Breakfast')
|
|
@@ -172,10 +172,10 @@ describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_ANON_KEY)('
|
|
|
172
172
|
});
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
-
describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !
|
|
175
|
+
describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_PUBLISHABLE_KEY)('Public Recipe View - Authenticated Access', () => {
|
|
176
176
|
it('should allow authenticated users to view public recipes', async () => {
|
|
177
177
|
const { data, error } = await authenticatedClient
|
|
178
|
-
.from('
|
|
178
|
+
.from('cake_public_recipe_details')
|
|
179
179
|
.select('*')
|
|
180
180
|
.eq('event_id', publicEvent.event_id);
|
|
181
181
|
|
|
@@ -188,7 +188,7 @@ describe.skipIf(!USE_REAL_DB || !TEST_SUPABASE_URL || !TEST_SUPABASE_ANON_KEY)('
|
|
|
188
188
|
// recipes from events in their organisation, even if not public_readable
|
|
189
189
|
// (This depends on the view definition and RLS policies)
|
|
190
190
|
const { data, error } = await authenticatedClient
|
|
191
|
-
.from('
|
|
191
|
+
.from('cake_public_recipe_details')
|
|
192
192
|
.select('*')
|
|
193
193
|
.eq('event_id', privateEvent.event_id);
|
|
194
194
|
|