@jmruthers/pace-core 0.6.5 → 0.6.7
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 +104 -0
- package/README.md +5 -403
- package/audit-tool/00-dependencies.cjs +394 -0
- package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
- package/audit-tool/audits/02-project-structure.cjs +255 -0
- package/audit-tool/audits/03-architecture.cjs +196 -0
- package/audit-tool/audits/04-code-quality.cjs +149 -0
- package/audit-tool/audits/05-styling.cjs +224 -0
- package/audit-tool/audits/06-security-rbac.cjs +544 -0
- package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
- package/audit-tool/audits/08-testing-documentation.cjs +202 -0
- package/audit-tool/audits/09-operations.cjs +208 -0
- package/audit-tool/index.cjs +291 -0
- package/audit-tool/utils/code-utils.cjs +218 -0
- package/audit-tool/utils/file-utils.cjs +230 -0
- package/audit-tool/utils/report-utils.cjs +241 -0
- package/core-usage-manifest.json +93 -0
- package/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/01-pace-core-compliance.mdc +586 -0
- package/cursor-rules/02-project-structure.mdc +42 -4
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
- package/cursor-rules/06-security-rbac.mdc +518 -0
- package/cursor-rules/07-api-tech-stack.mdc +377 -0
- package/cursor-rules/08-testing-documentation.mdc +324 -0
- package/cursor-rules/09-operations.mdc +365 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/DataTable-7PMH7XN7.js +15 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
- package/dist/chunk-6F3IILHI.js +62 -0
- package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
- package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
- package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
- package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
- package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/chunk-GHYHJTYV.js +994 -0
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
- package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
- package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
- package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
- package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
- package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
- package/dist/components.d.ts +7 -5
- package/dist/components.js +46 -257
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +35 -0
- package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
- package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
- package/dist/eslint-rules/rules/05-styling.cjs +61 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/eslint-rules/utils/helpers.cjs +42 -0
- package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +62 -172
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +12 -11
- package/dist/index.js +67 -660
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +8 -35
- package/dist/rbac/eslint-rules.d.ts +46 -44
- package/dist/rbac/eslint-rules.js +7 -4
- package/dist/rbac/index.d.ts +109 -586
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- package/dist/theming/runtime.d.ts +14 -1
- package/dist/theming/runtime.js +3 -19
- package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
- package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +17 -7
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +301 -871
- package/docs/api-reference/components.md +21 -21
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/hooks.md +80 -80
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/api-reference/types.md +1 -1
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -1
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +6 -6
- package/docs/core-concepts/permissions.md +6 -6
- package/docs/documentation-index.md +12 -18
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +4 -4
- package/docs/getting-started/examples/full-featured-app.md +1 -1
- package/docs/getting-started/faq.md +2 -2
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-reference.md +4 -4
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/authentication.md +15 -15
- package/docs/implementation-guides/component-styling.md +1 -1
- package/docs/implementation-guides/data-tables.md +126 -33
- package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
- package/docs/implementation-guides/dynamic-colors.md +3 -3
- package/docs/implementation-guides/file-upload-storage.md +2 -2
- package/docs/implementation-guides/hierarchical-datatable.md +40 -60
- package/docs/implementation-guides/inactivity-tracking.md +3 -3
- package/docs/implementation-guides/large-datasets.md +3 -2
- package/docs/implementation-guides/organisation-security.md +2 -2
- package/docs/implementation-guides/performance.md +2 -2
- package/docs/implementation-guides/permission-enforcement.md +5 -1
- package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
- package/docs/migration/V0.4.0_rbac-migration.md +6 -6
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +17 -8
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/event-based-apps.md +3 -3
- package/docs/rbac/examples.md +41 -41
- package/docs/rbac/getting-started.md +37 -37
- package/docs/rbac/performance.md +1 -1
- package/docs/rbac/quick-start.md +52 -52
- package/docs/rbac/secure-client-protection.md +1 -35
- package/docs/rbac/troubleshooting.md +1 -1
- package/docs/security/README.md +5 -5
- package/docs/standards/0-standards-overview.md +220 -0
- package/docs/standards/1-pace-core-compliance-standards.md +986 -0
- package/docs/standards/2-project-structure-standards.md +949 -0
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/5-styling-standards.md +348 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
- package/docs/standards/7-api-tech-stack-standards.md +662 -0
- package/docs/standards/8-testing-documentation-standards.md +401 -0
- package/docs/standards/9-operations-standards.md +1102 -0
- package/docs/standards/README.md +185 -57
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/common-issues.md +2 -2
- package/docs/troubleshooting/debugging.md +9 -9
- package/docs/troubleshooting/migration.md +4 -4
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +33 -6
- package/package.json +35 -23
- package/scripts/install-cursor-rules.cjs +25 -6
- package/scripts/install-eslint-config.cjs +284 -0
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
- package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
- package/src/__tests__/templates/component.test.template.tsx +18 -15
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/Calendar/Calendar.tsx +201 -47
- package/src/components/ContextSelector/ContextSelector.tsx +106 -119
- package/src/components/DataTable/AUDIT_REPORT.md +293 -0
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
- package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +12 -9
- package/src/components/DataTable/components/EmptyState.tsx +10 -9
- package/src/components/DataTable/components/FilterRow.tsx +2 -4
- package/src/components/DataTable/components/ImportModal.tsx +124 -126
- package/src/components/DataTable/components/LoadingState.tsx +5 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/SortIndicator.tsx +50 -0
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/components/index.ts +2 -1
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
- package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
- package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
- package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
- package/src/components/DataTable/types.ts +5 -18
- package/src/components/DataTable/utils/a11yUtils.ts +17 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
- package/src/components/DateTimeField/DateTimeField.tsx +10 -9
- package/src/components/Dialog/Dialog.test.tsx +128 -104
- package/src/components/Dialog/Dialog.tsx +742 -24
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
- package/src/components/FileDisplay/FileDisplay.tsx +23 -17
- package/src/components/FileUpload/FileUpload.test.tsx +52 -14
- package/src/components/FileUpload/FileUpload.tsx +112 -130
- package/src/components/Form/Form.test.tsx +6 -8
- package/src/components/Form/Form.tsx +365 -4
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +109 -98
- package/src/components/Select/types.ts +4 -1
- package/src/components/UserMenu/UserMenu.tsx +9 -6
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
- package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
- package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
- package/src/hooks/public/usePublicEvent.ts +67 -195
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +24 -14
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +5 -5
- package/src/hooks/useAppConfig.ts +28 -26
- package/src/hooks/useEventTheme.test.ts +217 -239
- package/src/hooks/useEventTheme.ts +16 -28
- package/src/hooks/useFileDisplay.ts +2 -2
- package/src/hooks/useOrganisationPermissions.ts +5 -7
- package/src/hooks/useQueryCache.ts +0 -1
- package/src/hooks/useSessionDraft.ts +380 -0
- package/src/hooks/useSessionRestoration.ts +3 -1
- package/src/icons/index.ts +27 -0
- package/src/index.ts +5 -0
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
- package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
- package/src/providers/__tests__/EventProvider.test.tsx +61 -61
- package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
- package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
- package/src/rbac/adapters.tsx +7 -295
- package/src/rbac/api.test.ts +44 -56
- package/src/rbac/api.ts +10 -17
- package/src/rbac/cache-invalidation.ts +0 -1
- package/src/rbac/compliance/index.ts +10 -0
- package/src/rbac/compliance/pattern-detector.ts +553 -0
- package/src/rbac/compliance/runtime-compliance.ts +22 -0
- package/src/rbac/components/AccessDenied.tsx +150 -0
- package/src/rbac/components/NavigationGuard.tsx +12 -20
- package/src/rbac/components/PagePermissionGuard.tsx +4 -24
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
- package/src/rbac/components/index.ts +3 -41
- package/src/rbac/eslint-rules.js +1 -1
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/permissions/index.ts +0 -3
- package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
- package/src/rbac/hooks/usePermissions.ts +0 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
- package/src/rbac/hooks/useResolvedScope.ts +58 -140
- package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
- package/src/rbac/hooks/useResourcePermissions.ts +139 -48
- package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
- package/src/rbac/hooks/useRoleManagement.ts +147 -19
- package/src/rbac/hooks/useSecureSupabase.ts +4 -8
- package/src/rbac/index.ts +7 -9
- package/src/rbac/utils/contextValidator.ts +9 -7
- package/src/services/AuthService.ts +130 -18
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +16 -0
- package/src/services/OrganisationService.ts +7 -44
- package/src/services/__tests__/OrganisationService.test.ts +26 -8
- package/src/services/base/BaseService.ts +0 -3
- package/src/styles/core.css +7 -0
- package/src/theming/__tests__/parseEventColours.test.ts +9 -3
- package/src/theming/parseEventColours.ts +22 -10
- package/src/types/database.generated.ts +4733 -3809
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
- package/src/utils/context/organisationContext.test.ts +13 -28
- package/src/utils/context/organisationContext.ts +21 -52
- package/src/utils/dynamic/dynamicUtils.ts +1 -1
- package/src/utils/file-reference/index.ts +39 -15
- package/src/utils/formatting/formatDateTime.test.ts +3 -2
- package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
- package/src/utils/index.ts +4 -1
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
- package/src/utils/persistence/keyDerivation.ts +304 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
- package/src/utils/security/secureStorage.ts +5 -5
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/helpers.ts +3 -3
- package/src/utils/supabase/createBaseClient.ts +147 -0
- package/src/utils/timezone/timezone.test.ts +1 -2
- package/src/utils/timezone/timezone.ts +1 -1
- package/src/utils/validation/csrf.ts +4 -4
- package/cursor-rules/00-pace-core-compliance.mdc +0 -331
- package/cursor-rules/01-standards-compliance.mdc +0 -244
- package/cursor-rules/04-testing-standards.mdc +0 -268
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
- package/cursor-rules/06-code-quality.mdc +0 -309
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
- package/cursor-rules/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-AOVNCPTX.js +0 -175
- package/dist/DataTable-AOVNCPTX.js.map +0 -1
- package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
- package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
- package/dist/api-O6HTBX5Y.js +0 -52
- package/dist/api-O6HTBX5Y.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6COVEUS7.js.map +0 -1
- package/dist/chunk-AFVQODI2.js +0 -263
- package/dist/chunk-AFVQODI2.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.js.map +0 -1
- package/dist/chunk-EFN2EIMK.js.map +0 -1
- package/dist/chunk-FFQEQTNW.js.map +0 -1
- package/dist/chunk-FMUCXFII.js.map +0 -1
- package/dist/chunk-G37KK66H.js.map +0 -1
- package/dist/chunk-G7QEZTYQ.js +0 -2053
- package/dist/chunk-G7QEZTYQ.js.map +0 -1
- package/dist/chunk-HU2C6SSC.js.map +0 -1
- package/dist/chunk-IHB5DR3H.js.map +0 -1
- package/dist/chunk-IVOFDYWT.js.map +0 -1
- package/dist/chunk-J36DSWQK.js.map +0 -1
- package/dist/chunk-JGRYX5UX.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js +0 -1
- package/dist/chunk-KQCRWDSA.js.map +0 -1
- package/dist/chunk-L4OXEN46.js.map +0 -1
- package/dist/chunk-LMC26NLJ.js +0 -84
- package/dist/chunk-LMC26NLJ.js.map +0 -1
- package/dist/chunk-M43Y4SSO.js.map +0 -1
- package/dist/chunk-M7MPQISP.js.map +0 -1
- package/dist/chunk-NTM7ZSB6.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-RGAWHO7N.js.map +0 -1
- package/dist/chunk-UPPMRMYG.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-5OGXSPKS.js +0 -9
- package/dist/contextValidator-5OGXSPKS.js.map +0 -1
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
- package/dist/hooks.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/providers.js.map +0 -1
- package/dist/rbac/eslint-rules.js.map +0 -1
- package/dist/rbac/index.js.map +0 -1
- package/dist/styles/index.js.map +0 -1
- package/dist/theming/runtime.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -601
- package/docs/best-practices/common-patterns.md +0 -516
- package/docs/best-practices/deployment.md +0 -1103
- package/docs/best-practices/performance.md +0 -1328
- package/docs/best-practices/security.md +0 -940
- package/docs/best-practices/testing.md +0 -1034
- package/docs/rbac/compliance/compliance-guide.md +0 -544
- package/docs/standards/01-architecture-standard.md +0 -44
- package/docs/standards/02-api-and-rpc-standard.md +0 -39
- package/docs/standards/03-component-standard.md +0 -32
- package/docs/standards/04-code-style-standard.md +0 -32
- package/docs/standards/05-security-standard.md +0 -44
- package/docs/standards/06-testing-and-docs-standard.md +0 -29
- package/docs/standards/pace-core-compliance.md +0 -432
- package/scripts/audit/core/checks/accessibility.cjs +0 -197
- package/scripts/audit/core/checks/api-usage.cjs +0 -191
- package/scripts/audit/core/checks/bundle.cjs +0 -142
- package/scripts/audit/core/checks/compliance.cjs +0 -2706
- package/scripts/audit/core/checks/config.cjs +0 -54
- package/scripts/audit/core/checks/coverage.cjs +0 -84
- package/scripts/audit/core/checks/dependencies.cjs +0 -994
- package/scripts/audit/core/checks/documentation.cjs +0 -268
- package/scripts/audit/core/checks/environment.cjs +0 -116
- package/scripts/audit/core/checks/error-handling.cjs +0 -340
- package/scripts/audit/core/checks/forms.cjs +0 -172
- package/scripts/audit/core/checks/heuristics.cjs +0 -68
- package/scripts/audit/core/checks/hooks.cjs +0 -334
- package/scripts/audit/core/checks/imports.cjs +0 -244
- package/scripts/audit/core/checks/performance.cjs +0 -325
- package/scripts/audit/core/checks/routes.cjs +0 -117
- package/scripts/audit/core/checks/state.cjs +0 -130
- package/scripts/audit/core/checks/structure.cjs +0 -65
- package/scripts/audit/core/checks/style.cjs +0 -584
- package/scripts/audit/core/checks/testing.cjs +0 -122
- package/scripts/audit/core/checks/typescript.cjs +0 -61
- package/scripts/audit/core/scanner.cjs +0 -199
- package/scripts/audit/core/utils.cjs +0 -137
- package/scripts/audit/index.cjs +0 -223
- package/scripts/audit/reporters/console.cjs +0 -151
- package/scripts/audit/reporters/json.cjs +0 -54
- package/scripts/audit/reporters/markdown.cjs +0 -124
- package/scripts/audit-consuming-app.cjs +0 -86
- package/src/components/DataTable/components/DataTableBody.tsx +0 -454
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
- package/src/components/DataTable/components/ExpandButton.tsx +0 -113
- package/src/components/DataTable/components/GroupHeader.tsx +0 -54
- package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
- package/src/components/DataTable/core/DataTableContext.tsx +0 -216
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
- package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
- package/src/components/DataTable/utils/debugTools.ts +0 -514
- package/src/eslint-rules/pace-core-compliance.js +0 -638
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
- package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
- package/src/rbac/components/NavigationProvider.test.tsx +0 -481
- package/src/rbac/components/NavigationProvider.tsx +0 -345
- package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
- package/src/rbac/components/PagePermissionProvider.tsx +0 -279
- package/src/rbac/components/PermissionEnforcer.tsx +0 -312
- package/src/rbac/components/RoleBasedRouter.tsx +0 -440
- package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
- package/src/rbac/components/SecureDataProvider.tsx +0 -339
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
|
@@ -7,6 +7,61 @@ rulesVersion: "2025-01-28"
|
|
|
7
7
|
---
|
|
8
8
|
# Markup Quality Guide
|
|
9
9
|
|
|
10
|
+
**📚 Human-Readable Standard**: See [5-styling-standards.md](../../packages/core/docs/standards/5-styling-standards.md) for complete documentation including **CRITICAL CSS configuration requirements**.
|
|
11
|
+
|
|
12
|
+
**⚠️ IMPORTANT**: This rule is ALWAYS APPLIED. The standard includes required CSS setup that MUST be followed for pace-core components to render correctly.
|
|
13
|
+
|
|
14
|
+
## AI Agent Instructions
|
|
15
|
+
|
|
16
|
+
**When writing or modifying code, ALWAYS:**
|
|
17
|
+
1. **Use pace-core components** - Never use native HTML elements when pace-core provides components
|
|
18
|
+
2. **Use semantic HTML** - Always use semantic elements (`<main>`, `<section>`, `<article>`, etc.) instead of `<div>` wrappers
|
|
19
|
+
3. **Never use inline styles** - Never use `style={{...}}`, always use pace-core components or Tailwind classes
|
|
20
|
+
4. **Use Grid for layout** - Prefer CSS Grid over Flexbox for centering, two-dimensional layouts, and full-page layouts
|
|
21
|
+
5. **Minimize elements** - Use the fewest elements possible, prefer React Fragments over wrapper divs
|
|
22
|
+
6. **Check CSS setup** - Before using pace-core components, verify CSS is configured correctly (see checklist below)
|
|
23
|
+
|
|
24
|
+
**Decision Tree: Markup & Styling**
|
|
25
|
+
```
|
|
26
|
+
1. What element should I use?
|
|
27
|
+
├─ Button → <Button> from pace-core (not <button>)
|
|
28
|
+
├─ Input → <Input> from pace-core (not <input>)
|
|
29
|
+
├─ Page wrapper → <main> (not <div>)
|
|
30
|
+
├─ Section → <section> (not <div>)
|
|
31
|
+
├─ Article → <article> (not <div>)
|
|
32
|
+
└─ Navigation → <nav> (not <div>)
|
|
33
|
+
|
|
34
|
+
2. Do I need a wrapper?
|
|
35
|
+
├─ For React single root → Use Fragment (<>...</>)
|
|
36
|
+
├─ For styling only → Apply to existing semantic parent
|
|
37
|
+
└─ For layout → Use Grid (grid place-items-center, grid grid-cols-*)
|
|
38
|
+
|
|
39
|
+
3. How do I center content?
|
|
40
|
+
├─ Both axes → grid place-items-center
|
|
41
|
+
└─ Single axis → flex (but prefer grid)
|
|
42
|
+
|
|
43
|
+
4. Am I using inline styles?
|
|
44
|
+
├─ YES → WRONG - Use pace-core component or Tailwind class
|
|
45
|
+
└─ NO → Continue
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## ⚠️ CRITICAL: CSS Configuration Required
|
|
49
|
+
|
|
50
|
+
**Before using pace-core components, you MUST configure CSS correctly.** Without proper configuration, pace-core components will appear unstyled or with incorrect styling.
|
|
51
|
+
|
|
52
|
+
**MUST read the standard for complete CSS setup instructions**: [5-styling-standards.md](../../packages/core/docs/standards/5-styling-standards.md)
|
|
53
|
+
|
|
54
|
+
**Quick checklist:**
|
|
55
|
+
- [ ] `src/app.css` exists with `@import "tailwindcss";`
|
|
56
|
+
- [ ] `@source` directives configured correctly (relative to CSS file location)
|
|
57
|
+
- [ ] `@import "@jmruthers/pace-core/styles/core.css";` in `app.css`
|
|
58
|
+
- [ ] All three color palettes defined (main, sec, acc) with all shades (50-950)
|
|
59
|
+
- [ ] `app.css` imported in `main.tsx` (NOT `core.css` directly)
|
|
60
|
+
|
|
61
|
+
**See the standard for detailed setup instructions and troubleshooting.**
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
10
65
|
This guide enforces clean markup standards, semantic HTML usage, and proper pace-core component patterns to ensure maintainable, accessible, and consistent code.
|
|
11
66
|
|
|
12
67
|
## pace-core First
|
|
@@ -17,18 +72,29 @@ This guide enforces clean markup standards, semantic HTML usage, and proper pace
|
|
|
17
72
|
|
|
18
73
|
```tsx
|
|
19
74
|
// ❌ WRONG: Custom button or native HTML element
|
|
20
|
-
<button className="btn-primary"
|
|
21
|
-
|
|
75
|
+
<button className="btn-primary" onClick={handleClick}>
|
|
76
|
+
Click me
|
|
77
|
+
</button>
|
|
78
|
+
<input
|
|
79
|
+
type="text"
|
|
80
|
+
className="form-input"
|
|
81
|
+
value={value}
|
|
82
|
+
onChange={handleChange}
|
|
83
|
+
/>
|
|
22
84
|
|
|
23
85
|
// ✅ CORRECT: Use pace-core components
|
|
24
86
|
import { Button, Input } from '@jmruthers/pace-core';
|
|
25
|
-
<Button>Click me</Button>
|
|
26
|
-
<Input
|
|
87
|
+
<Button onClick={handleClick}>Click me</Button>
|
|
88
|
+
<Input
|
|
89
|
+
type="text"
|
|
90
|
+
value={value}
|
|
91
|
+
onChange={handleChange}
|
|
92
|
+
/>
|
|
27
93
|
```
|
|
28
94
|
|
|
29
95
|
### MUST NOT: Add Custom Styles to pace-core Components
|
|
30
96
|
|
|
31
|
-
**
|
|
97
|
+
**NEVER add custom styles when pace-core components already provide styling. Use component variants/props instead.**
|
|
32
98
|
|
|
33
99
|
```tsx
|
|
34
100
|
// ❌ WRONG: Overriding pace-core component styles
|
|
@@ -88,41 +154,45 @@ import { Button, Input } from '@jmruthers/pace-core';
|
|
|
88
154
|
<footer>Footer</footer>
|
|
89
155
|
```
|
|
90
156
|
|
|
91
|
-
### MUST
|
|
157
|
+
### MUST: Prefer Semantic HTML (Limit `<div>`)
|
|
158
|
+
|
|
159
|
+
**ALWAYS prefer semantic HTML elements** (`<main>`, `<section>`, `<article>`, `<header>`, `<footer>`, `<nav>`, lists, etc.).
|
|
160
|
+
Using semantic elements improves accessibility, maintainability, and consistency.
|
|
92
161
|
|
|
93
|
-
|
|
162
|
+
#### `<div>` usage policy
|
|
163
|
+
|
|
164
|
+
- **MUST NOT** use `<div>` when a clear semantic element exists.
|
|
165
|
+
- `<div>` is **allowed only** for:
|
|
166
|
+
- purely presentational layout wrappers (flex/grid grouping) when no semantic element fits, OR
|
|
167
|
+
- React portal roots / app shell roots, OR
|
|
168
|
+
- third-party libraries that require specific DOM structure (document the reason in a short comment).
|
|
169
|
+
|
|
170
|
+
#### Severity guidance (for audits)
|
|
171
|
+
|
|
172
|
+
- **HIGH/BLOCKER**: `<div>` used in place of landmark/semantic structure (e.g., main page wrapper that should be `<main>`, navigation that should be `<nav>`, lists that should be `<ul>/<ol>`).
|
|
173
|
+
- **MEDIUM**: `<div>` used as a generic wrapper inside semantic structure where it could be a fragment or semantic element.
|
|
174
|
+
- **LOW**: `<div>` used for unavoidable third-party structure with a comment justifying it.
|
|
175
|
+
|
|
176
|
+
**Examples:**
|
|
94
177
|
|
|
95
178
|
```tsx
|
|
96
|
-
// ❌ WRONG:
|
|
97
|
-
<div className="
|
|
98
|
-
<div className="
|
|
179
|
+
// ❌ WRONG: Non-semantic page structure
|
|
180
|
+
<div className="page">
|
|
181
|
+
<div className="nav">...</div>
|
|
182
|
+
<div className="content">...</div>
|
|
99
183
|
</div>
|
|
100
184
|
|
|
101
|
-
// ✅ CORRECT:
|
|
185
|
+
// ✅ CORRECT: Semantic structure
|
|
102
186
|
<main>
|
|
103
|
-
<
|
|
187
|
+
<nav>...</nav>
|
|
188
|
+
<section>...</section>
|
|
104
189
|
</main>
|
|
105
|
-
// Or for grouping without semantic meaning:
|
|
106
|
-
<>
|
|
107
|
-
<Component1 />
|
|
108
|
-
<Component2 />
|
|
109
|
-
</>
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### MUST: Choose Most Semantic Element
|
|
113
190
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<section>Section of content</section>
|
|
120
|
-
<article>Article content</article>
|
|
121
|
-
<header>Header content</header>
|
|
122
|
-
<footer>Footer content</footer>
|
|
123
|
-
<nav>Navigation</nav>
|
|
124
|
-
<ul><li>List items</li></ul>
|
|
125
|
-
<p>Paragraph text</p>
|
|
191
|
+
// ✅ ACCEPTABLE: Layout-only wrapper (no semantic fit)
|
|
192
|
+
// (Prefer fragments where possible)
|
|
193
|
+
<section>
|
|
194
|
+
<div className="flex gap-2">...</div>
|
|
195
|
+
</section>
|
|
126
196
|
```
|
|
127
197
|
|
|
128
198
|
## Typography & Styling
|
|
@@ -445,8 +515,8 @@ Before committing code, verify:
|
|
|
445
515
|
|
|
446
516
|
## Reference
|
|
447
517
|
|
|
448
|
-
- **pace-core Components**: See
|
|
518
|
+
- **pace-core Components**: See [01-pace-core-compliance.mdc](./01-pace-core-compliance.mdc) for component usage
|
|
449
519
|
- **Semantic HTML**: https://developer.mozilla.org/en-US/docs/Glossary/Semantics
|
|
450
520
|
- **React Fragments**: https://react.dev/reference/react/Fragment
|
|
451
|
-
- **Accessibility**: See
|
|
521
|
+
- **Accessibility**: See [04-code-quality.mdc](./04-code-quality.mdc) for accessibility requirements
|
|
452
522
|
- **When in doubt**: Remove the element, remove the style, and rely on pace-core or semantic HTML
|
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enforce RBAC contract compliance, RLS policy patterns, and security requirements
|
|
3
|
+
globs: ["src/**/*.{ts,tsx,js,jsx}", "supabase/migrations/**/*.sql"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
paceCoreVersion: "0.6.x"
|
|
6
|
+
rulesVersion: "2025-01-28"
|
|
7
|
+
---
|
|
8
|
+
# Security & RBAC Standards Guide
|
|
9
|
+
|
|
10
|
+
**📚 Human-Readable Standard**: See [6-security-rbac-standards.md](../../packages/core/docs/standards/6-security-rbac-standards.md) for complete documentation including RLS policy patterns, helper functions, and security requirements.
|
|
11
|
+
|
|
12
|
+
This guide enforces the **mandatory RBAC contract** and security patterns between pace-core and consuming apps.
|
|
13
|
+
|
|
14
|
+
**⚠️ CRITICAL**: This contract is **non-negotiable**. See [RBAC Contract](../../packages/core/docs/rbac/RBAC_CONTRACT.md) for complete documentation.
|
|
15
|
+
|
|
16
|
+
## AI Agent Instructions
|
|
17
|
+
|
|
18
|
+
**When writing or modifying code, ALWAYS:**
|
|
19
|
+
1. **Protect all pages** - Wrap every protected page with `PagePermissionGuard` (no exceptions)
|
|
20
|
+
2. **Use pace-core hooks directly** - Never create wrapper functions around `useCan` or `useResourcePermissions`
|
|
21
|
+
3. **Use RESOURCE_NAMES constants** - Never use string literals in `useResourcePermissions` calls
|
|
22
|
+
4. **Validate all inputs** - Always validate user input with Zod schemas before processing
|
|
23
|
+
5. **Never expose internals** - Never show SQL errors, stack traces, or internal details to users
|
|
24
|
+
6. **Use helper functions in RLS** - Always use STABLE SECURITY DEFINER helper functions, never subqueries
|
|
25
|
+
7. **Sanitize logs** - Never log passwords, tokens, or sensitive data
|
|
26
|
+
|
|
27
|
+
**Decision Tree: Security & Permissions**
|
|
28
|
+
```
|
|
29
|
+
1. Is this a protected page?
|
|
30
|
+
├─ YES → Wrap with PagePermissionGuard (no exceptions)
|
|
31
|
+
└─ NO → Continue
|
|
32
|
+
|
|
33
|
+
2. Do I need to check permissions?
|
|
34
|
+
├─ YES → Use pace-core hooks directly (useCan, useResourcePermissions)
|
|
35
|
+
└─ NO → Continue
|
|
36
|
+
|
|
37
|
+
3. Am I handling user input?
|
|
38
|
+
├─ YES → Validate with Zod schema
|
|
39
|
+
└─ NO → Continue
|
|
40
|
+
|
|
41
|
+
4. Am I logging or showing errors?
|
|
42
|
+
├─ YES → Never expose internals, sanitize sensitive data
|
|
43
|
+
└─ NO → Continue
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## MUST: Use PagePermissionGuard for All Protected Pages
|
|
47
|
+
|
|
48
|
+
**MUST wrap all protected pages with `PagePermissionGuard`:**
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
// ✅ CORRECT
|
|
52
|
+
import { PagePermissionGuard } from '@jmruthers/pace-core/rbac';
|
|
53
|
+
|
|
54
|
+
function DashboardPage() {
|
|
55
|
+
return (
|
|
56
|
+
<PagePermissionGuard pageName="dashboard" operation="read">
|
|
57
|
+
<DashboardContent />
|
|
58
|
+
</PagePermissionGuard>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**MUST NOT:**
|
|
64
|
+
- Render protected content without `PagePermissionGuard`
|
|
65
|
+
- Create wrapper components around `PagePermissionGuard`
|
|
66
|
+
- Bypass page-level permission checks
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
// ❌ FORBIDDEN - No guard
|
|
70
|
+
function DashboardPage() {
|
|
71
|
+
return <DashboardContent />; // ERROR: Unprotected page
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ❌ FORBIDDEN - Wrapper component
|
|
75
|
+
function EventPageGuard({ pageName, children }) {
|
|
76
|
+
return <PagePermissionGuard pageName={pageName}>{children}</PagePermissionGuard>;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## MUST: Use pace-core Permission Hooks Directly
|
|
81
|
+
|
|
82
|
+
**MUST use pace-core hooks directly without wrappers:**
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
// ✅ CORRECT
|
|
86
|
+
import { useCan, useResourcePermissions } from '@jmruthers/pace-core/rbac';
|
|
87
|
+
import { RESOURCE_NAMES } from '@/config/resource-names';
|
|
88
|
+
|
|
89
|
+
function MyComponent() {
|
|
90
|
+
const { canUpdate, canDelete } = useResourcePermissions(RESOURCE_NAMES.JOURNAL);
|
|
91
|
+
const canEdit = useCan(userId, scope, 'update:users');
|
|
92
|
+
// Use canUpdate, canDelete, canEdit directly
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**MUST NOT:**
|
|
97
|
+
- Create wrapper functions around permission hooks
|
|
98
|
+
- Create custom permission utility functions
|
|
99
|
+
- Use string literals in `useResourcePermissions` calls
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
// ❌ FORBIDDEN - Wrapper function
|
|
103
|
+
const canEdit = (postId: string) => {
|
|
104
|
+
const hasPermission = canUpdate('journal');
|
|
105
|
+
const post = posts.find(p => p.id === postId);
|
|
106
|
+
return hasPermission && !!post;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// ❌ FORBIDDEN - Custom utility
|
|
110
|
+
function checkPermission(permission: string) {
|
|
111
|
+
// Custom logic...
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ❌ FORBIDDEN - String literal
|
|
115
|
+
const { canUpdate } = useResourcePermissions('journal');
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## MUST: Use RESOURCE_NAMES Constants
|
|
119
|
+
|
|
120
|
+
**MUST use `RESOURCE_NAMES` constant object for all resource permission checks:**
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
// ✅ CORRECT
|
|
124
|
+
import { RESOURCE_NAMES } from '@/config/resource-names';
|
|
125
|
+
const { canCreate, canUpdate, canDelete } = useResourcePermissions(RESOURCE_NAMES.JOURNAL);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**MUST NOT:**
|
|
129
|
+
- Use string literals in `useResourcePermissions` calls
|
|
130
|
+
- Hardcode resource names
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
// ❌ FORBIDDEN - String literal
|
|
134
|
+
const { canUpdate } = useResourcePermissions('journal');
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## MUST: Use Standard AccessDenied Component
|
|
138
|
+
|
|
139
|
+
**MUST use `AccessDenied` from pace-core for all access denied scenarios:**
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
// ✅ CORRECT
|
|
143
|
+
import { AccessDenied } from '@jmruthers/pace-core/rbac';
|
|
144
|
+
|
|
145
|
+
<PagePermissionGuard
|
|
146
|
+
pageName="dashboard"
|
|
147
|
+
operation="read"
|
|
148
|
+
fallback={<AccessDenied />}
|
|
149
|
+
>
|
|
150
|
+
<DashboardContent />
|
|
151
|
+
</PagePermissionGuard>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**MUST NOT:**
|
|
155
|
+
- Create custom access denied components
|
|
156
|
+
- Use custom permission denied components
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
// ❌ FORBIDDEN - Custom component
|
|
160
|
+
function CustomAccessDenied() {
|
|
161
|
+
return <div>Access Denied</div>;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## MUST: Use pace-core API for Permission Checks
|
|
166
|
+
|
|
167
|
+
**MUST use pace-core API functions for programmatic permission checks:**
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
// ✅ CORRECT
|
|
171
|
+
import { isPermitted, isPermittedCached } from '@jmruthers/pace-core/rbac';
|
|
172
|
+
|
|
173
|
+
const hasAccess = await isPermitted({
|
|
174
|
+
userId: 'user-123',
|
|
175
|
+
scope: { organisationId: 'org-456' },
|
|
176
|
+
permission: 'read:dashboard',
|
|
177
|
+
pageId: 'dashboard'
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**MUST NOT:**
|
|
182
|
+
- Call RBAC RPC functions directly
|
|
183
|
+
- Query RBAC tables directly (without `useSecureSupabase`)
|
|
184
|
+
- Create custom RBAC helper functions
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
// ❌ FORBIDDEN - Direct RPC call
|
|
188
|
+
const { data } = await supabase.rpc('rbac_check_permission_simplified', {
|
|
189
|
+
p_user_id: userId,
|
|
190
|
+
p_permission: 'read:dashboard'
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// ❌ FORBIDDEN - Direct table query
|
|
194
|
+
const { data } = await supabase
|
|
195
|
+
.from('rbac_user_profiles')
|
|
196
|
+
.select('*');
|
|
197
|
+
|
|
198
|
+
// ❌ FORBIDDEN - Custom RBAC helper
|
|
199
|
+
function checkPermission(userId: string, permission: string) {
|
|
200
|
+
// Custom logic that bypasses pace-core
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## MUST: Use pace-core API in Edge Functions (No Exceptions)
|
|
205
|
+
|
|
206
|
+
**Edge Functions (Deno serverless functions) MUST use pace-core's `isPermitted()` API function.** Edge Functions cannot use React hooks, but pace-core provides programmatic APIs that work outside React.
|
|
207
|
+
|
|
208
|
+
**✅ CORRECT - Edge Function Pattern:**
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
// supabase/functions/my-function/index.ts
|
|
212
|
+
import { createClient } from 'jsr:@supabase/supabase-js@2';
|
|
213
|
+
import { setupRBAC, isPermitted } from 'npm:@jmruthers/pace-core@^0.6.0/rbac';
|
|
214
|
+
|
|
215
|
+
Deno.serve(async (req: Request) => {
|
|
216
|
+
// 1. Create Supabase client from request headers
|
|
217
|
+
const authHeader = req.headers.get('Authorization');
|
|
218
|
+
if (!authHeader) {
|
|
219
|
+
return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 });
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const supabase = createClient(
|
|
223
|
+
Deno.env.get('SUPABASE_URL') ?? '',
|
|
224
|
+
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
|
|
225
|
+
{
|
|
226
|
+
global: {
|
|
227
|
+
headers: { Authorization: authHeader },
|
|
228
|
+
},
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// 2. Get user from session
|
|
233
|
+
const { data: { user } } = await supabase.auth.getUser();
|
|
234
|
+
if (!user) {
|
|
235
|
+
return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 3. Setup RBAC (required before using isPermitted)
|
|
239
|
+
setupRBAC(supabase);
|
|
240
|
+
|
|
241
|
+
// 4. Extract organisation context from request (headers, body, or query params)
|
|
242
|
+
const organisationId = req.headers.get('x-organisation-id') ||
|
|
243
|
+
(await req.json()).organisationId;
|
|
244
|
+
|
|
245
|
+
if (!organisationId) {
|
|
246
|
+
return new Response(JSON.stringify({ error: 'Organisation context required' }), { status: 400 });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 5. Check permission using pace-core API
|
|
250
|
+
const hasPermission = await isPermitted({
|
|
251
|
+
userId: user.id,
|
|
252
|
+
scope: { organisationId },
|
|
253
|
+
permission: 'read:dashboard',
|
|
254
|
+
pageId: 'dashboard'
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
if (!hasPermission) {
|
|
258
|
+
return new Response(JSON.stringify({ error: 'Permission denied' }), { status: 403 });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// 6. Proceed with function logic
|
|
262
|
+
return new Response(JSON.stringify({ success: true }));
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**❌ FORBIDDEN - Custom RBAC Helper in Edge Functions:**
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
// ❌ FORBIDDEN - Creating custom RBAC helper
|
|
270
|
+
// supabase/functions/_shared/rbac.ts
|
|
271
|
+
export async function checkPermission(userId: string, permission: string) {
|
|
272
|
+
// Custom logic that bypasses pace-core
|
|
273
|
+
const { data } = await supabase.rpc('rbac_check_permission_simplified', {
|
|
274
|
+
p_user_id: userId,
|
|
275
|
+
p_permission: permission
|
|
276
|
+
});
|
|
277
|
+
return data;
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Why No Exceptions:**
|
|
282
|
+
- pace-core provides `isPermitted()` API that works outside React
|
|
283
|
+
- `setupRBAC()` initializes the engine with a Supabase client
|
|
284
|
+
- No custom helpers needed - use pace-core APIs directly
|
|
285
|
+
- Custom helpers bypass security validation, caching, and audit logging
|
|
286
|
+
|
|
287
|
+
**Edge Function Requirements:**
|
|
288
|
+
1. **MUST** call `setupRBAC(supabase)` before using `isPermitted()`
|
|
289
|
+
2. **MUST** extract `userId` from Supabase auth session
|
|
290
|
+
3. **MUST** extract `organisationId` from request (headers, body, or query params)
|
|
291
|
+
4. **MUST** use `isPermitted()` with complete `PermissionCheck` input
|
|
292
|
+
5. **MUST NOT** create custom RBAC helper functions
|
|
293
|
+
6. **MUST NOT** call `rbac_check_permission_simplified` RPC directly
|
|
294
|
+
|
|
295
|
+
## MUST: Use useSecureSupabase for RBAC Table Queries
|
|
296
|
+
|
|
297
|
+
**If you must query RBAC tables (rare), MUST use `useSecureSupabase`:**
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
// ✅ CORRECT
|
|
301
|
+
import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
|
|
302
|
+
|
|
303
|
+
function MyComponent() {
|
|
304
|
+
const secureSupabase = useSecureSupabase();
|
|
305
|
+
// If your hook signature requires a client argument, pass ONLY the pace-core-provided client (never a local createClient()).
|
|
306
|
+
const { data } = await secureSupabase
|
|
307
|
+
.from('rbac_user_profiles') // Allowed through secure client
|
|
308
|
+
.select('*');
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**MUST NOT query these tables directly:**
|
|
313
|
+
- `rbac_organisation_roles`
|
|
314
|
+
- `rbac_event_app_roles`
|
|
315
|
+
- `rbac_global_roles`
|
|
316
|
+
- `rbac_apps`
|
|
317
|
+
- `rbac_app_pages`
|
|
318
|
+
- `rbac_page_permissions`
|
|
319
|
+
- `rbac_user_profiles`
|
|
320
|
+
|
|
321
|
+
## MUST NOT: Use Hardcoded Role Checks
|
|
322
|
+
|
|
323
|
+
**MUST NOT compare roles directly:**
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
// ❌ FORBIDDEN - Hardcoded role check
|
|
327
|
+
if (user.role === 'admin') {
|
|
328
|
+
// ...
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**MUST use pace-core APIs:**
|
|
333
|
+
|
|
334
|
+
```tsx
|
|
335
|
+
// ✅ CORRECT
|
|
336
|
+
import { useAccessLevel, getRoleContext } from '@jmruthers/pace-core/rbac';
|
|
337
|
+
|
|
338
|
+
const { accessLevel } = useAccessLevel(userId, scope);
|
|
339
|
+
const roleContext = await getRoleContext({ userId, scope });
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## MUST: Configure enforcePermissions Correctly
|
|
343
|
+
|
|
344
|
+
**For event-based apps (where pages handle their own checks):**
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
// ✅ CORRECT - Event-based app pattern
|
|
348
|
+
<PaceAppLayout
|
|
349
|
+
appName="MyApp"
|
|
350
|
+
enforcePermissions={false} // Pages handle checks via PagePermissionGuard
|
|
351
|
+
// ...
|
|
352
|
+
>
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**For organisation-based apps (where layout handles checks):**
|
|
356
|
+
|
|
357
|
+
```tsx
|
|
358
|
+
// ✅ CORRECT - Organisation-based app pattern
|
|
359
|
+
<PaceAppLayout
|
|
360
|
+
appName="MyApp"
|
|
361
|
+
enforcePermissions={true} // Layout handles checks
|
|
362
|
+
defaultPermission="read"
|
|
363
|
+
// ...
|
|
364
|
+
>
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## MUST: Setup RBAC Before Use
|
|
368
|
+
|
|
369
|
+
**MUST call `setupRBAC()` before any RBAC usage:**
|
|
370
|
+
|
|
371
|
+
```tsx
|
|
372
|
+
// main.tsx - MUST be first
|
|
373
|
+
import { setupRBAC } from '@jmruthers/pace-core/rbac';
|
|
374
|
+
setupRBAC(supabase);
|
|
375
|
+
// Then render app
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Enforcement
|
|
379
|
+
|
|
380
|
+
These rules are enforced by ESLint rules (all ERROR severity):
|
|
381
|
+
|
|
382
|
+
1. **`no-direct-rbac-rpc`** - Detects direct calls to `rbac_check_permission_simplified`
|
|
383
|
+
2. **`no-direct-rbac-tables`** - Detects direct queries to RBAC tables
|
|
384
|
+
3. **`no-bypass-page-guard`** - Detects routes without `PagePermissionGuard`
|
|
385
|
+
4. **`no-custom-access-denied`** - Detects custom access denied components
|
|
386
|
+
5. **`no-hardcoded-role-checks`** - Detects hardcoded role comparisons
|
|
387
|
+
6. **`no-custom-permission-utilities`** - Detects custom permission utility functions
|
|
388
|
+
7. **`no-resource-permission-string-literals`** - Detects string literals in `useResourcePermissions` calls
|
|
389
|
+
8. **`no-permission-wrapper-functions`** - Detects wrapper functions around pace-core permission hooks
|
|
390
|
+
|
|
391
|
+
## Common Mistakes to Avoid
|
|
392
|
+
|
|
393
|
+
**When writing code, NEVER:**
|
|
394
|
+
1. **Skip PagePermissionGuard** - Every protected page must be wrapped
|
|
395
|
+
```tsx
|
|
396
|
+
// ❌ WRONG
|
|
397
|
+
function DashboardPage() {
|
|
398
|
+
return <DashboardContent />;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// ✅ CORRECT
|
|
402
|
+
function DashboardPage() {
|
|
403
|
+
return (
|
|
404
|
+
<PagePermissionGuard pageName="dashboard" operation="read">
|
|
405
|
+
<DashboardContent />
|
|
406
|
+
</PagePermissionGuard>
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
2. **Create wrapper functions around permission hooks** - Use hooks directly
|
|
412
|
+
3. **Use string literals in useResourcePermissions** - Always use RESOURCE_NAMES constants
|
|
413
|
+
4. **Bypass input validation** - Always validate with Zod schemas
|
|
414
|
+
5. **Expose internal errors** - Never show SQL, stack traces, or file paths to users
|
|
415
|
+
6. **Log sensitive data** - Never log passwords, tokens, or PII
|
|
416
|
+
|
|
417
|
+
## Compliance Checklist
|
|
418
|
+
|
|
419
|
+
Before committing RBAC-related code, verify:
|
|
420
|
+
|
|
421
|
+
- [ ] All protected pages use `PagePermissionGuard`
|
|
422
|
+
- [ ] No wrapper components around `PagePermissionGuard`
|
|
423
|
+
- [ ] No wrapper functions around permission hooks
|
|
424
|
+
- [ ] All `useResourcePermissions` calls use `RESOURCE_NAMES` constants
|
|
425
|
+
- [ ] No direct RPC calls to `rbac_check_permission_simplified`
|
|
426
|
+
- [ ] No direct queries to RBAC tables (use `useSecureSupabase` if needed)
|
|
427
|
+
- [ ] No hardcoded role checks (use `useAccessLevel` or `getRoleContext`)
|
|
428
|
+
- [ ] No custom permission utility functions
|
|
429
|
+
- [ ] All access denied scenarios use `AccessDenied` from pace-core
|
|
430
|
+
- [ ] `enforcePermissions` configured correctly for app type
|
|
431
|
+
- [ ] `setupRBAC()` called before any RBAC usage
|
|
432
|
+
- [ ] Edge Functions use `isPermitted()` API (no custom RBAC helpers)
|
|
433
|
+
- [ ] All RLS policies for authenticated users include super-admin checks
|
|
434
|
+
- [ ] All `is_super_admin()` calls use explicit parameter (`safe_get_user_id_for_rls()`)
|
|
435
|
+
- [ ] No security-critical functions use fallback strategies
|
|
436
|
+
- [ ] ESLint rules pass without errors
|
|
437
|
+
|
|
438
|
+
## MUST: Include Super-Admin Checks in RLS Policies
|
|
439
|
+
|
|
440
|
+
**MUST include super-admin bypass in all RLS policies for authenticated users:**
|
|
441
|
+
|
|
442
|
+
```sql
|
|
443
|
+
-- ✅ CORRECT: Super-admin check included
|
|
444
|
+
CREATE POLICY "rbac_select_table_name" ON table_name
|
|
445
|
+
FOR SELECT TO authenticated
|
|
446
|
+
USING (
|
|
447
|
+
organisation_id IS NOT NULL
|
|
448
|
+
AND (
|
|
449
|
+
is_super_admin(safe_get_user_id_for_rls())
|
|
450
|
+
OR check_user_organisation_access(organisation_id)
|
|
451
|
+
)
|
|
452
|
+
);
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
**MUST NOT:**
|
|
456
|
+
- Create RLS policies without super-admin checks (except for public/anonymous policies)
|
|
457
|
+
- Use `is_super_admin()` without a parameter (relies on fallback strategies)
|
|
458
|
+
- Create security-critical functions with fallback strategies
|
|
459
|
+
|
|
460
|
+
```sql
|
|
461
|
+
-- ❌ FORBIDDEN: Missing super-admin check
|
|
462
|
+
CREATE POLICY "rbac_select_table_name" ON table_name
|
|
463
|
+
FOR SELECT TO authenticated
|
|
464
|
+
USING (
|
|
465
|
+
check_user_organisation_access(organisation_id)
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
-- ❌ FORBIDDEN: is_super_admin() without parameter
|
|
469
|
+
CREATE POLICY "rbac_select_table_name" ON table_name
|
|
470
|
+
FOR SELECT TO authenticated
|
|
471
|
+
USING (
|
|
472
|
+
is_super_admin() -- Missing parameter, uses fallback strategies
|
|
473
|
+
OR check_user_organisation_access(organisation_id)
|
|
474
|
+
);
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**MUST use explicit parameter passing:**
|
|
478
|
+
|
|
479
|
+
```sql
|
|
480
|
+
-- ✅ CORRECT: Explicit user ID parameter
|
|
481
|
+
is_super_admin(safe_get_user_id_for_rls())
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
**MUST NOT create security-critical functions with fallback strategies:**
|
|
485
|
+
|
|
486
|
+
```sql
|
|
487
|
+
-- ❌ FORBIDDEN: Multiple fallback strategies
|
|
488
|
+
CREATE FUNCTION is_super_admin(p_user_id UUID DEFAULT NULL)
|
|
489
|
+
RETURNS boolean AS $$
|
|
490
|
+
BEGIN
|
|
491
|
+
IF p_user_id IS NOT NULL THEN
|
|
492
|
+
v_user_id := p_user_id;
|
|
493
|
+
ELSE
|
|
494
|
+
v_user_id := auth.uid(); -- Fallback 1
|
|
495
|
+
END IF;
|
|
496
|
+
-- More fallbacks...
|
|
497
|
+
END;
|
|
498
|
+
$$;
|
|
499
|
+
|
|
500
|
+
-- ✅ CORRECT: Required parameter, no fallbacks
|
|
501
|
+
CREATE FUNCTION is_super_admin(p_user_id UUID)
|
|
502
|
+
RETURNS boolean AS $$
|
|
503
|
+
BEGIN
|
|
504
|
+
IF p_user_id IS NULL THEN
|
|
505
|
+
RETURN false; -- Fail secure
|
|
506
|
+
END IF;
|
|
507
|
+
-- Check super-admin status
|
|
508
|
+
END;
|
|
509
|
+
$$;
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
## Reference
|
|
513
|
+
|
|
514
|
+
- **RBAC Contract**: `packages/core/docs/rbac/RBAC_CONTRACT.md` - Complete contract documentation
|
|
515
|
+
- **Migration Guide**: `packages/core/docs/rbac/MIGRATION_GUIDE.md` - Migrating to RBAC Contract v2.0.0
|
|
516
|
+
- **Quick Start**: `packages/core/docs/rbac/quick-start.md` - Getting started guide
|
|
517
|
+
- **API Reference**: `packages/core/docs/rbac/api-reference.md` - Complete API documentation
|
|
518
|
+
- **RLS Standards**: [6-security-rbac-standards.md](../../packages/core/docs/standards/6-security-rbac-standards.md) - RLS policy patterns and super-admin requirements
|