@jmruthers/pace-core 0.5.191 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +372 -0
- package/cursor-rules/01-standards-compliance.mdc +275 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +341 -0
- package/cursor-rules/04-testing-standards.mdc +315 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +392 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
- package/cursor-rules/CHANGELOG.md +101 -0
- package/cursor-rules/README.md +191 -0
- package/dist/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
- package/dist/{DataTable-WKRZD47S.js → DataTable-DQ7RSOHE.js} +8 -7
- package/dist/{PublicPageProvider-ULXC_u6U.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +37 -156
- package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
- package/dist/{UnifiedAuthProvider-FTSG5XH7.js → UnifiedAuthProvider-ATAP5UTR.js} +3 -3
- package/dist/{api-IHKALJZD.js → api-N774RPUA.js} +2 -2
- package/dist/{chunk-6C4YBBJM.js → chunk-3QRJFVBR.js} +1 -1
- package/dist/chunk-3QRJFVBR.js.map +1 -0
- package/dist/{chunk-OETXORNB.js → chunk-3XTALGJF.js} +211 -136
- package/dist/chunk-3XTALGJF.js.map +1 -0
- package/dist/{chunk-6TQDD426.js → chunk-4N5C5XZU.js} +47 -228
- package/dist/chunk-4N5C5XZU.js.map +1 -0
- package/dist/{chunk-LOMZXPSN.js → chunk-4ZC4GX36.js} +47 -74
- package/dist/chunk-4ZC4GX36.js.map +1 -0
- package/dist/{chunk-6LTQQAT6.js → chunk-BYFSK72L.js} +357 -158
- package/dist/chunk-BYFSK72L.js.map +1 -0
- package/dist/{chunk-XYXSXPUK.js → chunk-EXUD6RNJ.js} +50 -10
- package/dist/chunk-EXUD6RNJ.js.map +1 -0
- package/dist/{chunk-VKB2CO4Z.js → chunk-GLK6VM3F.js} +244 -249
- package/dist/chunk-GLK6VM3F.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-XNYQOL3Z.js → chunk-JBKQ3SAO.js} +9 -18
- package/dist/chunk-JBKQ3SAO.js.map +1 -0
- package/dist/{chunk-ROXMHMY2.js → chunk-KNC55RTG.js} +13 -3
- package/dist/{chunk-ROXMHMY2.js.map → chunk-KNC55RTG.js.map} +1 -1
- package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
- package/dist/{chunk-ULHIJK66.js → chunk-T33XF5ZC.js} +255 -140
- package/dist/chunk-T33XF5ZC.js.map +1 -0
- package/dist/{chunk-VRGWKHDB.js → chunk-XM25TVIE.js} +100 -33
- package/dist/chunk-XM25TVIE.js.map +1 -0
- package/dist/components.d.ts +4 -4
- package/dist/components.js +9 -9
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +20 -25
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +18 -21
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +2 -20
- package/dist/rbac/index.js +7 -9
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -3
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +2 -2
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +10 -10
- 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 +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 +1 -1
- 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 +1 -1
- 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 +24 -11
- 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 +1 -1
- 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 +2 -2
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- 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 +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 +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
- 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 +4 -4
- package/docs/api/interfaces/ProgressProps.md +1 -1
- 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 +2 -2
- 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 +2 -2
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +2 -2
- 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 +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- 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 +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 +60 -38
- 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 +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +194 -209
- package/docs/getting-started/cursor-rules.md +262 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/MIGRATION_GUIDE.md +4 -4
- package/docs/migration/REACT_19_MIGRATION.md +227 -0
- package/docs/migration/database-changes-december-2025.md +2 -1
- package/docs/rbac/event-based-apps.md +124 -6
- package/docs/standards/README.md +39 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +11 -6
- package/scripts/audit-consuming-app.cjs +961 -0
- package/scripts/check-pace-core-compliance.cjs +315 -61
- package/scripts/install-cursor-rules.cjs +236 -0
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/__tests__/rls-policies.test.ts +3 -1
- package/src/components/Badge/Badge.tsx +2 -4
- package/src/components/Button/Button.tsx +5 -4
- package/src/components/Calendar/Calendar.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
- package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +88 -16
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/DataTableCore.tsx +4 -7
- package/src/components/DataTable/components/DataTableModals.tsx +1 -1
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +86 -17
- 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/hooks/useColumnReordering.ts +2 -2
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +6 -5
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
- package/src/components/EventSelector/EventSelector.tsx +1 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -3
- package/src/components/FileDisplay/FileDisplay.tsx +16 -4
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +30 -26
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.tsx +28 -30
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +8 -8
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -11
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -1
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +75 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +98 -69
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -8
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
- package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
- package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
- package/src/components/Select/Select.tsx +33 -22
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/UserMenu/UserMenu.tsx +1 -1
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +14 -7
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/public/usePublicEvent.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +1 -1
- package/src/hooks/public/usePublicRouteParams.ts +1 -1
- package/src/hooks/services/useAuthService.ts +21 -3
- package/src/hooks/services/useEventService.ts +21 -3
- package/src/hooks/services/useInactivityService.ts +21 -3
- package/src/hooks/services/useOrganisationService.ts +21 -3
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useFileDisplay.ts +10 -17
- package/src/hooks/useFocusManagement.ts +2 -2
- package/src/hooks/useFocusTrap.ts +4 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +1 -1
- package/src/hooks/usePermissionCache.ts +1 -1
- package/src/hooks/useSecureDataAccess.test.ts +16 -9
- package/src/hooks/useSecureDataAccess.ts +22 -6
- package/src/hooks/useToast.ts +2 -2
- package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/EventServiceProvider.tsx +0 -8
- package/src/providers/services/UnifiedAuthProvider.tsx +196 -46
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +34 -40
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
- package/src/rbac/adapters.tsx +3 -22
- package/src/rbac/api.test.ts +2 -2
- package/src/rbac/api.ts +7 -1
- package/src/rbac/components/EnhancedNavigationMenu.tsx +3 -16
- package/src/rbac/components/NavigationGuard.tsx +2 -11
- package/src/rbac/components/NavigationProvider.tsx +1 -2
- package/src/rbac/components/PagePermissionGuard.tsx +1 -1
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +46 -13
- package/src/rbac/components/RoleBasedRouter.tsx +1 -1
- package/src/rbac/components/SecureDataProvider.tsx +1 -2
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
- package/src/rbac/engine.ts +14 -2
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/usePermissions.ts +51 -11
- package/src/rbac/hooks/useRBAC.ts +3 -13
- package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
- package/src/rbac/hooks/useResolvedScope.ts +58 -33
- package/src/rbac/hooks/useSecureSupabase.ts +4 -9
- package/src/rbac/secureClient.ts +43 -0
- package/src/services/EventService.ts +4 -57
- package/src/services/InactivityService.ts +127 -34
- package/src/services/OrganisationService.ts +68 -10
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-6LTQQAT6.js.map +0 -1
- package/dist/chunk-6TQDD426.js.map +0 -1
- package/dist/chunk-LOMZXPSN.js.map +0 -1
- package/dist/chunk-OETXORNB.js.map +0 -1
- package/dist/chunk-ULHIJK66.js.map +0 -1
- package/dist/chunk-VKB2CO4Z.js.map +0 -1
- package/dist/chunk-VRGWKHDB.js.map +0 -1
- package/dist/chunk-XNYQOL3Z.js.map +0 -1
- package/dist/chunk-XYXSXPUK.js.map +0 -1
- package/scripts/check-pace-core-compliance.js +0 -512
- package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
- package/src/utils/context/superAdminOverride.ts +0 -58
- /package/dist/{DataTable-WKRZD47S.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-FTSG5XH7.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
- /package/dist/{api-IHKALJZD.js.map → api-N774RPUA.js.map} +0 -0
- /package/dist/{chunk-QWWZ5CAQ.js.map → chunk-LXQLPRQ2.js.map} +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
|
@@ -378,12 +378,20 @@ function scanFile(filePath, manifest) {
|
|
|
378
378
|
providerSetupIssues: [],
|
|
379
379
|
viteConfigIssues: [],
|
|
380
380
|
routerSetupIssues: [],
|
|
381
|
-
unnecessaryWrappers: []
|
|
381
|
+
unnecessaryWrappers: [],
|
|
382
|
+
appDiscoveryIssues: []
|
|
382
383
|
};
|
|
383
384
|
|
|
384
385
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
385
386
|
const relativePath = path.relative(process.cwd(), filePath);
|
|
386
387
|
|
|
388
|
+
// Normalize path for cross-platform compatibility (handle both forward and backslash paths)
|
|
389
|
+
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
390
|
+
|
|
391
|
+
// Skip Edge Functions - they run in Deno, not React, so React hooks aren't available
|
|
392
|
+
// Direct Supabase auth calls are the correct approach in Edge Functions
|
|
393
|
+
const isEdgeFunction = normalizedPath.includes('supabase/functions/');
|
|
394
|
+
|
|
387
395
|
// Check for restricted imports
|
|
388
396
|
manifest.restrictedImports.forEach(({ module, reason }) => {
|
|
389
397
|
const importPattern = new RegExp(`from\\s+['"]${module.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"]`, 'g');
|
|
@@ -513,9 +521,11 @@ function scanFile(filePath, manifest) {
|
|
|
513
521
|
/from\s+['"]@jmruthers\/pace-core\/providers/.test(content);
|
|
514
522
|
|
|
515
523
|
authRbacPatterns.forEach(({ pattern, name, type, replacement }) => {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
524
|
+
// Create a new regex instance to avoid state issues
|
|
525
|
+
const testPattern = new RegExp(pattern.source, pattern.flags);
|
|
526
|
+
if (testPattern.test(content)) {
|
|
527
|
+
// For custom RBAC hooks, check if they're actually using pace-core APIs
|
|
528
|
+
// If they use pace-core RBAC APIs (useRoleManagement, useSecureSupabase, etc.), they're compliant
|
|
519
529
|
const isCustomRBACHook = [
|
|
520
530
|
'useUserRoles',
|
|
521
531
|
'useUserOrganisationRoles',
|
|
@@ -526,6 +536,20 @@ function scanFile(filePath, manifest) {
|
|
|
526
536
|
'useRole'
|
|
527
537
|
].includes(name);
|
|
528
538
|
|
|
539
|
+
// Check if the hook uses pace-core RBAC APIs (use a fresh regex to avoid state issues)
|
|
540
|
+
const rbacApiPattern = /useRoleManagement|useSecureSupabase|useSecureDataAccess|useRBAC|usePermissions|useCan|rbac_role_grant|rbac_role_revoke|rbac_roles_list|data_rbac_apps_list/;
|
|
541
|
+
const usesPaceCoreRBAC = isCustomRBACHook && rbacApiPattern.test(content);
|
|
542
|
+
|
|
543
|
+
// Check for @pace-core-compliant comment (use a fresh regex)
|
|
544
|
+
const compliancePattern = /@pace-core-compliant|pace-core-compliant/i;
|
|
545
|
+
const hasComplianceComment = compliancePattern.test(content);
|
|
546
|
+
|
|
547
|
+
// If it's a custom RBAC hook but uses pace-core APIs or has compliance comment, skip it
|
|
548
|
+
if (isCustomRBACHook && (usesPaceCoreRBAC || hasComplianceComment)) {
|
|
549
|
+
// This hook is compliant - it uses pace-core APIs
|
|
550
|
+
return; // Skip to next pattern
|
|
551
|
+
}
|
|
552
|
+
|
|
529
553
|
// Flag custom RBAC hooks even if they import from pace-core (they're still duplicating functionality)
|
|
530
554
|
// For other hooks, only flag if they don't import from pace-core
|
|
531
555
|
if (isCustomRBACHook || !hasPaceCoreImport) {
|
|
@@ -640,7 +664,26 @@ function scanFile(filePath, manifest) {
|
|
|
640
664
|
// This includes all variations: supabase.auth.getUser(), client.auth.getUser(), etc.
|
|
641
665
|
// Priority patterns - these are the most common violations
|
|
642
666
|
// Using multiple patterns to catch all variations
|
|
643
|
-
|
|
667
|
+
|
|
668
|
+
// Skip Edge Functions - they run in Deno, not React, so React hooks are not available
|
|
669
|
+
// Edge Functions MUST use direct supabase.auth.getUser() calls - this is correct and required
|
|
670
|
+
// isEdgeFunction is already defined above
|
|
671
|
+
|
|
672
|
+
// Other auth patterns - defined here so it's accessible in all scopes
|
|
673
|
+
const otherAuthPatterns = [
|
|
674
|
+
{ pattern: /\.auth\.signIn\s*\(/g, method: 'signIn' },
|
|
675
|
+
{ pattern: /\.auth\.signUp\s*\(/g, method: 'signUp' },
|
|
676
|
+
{ pattern: /\.auth\.signOut\s*\(/g, method: 'signOut' },
|
|
677
|
+
{ pattern: /\.auth\.onAuthStateChange\s*\(/g, method: 'onAuthStateChange' }
|
|
678
|
+
];
|
|
679
|
+
|
|
680
|
+
// Skip all auth checks for Edge Functions - they cannot use React hooks
|
|
681
|
+
if (isEdgeFunction) {
|
|
682
|
+
// Edge Functions use service role client or direct auth calls - correct pattern
|
|
683
|
+
// Don't flag these as violations
|
|
684
|
+
} else {
|
|
685
|
+
// Only check for direct auth usage in client-side code (React components, hooks, etc.)
|
|
686
|
+
const priorityAuthPatterns = [
|
|
644
687
|
// Most specific patterns first
|
|
645
688
|
{ pattern: /supabase\.auth\.getUser\s*\(/g, method: 'getUser', specific: true },
|
|
646
689
|
{ pattern: /supabase\.auth\.getSession\s*\(/g, method: 'getSession', specific: true },
|
|
@@ -654,59 +697,51 @@ function scanFile(filePath, manifest) {
|
|
|
654
697
|
{ pattern: /\w+\.auth\.getSession\s*\(/g, method: 'getSession', specific: false }
|
|
655
698
|
];
|
|
656
699
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
700
|
+
// Check if file actually uses useUnifiedAuth hook (not just imports it)
|
|
701
|
+
const usesUnifiedAuthHook = /useUnifiedAuth\s*\(/.test(content);
|
|
702
|
+
const hasUnifiedAuthImport = /UnifiedAuthProvider/.test(content) ||
|
|
703
|
+
/useUnifiedAuth/.test(content) ||
|
|
704
|
+
/from\s+['"]@jmruthers\/pace-core\/providers/.test(content);
|
|
705
|
+
|
|
706
|
+
// Check for usage of useCurrentUser hook (even if imported from local file)
|
|
707
|
+
// This catches both local imports and direct usage
|
|
708
|
+
const useCurrentUserImportPattern = /import\s+.*useCurrentUser.*from\s+['"][^'"]*['"]/g;
|
|
709
|
+
const useCurrentUserUsagePattern = /useCurrentUser\s*\(/g;
|
|
710
|
+
|
|
711
|
+
// Check for local import (not from pace-core)
|
|
712
|
+
const useCurrentUserImportMatch = content.match(useCurrentUserImportPattern);
|
|
713
|
+
if (useCurrentUserImportMatch) {
|
|
714
|
+
const isFromPaceCore = useCurrentUserImportMatch.some(match => match.includes('@jmruthers/pace-core'));
|
|
715
|
+
if (!isFromPaceCore) {
|
|
716
|
+
useCurrentUserImportMatch.forEach(match => {
|
|
717
|
+
violations.customAuthCode.push({
|
|
718
|
+
name: 'useCurrentUser import',
|
|
719
|
+
type: 'hook import',
|
|
720
|
+
file: relativePath,
|
|
721
|
+
line: getLineNumber(content, match),
|
|
722
|
+
reason: 'useCurrentUser imported from local file. Replace with useUnifiedAuth from pace-core.',
|
|
723
|
+
replacement: 'useUnifiedAuth from @jmruthers/pace-core'
|
|
724
|
+
});
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Check for usage (even if imported)
|
|
730
|
+
const useCurrentUserUsageMatches = content.match(useCurrentUserUsagePattern);
|
|
731
|
+
if (useCurrentUserUsageMatches && !usesUnifiedAuthHook) {
|
|
732
|
+
useCurrentUserUsageMatches.forEach(match => {
|
|
682
733
|
violations.customAuthCode.push({
|
|
683
|
-
name: 'useCurrentUser
|
|
684
|
-
type: 'hook
|
|
734
|
+
name: 'useCurrentUser',
|
|
735
|
+
type: 'hook usage',
|
|
685
736
|
file: relativePath,
|
|
686
737
|
line: getLineNumber(content, match),
|
|
687
|
-
reason: 'useCurrentUser
|
|
688
|
-
replacement: 'useUnifiedAuth
|
|
738
|
+
reason: 'useCurrentUser hook usage detected. Replace with useUnifiedAuth from pace-core.',
|
|
739
|
+
replacement: 'useUnifiedAuth'
|
|
689
740
|
});
|
|
690
741
|
});
|
|
691
742
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
// Check for usage (even if imported)
|
|
695
|
-
const useCurrentUserUsageMatches = content.match(useCurrentUserUsagePattern);
|
|
696
|
-
if (useCurrentUserUsageMatches && !usesUnifiedAuthHook) {
|
|
697
|
-
useCurrentUserUsageMatches.forEach(match => {
|
|
698
|
-
violations.customAuthCode.push({
|
|
699
|
-
name: 'useCurrentUser',
|
|
700
|
-
type: 'hook usage',
|
|
701
|
-
file: relativePath,
|
|
702
|
-
line: getLineNumber(content, match),
|
|
703
|
-
reason: 'useCurrentUser hook usage detected. Replace with useUnifiedAuth from pace-core.',
|
|
704
|
-
replacement: 'useUnifiedAuth'
|
|
705
|
-
});
|
|
706
|
-
});
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// Check priority patterns first (getUser, getSession) - these should always be flagged
|
|
743
|
+
|
|
744
|
+
// Check priority patterns first (getUser, getSession) - these should always be flagged
|
|
710
745
|
// Use exec in a loop to get all matches with their correct positions
|
|
711
746
|
priorityAuthPatterns.forEach(({ pattern, method, specific }) => {
|
|
712
747
|
// Reset regex for each pattern
|
|
@@ -730,7 +765,8 @@ function scanFile(filePath, manifest) {
|
|
|
730
765
|
const isInLineComment = /\/\/[^\n]*$/.test(lineUpToMatch);
|
|
731
766
|
|
|
732
767
|
// Only skip if clearly in a comment - be conservative
|
|
733
|
-
|
|
768
|
+
// Also skip Edge Functions - they run in Deno, not React, so React hooks aren't available
|
|
769
|
+
if (!isInLineComment && !isEdgeFunction) {
|
|
734
770
|
violations.directSupabaseAuth.push({
|
|
735
771
|
file: relativePath,
|
|
736
772
|
line: getLineNumber(content, matchText),
|
|
@@ -745,8 +781,11 @@ function scanFile(filePath, manifest) {
|
|
|
745
781
|
}
|
|
746
782
|
}
|
|
747
783
|
});
|
|
784
|
+
} // End of else block for non-Edge Functions
|
|
748
785
|
|
|
749
786
|
// Additional simple pattern check as fallback - look for literal strings
|
|
787
|
+
// Skip for Edge Functions
|
|
788
|
+
if (!isEdgeFunction) {
|
|
750
789
|
// This catches cases where the regex might miss due to formatting
|
|
751
790
|
const simpleAuthPatterns = [
|
|
752
791
|
{ search: 'supabase.auth.getUser(', method: 'getUser' },
|
|
@@ -761,7 +800,8 @@ function scanFile(filePath, manifest) {
|
|
|
761
800
|
const lineUpToMatch = content.substring(lineStart, searchIndex);
|
|
762
801
|
const isInLineComment = /\/\/[^\n]*$/.test(lineUpToMatch);
|
|
763
802
|
|
|
764
|
-
|
|
803
|
+
// Skip Edge Functions - they run in Deno, not React, so React hooks aren't available
|
|
804
|
+
if (!isInLineComment && !isEdgeFunction) {
|
|
765
805
|
// Calculate line number from index
|
|
766
806
|
const lineNum = content.substring(0, searchIndex).split('\n').length;
|
|
767
807
|
|
|
@@ -787,8 +827,11 @@ function scanFile(filePath, manifest) {
|
|
|
787
827
|
searchIndex += search.length; // Move past this match
|
|
788
828
|
}
|
|
789
829
|
});
|
|
830
|
+
} // End of Edge Function check for simple patterns
|
|
790
831
|
|
|
791
832
|
// Check other auth patterns - flag if not using useUnifiedAuth
|
|
833
|
+
// Skip for Edge Functions
|
|
834
|
+
if (!isEdgeFunction) {
|
|
792
835
|
otherAuthPatterns.forEach(({ pattern, method }) => {
|
|
793
836
|
let match;
|
|
794
837
|
const regex = new RegExp(pattern.source, pattern.flags);
|
|
@@ -819,7 +862,8 @@ function scanFile(filePath, manifest) {
|
|
|
819
862
|
(doubleQuotes % 2 === 1 && beforeMatch.endsWith('"')) ||
|
|
820
863
|
(backticks % 2 === 1 && beforeMatch.endsWith('`'));
|
|
821
864
|
|
|
822
|
-
|
|
865
|
+
// Skip Edge Functions - they run in Deno, not React, so React hooks aren't available
|
|
866
|
+
if (!isInLineComment && !isInBlockComment && !isInString && !usesUnifiedAuthHook && !isEdgeFunction) {
|
|
823
867
|
violations.directSupabaseAuth.push({
|
|
824
868
|
file: relativePath,
|
|
825
869
|
line: getLineNumber(content, matchText),
|
|
@@ -829,6 +873,7 @@ function scanFile(filePath, manifest) {
|
|
|
829
873
|
}
|
|
830
874
|
}
|
|
831
875
|
});
|
|
876
|
+
} // End of Edge Function check for other auth patterns
|
|
832
877
|
|
|
833
878
|
// Check for direct RBAC table queries (should use pace-core RBAC APIs/RPC functions)
|
|
834
879
|
// List of all RBAC tables with specific recommendations
|
|
@@ -1143,6 +1188,16 @@ function scanFile(filePath, manifest) {
|
|
|
1143
1188
|
const matchIndex = match.index;
|
|
1144
1189
|
const matchText = match[0];
|
|
1145
1190
|
|
|
1191
|
+
// Check if this is an edge function (supabase/functions directory) - check early to skip
|
|
1192
|
+
// Handle both forward and backslash paths (Windows vs Unix)
|
|
1193
|
+
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
1194
|
+
const isEdgeFunction = normalizedPath.includes('supabase/functions/');
|
|
1195
|
+
|
|
1196
|
+
// Skip edge functions - they use service role client which is correct for server-side operations
|
|
1197
|
+
if (isEdgeFunction) {
|
|
1198
|
+
continue; // Edge functions use service role client - correct pattern
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1146
1201
|
// Extract table name
|
|
1147
1202
|
const afterMatch = content.substring(matchIndex, Math.min(content.length, matchIndex + 100));
|
|
1148
1203
|
const tableMatch = afterMatch.match(/['"]rbac_([^'"]+)['"]/);
|
|
@@ -1155,7 +1210,8 @@ function scanFile(filePath, manifest) {
|
|
|
1155
1210
|
|
|
1156
1211
|
// Extract variable name if pattern matches variable.from() (handle newlines)
|
|
1157
1212
|
let variableName = null;
|
|
1158
|
-
|
|
1213
|
+
// Use wider context to find function signatures (up to 500 chars before)
|
|
1214
|
+
const beforeMatch = content.substring(Math.max(0, matchIndex - 500), matchIndex);
|
|
1159
1215
|
// Find the last word/identifier before .from (same logic as main pattern)
|
|
1160
1216
|
const parts = beforeMatch.split('.from');
|
|
1161
1217
|
if (parts.length > 0) {
|
|
@@ -1169,9 +1225,28 @@ function scanFile(filePath, manifest) {
|
|
|
1169
1225
|
// Check if using secure variable (check both set and direct pattern match)
|
|
1170
1226
|
// Escape special regex characters in variable name and use multiline flag to handle newlines
|
|
1171
1227
|
const escapedVarName = variableName ? variableName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') : '';
|
|
1172
|
-
|
|
1228
|
+
// Check if variable is declared with useSecureSupabase or useSecureDataAccess
|
|
1229
|
+
const isDeclaredSecure = (variableName && secureVariables.has(variableName)) ||
|
|
1173
1230
|
(variableName && new RegExp(`(const|let)\\s+${escapedVarName}\\s*=\\s*useSecureSupabase\\s*\\(`, 'm').test(content)) ||
|
|
1174
1231
|
(variableName && new RegExp(`(const|let)\\s+${escapedVarName}\\s*=\\s*useSecureDataAccess\\s*\\(`, 'm').test(content));
|
|
1232
|
+
// Check if variable is passed as parameter with useSecureSupabase type annotation
|
|
1233
|
+
// Look for the parameter in function signatures (check both beforeMatch and full content)
|
|
1234
|
+
const isParameterSecure = variableName && (
|
|
1235
|
+
// Check in beforeMatch (500 chars before)
|
|
1236
|
+
new RegExp(`\\b${escapedVarName}\\s*:\\s*.*useSecureSupabase`, 'm').test(beforeMatch) ||
|
|
1237
|
+
new RegExp(`\\b${escapedVarName}\\s*:\\s*.*ReturnType`, 'm').test(beforeMatch) ||
|
|
1238
|
+
new RegExp(`\\b${escapedVarName}\\s*:\\s*.*secureSupabase`, 'i').test(beforeMatch) ||
|
|
1239
|
+
// Also check the full function signature area (wider context in full content)
|
|
1240
|
+
new RegExp(`(function|=>|async\\s+function)[^(]*\\([^)]*\\b${escapedVarName}\\s*:\\s*.*(useSecureSupabase|ReturnType|secureSupabase)`, 'm').test(content) ||
|
|
1241
|
+
// Check for ReturnType<typeof import pattern (common in TypeScript)
|
|
1242
|
+
new RegExp(`\\b${escapedVarName}\\s*:\\s*.*ReturnType.*useSecureSupabase`, 'm').test(content)
|
|
1243
|
+
);
|
|
1244
|
+
// Check for comments indicating secureSupabase usage
|
|
1245
|
+
const hasSecureComment = variableName && (
|
|
1246
|
+
new RegExp(`secureSupabase|useSecureSupabase`, 'i').test(beforeMatch) ||
|
|
1247
|
+
new RegExp(`COMPLIANCE.*secureSupabase|pace-core.*secureSupabase`, 'i').test(beforeMatch)
|
|
1248
|
+
);
|
|
1249
|
+
const isUsingSecureVariable = isDeclaredSecure || isParameterSecure || hasSecureComment;
|
|
1175
1250
|
|
|
1176
1251
|
// Skip if we already reported this specific table
|
|
1177
1252
|
const alreadyReported = violations.customAuthCode.some(v =>
|
|
@@ -1776,6 +1851,119 @@ function scanFile(filePath, manifest) {
|
|
|
1776
1851
|
violations.unnecessaryWrappers.push(...wrapperIssues);
|
|
1777
1852
|
}
|
|
1778
1853
|
|
|
1854
|
+
// ============================================
|
|
1855
|
+
// App Discovery Compliance Checks
|
|
1856
|
+
// ============================================
|
|
1857
|
+
// Check for direct queries to rbac_apps table or hardcoded app names
|
|
1858
|
+
// Should use data_rbac_apps_list RPC function instead
|
|
1859
|
+
|
|
1860
|
+
// Check for direct queries to rbac_apps table
|
|
1861
|
+
const rbacAppsQueryPatterns = [
|
|
1862
|
+
// Supabase client queries
|
|
1863
|
+
/\.from\s*\(\s*['"]rbac_apps['"]\s*\)/g,
|
|
1864
|
+
/\.from\s*\(\s*['"]rbac_apps['"]\s*\)/g,
|
|
1865
|
+
// SQL queries (less common but possible)
|
|
1866
|
+
/FROM\s+rbac_apps\b/gi,
|
|
1867
|
+
/SELECT\s+.*\s+FROM\s+rbac_apps\b/gi
|
|
1868
|
+
];
|
|
1869
|
+
|
|
1870
|
+
// Check if file uses data_rbac_apps_list RPC function
|
|
1871
|
+
const usesRpcFunction = /data_rbac_apps_list|rpc\s*\(\s*['"]data_rbac_apps_list['"]/gi.test(content);
|
|
1872
|
+
|
|
1873
|
+
rbacAppsQueryPatterns.forEach(pattern => {
|
|
1874
|
+
let match;
|
|
1875
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
1876
|
+
// Skip if in a line comment
|
|
1877
|
+
const lineStart = content.lastIndexOf('\n', match.index) + 1;
|
|
1878
|
+
const lineUpToMatch = content.substring(lineStart, match.index);
|
|
1879
|
+
const isInLineComment = /\/\/[^\n]*$/.test(lineUpToMatch);
|
|
1880
|
+
|
|
1881
|
+
if (isInLineComment) {
|
|
1882
|
+
continue;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
// Check what comes after .from('rbac_apps') to determine if it's a SELECT query or CRUD operation
|
|
1886
|
+
const afterMatch = content.substring(match.index, Math.min(content.length, match.index + 200));
|
|
1887
|
+
const isSelectQuery = /\.(select|selectAll)\s*\(/i.test(afterMatch);
|
|
1888
|
+
const isCrudOperation = /\.(update|insert|delete|upsert)\s*\(/i.test(afterMatch);
|
|
1889
|
+
|
|
1890
|
+
// Only flag SELECT queries - UPDATE/INSERT/DELETE operations are acceptable with secureSupabase
|
|
1891
|
+
if (isSelectQuery && !isCrudOperation) {
|
|
1892
|
+
violations.appDiscoveryIssues.push({
|
|
1893
|
+
type: 'direct_table_query',
|
|
1894
|
+
file: relativePath,
|
|
1895
|
+
line: getLineNumber(content, match[0]),
|
|
1896
|
+
reason: 'Direct query to rbac_apps table detected. Use data_rbac_apps_list RPC function for dynamic app discovery.',
|
|
1897
|
+
recommendation: 'Replace with: const { data } = await supabase.rpc(\'data_rbac_apps_list\');',
|
|
1898
|
+
severity: 'warning'
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
// Skip CRUD operations (UPDATE/INSERT/DELETE) - these are acceptable with secureSupabase
|
|
1902
|
+
}
|
|
1903
|
+
});
|
|
1904
|
+
|
|
1905
|
+
// Check for hardcoded app names in arrays or string literals
|
|
1906
|
+
// Known app names: BASE, CAKE, PACE, MINT, TRAC, PORTAL, MEDI
|
|
1907
|
+
// Only flag if they appear to be used for app discovery (arrays, comparisons, etc.)
|
|
1908
|
+
const hardcodedAppNamePatterns = [
|
|
1909
|
+
// Array of app names (likely used for iteration/discovery)
|
|
1910
|
+
/\[['"]\s*(BASE|CAKE|PACE|MINT|TRAC|PORTAL|MEDI)\s*['"]/gi,
|
|
1911
|
+
// String literals in comparisons or includes checks (app discovery patterns)
|
|
1912
|
+
/(app|apps|appName|app_name)\s*[=!]==?\s*['"]\s*(BASE|CAKE|PACE|MINT|TRAC|PORTAL|MEDI)\s*['"]/gi,
|
|
1913
|
+
/(app|apps|appName|app_name)\s*\.(includes|indexOf|find|filter)\s*\([^)]*['"]\s*(BASE|CAKE|PACE|MINT|TRAC|PORTAL|MEDI)\s*['"]/gi,
|
|
1914
|
+
/['"]\s*(BASE|CAKE|PACE|MINT|TRAC|PORTAL|MEDI)\s*['"]\s*\.(includes|indexOf)/gi
|
|
1915
|
+
];
|
|
1916
|
+
|
|
1917
|
+
hardcodedAppNamePatterns.forEach(pattern => {
|
|
1918
|
+
let match;
|
|
1919
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
1920
|
+
// Skip if it's in a get_app_id call (acceptable usage)
|
|
1921
|
+
const beforeMatch = content.substring(Math.max(0, match.index - 50), match.index);
|
|
1922
|
+
const isInGetAppId = /get_app_id\s*\(/i.test(beforeMatch);
|
|
1923
|
+
|
|
1924
|
+
// Skip if in a line comment
|
|
1925
|
+
const lineStart = content.lastIndexOf('\n', match.index) + 1;
|
|
1926
|
+
const lineUpToMatch = content.substring(lineStart, match.index);
|
|
1927
|
+
const isInLineComment = /\/\/[^\n]*$/.test(lineUpToMatch);
|
|
1928
|
+
|
|
1929
|
+
// Skip if it's a comment about app names
|
|
1930
|
+
const isInComment = /\/\*[\s\S]*?\*\//.test(beforeMatch + match[0]);
|
|
1931
|
+
|
|
1932
|
+
// Skip if it's in a data_rbac_apps_list call (already using the function)
|
|
1933
|
+
const isInRpcCall = /data_rbac_apps_list/i.test(beforeMatch);
|
|
1934
|
+
|
|
1935
|
+
// Extract app name (could be in different capture groups depending on pattern)
|
|
1936
|
+
const appName = match[1] || match[2] || match[3];
|
|
1937
|
+
|
|
1938
|
+
if (!isInGetAppId && !isInLineComment && !isInComment && !isInRpcCall && appName) {
|
|
1939
|
+
violations.appDiscoveryIssues.push({
|
|
1940
|
+
type: 'hardcoded_app_name',
|
|
1941
|
+
file: relativePath,
|
|
1942
|
+
line: getLineNumber(content, match[0]),
|
|
1943
|
+
appName: appName,
|
|
1944
|
+
reason: `Hardcoded app name '${appName}' detected. Use data_rbac_apps_list RPC function for dynamic app discovery.`,
|
|
1945
|
+
recommendation: 'Use data_rbac_apps_list() to discover apps dynamically instead of hardcoding app names.',
|
|
1946
|
+
severity: 'warning'
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1951
|
+
|
|
1952
|
+
// If file has app discovery code but doesn't use the RPC function, suggest it
|
|
1953
|
+
if (violations.appDiscoveryIssues.length > 0 && !usesRpcFunction) {
|
|
1954
|
+
// Add a general suggestion if there are multiple issues
|
|
1955
|
+
if (violations.appDiscoveryIssues.length > 1) {
|
|
1956
|
+
violations.appDiscoveryIssues.push({
|
|
1957
|
+
type: 'suggestion',
|
|
1958
|
+
file: relativePath,
|
|
1959
|
+
line: 1,
|
|
1960
|
+
reason: 'Multiple app discovery issues found. Consider using data_rbac_apps_list RPC function for all app discovery needs.',
|
|
1961
|
+
recommendation: 'Replace all hardcoded app names and direct table queries with: const { data: apps } = await supabase.rpc(\'data_rbac_apps_list\');',
|
|
1962
|
+
severity: 'info'
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1779
1967
|
return violations;
|
|
1780
1968
|
}
|
|
1781
1969
|
|
|
@@ -1985,7 +2173,8 @@ function generateReport(allViolations, manifest) {
|
|
|
1985
2173
|
allViolations.viteConfigIssues.length +
|
|
1986
2174
|
allViolations.routerSetupIssues.length;
|
|
1987
2175
|
const totalUnnecessaryWrappers = allViolations.unnecessaryWrappers.length;
|
|
1988
|
-
const
|
|
2176
|
+
const totalAppDiscovery = allViolations.appDiscoveryIssues.length;
|
|
2177
|
+
const totalIssues = totalRestricted + totalDuplicates + totalSuggestions + totalRbacAuth + totalSetupIssues + totalUnnecessaryWrappers + totalAppDiscovery;
|
|
1989
2178
|
|
|
1990
2179
|
console.log(`\n${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
|
|
1991
2180
|
console.log(`${colors.bold}${colors.cyan} pace-core Compliance Report${colors.reset}`);
|
|
@@ -2065,6 +2254,52 @@ function generateReport(allViolations, manifest) {
|
|
|
2065
2254
|
});
|
|
2066
2255
|
}
|
|
2067
2256
|
|
|
2257
|
+
// App Discovery Issues
|
|
2258
|
+
if (totalAppDiscovery > 0) {
|
|
2259
|
+
console.log(`\n${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
|
|
2260
|
+
console.log(`${colors.bold}${colors.cyan} App Discovery Compliance${colors.reset}`);
|
|
2261
|
+
console.log(`${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}\n`);
|
|
2262
|
+
|
|
2263
|
+
// Separate by type
|
|
2264
|
+
const directQueries = allViolations.appDiscoveryIssues.filter(v => v.type === 'direct_table_query');
|
|
2265
|
+
const hardcodedNames = allViolations.appDiscoveryIssues.filter(v => v.type === 'hardcoded_app_name');
|
|
2266
|
+
const suggestions = allViolations.appDiscoveryIssues.filter(v => v.type === 'suggestion');
|
|
2267
|
+
|
|
2268
|
+
if (directQueries.length > 0) {
|
|
2269
|
+
console.log(`${colors.yellow}${colors.bold}⚠️ Direct rbac_apps Queries: ${directQueries.length}${colors.reset}\n`);
|
|
2270
|
+
directQueries.forEach(({ file, line, reason, recommendation }) => {
|
|
2271
|
+
console.log(` ${colors.yellow}•${colors.reset} ${colors.yellow}${file}:${line}${colors.reset}`);
|
|
2272
|
+
console.log(` ${colors.yellow}Issue:${colors.reset} ${reason}`);
|
|
2273
|
+
console.log(` ${colors.green}Fix:${colors.reset} ${recommendation}\n`);
|
|
2274
|
+
});
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
if (hardcodedNames.length > 0) {
|
|
2278
|
+
console.log(`${colors.yellow}${colors.bold}⚠️ Hardcoded App Names: ${hardcodedNames.length}${colors.reset}\n`);
|
|
2279
|
+
hardcodedNames.forEach(({ file, line, appName, reason, recommendation }) => {
|
|
2280
|
+
console.log(` ${colors.yellow}•${colors.reset} ${colors.yellow}${file}:${line}${colors.reset}`);
|
|
2281
|
+
console.log(` App Name: ${colors.cyan}${appName}${colors.reset}`);
|
|
2282
|
+
console.log(` ${colors.yellow}Issue:${colors.reset} ${reason}`);
|
|
2283
|
+
console.log(` ${colors.green}Fix:${colors.reset} ${recommendation}\n`);
|
|
2284
|
+
});
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
if (suggestions.length > 0) {
|
|
2288
|
+
console.log(`${colors.cyan}${colors.bold}💡 Suggestions: ${suggestions.length}${colors.reset}\n`);
|
|
2289
|
+
suggestions.forEach(({ file, reason, recommendation }) => {
|
|
2290
|
+
console.log(` ${colors.cyan}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
|
|
2291
|
+
console.log(` ${reason}`);
|
|
2292
|
+
console.log(` ${colors.green}Recommendation:${colors.reset} ${recommendation}\n`);
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
console.log(`${colors.cyan}Example Usage:${colors.reset}`);
|
|
2297
|
+
console.log(` ${colors.green}const { data: apps } = await supabase.rpc('data_rbac_apps_list');${colors.reset}`);
|
|
2298
|
+
console.log(` ${colors.green}const appNames = apps?.map(app => app.name) || [];${colors.reset}\n`);
|
|
2299
|
+
} else {
|
|
2300
|
+
console.log(`${colors.green}✅ App discovery compliance: Using data_rbac_apps_list RPC function${colors.reset}\n`);
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2068
2303
|
// RBAC/Auth Compliance Section
|
|
2069
2304
|
if (totalRbacAuth > 0) {
|
|
2070
2305
|
console.log(`\n${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
|
|
@@ -2115,7 +2350,11 @@ function generateReport(allViolations, manifest) {
|
|
|
2115
2350
|
console.log(` ${colors.green} p_user_id: userId,${colors.reset}`);
|
|
2116
2351
|
console.log(` ${colors.green} p_event_id: eventId${colors.reset}`);
|
|
2117
2352
|
console.log(` ${colors.green}});${colors.reset}`);
|
|
2118
|
-
} else if (type === 'rbac query' &&
|
|
2353
|
+
} else if (type === 'rbac query' && name.includes('rbac_apps')) {
|
|
2354
|
+
console.log(` ${colors.cyan}Example (app discovery):${colors.reset}`);
|
|
2355
|
+
console.log(` ${colors.green}const { data: apps } = await supabase.rpc('data_rbac_apps_list');${colors.reset}`);
|
|
2356
|
+
console.log(` ${colors.yellow}Note:${colors.reset} Use data_rbac_apps_list RPC function for dynamic app discovery instead of querying rbac_apps directly.`);
|
|
2357
|
+
} else if (type === 'rbac query' && (name.includes('rbac_app_pages') || name.includes('rbac_page_permissions'))) {
|
|
2119
2358
|
console.log(` ${colors.cyan}Example (admin operations):${colors.reset}`);
|
|
2120
2359
|
console.log(` ${colors.green}import { useSecureSupabase } from '@jmruthers/pace-core/rbac';${colors.reset}`);
|
|
2121
2360
|
console.log(` ${colors.green}const supabase = useSecureSupabase();${colors.reset}`);
|
|
@@ -2243,6 +2482,8 @@ function generateReport(allViolations, manifest) {
|
|
|
2243
2482
|
console.log(` - Suggestions: ${colors.yellow}${totalSuggestions}${colors.reset}`);
|
|
2244
2483
|
console.log(` - RBAC/Auth Issues: ${totalRbacAuth > 0 ? colors.red : colors.green}${totalRbacAuth}${colors.reset}`);
|
|
2245
2484
|
console.log(` - Setup/Configuration Issues: ${totalSetupIssues > 0 ? colors.red : colors.green}${totalSetupIssues}${colors.reset}`);
|
|
2485
|
+
console.log(` - Unnecessary Wrappers: ${totalUnnecessaryWrappers > 0 ? colors.yellow : colors.green}${totalUnnecessaryWrappers}${colors.reset}`);
|
|
2486
|
+
console.log(` - App Discovery Issues: ${totalAppDiscovery > 0 ? colors.yellow : colors.green}${totalAppDiscovery}${colors.reset}`);
|
|
2246
2487
|
|
|
2247
2488
|
if (totalIssues === 0) {
|
|
2248
2489
|
console.log(`\n${colors.green}${colors.bold}✅ Excellent! Your codebase is fully compliant with pace-core standards.${colors.reset}\n`);
|
|
@@ -2267,6 +2508,10 @@ function findSourceFiles(dir, fileList = []) {
|
|
|
2267
2508
|
const stat = fs.statSync(fullPath);
|
|
2268
2509
|
|
|
2269
2510
|
if (stat.isDirectory()) {
|
|
2511
|
+
// Skip Edge Functions directory - they run in Deno, not React, so React hooks aren't available
|
|
2512
|
+
if (item === 'functions' && dir.includes('supabase')) {
|
|
2513
|
+
return; // Skip supabase/functions directory
|
|
2514
|
+
}
|
|
2270
2515
|
if (!ignoreDirs.includes(item) && !item.startsWith('.')) {
|
|
2271
2516
|
findSourceFiles(fullPath, fileList);
|
|
2272
2517
|
}
|
|
@@ -2309,7 +2554,8 @@ function main() {
|
|
|
2309
2554
|
providerSetupIssues: [],
|
|
2310
2555
|
viteConfigIssues: [],
|
|
2311
2556
|
routerSetupIssues: [],
|
|
2312
|
-
unnecessaryWrappers: []
|
|
2557
|
+
unnecessaryWrappers: [],
|
|
2558
|
+
appDiscoveryIssues: []
|
|
2313
2559
|
};
|
|
2314
2560
|
|
|
2315
2561
|
files.forEach(file => {
|
|
@@ -2328,6 +2574,7 @@ function main() {
|
|
|
2328
2574
|
allViolations.viteConfigIssues.push(...violations.viteConfigIssues);
|
|
2329
2575
|
allViolations.routerSetupIssues.push(...violations.routerSetupIssues);
|
|
2330
2576
|
allViolations.unnecessaryWrappers.push(...violations.unnecessaryWrappers);
|
|
2577
|
+
allViolations.appDiscoveryIssues.push(...violations.appDiscoveryIssues);
|
|
2331
2578
|
} catch (error) {
|
|
2332
2579
|
console.error(`${colors.red}Error scanning ${file}: ${error.message}${colors.reset}`);
|
|
2333
2580
|
}
|
|
@@ -2349,5 +2596,12 @@ if (require.main === module) {
|
|
|
2349
2596
|
}
|
|
2350
2597
|
}
|
|
2351
2598
|
|
|
2352
|
-
module.exports = {
|
|
2599
|
+
module.exports = {
|
|
2600
|
+
main,
|
|
2601
|
+
scanFile,
|
|
2602
|
+
generateReport,
|
|
2603
|
+
loadManifest,
|
|
2604
|
+
findProjectRoot,
|
|
2605
|
+
findSourceFiles
|
|
2606
|
+
};
|
|
2353
2607
|
|