@jmruthers/pace-core 0.6.1 → 0.6.3
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 +88 -10
- package/cursor-rules/00-pace-core-compliance.mdc +46 -87
- package/cursor-rules/01-standards-compliance.mdc +16 -47
- package/cursor-rules/02-project-structure.mdc +4 -4
- package/cursor-rules/03-solid-principles.mdc +45 -164
- package/cursor-rules/04-testing-standards.mdc +22 -69
- package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
- package/cursor-rules/06-code-quality.mdc +42 -125
- package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
- package/cursor-rules/08-markup-quality.mdc +452 -0
- package/cursor-rules/CHANGELOG.md +18 -0
- package/cursor-rules/README.md +2 -1
- package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-Cb34EQs3.d.ts} +63 -1
- package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
- package/dist/{DataTable-DQ7RSOHE.js → DataTable-THFPBKTP.js} +12 -10
- package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DEMpysFR.d.ts} +394 -171
- package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +30 -8
- package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
- package/dist/{api-N774RPUA.js → api-IAGWF3ZG.js} +10 -10
- package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
- package/dist/{chunk-JBKQ3SAO.js → chunk-2T2IG7T7.js} +107 -57
- package/dist/chunk-2T2IG7T7.js.map +1 -0
- package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
- package/dist/chunk-6SOIHG6Z.js.map +1 -0
- package/dist/{chunk-3XTALGJF.js → chunk-6Z7LTB3D.js} +69 -240
- package/dist/chunk-6Z7LTB3D.js.map +1 -0
- package/dist/{chunk-4ZC4GX36.js → chunk-CNCQDFLN.js} +199 -46
- package/dist/chunk-CNCQDFLN.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-BYFSK72L.js → chunk-DWUBLJJM.js} +361 -187
- package/dist/chunk-DWUBLJJM.js.map +1 -0
- package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
- 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-4N5C5XZU.js → chunk-HFZBI76P.js} +4 -4
- package/dist/chunk-HFZBI76P.js.map +1 -0
- package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
- package/dist/chunk-L4OXEN46.js.map +1 -0
- package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
- package/dist/chunk-M43Y4SSO.js.map +1 -0
- package/dist/{chunk-I7PSE6JW.js → chunk-M7MPQISP.js} +3 -76
- package/dist/chunk-M7MPQISP.js.map +1 -0
- package/dist/chunk-PQBSKX33.js +7793 -0
- package/dist/chunk-PQBSKX33.js.map +1 -0
- package/dist/chunk-QRPVRXYT.js +226 -0
- package/dist/chunk-QRPVRXYT.js.map +1 -0
- package/dist/{chunk-KNC55RTG.js → chunk-RWEBCB47.js} +194 -416
- package/dist/chunk-RWEBCB47.js.map +1 -0
- package/dist/{chunk-XM25TVIE.js → chunk-YDQHOZNA.js} +843 -388
- package/dist/chunk-YDQHOZNA.js.map +1 -0
- package/dist/{chunk-GLK6VM3F.js → chunk-ZNIWI3UC.js} +739 -737
- package/dist/chunk-ZNIWI3UC.js.map +1 -0
- package/dist/components.d.ts +5 -5
- package/dist/components.js +18 -16
- package/dist/components.js.map +1 -1
- package/dist/contextValidator-3JNZKUTX.js +9 -0
- package/dist/contextValidator-3JNZKUTX.js.map +1 -0
- package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
- package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
- package/dist/hooks.d.ts +55 -122
- package/dist/hooks.js +10 -13
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +60 -13
- package/dist/index.js +30 -25
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +21 -3
- package/dist/providers.js +4 -3
- package/dist/rbac/index.d.ts +210 -139
- package/dist/rbac/index.js +17 -13
- package/dist/styles/index.js +1 -1
- package/dist/theming/runtime.d.ts +1 -13
- package/dist/theming/runtime.js +2 -2
- 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/types.js +1 -1
- package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +38 -17
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +17 -19
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +21 -17
- package/docs/api/modules.md +4191 -2967
- package/docs/architecture/database-schema-requirements.md +161 -0
- package/docs/components/context-selector.md +126 -0
- package/docs/core-concepts/rbac-system.md +3 -3
- package/docs/documentation-index.md +2 -4
- package/docs/getting-started/cursor-rules.md +2 -1
- package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
- package/docs/migration/MIGRATION_GUIDE.md +2 -24
- package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
- package/docs/migration/README.md +52 -6
- package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
- package/docs/migration/database-changes-december-2025.md +3 -3
- package/docs/pace-mint-fix-auto-selection.md +218 -0
- package/docs/pace-mint-rbac-setup.md +391 -0
- 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/rbac/secure-client-protection.md +330 -0
- package/docs/standards/README.md +1 -0
- package/package.json +4 -3
- 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} +784 -685
- 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 +985 -0
- package/scripts/audit/core/checks/documentation.cjs +268 -0
- package/scripts/audit/core/checks/environment.cjs +116 -0
- package/scripts/audit/core/checks/error-handling.cjs +340 -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 +61 -936
- 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/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__/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 +14 -0
- package/src/components/Button/Button.tsx +22 -0
- package/src/components/Calendar/Calendar.tsx +8 -2
- 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/ContextSelector/ContextSelector.tsx +384 -0
- package/src/components/ContextSelector/index.ts +3 -0
- package/src/components/DataTable/DataTable.tsx +38 -4
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
- package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
- package/src/components/DataTable/components/ActionButtons.tsx +10 -7
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- 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 +196 -554
- 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 +8 -0
- 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 +8 -0
- 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 +63 -851
- 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/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 +12 -0
- 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 +127 -33
- package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
- 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/Dialog/Dialog.tsx +31 -3
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
- package/src/components/ErrorBoundary/index.ts +27 -2
- package/src/components/FileDisplay/FileDisplay.tsx +74 -28
- package/src/components/FileUpload/FileUpload.tsx +22 -2
- package/src/components/Footer/Footer.test.tsx +16 -16
- package/src/components/Footer/Footer.tsx +14 -11
- package/src/components/Form/Form.tsx +1 -0
- package/src/components/Header/Header.test.tsx +43 -73
- package/src/components/Header/Header.tsx +59 -49
- package/src/components/Input/Input.test.tsx +2 -2
- package/src/components/Input/Input.tsx +8 -4
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
- package/src/components/LoginForm/LoginForm.tsx +4 -0
- package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
- package/src/components/NavigationMenu/types.ts +56 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +13 -11
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +167 -44
- package/src/components/PaceAppLayout/README.md +14 -17
- package/src/components/PaceAppLayout/test-setup.tsx +3 -4
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
- package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
- package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
- package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
- package/src/components/Select/Select.tsx +80 -434
- 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 +4 -5
- package/src/components/Switch/Switch.tsx +4 -4
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Toast/Toast.tsx +4 -0
- package/src/components/Tooltip/Tooltip.tsx +2 -2
- package/src/components/UserMenu/UserMenu.test.tsx +24 -11
- package/src/components/UserMenu/UserMenu.tsx +21 -18
- package/src/components/index.ts +7 -7
- package/src/eslint-rules/pace-core-compliance.cjs +106 -0
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
- package/src/hooks/index.ts +1 -2
- package/src/hooks/public/usePublicEvent.ts +4 -0
- package/src/hooks/public/usePublicEventLogo.ts +4 -0
- package/src/hooks/public/usePublicFileDisplay.ts +4 -0
- package/src/hooks/public/usePublicRouteParams.ts +4 -0
- 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/useAppConfig.ts +15 -30
- package/src/hooks/useDebounce.ts +9 -0
- package/src/hooks/useEventTheme.ts +6 -0
- package/src/hooks/useFileDisplay.ts +81 -50
- package/src/hooks/useFileReference.ts +25 -7
- package/src/hooks/useFileUrl.ts +11 -1
- package/src/hooks/useFocusManagement.ts +14 -0
- package/src/hooks/useFocusTrap.ts +3 -0
- package/src/hooks/useInactivityTracker.ts +3 -0
- 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 +7 -0
- 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 +1 -1
- package/src/index.ts +6 -6
- package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
- package/src/providers/services/AuthServiceProvider.tsx +35 -7
- package/src/providers/services/EventServiceProvider.tsx +51 -5
- package/src/providers/services/InactivityServiceProvider.tsx +18 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +126 -134
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
- package/src/rbac/adapters.tsx +12 -3
- package/src/rbac/api.test.ts +59 -51
- package/src/rbac/api.ts +246 -167
- package/src/rbac/components/NavigationProvider.tsx +4 -1
- package/src/rbac/components/PagePermissionGuard.tsx +185 -17
- package/src/rbac/components/RoleBasedRouter.tsx +5 -1
- package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
- package/src/rbac/components/SecureDataProvider.tsx +20 -5
- 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/__tests__/useSecureSupabase.test.ts +32 -21
- 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 +377 -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 +64 -66
- package/src/rbac/hooks/usePermissions.ts +14 -995
- package/src/rbac/hooks/useRBAC.test.ts +1 -5
- package/src/rbac/hooks/useRBAC.ts +36 -37
- package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
- package/src/rbac/hooks/useResolvedScope.ts +35 -40
- package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
- package/src/rbac/hooks/useResourcePermissions.ts +14 -4
- package/src/rbac/hooks/useSecureSupabase.ts +27 -7
- package/src/rbac/index.ts +7 -0
- package/src/rbac/permissions.ts +0 -30
- package/src/rbac/secureClient.test.ts +22 -18
- package/src/rbac/secureClient.ts +294 -68
- package/src/rbac/security.ts +0 -17
- package/src/rbac/types.ts +9 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
- package/src/rbac/utils/clientSecurity.ts +93 -0
- package/src/rbac/utils/contextValidator.ts +77 -168
- package/src/services/AuthService.ts +39 -7
- package/src/services/EventService.ts +186 -54
- package/src/services/OrganisationService.ts +81 -14
- package/src/services/__tests__/EventService.test.ts +1 -2
- package/src/services/base/BaseService.ts +3 -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/dynamic/dynamicUtils.ts +7 -4
- 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/dist/chunk-3QRJFVBR.js.map +0 -1
- package/dist/chunk-3XTALGJF.js.map +0 -1
- package/dist/chunk-4N5C5XZU.js.map +0 -1
- package/dist/chunk-4ZC4GX36.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js +0 -38
- package/dist/chunk-BYFSK72L.js.map +0 -1
- package/dist/chunk-EXUD6RNJ.js +0 -451
- package/dist/chunk-EXUD6RNJ.js.map +0 -1
- package/dist/chunk-GLK6VM3F.js.map +0 -1
- package/dist/chunk-I7PSE6JW.js.map +0 -1
- package/dist/chunk-JBKQ3SAO.js.map +0 -1
- package/dist/chunk-KNC55RTG.js.map +0 -1
- package/dist/chunk-LXQLPRQ2.js.map +0 -1
- package/dist/chunk-R77UEZ4E.js.map +0 -1
- package/dist/chunk-SQGMNID3.js.map +0 -1
- package/dist/chunk-T33XF5ZC.js +0 -12922
- package/dist/chunk-T33XF5ZC.js.map +0 -1
- package/dist/chunk-XM25TVIE.js.map +0 -1
- package/docs/api/classes/ColumnFactory.md +0 -243
- package/docs/api/classes/ErrorBoundary.md +0 -144
- package/docs/api/classes/InvalidScopeError.md +0 -73
- package/docs/api/classes/Logger.md +0 -178
- package/docs/api/classes/MissingUserContextError.md +0 -66
- package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
- package/docs/api/classes/PermissionDeniedError.md +0 -73
- package/docs/api/classes/RBACAuditManager.md +0 -297
- package/docs/api/classes/RBACCache.md +0 -322
- package/docs/api/classes/RBACEngine.md +0 -171
- package/docs/api/classes/RBACError.md +0 -76
- package/docs/api/classes/RBACNotInitializedError.md +0 -66
- package/docs/api/classes/SecureSupabaseClient.md +0 -160
- package/docs/api/classes/StorageUtils.md +0 -328
- package/docs/api/enums/FileCategory.md +0 -184
- package/docs/api/enums/LogLevel.md +0 -54
- package/docs/api/enums/RBACErrorCode.md +0 -228
- package/docs/api/enums/RPCFunction.md +0 -118
- package/docs/api/interfaces/AddressFieldProps.md +0 -241
- package/docs/api/interfaces/AddressFieldRef.md +0 -94
- package/docs/api/interfaces/AggregateConfig.md +0 -43
- package/docs/api/interfaces/AutocompleteOptions.md +0 -75
- package/docs/api/interfaces/AvatarProps.md +0 -128
- package/docs/api/interfaces/BadgeProps.md +0 -27
- package/docs/api/interfaces/ButtonProps.md +0 -53
- package/docs/api/interfaces/CalendarProps.md +0 -70
- package/docs/api/interfaces/CardProps.md +0 -66
- package/docs/api/interfaces/ColorPalette.md +0 -7
- package/docs/api/interfaces/ColorShade.md +0 -66
- package/docs/api/interfaces/ComplianceResult.md +0 -30
- package/docs/api/interfaces/DataAccessRecord.md +0 -96
- package/docs/api/interfaces/DataRecord.md +0 -11
- package/docs/api/interfaces/DataTableAction.md +0 -249
- package/docs/api/interfaces/DataTableColumn.md +0 -504
- package/docs/api/interfaces/DataTableProps.md +0 -625
- package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
- package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
- package/docs/api/interfaces/DatabaseIssue.md +0 -41
- package/docs/api/interfaces/EmptyStateConfig.md +0 -61
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
- package/docs/api/interfaces/EventAppRoleData.md +0 -71
- package/docs/api/interfaces/ExportColumn.md +0 -90
- package/docs/api/interfaces/ExportOptions.md +0 -126
- package/docs/api/interfaces/FileDisplayProps.md +0 -249
- package/docs/api/interfaces/FileMetadata.md +0 -129
- package/docs/api/interfaces/FileReference.md +0 -118
- package/docs/api/interfaces/FileSizeLimits.md +0 -7
- package/docs/api/interfaces/FileUploadOptions.md +0 -139
- package/docs/api/interfaces/FileUploadProps.md +0 -293
- package/docs/api/interfaces/FooterProps.md +0 -105
- package/docs/api/interfaces/FormFieldProps.md +0 -166
- package/docs/api/interfaces/FormProps.md +0 -113
- package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
- package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
- package/docs/api/interfaces/InputProps.md +0 -53
- package/docs/api/interfaces/LabelProps.md +0 -107
- package/docs/api/interfaces/LoggerConfig.md +0 -62
- package/docs/api/interfaces/LoginFormProps.md +0 -184
- package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
- package/docs/api/interfaces/NavigationContextType.md +0 -164
- package/docs/api/interfaces/NavigationGuardProps.md +0 -139
- package/docs/api/interfaces/NavigationItem.md +0 -120
- package/docs/api/interfaces/NavigationMenuProps.md +0 -221
- package/docs/api/interfaces/NavigationProviderProps.md +0 -117
- package/docs/api/interfaces/Organisation.md +0 -140
- package/docs/api/interfaces/OrganisationContextType.md +0 -388
- package/docs/api/interfaces/OrganisationMembership.md +0 -140
- package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
- package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
- package/docs/api/interfaces/PaceAppLayoutProps.md +0 -406
- package/docs/api/interfaces/PaceLoginPageProps.md +0 -47
- package/docs/api/interfaces/PageAccessRecord.md +0 -85
- package/docs/api/interfaces/PagePermissionContextType.md +0 -140
- package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
- package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
- package/docs/api/interfaces/PaletteData.md +0 -41
- package/docs/api/interfaces/ParsedAddress.md +0 -120
- package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
- package/docs/api/interfaces/ProgressProps.md +0 -42
- package/docs/api/interfaces/ProtectedRouteProps.md +0 -97
- package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
- package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
- package/docs/api/interfaces/PublicPageLayoutProps.md +0 -198
- package/docs/api/interfaces/QuickFix.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
- package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
- package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
- package/docs/api/interfaces/RBACConfig.md +0 -133
- package/docs/api/interfaces/RBACContext.md +0 -52
- package/docs/api/interfaces/RBACLogger.md +0 -112
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
- package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
- package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
- package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
- package/docs/api/interfaces/RBACResult.md +0 -58
- package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
- package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
- package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
- package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
- package/docs/api/interfaces/RBACRolesListParams.md +0 -52
- package/docs/api/interfaces/RBACRolesListResult.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
- package/docs/api/interfaces/ResourcePermissions.md +0 -155
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
- package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
- package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
- package/docs/api/interfaces/RoleManagementResult.md +0 -52
- package/docs/api/interfaces/RouteAccessRecord.md +0 -107
- package/docs/api/interfaces/RouteConfig.md +0 -134
- package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
- package/docs/api/interfaces/SecureDataContextType.md +0 -168
- package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
- package/docs/api/interfaces/SetupIssue.md +0 -41
- package/docs/api/interfaces/StorageConfig.md +0 -41
- package/docs/api/interfaces/StorageFileInfo.md +0 -74
- package/docs/api/interfaces/StorageFileMetadata.md +0 -151
- package/docs/api/interfaces/StorageListOptions.md +0 -99
- package/docs/api/interfaces/StorageListResult.md +0 -41
- package/docs/api/interfaces/StorageUploadOptions.md +0 -101
- package/docs/api/interfaces/StorageUploadResult.md +0 -63
- package/docs/api/interfaces/StorageUrlOptions.md +0 -60
- package/docs/api/interfaces/StyleImport.md +0 -19
- package/docs/api/interfaces/SwitchProps.md +0 -34
- package/docs/api/interfaces/TabsContentProps.md +0 -9
- package/docs/api/interfaces/TabsListProps.md +0 -9
- package/docs/api/interfaces/TabsProps.md +0 -9
- package/docs/api/interfaces/TabsTriggerProps.md +0 -50
- package/docs/api/interfaces/TextareaProps.md +0 -53
- package/docs/api/interfaces/ToastActionElement.md +0 -9
- package/docs/api/interfaces/ToastProps.md +0 -9
- package/docs/api/interfaces/UnifiedAuthContextType.md +0 -820
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -171
- package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
- package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -136
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -81
- package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
- package/docs/api/interfaces/UsePublicEventReturn.md +0 -68
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -120
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -94
- package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
- package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
- package/docs/api/interfaces/UserEventAccess.md +0 -118
- package/docs/api/interfaces/UserMenuProps.md +0 -86
- package/docs/api/interfaces/UserProfile.md +0 -63
- package/docs/migration/quick-migration-guide.md +0 -356
- package/docs/migration/service-architecture.md +0 -281
- package/src/components/EventSelector/EventSelector.test.tsx +0 -720
- package/src/components/EventSelector/EventSelector.tsx +0 -420
- package/src/components/EventSelector/index.ts +0 -3
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -324
- package/src/components/OrganisationSelector/index.ts +0 -9
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
- package/src/hooks/useSecureDataAccess.test.ts +0 -559
- package/src/hooks/useSecureDataAccess.ts +0 -681
- /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-THFPBKTP.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
- /package/dist/{api-N774RPUA.js.map → api-IAGWF3ZG.js.map} +0 -0
- /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
- /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.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/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# Secure Supabase Client Protection
|
|
2
|
+
|
|
3
|
+
This document describes the multi-layered protection system to prevent consuming apps from using insecure Supabase clients that bypass organisation context and RLS policies.
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
Using `createClient()` from `@supabase/supabase-js` directly bypasses:
|
|
8
|
+
- Organisation context enforcement
|
|
9
|
+
- RLS (Row Level Security) policies
|
|
10
|
+
- Event and app context injection
|
|
11
|
+
- Security safeguards built into pace-core
|
|
12
|
+
|
|
13
|
+
This can lead to:
|
|
14
|
+
- **Cross-organisation data access** - Users accessing data from organisations they shouldn't
|
|
15
|
+
- **Security vulnerabilities** - Bypassing permission checks
|
|
16
|
+
- **Data leakage** - Accidental exposure of sensitive data
|
|
17
|
+
|
|
18
|
+
## Protection Layers
|
|
19
|
+
|
|
20
|
+
### 1. ESLint Rule: `no-direct-supabase-client`
|
|
21
|
+
|
|
22
|
+
**Location**: `packages/core/src/eslint-rules/pace-core-compliance.cjs`
|
|
23
|
+
|
|
24
|
+
**What it does**:
|
|
25
|
+
- Detects `createClient` imports from `@supabase/supabase-js`
|
|
26
|
+
- Detects `createClient()` function calls
|
|
27
|
+
- Reports errors with helpful suggestions
|
|
28
|
+
|
|
29
|
+
**How to use**:
|
|
30
|
+
```js
|
|
31
|
+
// In your consuming app's eslint.config.js:
|
|
32
|
+
import paceCoreConfig from '@jmruthers/pace-core/eslint-config';
|
|
33
|
+
|
|
34
|
+
export default [
|
|
35
|
+
...paceCoreConfig,
|
|
36
|
+
// pace-core-compliance/no-direct-supabase-client is automatically enabled
|
|
37
|
+
];
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Example violations**:
|
|
41
|
+
```tsx
|
|
42
|
+
// ❌ ERROR: Direct import detected
|
|
43
|
+
import { createClient } from '@supabase/supabase-js';
|
|
44
|
+
|
|
45
|
+
// ❌ ERROR: Direct client creation
|
|
46
|
+
const supabase = createClient(url, key);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 2. Runtime Detection: `warnIfInsecureClient()`
|
|
50
|
+
|
|
51
|
+
**Location**: `packages/core/src/rbac/utils/clientSecurity.ts`
|
|
52
|
+
|
|
53
|
+
**What it does**:
|
|
54
|
+
- Checks if a client is marked as secure
|
|
55
|
+
- Warns in development mode when insecure clients are detected
|
|
56
|
+
- Provides helpful error messages with fix suggestions
|
|
57
|
+
|
|
58
|
+
**How to use**:
|
|
59
|
+
```tsx
|
|
60
|
+
import { warnIfInsecureClient } from '@jmruthers/pace-core/rbac';
|
|
61
|
+
|
|
62
|
+
function MyComponent() {
|
|
63
|
+
const supabase = useSecureSupabase();
|
|
64
|
+
|
|
65
|
+
// Optional: Warn if client is insecure (development only)
|
|
66
|
+
warnIfInsecureClient(supabase, 'MyComponent');
|
|
67
|
+
|
|
68
|
+
// Use supabase...
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Output** (development mode only):
|
|
73
|
+
```
|
|
74
|
+
[pace-core Security Warning] Non-secure Supabase client detected in MyComponent.
|
|
75
|
+
You are using a Supabase client created with createClient() instead of useSecureSupabase().
|
|
76
|
+
This bypasses organisation context enforcement and RLS policies...
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 3. Type Safety: `isSecureClient()`
|
|
80
|
+
|
|
81
|
+
**Location**: `packages/core/src/rbac/utils/clientSecurity.ts`
|
|
82
|
+
|
|
83
|
+
**What it does**:
|
|
84
|
+
- Type guard to check if a client is secure
|
|
85
|
+
- Returns `true` only for clients created via `useSecureSupabase()` or `createSecureClient()`
|
|
86
|
+
- Can be used for runtime checks and TypeScript narrowing
|
|
87
|
+
|
|
88
|
+
**How to use**:
|
|
89
|
+
```tsx
|
|
90
|
+
import { isSecureClient } from '@jmruthers/pace-core/rbac';
|
|
91
|
+
|
|
92
|
+
function MyComponent() {
|
|
93
|
+
const supabase = useSecureSupabase();
|
|
94
|
+
|
|
95
|
+
if (isSecureClient(supabase)) {
|
|
96
|
+
// TypeScript knows supabase is secure here
|
|
97
|
+
// Safe to use for database operations
|
|
98
|
+
} else {
|
|
99
|
+
// Client is not secure - handle error
|
|
100
|
+
console.error('Insecure client detected!');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 4. Automatic Client Marking
|
|
106
|
+
|
|
107
|
+
**Location**: `packages/core/src/rbac/secureClient.ts`
|
|
108
|
+
|
|
109
|
+
**What it does**:
|
|
110
|
+
- Automatically marks clients created via `SecureSupabaseClient` as secure
|
|
111
|
+
- Uses a Symbol (`SECURE_CLIENT_SYMBOL`) to mark secure clients
|
|
112
|
+
- Works transparently - no action needed from developers
|
|
113
|
+
|
|
114
|
+
**How it works**:
|
|
115
|
+
```tsx
|
|
116
|
+
// When you use useSecureSupabase():
|
|
117
|
+
const supabase = useSecureSupabase();
|
|
118
|
+
// Client is automatically marked as secure internally
|
|
119
|
+
|
|
120
|
+
// When you use createSecureClient():
|
|
121
|
+
const secureClient = createSecureClient(url, key, orgId);
|
|
122
|
+
const supabase = secureClient.getClient();
|
|
123
|
+
// Client is automatically marked as secure internally
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Correct Usage
|
|
127
|
+
|
|
128
|
+
### ✅ Use `useSecureSupabase()` Hook
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
|
|
132
|
+
|
|
133
|
+
function MyComponent() {
|
|
134
|
+
const supabase = useSecureSupabase();
|
|
135
|
+
|
|
136
|
+
if (!supabase) {
|
|
137
|
+
return <div>Loading...</div>;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Organisation context is automatically enforced
|
|
141
|
+
const { data } = await supabase.from('users').select('*');
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### ✅ Use `createSecureClient()` for Non-React Code
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { createSecureClient } from '@jmruthers/pace-core/rbac';
|
|
149
|
+
|
|
150
|
+
// For server-side or non-React code
|
|
151
|
+
const secureClient = createSecureClient(
|
|
152
|
+
url,
|
|
153
|
+
key,
|
|
154
|
+
organisationId,
|
|
155
|
+
eventId,
|
|
156
|
+
appId,
|
|
157
|
+
isSuperAdmin
|
|
158
|
+
);
|
|
159
|
+
const supabase = secureClient.getClient();
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### ✅ Verify Client Security (Optional)
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
import { useSecureSupabase, isSecureClient, warnIfInsecureClient } from '@jmruthers/pace-core/rbac';
|
|
166
|
+
|
|
167
|
+
function MyComponent() {
|
|
168
|
+
const supabase = useSecureSupabase();
|
|
169
|
+
|
|
170
|
+
// Development-only warning
|
|
171
|
+
warnIfInsecureClient(supabase, 'MyComponent');
|
|
172
|
+
|
|
173
|
+
// Runtime check
|
|
174
|
+
if (!isSecureClient(supabase)) {
|
|
175
|
+
throw new Error('Insecure client detected!');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Use supabase...
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Incorrect Usage (Will Be Detected)
|
|
183
|
+
|
|
184
|
+
### ❌ Direct `createClient()` Import
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
// ESLint will report error
|
|
188
|
+
import { createClient } from '@supabase/supabase-js';
|
|
189
|
+
const supabase = createClient(url, key);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### ❌ Direct `createClient()` Call
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
// ESLint will report error
|
|
196
|
+
const supabase = createClient(url, key);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### ❌ Using Base Client for Queries
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
// Even if imported from config file, using for queries is wrong
|
|
203
|
+
import { supabase } from './lib/supabase'; // Base client
|
|
204
|
+
const { data } = await supabase.from('users').select('*'); // ❌ Bypasses security
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Configuration Files Exception
|
|
208
|
+
|
|
209
|
+
The ESLint rule allows `createClient()` in configuration files (files matching `supabase*.ts/js` or `*client*.ts/js`). This is intentional because:
|
|
210
|
+
|
|
211
|
+
1. Base clients are needed for authentication setup
|
|
212
|
+
2. Base clients should only be used for auth operations
|
|
213
|
+
3. Base clients should be passed to `useSecureSupabase()` as fallback
|
|
214
|
+
|
|
215
|
+
**Example of acceptable config file**:
|
|
216
|
+
```tsx
|
|
217
|
+
// supabaseClient.ts - Config file (allowed)
|
|
218
|
+
import { createClient } from '@supabase/supabase-js';
|
|
219
|
+
|
|
220
|
+
export const supabase = createClient(url, key);
|
|
221
|
+
|
|
222
|
+
// Then in components:
|
|
223
|
+
import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
|
|
224
|
+
import { supabase } from './supabaseClient';
|
|
225
|
+
|
|
226
|
+
function MyComponent() {
|
|
227
|
+
// ✅ Correct: Pass base client as fallback
|
|
228
|
+
const secureSupabase = useSecureSupabase(supabase);
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Audit Script Detection
|
|
233
|
+
|
|
234
|
+
The pace-core audit script comprehensively detects insecure client usage:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
npm run audit
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The audit will report violations including:
|
|
241
|
+
- ✅ Direct `createClient` imports from `@supabase/supabase-js`
|
|
242
|
+
- ✅ Direct `createClient()` function calls
|
|
243
|
+
- ✅ Usage of non-secure clients for database queries (`.from()` calls)
|
|
244
|
+
- ✅ Files that import `createClient` but don't use `useSecureSupabase()`
|
|
245
|
+
- ✅ Variables created with `createClient()` that are used for queries
|
|
246
|
+
|
|
247
|
+
**Example audit output**:
|
|
248
|
+
```
|
|
249
|
+
❌ Direct Supabase client usage detected
|
|
250
|
+
File: src/components/UserList.tsx
|
|
251
|
+
Line: 15
|
|
252
|
+
Variable: supabase
|
|
253
|
+
Table: users
|
|
254
|
+
Reason: Direct Supabase client usage detected. Variable 'supabase' is created with createClient() and used for database queries. You MUST use useSecureSupabase() instead to ensure RLS policies and organisation context are enforced.
|
|
255
|
+
Recommendation: Replace with: import { useSecureSupabase } from '@jmruthers/pace-core/rbac'; const supabase = useSecureSupabase();
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The audit tool provides the same level of detection as the ESLint rule, making it useful for:
|
|
259
|
+
- Pre-commit checks
|
|
260
|
+
- CI/CD pipelines
|
|
261
|
+
- Code reviews
|
|
262
|
+
- Migration validation
|
|
263
|
+
|
|
264
|
+
## Best Practices
|
|
265
|
+
|
|
266
|
+
1. **Always use `useSecureSupabase()`** in React components
|
|
267
|
+
2. **Use `createSecureClient()`** for server-side or non-React code
|
|
268
|
+
3. **Never import `createClient`** from `@supabase/supabase-js` in component files
|
|
269
|
+
4. **Verify client security** in critical code paths (optional but recommended)
|
|
270
|
+
5. **Run ESLint** regularly to catch violations early
|
|
271
|
+
6. **Run audit script** before deploying to catch any missed violations
|
|
272
|
+
|
|
273
|
+
## Troubleshooting
|
|
274
|
+
|
|
275
|
+
### "ESLint reports error but I need createClient for auth"
|
|
276
|
+
|
|
277
|
+
**Solution**: Create a config file (e.g., `supabaseClient.ts`) and use `useSecureSupabase()` with the base client as fallback:
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
// supabaseClient.ts
|
|
281
|
+
import { createClient } from '@supabase/supabase-js';
|
|
282
|
+
export const supabase = createClient(url, key);
|
|
283
|
+
|
|
284
|
+
// MyComponent.tsx
|
|
285
|
+
import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
|
|
286
|
+
import { supabase } from './supabaseClient';
|
|
287
|
+
|
|
288
|
+
function MyComponent() {
|
|
289
|
+
const secureSupabase = useSecureSupabase(supabase);
|
|
290
|
+
// Use secureSupabase for all database operations
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### "I'm getting runtime warnings in development"
|
|
295
|
+
|
|
296
|
+
**Solution**: Ensure you're using `useSecureSupabase()` instead of a base client:
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
// ❌ Wrong
|
|
300
|
+
const supabase = createClient(url, key);
|
|
301
|
+
|
|
302
|
+
// ✅ Correct
|
|
303
|
+
const supabase = useSecureSupabase();
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### "How do I check if my client is secure?"
|
|
307
|
+
|
|
308
|
+
**Solution**: Use `isSecureClient()`:
|
|
309
|
+
|
|
310
|
+
```tsx
|
|
311
|
+
import { isSecureClient } from '@jmruthers/pace-core/rbac';
|
|
312
|
+
|
|
313
|
+
if (isSecureClient(supabase)) {
|
|
314
|
+
console.log('Client is secure!');
|
|
315
|
+
} else {
|
|
316
|
+
console.error('Client is NOT secure!');
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Summary
|
|
321
|
+
|
|
322
|
+
The protection system provides:
|
|
323
|
+
- ✅ **ESLint rules** to catch violations at development time
|
|
324
|
+
- ✅ **Runtime warnings** to alert developers in development mode
|
|
325
|
+
- ✅ **Type safety** to verify client security
|
|
326
|
+
- ✅ **Automatic marking** of secure clients
|
|
327
|
+
- ✅ **Audit scripts** to catch violations before deployment
|
|
328
|
+
|
|
329
|
+
By following these guidelines, you ensure that all database operations respect organisation context and RLS policies, preventing security vulnerabilities and data leakage.
|
|
330
|
+
|
package/docs/standards/README.md
CHANGED
|
@@ -57,6 +57,7 @@ The cursor rules cover:
|
|
|
57
57
|
- **05-bug-reports-and-features** - Templates for issue reporting
|
|
58
58
|
- **06-code-quality** - Enforce code quality standards
|
|
59
59
|
- **07-tech-stack-compliance** - Enforce tech stack versions
|
|
60
|
+
- **08-markup-quality** - Enforce clean markup standards, semantic HTML usage, and pace-core component patterns
|
|
60
61
|
|
|
61
62
|
### Audit Tool
|
|
62
63
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jmruthers/pace-core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"./core-usage-manifest.json": "./core-usage-manifest.json",
|
|
74
74
|
"./cursor-rules": "./cursor-rules",
|
|
75
75
|
"./scripts/install-cursor-rules": "./scripts/install-cursor-rules.cjs",
|
|
76
|
+
"./scripts/audit": "./scripts/audit/index.cjs",
|
|
76
77
|
"./scripts/audit-consuming-app": "./scripts/audit-consuming-app.cjs",
|
|
77
78
|
"./source": {
|
|
78
79
|
"import": "./src/index.ts",
|
|
@@ -237,10 +238,10 @@
|
|
|
237
238
|
"globals": "^16.3.0",
|
|
238
239
|
"jsdom": "^25.0.1",
|
|
239
240
|
"react-router-dom": "^6.26.2",
|
|
240
|
-
"
|
|
241
|
+
"tsup": "^8.5.0",
|
|
242
|
+
"typedoc": "^0.26.11",
|
|
241
243
|
"typedoc-plugin-markdown": "^3.17.1",
|
|
242
244
|
"typedoc-plugin-merge-modules": "^5.1.0",
|
|
243
|
-
"tsup": "^8.5.0",
|
|
244
245
|
"typescript": "^5.4.0",
|
|
245
246
|
"typescript-eslint": "^8.39.0",
|
|
246
247
|
"vite": "^6.0.3"
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Accessibility Check Module
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Audit/Checks/Accessibility
|
|
7
|
+
*
|
|
8
|
+
* Checks for:
|
|
9
|
+
* - Missing aria-* attributes
|
|
10
|
+
* - Missing keyboard navigation
|
|
11
|
+
* - Missing focus management
|
|
12
|
+
* - Missing alt text on images
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const { getRelativePath, getLineNumber } = require('../utils.cjs');
|
|
17
|
+
|
|
18
|
+
const accessibilityCheck = {
|
|
19
|
+
name: 'accessibility',
|
|
20
|
+
description: 'Accessibility checks (aria attributes, keyboard navigation, alt text)',
|
|
21
|
+
severity: 'warning',
|
|
22
|
+
|
|
23
|
+
async run(context) {
|
|
24
|
+
const { projectRoot, files } = context;
|
|
25
|
+
const issues = [];
|
|
26
|
+
const warnings = [];
|
|
27
|
+
const suggestions = [];
|
|
28
|
+
|
|
29
|
+
if (!files || files.length === 0) {
|
|
30
|
+
return { issues, warnings, suggestions };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const filePath of files) {
|
|
34
|
+
try {
|
|
35
|
+
// Only check React component files
|
|
36
|
+
if (!filePath.match(/\.(tsx|jsx)$/)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
41
|
+
const relativePath = getRelativePath(filePath, projectRoot);
|
|
42
|
+
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
43
|
+
|
|
44
|
+
// Skip root-level src directory - in pace-core repository, this is a demo/showcase app
|
|
45
|
+
// Note: We DO check packages/core/ files because accessibility issues (missing alt attributes, missing form labels) are real issues that should be fixed
|
|
46
|
+
const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
|
|
47
|
+
if (isRootSrc) {
|
|
48
|
+
continue; // Skip demo app files
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Skip scripts directory - utility scripts don't need accessibility validation
|
|
52
|
+
const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
|
|
53
|
+
if (isScript) {
|
|
54
|
+
continue; // Skip script files
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Skip pace-core library components and examples - these are designed to accept accessibility props
|
|
58
|
+
// Library components accept id, aria-label, etc. via props spread
|
|
59
|
+
// Examples are demonstration code, not production code
|
|
60
|
+
const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
|
|
61
|
+
const isExample = normalizedPath.includes('/examples/');
|
|
62
|
+
const isLibraryComponent = isPaceCorePackage && !isExample;
|
|
63
|
+
|
|
64
|
+
// Check for images without alt text
|
|
65
|
+
// Skip library components - they accept alt as a prop
|
|
66
|
+
if (!isLibraryComponent) {
|
|
67
|
+
const imgPattern = /<img[^>]*>/g;
|
|
68
|
+
let imgMatch;
|
|
69
|
+
while ((imgMatch = imgPattern.exec(content)) !== null) {
|
|
70
|
+
const imgTag = imgMatch[0];
|
|
71
|
+
// Check for alt attribute, including React props (alt=, alt =, alt={)
|
|
72
|
+
const hasAlt = imgTag.includes('alt=') || imgTag.includes('alt =') || imgTag.includes('alt={');
|
|
73
|
+
if (!hasAlt) {
|
|
74
|
+
warnings.push({
|
|
75
|
+
type: 'missing-alt-text',
|
|
76
|
+
file: relativePath,
|
|
77
|
+
line: getLineNumber(content, imgMatch.index),
|
|
78
|
+
message: 'Image element missing alt attribute',
|
|
79
|
+
recommendation: 'Add alt text to all images for accessibility: <img alt="description" ... />'
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check for buttons/clickable elements without aria-label or accessible text
|
|
86
|
+
const buttonPattern = /<(button|a|div)\s+[^>]*(onClick|role=["']button["'])[^>]*>/g;
|
|
87
|
+
let buttonMatch;
|
|
88
|
+
while ((buttonMatch = buttonPattern.exec(content)) !== null) {
|
|
89
|
+
const buttonTag = buttonMatch[0];
|
|
90
|
+
const hasAriaLabel = buttonTag.includes('aria-label=') || buttonTag.includes('ariaLabel=');
|
|
91
|
+
const hasAccessibleText = buttonTag.includes('>') && content.substring(buttonMatch.index).match(/<[^>]*>([^<]+)</);
|
|
92
|
+
|
|
93
|
+
if (!hasAriaLabel && !hasAccessibleText) {
|
|
94
|
+
warnings.push({
|
|
95
|
+
type: 'missing-aria-label',
|
|
96
|
+
file: relativePath,
|
|
97
|
+
line: getLineNumber(content, buttonMatch.index),
|
|
98
|
+
message: 'Interactive element missing accessible label',
|
|
99
|
+
recommendation: 'Add aria-label or ensure element contains accessible text content'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check for form inputs without labels
|
|
105
|
+
// Skip library components - they accept id, aria-label, etc. via props spread
|
|
106
|
+
// Skip examples - they're demonstration code
|
|
107
|
+
if (!isLibraryComponent && !isExample) {
|
|
108
|
+
const inputPattern = /<input[^>]*>/g;
|
|
109
|
+
let inputMatch;
|
|
110
|
+
while ((inputMatch = inputPattern.exec(content)) !== null) {
|
|
111
|
+
// Get the full input tag including multi-line attributes
|
|
112
|
+
const inputStart = inputMatch.index;
|
|
113
|
+
const afterInput = content.substring(inputStart, Math.min(content.length, inputStart + 500));
|
|
114
|
+
const inputTagEnd = afterInput.indexOf('>');
|
|
115
|
+
const fullInputTag = inputTagEnd !== -1 ? afterInput.substring(0, inputTagEnd + 1) : inputMatch[0];
|
|
116
|
+
|
|
117
|
+
// Check for id (including React props)
|
|
118
|
+
const hasId = /id=["']([^"']+)["']|id=\{/.test(fullInputTag);
|
|
119
|
+
// Check for aria-label (including React props)
|
|
120
|
+
const hasAriaLabel = /aria-label=["']|ariaLabel=|aria-label=\{/.test(fullInputTag);
|
|
121
|
+
|
|
122
|
+
if (hasId) {
|
|
123
|
+
const idMatch = fullInputTag.match(/id=["']([^"']+)["']/);
|
|
124
|
+
if (idMatch) {
|
|
125
|
+
const id = idMatch[1];
|
|
126
|
+
// Check if there's a corresponding label
|
|
127
|
+
const beforeInput = content.substring(Math.max(0, inputMatch.index - 200), inputMatch.index);
|
|
128
|
+
const hasLabel = new RegExp(`<label[^>]*for=["']${id}["']`, 'i').test(beforeInput);
|
|
129
|
+
|
|
130
|
+
if (!hasLabel && !hasAriaLabel) {
|
|
131
|
+
warnings.push({
|
|
132
|
+
type: 'missing-input-label',
|
|
133
|
+
file: relativePath,
|
|
134
|
+
line: getLineNumber(content, inputMatch.index),
|
|
135
|
+
message: 'Form input missing associated label',
|
|
136
|
+
recommendation: 'Add a <label> element with for attribute matching input id, or use aria-label'
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} else if (!hasAriaLabel) {
|
|
141
|
+
warnings.push({
|
|
142
|
+
type: 'missing-input-label',
|
|
143
|
+
file: relativePath,
|
|
144
|
+
line: getLineNumber(content, inputMatch.index),
|
|
145
|
+
message: 'Form input missing id and label',
|
|
146
|
+
recommendation: 'Add id to input and corresponding label, or use aria-label'
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Check for missing focus management in modals/dialogs
|
|
153
|
+
const dialogPattern = /<Dialog|<dialog/gi;
|
|
154
|
+
if (dialogPattern.test(content)) {
|
|
155
|
+
// Skip Dialog.tsx itself - it IS the Dialog component
|
|
156
|
+
if (normalizedPath.includes('/Dialog/Dialog.tsx') || normalizedPath.includes('/Dialog/Dialog.jsx')) {
|
|
157
|
+
continue; // Skip the Dialog component file itself
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check if Dialog from pace-core is used (which handles focus automatically)
|
|
161
|
+
// Check for both published package import and relative imports (within pace-core repo)
|
|
162
|
+
const usesPaceCoreDialog = content.includes('from \'@jmruthers/pace-core\'') ||
|
|
163
|
+
content.includes('from "@jmruthers/pace-core"') ||
|
|
164
|
+
content.includes('from \'../Dialog') ||
|
|
165
|
+
content.includes('from "../Dialog') ||
|
|
166
|
+
content.includes('from \'../../Dialog') ||
|
|
167
|
+
content.includes('from "../../Dialog') ||
|
|
168
|
+
content.includes('from \'../../../Dialog') ||
|
|
169
|
+
content.includes('from "../../../Dialog') ||
|
|
170
|
+
content.includes('from \'./Dialog') ||
|
|
171
|
+
content.includes('from "./Dialog');
|
|
172
|
+
|
|
173
|
+
if (!usesPaceCoreDialog) {
|
|
174
|
+
suggestions.push({
|
|
175
|
+
type: 'dialog-focus-management',
|
|
176
|
+
file: relativePath,
|
|
177
|
+
message: 'Custom dialog implementation detected',
|
|
178
|
+
recommendation: 'Use Dialog component from @jmruthers/pace-core which handles focus management automatically'
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check for color contrast issues (heuristic - check for color props without sufficient contrast)
|
|
184
|
+
const colorPattern = /(?:color|bg-|text-)=["']([^"']+)["']/g;
|
|
185
|
+
// This is a simplified check - full implementation would need color contrast calculation
|
|
186
|
+
// For now, just suggest using pace-core components which handle contrast
|
|
187
|
+
|
|
188
|
+
} catch (error) {
|
|
189
|
+
// Skip files with errors
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return { issues, warnings, suggestions };
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
module.exports = accessibilityCheck;
|