@jmruthers/pace-core 0.5.193 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +299 -0
- package/cursor-rules/01-standards-compliance.mdc +244 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +222 -0
- package/cursor-rules/04-testing-standards.mdc +268 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +309 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
- package/cursor-rules/08-markup-quality.mdc +452 -0
- package/cursor-rules/CHANGELOG.md +119 -0
- package/cursor-rules/README.md +192 -0
- package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
- package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
- package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
- package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
- package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
- package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
- package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
- package/dist/chunk-24UVZUZG.js.map +1 -0
- package/dist/{chunk-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
- package/dist/chunk-2UOI2FG5.js.map +1 -0
- package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
- package/dist/chunk-3XC4CPTD.js.map +1 -0
- package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
- package/dist/chunk-6J4GEEJR.js.map +1 -0
- package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
- package/dist/chunk-6SOIHG6Z.js.map +1 -0
- package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
- package/dist/chunk-EHMR7VYL.js.map +1 -0
- package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
- package/dist/chunk-F2IMUDXZ.js.map +1 -0
- package/dist/{chunk-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
- package/dist/chunk-FFQEQTNW.js.map +1 -0
- package/dist/chunk-FMUCXFII.js +76 -0
- package/dist/chunk-FMUCXFII.js.map +1 -0
- package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
- package/dist/chunk-L4OXEN46.js.map +1 -0
- package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
- package/dist/chunk-M43Y4SSO.js.map +1 -0
- package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
- package/dist/chunk-MMZ7JXPU.js.map +1 -0
- package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
- package/dist/chunk-NECFR5MM.js.map +1 -0
- package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
- package/dist/chunk-SFZUDBL5.js.map +1 -0
- package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
- package/dist/chunk-XWQCNGTQ.js.map +1 -0
- package/dist/components.d.ts +6 -6
- package/dist/components.js +15 -12
- package/dist/components.js.map +1 -1
- package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
- package/dist/hooks.d.ts +59 -126
- package/dist/hooks.js +19 -28
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +63 -16
- package/dist/index.js +23 -24
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +21 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +146 -115
- package/dist/rbac/index.js +8 -11
- package/dist/theming/runtime.d.ts +1 -13
- package/dist/theming/runtime.js +1 -1
- package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
- package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
- package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
- package/dist/types.d.ts +2 -2
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +15 -15
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +7 -1
- package/docs/api/classes/ColumnFactory.md +8 -8
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +4 -4
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- package/docs/api/classes/SecureSupabaseClient.md +18 -15
- package/docs/api/classes/StorageUtils.md +1 -1
- 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 +4 -4
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +9 -2
- package/docs/api/interfaces/ButtonProps.md +7 -4
- package/docs/api/interfaces/CalendarProps.md +8 -5
- package/docs/api/interfaces/CardProps.md +8 -5
- 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 +24 -21
- package/docs/api/interfaces/DataTableColumn.md +31 -31
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
- package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
- package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +8 -8
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +26 -23
- package/docs/api/interfaces/FooterProps.md +10 -8
- package/docs/api/interfaces/FormFieldProps.md +10 -10
- 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 +7 -4
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +14 -11
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +11 -11
- package/docs/api/interfaces/NavigationMenuProps.md +15 -15
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- 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 +30 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
- package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
- package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
- package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
- 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 +1 -1
- 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 +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- 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 +3 -3
- package/docs/api/interfaces/SetupIssue.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- 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 +3 -3
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +4 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +14 -11
- package/docs/api/interfaces/UserMenuProps.md +8 -6
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +575 -634
- package/docs/architecture/database-schema-requirements.md +161 -0
- package/docs/core-concepts/rbac-system.md +3 -3
- package/docs/documentation-index.md +2 -4
- package/docs/getting-started/cursor-rules.md +263 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
- package/docs/migration/MIGRATION_GUIDE.md +6 -28
- package/docs/migration/README.md +52 -6
- package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
- package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -0
- package/docs/migration/database-changes-december-2025.md +3 -3
- package/docs/rbac/event-based-apps.md +1 -1
- package/docs/rbac/getting-started.md +1 -1
- package/docs/rbac/quick-start.md +1 -1
- package/docs/standards/README.md +40 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +12 -6
- package/scripts/audit/core/checks/accessibility.cjs +197 -0
- package/scripts/audit/core/checks/api-usage.cjs +191 -0
- package/scripts/audit/core/checks/bundle.cjs +142 -0
- package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +737 -691
- package/scripts/audit/core/checks/config.cjs +54 -0
- package/scripts/audit/core/checks/coverage.cjs +84 -0
- package/scripts/audit/core/checks/dependencies.cjs +454 -0
- package/scripts/audit/core/checks/documentation.cjs +203 -0
- package/scripts/audit/core/checks/environment.cjs +128 -0
- package/scripts/audit/core/checks/error-handling.cjs +299 -0
- package/scripts/audit/core/checks/forms.cjs +172 -0
- package/scripts/audit/core/checks/heuristics.cjs +68 -0
- package/scripts/audit/core/checks/hooks.cjs +334 -0
- package/scripts/audit/core/checks/imports.cjs +244 -0
- package/scripts/audit/core/checks/performance.cjs +325 -0
- package/scripts/audit/core/checks/routes.cjs +117 -0
- package/scripts/audit/core/checks/state.cjs +130 -0
- package/scripts/audit/core/checks/structure.cjs +65 -0
- package/scripts/audit/core/checks/style.cjs +584 -0
- package/scripts/audit/core/checks/testing.cjs +122 -0
- package/scripts/audit/core/checks/typescript.cjs +61 -0
- package/scripts/audit/core/scanner.cjs +199 -0
- package/scripts/audit/core/utils.cjs +137 -0
- package/scripts/audit/index.cjs +223 -0
- package/scripts/audit/reporters/console.cjs +151 -0
- package/scripts/audit/reporters/json.cjs +54 -0
- package/scripts/audit/reporters/markdown.cjs +124 -0
- package/scripts/audit-consuming-app.cjs +86 -0
- package/scripts/build-docs/build-decision.js +240 -0
- package/scripts/build-docs/cache-utils.js +105 -0
- package/scripts/build-docs/content-normalization.js +150 -0
- package/scripts/build-docs/file-utils.js +105 -0
- package/scripts/build-docs/git-utils.js +86 -0
- package/scripts/build-docs/hash-utils.js +116 -0
- package/scripts/build-docs/typedoc-runner.js +220 -0
- package/scripts/build-docs-incremental.js +77 -913
- package/scripts/install-cursor-rules.cjs +236 -0
- package/scripts/utils/command-runner.js +16 -11
- package/scripts/validate-formats.js +61 -56
- package/scripts/validate-master.js +74 -69
- package/scripts/validate-pre-publish.js +70 -65
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/__tests__/hooks/usePermissions.test.ts +2 -2
- package/src/components/Alert/Alert.test.tsx +12 -18
- package/src/components/Alert/Alert.tsx +5 -7
- package/src/components/Avatar/Avatar.test.tsx +4 -4
- package/src/components/Badge/Badge.tsx +16 -4
- package/src/components/Button/Button.tsx +27 -4
- package/src/components/Calendar/Calendar.tsx +9 -3
- package/src/components/Card/Card.tsx +4 -0
- package/src/components/Checkbox/Checkbox.test.tsx +12 -12
- package/src/components/Checkbox/Checkbox.tsx +2 -2
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +40 -6
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
- package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
- package/src/components/DataTable/components/ActionButtons.tsx +10 -7
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
- package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
- package/src/components/DataTable/components/DataTableBody.tsx +8 -0
- package/src/components/DataTable/components/DataTableCore.tsx +200 -561
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
- package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
- package/src/components/DataTable/components/DataTableModals.tsx +9 -1
- package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
- package/src/components/DataTable/components/EditFields.tsx +307 -0
- package/src/components/DataTable/components/EditableRow.tsx +9 -1
- package/src/components/DataTable/components/EmptyState.tsx +10 -0
- package/src/components/DataTable/components/FilterRow.tsx +12 -0
- package/src/components/DataTable/components/GroupHeader.tsx +12 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
- package/src/components/DataTable/components/ImportModal.tsx +7 -0
- package/src/components/DataTable/components/LoadingState.tsx +6 -0
- package/src/components/DataTable/components/PaginationControls.tsx +16 -1
- package/src/components/DataTable/components/RowComponent.tsx +391 -0
- package/src/components/DataTable/components/UnifiedTableBody.tsx +62 -852
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- package/src/components/DataTable/components/cellValueUtils.ts +40 -0
- package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
- package/src/components/DataTable/context/DataTableContext.tsx +50 -0
- package/src/components/DataTable/core/ColumnFactory.ts +31 -0
- package/src/components/DataTable/core/DataTableContext.tsx +32 -1
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useColumnReordering.ts +14 -2
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
- package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
- package/src/components/DataTable/styles.ts +6 -6
- package/src/components/DataTable/types.ts +6 -10
- package/src/components/DataTable/utils/a11yUtils.ts +7 -0
- package/src/components/DataTable/utils/debugTools.ts +18 -113
- package/src/components/DataTable/utils/errorHandling.ts +12 -0
- package/src/components/DataTable/utils/exportUtils.ts +9 -0
- package/src/components/DataTable/utils/flexibleImport.ts +12 -48
- package/src/components/DataTable/utils/paginationUtils.ts +8 -0
- package/src/components/DataTable/utils/performanceUtils.ts +5 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +8 -7
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
- package/src/components/ErrorBoundary/index.ts +27 -2
- package/src/components/EventSelector/EventSelector.tsx +4 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
- package/src/components/FileDisplay/FileDisplay.tsx +32 -18
- package/src/components/FileUpload/FileUpload.tsx +22 -2
- package/src/components/Footer/Footer.test.tsx +16 -16
- package/src/components/Footer/Footer.tsx +15 -12
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +31 -26
- package/src/components/Header/Header.tsx +22 -11
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.test.tsx +2 -2
- package/src/components/Input/Input.tsx +36 -34
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +12 -8
- package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
- package/src/components/NavigationMenu/types.ts +56 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceAppLayout/test-setup.tsx +1 -2
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
- package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
- package/src/components/Select/Select.tsx +95 -438
- package/src/components/Select/context.ts +23 -0
- package/src/components/Select/hooks/useSelectEvents.ts +87 -0
- package/src/components/Select/hooks/useSelectSearch.ts +91 -0
- package/src/components/Select/hooks/useSelectState.ts +104 -0
- package/src/components/Select/index.ts +9 -1
- package/src/components/Select/types.ts +123 -0
- package/src/components/Select/utils/text.ts +26 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +5 -6
- package/src/components/Switch/Switch.tsx +4 -4
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +5 -1
- package/src/components/Tooltip/Tooltip.tsx +3 -3
- package/src/components/UserMenu/UserMenu.test.tsx +24 -11
- package/src/components/UserMenu/UserMenu.tsx +22 -19
- package/src/components/index.ts +2 -2
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/index.ts +1 -2
- package/src/hooks/public/usePublicEvent.ts +5 -1
- package/src/hooks/public/usePublicEventLogo.ts +5 -1
- package/src/hooks/public/usePublicFileDisplay.ts +4 -0
- package/src/hooks/public/usePublicRouteParams.ts +5 -1
- package/src/hooks/services/useAuth.ts +32 -0
- package/src/hooks/services/useCurrentEvent.ts +6 -0
- package/src/hooks/services/useCurrentOrganisation.ts +6 -0
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useDebounce.ts +9 -0
- package/src/hooks/useEventTheme.ts +6 -0
- package/src/hooks/useFileDisplay.ts +4 -0
- package/src/hooks/useFileReference.ts +25 -7
- package/src/hooks/useFileUrl.ts +11 -1
- package/src/hooks/useFocusManagement.ts +16 -2
- package/src/hooks/useFocusTrap.ts +7 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +4 -1
- package/src/hooks/useKeyboardShortcuts.ts +4 -0
- package/src/hooks/useOrganisationPermissions.ts +4 -0
- package/src/hooks/useOrganisationSecurity.ts +4 -0
- package/src/hooks/usePerformanceMonitor.ts +4 -0
- package/src/hooks/usePermissionCache.ts +8 -1
- package/src/hooks/useQueryCache.ts +12 -1
- package/src/hooks/useSessionRestoration.ts +4 -0
- package/src/hooks/useStorage.ts +4 -0
- package/src/hooks/useToast.ts +3 -3
- package/src/index.ts +2 -1
- package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/AuthServiceProvider.tsx +18 -0
- package/src/providers/services/EventServiceProvider.tsx +18 -0
- package/src/providers/services/InactivityServiceProvider.tsx +18 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +58 -22
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
- package/src/rbac/adapters.tsx +14 -5
- package/src/rbac/api.ts +100 -67
- package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +1 -1
- package/src/rbac/components/NavigationProvider.tsx +5 -2
- package/src/rbac/components/PagePermissionGuard.tsx +158 -18
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +1 -1
- package/src/rbac/components/RoleBasedRouter.tsx +6 -2
- package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
- package/src/rbac/components/SecureDataProvider.tsx +21 -6
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
- package/src/rbac/engine.ts +38 -14
- package/src/rbac/hooks/permissions/index.ts +7 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
- package/src/rbac/hooks/permissions/useCan.ts +347 -0
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
- package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
- package/src/rbac/hooks/useCan.test.ts +71 -64
- package/src/rbac/hooks/usePermissions.ts +14 -995
- package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
- package/src/rbac/hooks/useResourcePermissions.ts +14 -4
- package/src/rbac/hooks/useSecureSupabase.ts +33 -13
- package/src/rbac/permissions.ts +0 -30
- package/src/rbac/secureClient.ts +212 -61
- package/src/rbac/types.ts +8 -0
- package/src/theming/__tests__/parseEventColours.test.ts +6 -9
- package/src/theming/parseEventColours.ts +5 -19
- package/src/types/vitest-globals.d.ts +51 -26
- package/src/utils/__mocks__/supabaseMock.ts +1 -3
- package/src/utils/__tests__/formatting.unit.test.ts +4 -4
- package/src/utils/__tests__/index.unit.test.ts +2 -2
- package/src/utils/audit/audit.ts +0 -3
- package/src/utils/core/cn.ts +1 -1
- package/src/utils/file-reference/index.ts +53 -1
- package/src/utils/formatting/formatting.ts +8 -18
- package/src/utils/index.ts +0 -1
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js +0 -628
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js.map +0 -1
- package/dist/chunk-7FLMSG37.js 2.map +0 -1
- package/dist/chunk-7FLMSG37.js.map +0 -1
- package/dist/chunk-BC4IJKSL.js.map +0 -1
- package/dist/chunk-E3SPN4VZ.js +0 -12917
- package/dist/chunk-E3SPN4VZ.js.map +0 -1
- package/dist/chunk-E66EQZE6 5.js +0 -37
- package/dist/chunk-E66EQZE6.js 2.map +0 -1
- package/dist/chunk-HWIIPPNI.js.map +0 -1
- package/dist/chunk-I7PSE6JW 5.js +0 -191
- package/dist/chunk-I7PSE6JW.js 2.map +0 -1
- package/dist/chunk-I7PSE6JW.js.map +0 -1
- package/dist/chunk-IIELH4DL.js.map +0 -1
- package/dist/chunk-KNC55RTG.js 5.map +0 -1
- package/dist/chunk-KNC55RTG.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js 5.map +0 -1
- package/dist/chunk-LFNCN2SP.js +0 -412
- package/dist/chunk-LFNCN2SP.js 2.map +0 -1
- package/dist/chunk-LFNCN2SP.js.map +0 -1
- package/dist/chunk-LMC26NLJ 2.js +0 -84
- package/dist/chunk-NOAYCWCX.js +0 -4993
- package/dist/chunk-NOAYCWCX.js.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js.map +0 -1
- package/dist/chunk-QXHPKYJV 3.js +0 -113
- package/dist/chunk-R77UEZ4E.js +0 -68
- package/dist/chunk-R77UEZ4E.js.map +0 -1
- package/dist/chunk-SQGMNID3.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
- package/dist/chunk-XNXXZ43G.js.map +0 -1
- package/dist/chunk-ZSAAAMVR 6.js +0 -25
- package/dist/components.js 5.map +0 -1
- package/dist/styles/index 2.js +0 -12
- package/dist/styles/index.js 5.map +0 -1
- package/dist/theming/runtime 5.js +0 -19
- package/dist/theming/runtime.js 5.map +0 -1
- package/docs/api/classes/ErrorBoundary.md +0 -144
- package/docs/migration/quick-migration-guide.md +0 -356
- package/docs/migration/service-architecture.md +0 -281
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
- package/src/hooks/useSecureDataAccess.test.ts +0 -559
- package/src/hooks/useSecureDataAccess.ts +0 -666
- /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
- /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
- /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
- /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
- /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
- /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
package/src/rbac/secureClient.ts
CHANGED
|
@@ -19,26 +19,44 @@ import { OrganisationContextRequiredError } from './types';
|
|
|
19
19
|
* This client automatically injects organisation context into all requests
|
|
20
20
|
* and prevents queries that don't have the required context.
|
|
21
21
|
*
|
|
22
|
-
* Note:
|
|
23
|
-
*
|
|
22
|
+
* Note: For non-super-admins, organisationId is required. Super-admins can operate
|
|
23
|
+
* without organisationId to access system-wide tables (like core_organisations).
|
|
24
|
+
* Callers should derive organisationId from eventId before creating this client
|
|
25
|
+
* if working with event-required apps.
|
|
24
26
|
*/
|
|
25
27
|
export class SecureSupabaseClient {
|
|
26
28
|
private supabase: SupabaseClient<Database>;
|
|
27
29
|
private edgeFunctionClient: SupabaseClient<Database> | null = null;
|
|
28
30
|
private supabaseUrl: string;
|
|
29
31
|
private supabaseKey: string;
|
|
30
|
-
private organisationId: UUID;
|
|
32
|
+
private organisationId: UUID | null;
|
|
31
33
|
private eventId?: string;
|
|
32
34
|
private appId?: UUID;
|
|
33
35
|
private isSuperAdmin: boolean;
|
|
36
|
+
private usesExistingClient: boolean = false;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* RPC functions that are safe to call without organisation context.
|
|
40
|
+
*
|
|
41
|
+
* These functions must:
|
|
42
|
+
* - rely on JWT context (auth.uid()) for authentication
|
|
43
|
+
* - not read or write organisation-scoped data
|
|
44
|
+
*
|
|
45
|
+
* This allowlist enables compliant consuming apps to use `secureSupabase.rpc(...)`
|
|
46
|
+
* even before an organisation is selected (common during initial page load/refresh).
|
|
47
|
+
*/
|
|
48
|
+
private static readonly GLOBAL_RPC_ALLOWLIST = new Set<string>([
|
|
49
|
+
'data_rbac_apps_list',
|
|
50
|
+
]);
|
|
34
51
|
|
|
35
52
|
constructor(
|
|
36
53
|
supabaseUrl: string,
|
|
37
54
|
supabaseKey: string,
|
|
38
|
-
organisationId: UUID,
|
|
55
|
+
organisationId: UUID | null,
|
|
39
56
|
eventId?: string,
|
|
40
57
|
appId?: UUID,
|
|
41
|
-
isSuperAdmin: boolean = false
|
|
58
|
+
isSuperAdmin: boolean = false,
|
|
59
|
+
existingClient?: SupabaseClient<Database>
|
|
42
60
|
) {
|
|
43
61
|
this.supabaseUrl = supabaseUrl;
|
|
44
62
|
this.supabaseKey = supabaseKey;
|
|
@@ -47,18 +65,25 @@ export class SecureSupabaseClient {
|
|
|
47
65
|
this.appId = appId;
|
|
48
66
|
this.isSuperAdmin = isSuperAdmin;
|
|
49
67
|
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
68
|
+
// Prefer reusing an existing authenticated client (avoids multiple GoTrue instances
|
|
69
|
+
// and ensures auth context is present for RLS policies).
|
|
70
|
+
if (existingClient) {
|
|
71
|
+
this.supabase = existingClient;
|
|
72
|
+
this.usesExistingClient = true;
|
|
73
|
+
} else {
|
|
74
|
+
// Create the base Supabase client with context headers
|
|
75
|
+
// Note: We'll override functions.invoke to exclude headers for Edge Functions
|
|
76
|
+
// as they may not have CORS configured to accept custom headers
|
|
77
|
+
this.supabase = createClient<Database>(supabaseUrl, supabaseKey, {
|
|
78
|
+
global: {
|
|
79
|
+
headers: {
|
|
80
|
+
'x-organisation-id': organisationId || '',
|
|
81
|
+
'x-event-id': eventId || '',
|
|
82
|
+
'x-app-id': appId || '',
|
|
83
|
+
},
|
|
59
84
|
},
|
|
60
|
-
}
|
|
61
|
-
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
62
87
|
|
|
63
88
|
// Override the auth methods to inject context
|
|
64
89
|
this.setupContextInjection();
|
|
@@ -75,8 +100,10 @@ export class SecureSupabaseClient {
|
|
|
75
100
|
const originalFrom = this.supabase.from.bind(this.supabase);
|
|
76
101
|
|
|
77
102
|
(this.supabase as any).from = (table: string): any => {
|
|
78
|
-
// Validate context before allowing
|
|
79
|
-
|
|
103
|
+
// Validate context before allowing database operations.
|
|
104
|
+
// Some tables are not organisation-scoped (e.g. rbac_apps) and must be queryable
|
|
105
|
+
// without organisation context for initial bootstrapping.
|
|
106
|
+
this.validateContextForTable(table);
|
|
80
107
|
|
|
81
108
|
// Type assertion needed because table is a string but Supabase expects specific table names
|
|
82
109
|
const query = originalFrom(table as any);
|
|
@@ -94,15 +121,25 @@ export class SecureSupabaseClient {
|
|
|
94
121
|
// Type assertion needed because we're wrapping the generic rpc method
|
|
95
122
|
// The fn parameter is typed as string to match Supabase's rpc signature
|
|
96
123
|
(this.supabase as any).rpc = (fn: string, args?: any, options?: any): any => {
|
|
97
|
-
// Validate context before allowing
|
|
98
|
-
|
|
124
|
+
// Validate context before allowing RPC calls.
|
|
125
|
+
// Some RPCs are global (not organisation-scoped) but still require auth.uid() from JWT.
|
|
126
|
+
// Allow these even without organisation context.
|
|
127
|
+
this.validateContextForRpc(fn);
|
|
99
128
|
|
|
100
129
|
// Inject context into RPC calls
|
|
130
|
+
// Only include organisation_id if it's available (super-admins may not have it)
|
|
131
|
+
// IMPORTANT:
|
|
132
|
+
// Do NOT overwrite explicitly provided RPC parameters.
|
|
133
|
+
// Some RPCs legitimately take `p_app_id`/`p_event_id` as the *target* entity,
|
|
134
|
+
// which may differ from the current RBAC scope app/event.
|
|
135
|
+
const safeArgs = (args ?? {}) as Record<string, unknown>;
|
|
101
136
|
const contextArgs = {
|
|
102
|
-
...
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
137
|
+
...safeArgs,
|
|
138
|
+
...(this.organisationId && safeArgs.p_organisation_id === undefined
|
|
139
|
+
? { p_organisation_id: this.organisationId }
|
|
140
|
+
: {}),
|
|
141
|
+
...(this.eventId && safeArgs.p_event_id === undefined ? { p_event_id: this.eventId } : {}),
|
|
142
|
+
...(this.appId && safeArgs.p_app_id === undefined ? { p_app_id: this.appId } : {}),
|
|
106
143
|
};
|
|
107
144
|
|
|
108
145
|
return originalRpc(fn as any, contextArgs, options);
|
|
@@ -119,10 +156,19 @@ export class SecureSupabaseClient {
|
|
|
119
156
|
* This avoids interfering with the main client's operations.
|
|
120
157
|
*/
|
|
121
158
|
private setupEdgeFunctionHandling() {
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
159
|
+
// IMPORTANT:
|
|
160
|
+
// Do not create a second Supabase client when we are already reusing an authenticated
|
|
161
|
+
// base client (this triggers "Multiple GoTrueClient instances" warnings and can cause
|
|
162
|
+
// session/auth desync that breaks RLS-protected reads).
|
|
163
|
+
//
|
|
164
|
+
// If we're using an existing client, just use it for Edge Functions too.
|
|
165
|
+
if (this.usesExistingClient) {
|
|
166
|
+
this.edgeFunctionClient = null;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Otherwise, create a separate client without the custom RBAC headers for Edge Functions.
|
|
171
|
+
// This prevents CORS errors when Edge Functions don't accept custom headers.
|
|
126
172
|
this.edgeFunctionClient = createClient<Database>(this.supabaseUrl, this.supabaseKey);
|
|
127
173
|
}
|
|
128
174
|
|
|
@@ -173,13 +219,28 @@ export class SecureSupabaseClient {
|
|
|
173
219
|
return originalInsert(values);
|
|
174
220
|
}
|
|
175
221
|
// Non-super-admin: Add organisation_id as defense in depth
|
|
222
|
+
// organisationId should always be available for non-super-admins (validateContext ensures this)
|
|
223
|
+
if (!this.organisationId) {
|
|
224
|
+
throw new OrganisationContextRequiredError();
|
|
225
|
+
}
|
|
176
226
|
const contextValues = Array.isArray(values)
|
|
177
227
|
? values.map(v => ({ ...v, organisation_id: this.organisationId }))
|
|
178
228
|
: { ...values, organisation_id: this.organisationId };
|
|
179
229
|
return originalInsert(contextValues);
|
|
180
230
|
}
|
|
181
231
|
|
|
182
|
-
// For other tables,
|
|
232
|
+
// For other tables, add organisation_id if available
|
|
233
|
+
// Super-admins might not have organisationId set, so allow them to set it explicitly
|
|
234
|
+
if (this.isSuperAdmin && !this.organisationId) {
|
|
235
|
+
// Super admin without organisationId: Don't force it (can be set explicitly if needed)
|
|
236
|
+
return originalInsert(values);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Non-super-admin or super-admin with organisationId: Add organisation_id
|
|
240
|
+
// organisationId should always be available for non-super-admins (validateContext ensures this)
|
|
241
|
+
if (!this.organisationId) {
|
|
242
|
+
throw new OrganisationContextRequiredError();
|
|
243
|
+
}
|
|
183
244
|
const contextValues = Array.isArray(values)
|
|
184
245
|
? values.map(v => ({ ...v, organisation_id: this.organisationId }))
|
|
185
246
|
: { ...values, organisation_id: this.organisationId };
|
|
@@ -213,6 +274,10 @@ export class SecureSupabaseClient {
|
|
|
213
274
|
* - Super admins: No org filter (see all users) - RLS will allow access
|
|
214
275
|
* - Non-super-admins: Apply org filter as defense in depth - RLS will also filter
|
|
215
276
|
*
|
|
277
|
+
* For system-wide tables (like core_organisations):
|
|
278
|
+
* - Super admins: No org filter (see all records) - RLS will allow access
|
|
279
|
+
* - Non-super-admins: Apply org filter as defense in depth - RLS will also filter
|
|
280
|
+
*
|
|
216
281
|
* For other tables:
|
|
217
282
|
* - Always apply org filter unless super admin bypasses it
|
|
218
283
|
*/
|
|
@@ -223,6 +288,18 @@ export class SecureSupabaseClient {
|
|
|
223
288
|
'rbac_apps', // App configuration table - no organisation scope
|
|
224
289
|
'rbac_app_pages', // Page configuration table - scoped by app_id, not organisation_id
|
|
225
290
|
'rbac_global_roles', // Global roles - no organisation scope
|
|
291
|
+
// Person-scoped tables (organisation_id was removed in person-scoped profiles migration)
|
|
292
|
+
'core_person', // Person records - person-scoped, no organisation_id
|
|
293
|
+
'core_member', // Member profiles - person-scoped, no organisation_id
|
|
294
|
+
'core_contact', // Contact profiles - person-scoped, no organisation_id
|
|
295
|
+
'core_consent', // Consent records - person-scoped, no organisation_id
|
|
296
|
+
'core_identification', // Identification records - person-scoped, no organisation_id
|
|
297
|
+
'core_qualification', // Qualification records - person-scoped, no organisation_id
|
|
298
|
+
'medi_profile', // Medical profiles - person-scoped, no organisation_id
|
|
299
|
+
'medi_condition', // Medical conditions - person-scoped via medi_profile, no organisation_id
|
|
300
|
+
'medi_diet', // Medical diets - person-scoped via medi_profile, no organisation_id
|
|
301
|
+
'medi_action_plan', // Medical action plans - person-scoped via medi_profile, no organisation_id
|
|
302
|
+
'medi_profile_versions', // Medical profile versions - person-scoped via medi_profile, no organisation_id
|
|
226
303
|
];
|
|
227
304
|
|
|
228
305
|
// Skip organisation filter for tables that don't have organisation_id column
|
|
@@ -235,6 +312,17 @@ export class SecureSupabaseClient {
|
|
|
235
312
|
return query;
|
|
236
313
|
}
|
|
237
314
|
|
|
315
|
+
// System-wide tables that super-admins should be able to query without organisation filters
|
|
316
|
+
// These tables have organisation_id but super-admins need to see all records
|
|
317
|
+
const systemWideTablesForSuperAdmins = [
|
|
318
|
+
'core_organisations', // Super-admins need to see all organisations
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
// For system-wide tables, super-admins bypass organisation filter
|
|
322
|
+
if (systemWideTablesForSuperAdmins.includes(tableName) && this.isSuperAdmin) {
|
|
323
|
+
return query; // No filter - RLS handles access control
|
|
324
|
+
}
|
|
325
|
+
|
|
238
326
|
// For rbac_user_profiles, use conditional filtering based on super admin status
|
|
239
327
|
if (tableName === 'rbac_user_profiles') {
|
|
240
328
|
// Super admins: No org filter (see all users via RLS)
|
|
@@ -261,17 +349,69 @@ export class SecureSupabaseClient {
|
|
|
261
349
|
|
|
262
350
|
/**
|
|
263
351
|
* Validate that required context is present
|
|
352
|
+
* Super-admins can operate without organisation context
|
|
264
353
|
*/
|
|
265
354
|
private validateContext() {
|
|
355
|
+
// Super-admins can operate without organisation context
|
|
356
|
+
if (this.isSuperAdmin) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
266
360
|
if (!this.organisationId) {
|
|
267
361
|
throw new OrganisationContextRequiredError();
|
|
268
362
|
}
|
|
269
363
|
}
|
|
270
364
|
|
|
365
|
+
/**
|
|
366
|
+
* Determine whether a table requires organisation context.
|
|
367
|
+
* Tables without an organisation_id column (or global configuration tables) are safe without org context.
|
|
368
|
+
*/
|
|
369
|
+
private tableRequiresOrganisationContext(tableName: string): boolean {
|
|
370
|
+
// Keep this list aligned with the tables handled in `addOrganisationFilter` / `injectContext`.
|
|
371
|
+
const tablesWithoutOrganisationId = new Set<string>([
|
|
372
|
+
'core_organisations',
|
|
373
|
+
'rbac_apps',
|
|
374
|
+
'rbac_app_pages',
|
|
375
|
+
'rbac_global_roles',
|
|
376
|
+
'core_person',
|
|
377
|
+
'core_member',
|
|
378
|
+
'core_contact',
|
|
379
|
+
'core_consent',
|
|
380
|
+
'core_identification',
|
|
381
|
+
'core_qualification',
|
|
382
|
+
'medi_profile',
|
|
383
|
+
'medi_condition',
|
|
384
|
+
'medi_diet',
|
|
385
|
+
'medi_action_plan',
|
|
386
|
+
'medi_profile_versions',
|
|
387
|
+
]);
|
|
388
|
+
|
|
389
|
+
return !tablesWithoutOrganisationId.has(tableName);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Validate context for a specific table operation.
|
|
394
|
+
*/
|
|
395
|
+
private validateContextForTable(tableName: string) {
|
|
396
|
+
if (this.isSuperAdmin) return;
|
|
397
|
+
if (!this.organisationId && this.tableRequiresOrganisationContext(tableName)) {
|
|
398
|
+
throw new OrganisationContextRequiredError();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Validate context for a specific RPC call.
|
|
404
|
+
*/
|
|
405
|
+
private validateContextForRpc(fn: string) {
|
|
406
|
+
if (this.isSuperAdmin) return;
|
|
407
|
+
if (SecureSupabaseClient.GLOBAL_RPC_ALLOWLIST.has(fn)) return;
|
|
408
|
+
this.validateContext();
|
|
409
|
+
}
|
|
410
|
+
|
|
271
411
|
/**
|
|
272
412
|
* Get the current organisation ID
|
|
273
413
|
*/
|
|
274
|
-
getOrganisationId(): UUID {
|
|
414
|
+
getOrganisationId(): UUID | null {
|
|
275
415
|
return this.organisationId;
|
|
276
416
|
}
|
|
277
417
|
|
|
@@ -293,7 +433,7 @@ export class SecureSupabaseClient {
|
|
|
293
433
|
* Create a new client with updated context
|
|
294
434
|
*/
|
|
295
435
|
withContext(updates: {
|
|
296
|
-
organisationId?: UUID;
|
|
436
|
+
organisationId?: UUID | null;
|
|
297
437
|
eventId?: string;
|
|
298
438
|
appId?: UUID;
|
|
299
439
|
isSuperAdmin?: boolean;
|
|
@@ -301,7 +441,7 @@ export class SecureSupabaseClient {
|
|
|
301
441
|
return new SecureSupabaseClient(
|
|
302
442
|
this.supabaseUrl,
|
|
303
443
|
this.supabaseKey,
|
|
304
|
-
updates.organisationId
|
|
444
|
+
updates.organisationId !== undefined ? updates.organisationId : this.organisationId,
|
|
305
445
|
updates.eventId !== undefined ? updates.eventId : this.eventId,
|
|
306
446
|
updates.appId !== undefined ? updates.appId : this.appId,
|
|
307
447
|
updates.isSuperAdmin !== undefined ? updates.isSuperAdmin : this.isSuperAdmin
|
|
@@ -329,33 +469,43 @@ export class SecureSupabaseClient {
|
|
|
329
469
|
}
|
|
330
470
|
}
|
|
331
471
|
|
|
332
|
-
/**
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
472
|
+
/**
|
|
473
|
+
* Create a secure Supabase client with organisation context
|
|
474
|
+
*
|
|
475
|
+
* @param supabaseUrl - Supabase project URL
|
|
476
|
+
* @param supabaseKey - Supabase publishable key or anon key (accepts both legacy anon keys and modern publishable keys)
|
|
477
|
+
* @param organisationId - Organisation ID (optional for super-admins)
|
|
478
|
+
* @param eventId - Optional event ID
|
|
479
|
+
* @param appId - Optional app ID
|
|
480
|
+
* @param isSuperAdmin - Optional super admin flag (defaults to false). When true, organisationId can be null.
|
|
481
|
+
* @returns SecureSupabaseClient instance
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
* ```typescript
|
|
485
|
+
* const client = createSecureClient(
|
|
486
|
+
* 'https://your-project.supabase.co',
|
|
487
|
+
* 'your-publishable-key-or-anon-key',
|
|
488
|
+
* 'org-123',
|
|
489
|
+
* 'event-456',
|
|
490
|
+
* 'app-789',
|
|
491
|
+
* false // isSuperAdmin
|
|
492
|
+
* );
|
|
493
|
+
*
|
|
494
|
+
* // For super-admins, organisationId can be null
|
|
495
|
+
* const superAdminClient = createSecureClient(
|
|
496
|
+
* 'https://your-project.supabase.co',
|
|
497
|
+
* 'your-publishable-key-or-anon-key',
|
|
498
|
+
* null, // organisationId not required for super-admins
|
|
499
|
+
* undefined,
|
|
500
|
+
* undefined,
|
|
501
|
+
* true // isSuperAdmin
|
|
502
|
+
* );
|
|
503
|
+
* ```
|
|
504
|
+
*/
|
|
355
505
|
export function createSecureClient(
|
|
356
506
|
supabaseUrl: string,
|
|
357
507
|
supabaseKey: string,
|
|
358
|
-
organisationId: UUID,
|
|
508
|
+
organisationId: UUID | null,
|
|
359
509
|
eventId?: string,
|
|
360
510
|
appId?: UUID,
|
|
361
511
|
isSuperAdmin: boolean = false
|
|
@@ -374,11 +524,12 @@ export function createSecureClient(
|
|
|
374
524
|
*/
|
|
375
525
|
export function fromSupabaseClient(
|
|
376
526
|
client: SupabaseClient<Database>,
|
|
377
|
-
organisationId: UUID,
|
|
527
|
+
organisationId: UUID | null,
|
|
378
528
|
eventId?: string,
|
|
379
|
-
appId?: UUID
|
|
529
|
+
appId?: UUID,
|
|
530
|
+
isSuperAdmin: boolean = false
|
|
380
531
|
): SecureSupabaseClient {
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
|
|
532
|
+
// Wrap the existing client to reuse auth/session while enforcing organisation/event/app context.
|
|
533
|
+
// URL/key are unused in this mode.
|
|
534
|
+
return new SecureSupabaseClient('', '', organisationId, eventId, appId, isSuperAdmin, client);
|
|
384
535
|
}
|
package/src/rbac/types.ts
CHANGED
|
@@ -28,12 +28,20 @@ export type AccessLevel =
|
|
|
28
28
|
| 'admin'
|
|
29
29
|
| 'super';
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Scope defines the context for permission checks.
|
|
33
|
+
* Can include organisation, event, and/or app identifiers.
|
|
34
|
+
*/
|
|
31
35
|
export type Scope = {
|
|
32
36
|
organisationId?: UUID;
|
|
33
37
|
eventId?: string; // event_id is text/varchar
|
|
34
38
|
appId?: AppId | UUID;
|
|
35
39
|
};
|
|
36
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Permission check request parameters.
|
|
43
|
+
* Defines who (userId) is checking what permission in what context (scope).
|
|
44
|
+
*/
|
|
37
45
|
export type PermissionCheck = {
|
|
38
46
|
userId: UUID;
|
|
39
47
|
scope: Scope;
|
|
@@ -87,8 +87,8 @@ describe('parseAndNormalizeEventColours', () => {
|
|
|
87
87
|
});
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
-
describe('
|
|
91
|
-
it('
|
|
90
|
+
describe('Standard format only', () => {
|
|
91
|
+
it('returns null for legacy format with ev-main, ev-sec, ev-acc keys', () => {
|
|
92
92
|
const input = {
|
|
93
93
|
'ev-main': { 500: { L: 0.5, C: 0.2, H: 0 } },
|
|
94
94
|
'ev-sec': { 500: { L: 0.5, C: 0.2, H: 120 } },
|
|
@@ -96,13 +96,11 @@ describe('parseAndNormalizeEventColours', () => {
|
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
const result = parseAndNormalizeEventColours(input);
|
|
99
|
-
|
|
100
|
-
expect(result
|
|
101
|
-
expect(result?.sec).toBeDefined();
|
|
102
|
-
expect(result?.acc).toBeDefined();
|
|
99
|
+
// Legacy format is no longer supported - returns null
|
|
100
|
+
expect(result).toBeNull();
|
|
103
101
|
});
|
|
104
102
|
|
|
105
|
-
it('
|
|
103
|
+
it('uses standard keys when provided', () => {
|
|
106
104
|
const input = {
|
|
107
105
|
main: { 500: { L: 0.5, C: 0.2, H: 0 } },
|
|
108
106
|
'ev-main': { 500: { L: 0.7, C: 0.3, H: 10 } }
|
|
@@ -110,8 +108,7 @@ describe('parseAndNormalizeEventColours', () => {
|
|
|
110
108
|
|
|
111
109
|
const result = parseAndNormalizeEventColours(input);
|
|
112
110
|
expect(result).not.toBeNull();
|
|
113
|
-
//
|
|
114
|
-
// So when both exist, 'main' is used
|
|
111
|
+
// Only standard keys are used - legacy keys are ignored
|
|
115
112
|
expect(result?.main[500]).toEqual({ L: 0.5, C: 0.2, H: 0 });
|
|
116
113
|
});
|
|
117
114
|
});
|
|
@@ -15,9 +15,8 @@ const log = createLogger('ParseEventColours');
|
|
|
15
15
|
/**
|
|
16
16
|
* Parse and normalize event_colours to PaletteData
|
|
17
17
|
*
|
|
18
|
-
* Supports
|
|
18
|
+
* Supports input formats:
|
|
19
19
|
* - Object with 'main', 'sec', 'acc' keys
|
|
20
|
-
* - Object with 'ev-main', 'ev-sec', 'ev-acc' keys (legacy)
|
|
21
20
|
* - JSON string that will be parsed
|
|
22
21
|
*
|
|
23
22
|
* Only includes explicitly defined color values. Does not fill
|
|
@@ -39,17 +38,6 @@ const log = createLogger('ParseEventColours');
|
|
|
39
38
|
* // Returns: { main: { 500: {...}, raw: {...} }, sec: { 500: {...} }, acc: { 500: {...} } }
|
|
40
39
|
* ```
|
|
41
40
|
*
|
|
42
|
-
* @example
|
|
43
|
-
* ```ts
|
|
44
|
-
* // Legacy format with ev-* keys
|
|
45
|
-
* const colours = {
|
|
46
|
-
* "ev-main": { 500: { L: 0.5, C: 0.2, H: 0 } },
|
|
47
|
-
* "ev-sec": { 500: { L: 0.5, C: 0.2, H: 120 } },
|
|
48
|
-
* "ev-acc": { 500: { L: 0.5, C: 0.2, H: 240 } }
|
|
49
|
-
* };
|
|
50
|
-
* const palette = parseAndNormalizeEventColours(colours);
|
|
51
|
-
* // Returns normalized palette with only defined shades included
|
|
52
|
-
* ```
|
|
53
41
|
*/
|
|
54
42
|
export function parseAndNormalizeEventColours(input: unknown): { main: any; sec: any; acc: any } | null {
|
|
55
43
|
try {
|
|
@@ -67,12 +55,10 @@ export function parseAndNormalizeEventColours(input: unknown): { main: any; sec:
|
|
|
67
55
|
return null;
|
|
68
56
|
}
|
|
69
57
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const sec = pick(obj, 'sec', 'ev-sec');
|
|
75
|
-
const acc = pick(obj, 'acc', 'ev-acc');
|
|
58
|
+
// Use standard 'main'/'sec'/'acc' keys
|
|
59
|
+
const main = obj?.main || null;
|
|
60
|
+
const sec = obj?.sec || null;
|
|
61
|
+
const acc = obj?.acc || null;
|
|
76
62
|
|
|
77
63
|
// If no palette data found, return null
|
|
78
64
|
if (!main && !sec && !acc) return null;
|
|
@@ -1,33 +1,58 @@
|
|
|
1
1
|
|
|
2
2
|
/// <reference types="vitest/globals" />
|
|
3
|
+
/// <reference types="@testing-library/jest-dom" />
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// Type definitions for @testing-library/jest-dom
|
|
7
|
-
interface CustomMatchers<R = unknown> {
|
|
8
|
-
toBeInTheDocument(): R;
|
|
9
|
-
toBeVisible(): R;
|
|
10
|
-
toBeDisabled(): R;
|
|
11
|
-
toHaveAttribute(attr: string, value?: string): R;
|
|
12
|
-
toHaveClass(...classNames: string[]): R;
|
|
13
|
-
toHaveValue(value: string | string[] | number): R;
|
|
14
|
-
toHaveTextContent(text: string | RegExp, options?: { normalizeWhitespace: boolean }): R;
|
|
15
|
-
toBeChecked(): R;
|
|
16
|
-
toBeEmpty(): R;
|
|
17
|
-
toBeInvalid(): R;
|
|
18
|
-
toBeRequired(): R;
|
|
19
|
-
toBeValid(): R;
|
|
20
|
-
toContainElement(element: HTMLElement | null): R;
|
|
21
|
-
toContainHTML(htmlText: string): R;
|
|
22
|
-
toHaveFocus(): R;
|
|
23
|
-
toHaveFormValues(expectedValues: Record<string, unknown>): R;
|
|
24
|
-
toHaveStyle(css: string | Record<string, unknown>): R;
|
|
25
|
-
toBeEnabled(): R;
|
|
26
|
-
}
|
|
27
|
-
|
|
5
|
+
// Type definitions for @testing-library/jest-dom matchers
|
|
6
|
+
// These extend Vitest's Assertion interface with jest-dom matchers
|
|
28
7
|
declare module 'vitest' {
|
|
29
|
-
interface Assertion<T = unknown>
|
|
30
|
-
|
|
8
|
+
interface Assertion<T = unknown> {
|
|
9
|
+
// DOM matchers
|
|
10
|
+
toBeInTheDocument(): void;
|
|
11
|
+
toBeVisible(): void;
|
|
12
|
+
toBeDisabled(): void;
|
|
13
|
+
toBeEnabled(): void;
|
|
14
|
+
toBeEmpty(): void;
|
|
15
|
+
toBeEmptyDOMElement(): void;
|
|
16
|
+
toBeInvalid(): void;
|
|
17
|
+
toBeValid(): void;
|
|
18
|
+
toBeRequired(): void;
|
|
19
|
+
toBeChecked(): void;
|
|
20
|
+
toHaveFocus(): void;
|
|
21
|
+
|
|
22
|
+
// Attribute and class matchers
|
|
23
|
+
toHaveAttribute(attr: string, value?: string): void;
|
|
24
|
+
toHaveClass(...classNames: string[]): void;
|
|
25
|
+
toHaveStyle(css: string | Record<string, unknown>): void;
|
|
26
|
+
|
|
27
|
+
// Content matchers
|
|
28
|
+
toHaveTextContent(text: string | RegExp, options?: { normalizeWhitespace?: boolean }): void;
|
|
29
|
+
toHaveValue(value: string | string[] | number): void;
|
|
30
|
+
toContainElement(element: HTMLElement | null): void;
|
|
31
|
+
toContainHTML(htmlText: string): void;
|
|
32
|
+
toHaveFormValues(expectedValues: Record<string, unknown>): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface AsymmetricMatchersContaining {
|
|
36
|
+
toBeInTheDocument(): void;
|
|
37
|
+
toBeVisible(): void;
|
|
38
|
+
toBeDisabled(): void;
|
|
39
|
+
toBeEnabled(): void;
|
|
40
|
+
toBeEmpty(): void;
|
|
41
|
+
toBeEmptyDOMElement(): void;
|
|
42
|
+
toBeInvalid(): void;
|
|
43
|
+
toBeValid(): void;
|
|
44
|
+
toBeRequired(): void;
|
|
45
|
+
toBeChecked(): void;
|
|
46
|
+
toHaveFocus(): void;
|
|
47
|
+
toHaveAttribute(attr: string, value?: string): void;
|
|
48
|
+
toHaveClass(...classNames: string[]): void;
|
|
49
|
+
toHaveStyle(css: string | Record<string, unknown>): void;
|
|
50
|
+
toHaveTextContent(text: string | RegExp, options?: { normalizeWhitespace?: boolean }): void;
|
|
51
|
+
toHaveValue(value: string | string[] | number): void;
|
|
52
|
+
toContainElement(element: HTMLElement | null): void;
|
|
53
|
+
toContainHTML(htmlText: string): void;
|
|
54
|
+
toHaveFormValues(expectedValues: Record<string, unknown>): void;
|
|
55
|
+
}
|
|
31
56
|
}
|
|
32
57
|
|
|
33
58
|
// Ensure vitest globals are available
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/// <reference types="vitest/globals" />
|
|
2
2
|
/**
|
|
3
3
|
* @file Supabase mock for testing
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { vi } from 'vitest';
|
|
7
|
-
|
|
8
6
|
export function createMockSupabaseClient(overrides: Record<string, unknown> = {}) {
|
|
9
7
|
const mockOrder = vi.fn().mockResolvedValue({
|
|
10
8
|
data: [
|
|
@@ -35,7 +35,7 @@ describe('formatting utilities', () => {
|
|
|
35
35
|
expect(formatPercent(0.5)).toBe('0.5%');
|
|
36
36
|
});
|
|
37
37
|
it('formats as percent with custom decimals', () => {
|
|
38
|
-
expect(formatPercent(0.25, 'en-US', 2)).toBe('0.25%');
|
|
38
|
+
expect(formatPercent(0.25, 'en-US', { decimals: 2 })).toBe('0.25%');
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
// Tests for preserveDecimals functionality
|
|
@@ -65,9 +65,9 @@ describe('formatting utilities', () => {
|
|
|
65
65
|
expect(formatPercent(0.81, 'en-US', { decimals: 3, preserveDecimals: false })).toBe('0.810%');
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
it('
|
|
69
|
-
expect(formatPercent(0.81, 'en-US', 1)).toBe('0.8%');
|
|
70
|
-
expect(formatPercent(0.25, 'en-US', 2)).toBe('0.25%');
|
|
68
|
+
it('works with explicit decimals option', () => {
|
|
69
|
+
expect(formatPercent(0.81, 'en-US', { decimals: 1 })).toBe('0.8%');
|
|
70
|
+
expect(formatPercent(0.25, 'en-US', { decimals: 2 })).toBe('0.25%');
|
|
71
71
|
});
|
|
72
72
|
});
|
|
73
73
|
});
|
|
@@ -137,7 +137,7 @@ describe('utils index exports', () => {
|
|
|
137
137
|
|
|
138
138
|
describe('Audit utilities', () => {
|
|
139
139
|
it('should export audit functions', () => {
|
|
140
|
-
expect(Utils).toHaveProperty('
|
|
140
|
+
expect(Utils).toHaveProperty('auditLogger');
|
|
141
141
|
expect(Utils).toHaveProperty('logAuditEvent');
|
|
142
142
|
});
|
|
143
143
|
});
|
|
@@ -237,7 +237,7 @@ describe('utils index exports', () => {
|
|
|
237
237
|
'pickSchema', // Schema utilities
|
|
238
238
|
'securityMonitor', // Security monitoring
|
|
239
239
|
'useSessionTracking', // Session tracking
|
|
240
|
-
'
|
|
240
|
+
'auditLogger', // Audit
|
|
241
241
|
'generateDeviceFingerprint', // Device fingerprinting
|
|
242
242
|
'formatDate', // Formatting
|
|
243
243
|
'setOrganisationContext' // Organisation context
|
package/src/utils/audit/audit.ts
CHANGED
|
@@ -84,9 +84,6 @@ class AuditLogger {
|
|
|
84
84
|
|
|
85
85
|
export const auditLogger = new AuditLogger();
|
|
86
86
|
|
|
87
|
-
// Alias for backward compatibility
|
|
88
|
-
export const auditLog = auditLogger;
|
|
89
|
-
|
|
90
87
|
export function logAuthEvent(action: string, user?: string, details?: Record<string, unknown>): void {
|
|
91
88
|
auditLogger.log({
|
|
92
89
|
type: 'auth',
|