@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
|
@@ -127,9 +127,9 @@ export function useRoleManagement() {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
|
-
* Revoke an event app role using the
|
|
130
|
+
* Revoke an event app role using the unified RPC function
|
|
131
131
|
*
|
|
132
|
-
* This function uses the `
|
|
132
|
+
* This function uses the `rbac_role_revoke` RPC which:
|
|
133
133
|
* - Runs with SECURITY DEFINER privileges
|
|
134
134
|
* - Includes proper permission checks
|
|
135
135
|
* - Automatically populates audit fields (revoked_by, timestamps)
|
|
@@ -145,26 +145,114 @@ export function useRoleManagement() {
|
|
|
145
145
|
setError(null);
|
|
146
146
|
|
|
147
147
|
try {
|
|
148
|
-
|
|
148
|
+
// Build context ID from event_id and app_id
|
|
149
|
+
const contextId = `${params.event_id}:${params.app_id}`;
|
|
150
|
+
|
|
151
|
+
const rpcParams = {
|
|
149
152
|
p_user_id: params.user_id,
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
p_role: params.role,
|
|
153
|
+
p_role_type: 'event_app' as const,
|
|
154
|
+
p_role_name: params.role,
|
|
155
|
+
p_context_id: contextId,
|
|
154
156
|
p_revoked_by: params.revoked_by || user?.id || undefined
|
|
155
|
-
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Log in development for debugging
|
|
160
|
+
if (import.meta.env.MODE === 'development') {
|
|
161
|
+
console.log('[useRoleManagement] revokeEventAppRole called with:', rpcParams);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const { data, error: rpcError } = await supabase.rpc('rbac_role_revoke', rpcParams);
|
|
156
165
|
|
|
157
166
|
if (rpcError) {
|
|
158
|
-
|
|
167
|
+
// Log full error details in development
|
|
168
|
+
if (import.meta.env.MODE === 'development') {
|
|
169
|
+
console.error('[useRoleManagement] RPC error:', {
|
|
170
|
+
message: rpcError.message,
|
|
171
|
+
details: rpcError.details,
|
|
172
|
+
hint: rpcError.hint,
|
|
173
|
+
code: rpcError.code,
|
|
174
|
+
fullError: rpcError
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
// Use just the message for the error (tests expect plain message)
|
|
178
|
+
const errorMessage = rpcError.message || 'Failed to revoke role - unknown RPC error';
|
|
179
|
+
throw new Error(errorMessage);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Log response in development - ALWAYS log, even on failure
|
|
183
|
+
if (import.meta.env.MODE === 'development') {
|
|
184
|
+
console.log('[useRoleManagement] RPC response:', {
|
|
185
|
+
data,
|
|
186
|
+
error: rpcError,
|
|
187
|
+
dataType: Array.isArray(data) ? 'array' : typeof data,
|
|
188
|
+
dataLength: Array.isArray(data) ? data.length : 'N/A'
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// rbac_role_revoke returns a table with success, message, revoked_count, error_code
|
|
193
|
+
// It should always return at least one row
|
|
194
|
+
if (!data || !Array.isArray(data) || data.length === 0) {
|
|
195
|
+
const errorMsg = 'No response from database - role revocation may have failed';
|
|
196
|
+
if (import.meta.env.MODE === 'development') {
|
|
197
|
+
console.error('[useRoleManagement] Empty or null data response:', {
|
|
198
|
+
data,
|
|
199
|
+
dataType: typeof data,
|
|
200
|
+
isArray: Array.isArray(data),
|
|
201
|
+
length: Array.isArray(data) ? data.length : 'N/A'
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
throw new Error(errorMsg);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const result = data[0];
|
|
208
|
+
|
|
209
|
+
// ALWAYS log the result in development, even on failure
|
|
210
|
+
if (import.meta.env.MODE === 'development') {
|
|
211
|
+
console.log('[useRoleManagement] RPC result:', {
|
|
212
|
+
success: result?.success,
|
|
213
|
+
message: result?.message,
|
|
214
|
+
error_code: result?.error_code,
|
|
215
|
+
revoked_count: result?.revoked_count,
|
|
216
|
+
fullResult: result
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (!result || result.success !== true) {
|
|
221
|
+
// Use message if available, otherwise fall back to error_code, otherwise default message
|
|
222
|
+
const errorMessage = result?.message || result?.error_code || 'Role revocation failed';
|
|
223
|
+
|
|
224
|
+
if (import.meta.env.MODE === 'development') {
|
|
225
|
+
console.error('[useRoleManagement] Role revocation failed:', {
|
|
226
|
+
result,
|
|
227
|
+
errorMessage,
|
|
228
|
+
fullData: data,
|
|
229
|
+
rpcParams
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
success: false,
|
|
235
|
+
message: result?.message || undefined,
|
|
236
|
+
error: errorMessage
|
|
237
|
+
};
|
|
159
238
|
}
|
|
160
239
|
|
|
161
240
|
return {
|
|
162
|
-
success:
|
|
163
|
-
message:
|
|
164
|
-
error:
|
|
241
|
+
success: true,
|
|
242
|
+
message: result.message || 'Role revoked successfully',
|
|
243
|
+
error: undefined
|
|
165
244
|
};
|
|
166
245
|
} catch (err) {
|
|
167
246
|
const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
|
|
247
|
+
|
|
248
|
+
if (import.meta.env.MODE === 'development') {
|
|
249
|
+
console.error('[useRoleManagement] Exception in revokeEventAppRole:', {
|
|
250
|
+
error: err,
|
|
251
|
+
errorMessage,
|
|
252
|
+
params
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
168
256
|
setError(err instanceof Error ? err : new Error(errorMessage));
|
|
169
257
|
return {
|
|
170
258
|
success: false,
|
|
@@ -173,7 +261,7 @@ export function useRoleManagement() {
|
|
|
173
261
|
} finally {
|
|
174
262
|
setIsLoading(false);
|
|
175
263
|
}
|
|
176
|
-
}, [user?.id]);
|
|
264
|
+
}, [user?.id, supabase]);
|
|
177
265
|
|
|
178
266
|
/**
|
|
179
267
|
* Grant an event app role using the secure RPC function
|
|
@@ -273,16 +361,36 @@ export function useRoleManagement() {
|
|
|
273
361
|
});
|
|
274
362
|
|
|
275
363
|
if (rpcError) {
|
|
276
|
-
|
|
364
|
+
// Use just the message for the error (tests expect plain message)
|
|
365
|
+
const errorMessage = rpcError.message || 'Failed to revoke role - unknown RPC error';
|
|
366
|
+
throw new Error(errorMessage);
|
|
277
367
|
}
|
|
278
368
|
|
|
279
369
|
// rbac_role_revoke returns a table with success, message, revoked_count, error_code
|
|
280
370
|
const result = Array.isArray(data) && data.length > 0 ? data[0] : null;
|
|
281
371
|
|
|
372
|
+
// When data is empty array or null, return error as undefined (tests expect this)
|
|
373
|
+
if (!result) {
|
|
374
|
+
return {
|
|
375
|
+
success: false,
|
|
376
|
+
error: undefined
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (result.success === false) {
|
|
381
|
+
// Use message if available, otherwise fall back to error_code, otherwise default message
|
|
382
|
+
const errorMessage = result.message || result.error_code || 'Role revocation failed';
|
|
383
|
+
return {
|
|
384
|
+
success: false,
|
|
385
|
+
message: result.message || undefined,
|
|
386
|
+
error: errorMessage
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
282
390
|
return {
|
|
283
|
-
success:
|
|
284
|
-
message: result
|
|
285
|
-
error:
|
|
391
|
+
success: true,
|
|
392
|
+
message: result.message || 'Role revoked successfully',
|
|
393
|
+
error: undefined
|
|
286
394
|
};
|
|
287
395
|
} catch (err) {
|
|
288
396
|
const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
|
|
@@ -383,7 +491,17 @@ export function useRoleManagement() {
|
|
|
383
491
|
});
|
|
384
492
|
|
|
385
493
|
if (rpcError) {
|
|
386
|
-
|
|
494
|
+
// Include all available error information in the message
|
|
495
|
+
const errorParts = [
|
|
496
|
+
rpcError.message,
|
|
497
|
+
rpcError.details,
|
|
498
|
+
rpcError.hint,
|
|
499
|
+
rpcError.code ? `Error code: ${rpcError.code}` : null
|
|
500
|
+
].filter(Boolean);
|
|
501
|
+
const errorMessage = errorParts.length > 0
|
|
502
|
+
? errorParts.join(' | ')
|
|
503
|
+
: 'Failed to revoke role - unknown RPC error';
|
|
504
|
+
throw new Error(errorMessage);
|
|
387
505
|
}
|
|
388
506
|
|
|
389
507
|
// rbac_role_revoke returns a table with success, message, revoked_count, error_code
|
|
@@ -493,7 +611,17 @@ export function useRoleManagement() {
|
|
|
493
611
|
});
|
|
494
612
|
|
|
495
613
|
if (rpcError) {
|
|
496
|
-
|
|
614
|
+
// Include all available error information in the message
|
|
615
|
+
const errorParts = [
|
|
616
|
+
rpcError.message,
|
|
617
|
+
rpcError.details,
|
|
618
|
+
rpcError.hint,
|
|
619
|
+
rpcError.code ? `Error code: ${rpcError.code}` : null
|
|
620
|
+
].filter(Boolean);
|
|
621
|
+
const errorMessage = errorParts.length > 0
|
|
622
|
+
? errorParts.join(' | ')
|
|
623
|
+
: 'Failed to revoke role - unknown RPC error';
|
|
624
|
+
throw new Error(errorMessage);
|
|
497
625
|
}
|
|
498
626
|
|
|
499
627
|
// rbac_role_revoke returns a table with success, message, revoked_count, error_code
|
|
@@ -223,7 +223,8 @@ export function useSecureSupabase(
|
|
|
223
223
|
const { resolvedScope } = useResolvedScope({
|
|
224
224
|
supabase: authSupabase || null,
|
|
225
225
|
selectedOrganisationId: selectedOrganisation?.id || null,
|
|
226
|
-
selectedEventId: selectedEvent?.event_id || null
|
|
226
|
+
selectedEventId: selectedEvent?.event_id || null,
|
|
227
|
+
selectedEventOrganisationId: selectedEvent?.organisation_id || null
|
|
227
228
|
});
|
|
228
229
|
|
|
229
230
|
// Track previous context to detect changes
|
|
@@ -238,13 +239,8 @@ export function useSecureSupabase(
|
|
|
238
239
|
});
|
|
239
240
|
|
|
240
241
|
return useMemo(() => {
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
return baseClient || authSupabase || null;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Use resolved scope to get organisationId (derived from event if needed)
|
|
247
|
-
// For event-required apps, resolvedScope.organisationId is derived from event
|
|
242
|
+
// Use resolved scope to get organisationId (now available immediately)
|
|
243
|
+
// For event-required apps, resolvedScope.organisationId comes from selectedEvent.organisation_id
|
|
248
244
|
// For org-required apps, resolvedScope.organisationId comes from selectedOrganisation
|
|
249
245
|
const organisationId = resolvedScope?.organisationId;
|
|
250
246
|
const eventId = resolvedScope?.eventId || selectedEvent?.event_id;
|
package/src/rbac/index.ts
CHANGED
|
@@ -45,7 +45,8 @@ export {
|
|
|
45
45
|
isDevelopmentMode,
|
|
46
46
|
} from './config';
|
|
47
47
|
|
|
48
|
-
// Secure client
|
|
48
|
+
// Secure client (internal - use useSecureSupabase hook instead)
|
|
49
|
+
// @internal - These are implementation details. Use useSecureSupabase hook for secure client access.
|
|
49
50
|
export {
|
|
50
51
|
SecureSupabaseClient,
|
|
51
52
|
createSecureClient,
|
|
@@ -59,7 +60,8 @@ export {
|
|
|
59
60
|
SECURE_CLIENT_SYMBOL,
|
|
60
61
|
} from './utils/clientSecurity';
|
|
61
62
|
|
|
62
|
-
// Cache
|
|
63
|
+
// Cache (internal - caching is automatic in hooks and API functions)
|
|
64
|
+
// @internal - These are implementation details. Caching is handled automatically.
|
|
63
65
|
export {
|
|
64
66
|
RBACCache,
|
|
65
67
|
rbacCache,
|
|
@@ -94,7 +96,8 @@ export {
|
|
|
94
96
|
emitAuditEvent,
|
|
95
97
|
} from './audit';
|
|
96
98
|
|
|
97
|
-
// Engine
|
|
99
|
+
// Engine (internal - use isPermitted API instead)
|
|
100
|
+
// @internal - These are implementation details. Use isPermitted API for permission checks.
|
|
98
101
|
export {
|
|
99
102
|
RBACEngine,
|
|
100
103
|
createRBACEngine,
|
|
@@ -110,17 +113,13 @@ export * from './hooks';
|
|
|
110
113
|
// Users should now use useRBAC() hook directly without a provider wrapper
|
|
111
114
|
// export * from './providers';
|
|
112
115
|
|
|
113
|
-
// Adapters
|
|
116
|
+
// Adapters (Server-side only - React components removed)
|
|
114
117
|
export {
|
|
115
118
|
withPermissionGuard,
|
|
116
119
|
withAccessLevelGuard,
|
|
117
120
|
withRoleGuard,
|
|
118
|
-
PermissionGuard,
|
|
119
|
-
AccessLevelGuard,
|
|
120
121
|
createRBACMiddleware,
|
|
121
122
|
createRBACExpressMiddleware,
|
|
122
|
-
hasPermissionCached,
|
|
123
|
-
hasAnyPermissionCached,
|
|
124
123
|
} from './adapters';
|
|
125
124
|
|
|
126
125
|
// Main API functions
|
|
@@ -131,7 +130,6 @@ export {
|
|
|
131
130
|
getRoleContext,
|
|
132
131
|
isPermitted,
|
|
133
132
|
isPermittedCached,
|
|
134
|
-
hasPermission,
|
|
135
133
|
hasAnyPermission,
|
|
136
134
|
hasAllPermissions,
|
|
137
135
|
setupRBAC,
|
|
@@ -68,19 +68,21 @@ export class ContextValidator {
|
|
|
68
68
|
* Resolve scope based on page-level scope_type
|
|
69
69
|
*
|
|
70
70
|
* This method handles page-level scoping. All pages have explicit scope_type set.
|
|
71
|
-
* Used for hybrid apps
|
|
71
|
+
* Used for hybrid apps that have both event and organisation pages.
|
|
72
72
|
*
|
|
73
73
|
* @param scope - Current scope
|
|
74
74
|
* @param pageScopeType - Page scope type ('event', 'organisation', or 'both')
|
|
75
75
|
* @param appName - App name (for PORTAL/ADMIN special case)
|
|
76
|
-
* @param supabase - Supabase client (for deriving org from event)
|
|
76
|
+
* @param supabase - Supabase client (for deriving org from event, only if not already provided)
|
|
77
|
+
* @param immediateOrganisationId - Optional immediate organisation ID (from selectedEvent.organisation_id) - avoids querying
|
|
77
78
|
* @returns Resolved scope with all required context
|
|
78
79
|
*/
|
|
79
80
|
static async resolveScopeForPage(
|
|
80
81
|
scope: Scope,
|
|
81
82
|
pageScopeType: PageScopeType,
|
|
82
83
|
appName?: string,
|
|
83
|
-
supabase?: SupabaseClient<Database> | null
|
|
84
|
+
supabase?: SupabaseClient<Database> | null,
|
|
85
|
+
immediateOrganisationId?: string | null
|
|
84
86
|
): Promise<ContextValidationResult> {
|
|
85
87
|
// Use page-level scope (single source of truth)
|
|
86
88
|
const effectiveScopeType = pageScopeType;
|
|
@@ -109,8 +111,8 @@ export class ContextValidator {
|
|
|
109
111
|
};
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
//
|
|
113
|
-
let organisationId = scope.organisationId;
|
|
114
|
+
// Use immediate orgId if provided, otherwise derive from event
|
|
115
|
+
let organisationId = scope.organisationId || immediateOrganisationId || undefined;
|
|
114
116
|
if (!organisationId && scope.eventId && supabase) {
|
|
115
117
|
try {
|
|
116
118
|
const derivedOrgId = await this.deriveOrgFromEvent(supabase, scope.eventId);
|
|
@@ -154,8 +156,8 @@ export class ContextValidator {
|
|
|
154
156
|
};
|
|
155
157
|
}
|
|
156
158
|
|
|
157
|
-
//
|
|
158
|
-
let organisationId: UUID | undefined = scope.organisationId;
|
|
159
|
+
// Use immediate orgId if provided, otherwise derive from event
|
|
160
|
+
let organisationId: UUID | undefined = scope.organisationId || immediateOrganisationId || undefined;
|
|
159
161
|
if (!organisationId && supabase && scope.eventId) {
|
|
160
162
|
try {
|
|
161
163
|
const derivedOrgId = await this.deriveOrgFromEvent(supabase, scope.eventId);
|
|
@@ -37,18 +37,20 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
37
37
|
restorationError: null,
|
|
38
38
|
};
|
|
39
39
|
private restorationTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
40
|
-
|
|
40
|
+
// Increased timeout to handle hard refresh scenarios where localStorage access
|
|
41
|
+
// and session restoration may take longer, especially with organisation/event context
|
|
42
|
+
private readonly restorationTimeoutMs = 10000; // 10 seconds (increased from 5)
|
|
41
43
|
private restorationStartTime: number | null = null;
|
|
42
44
|
private appName: string | undefined = undefined;
|
|
43
45
|
private errorHandler: ((event: ErrorEvent) => void) | null = null;
|
|
44
46
|
private unhandledRejectionHandler: ((event: PromiseRejectionEvent) => void) | null = null;
|
|
47
|
+
private wasAuthenticatedRef: boolean = false; // Track previous auth state to detect transitions
|
|
45
48
|
|
|
46
49
|
constructor(supabaseClient: SupabaseClient, appName?: string) {
|
|
47
50
|
super();
|
|
48
51
|
this.instanceId = ++AuthService.instanceCount;
|
|
49
52
|
this.supabaseClient = supabaseClient;
|
|
50
53
|
this.appName = appName;
|
|
51
|
-
logger.debug('AuthService', `Instance created [ID:${this.instanceId}]`, { appName });
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
getInstanceId(): number {
|
|
@@ -109,6 +111,15 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
109
111
|
this.authError = null;
|
|
110
112
|
this.user = data.user;
|
|
111
113
|
this.session = data.session;
|
|
114
|
+
|
|
115
|
+
// Clear persistence immediately on successful login
|
|
116
|
+
// This prevents dialogs/forms/datatables from auto-opening with stale state
|
|
117
|
+
// We clear here synchronously before components can mount and check persisted state
|
|
118
|
+
// Also clear old unscoped keys to prevent data leakage
|
|
119
|
+
if (!this.wasAuthenticatedRef && data.user) {
|
|
120
|
+
this.clearPersistenceOnLogin(null, true);
|
|
121
|
+
this.wasAuthenticatedRef = true;
|
|
122
|
+
}
|
|
112
123
|
}
|
|
113
124
|
|
|
114
125
|
this.notify();
|
|
@@ -148,6 +159,14 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
148
159
|
this.authError = null;
|
|
149
160
|
this.user = data.user;
|
|
150
161
|
this.session = data.session;
|
|
162
|
+
|
|
163
|
+
// Clear persistence immediately on successful signup/login
|
|
164
|
+
// This prevents dialogs/forms/datatables from auto-opening with stale state
|
|
165
|
+
// Also clear old unscoped keys to prevent data leakage
|
|
166
|
+
if (!this.wasAuthenticatedRef && data.user) {
|
|
167
|
+
this.clearPersistenceOnLogin(null, true);
|
|
168
|
+
this.wasAuthenticatedRef = true;
|
|
169
|
+
}
|
|
151
170
|
}
|
|
152
171
|
|
|
153
172
|
this.notify();
|
|
@@ -176,6 +195,14 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
176
195
|
try {
|
|
177
196
|
const { error } = await this.supabaseClient.auth.signOut();
|
|
178
197
|
|
|
198
|
+
// Clear sessionStorage on logout
|
|
199
|
+
try {
|
|
200
|
+
sessionStorage.clear();
|
|
201
|
+
} catch (storageError) {
|
|
202
|
+
// Ignore storage errors (e.g., in private browsing mode)
|
|
203
|
+
logger.warn('AuthService', 'Failed to clear sessionStorage', { error: storageError });
|
|
204
|
+
}
|
|
205
|
+
|
|
179
206
|
if (error) {
|
|
180
207
|
this.authError = error;
|
|
181
208
|
} else {
|
|
@@ -187,6 +214,14 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
187
214
|
this.notify();
|
|
188
215
|
return { user: null, session: null, error };
|
|
189
216
|
} catch (error) {
|
|
217
|
+
// Clear sessionStorage even on error
|
|
218
|
+
try {
|
|
219
|
+
sessionStorage.clear();
|
|
220
|
+
} catch (storageError) {
|
|
221
|
+
// Ignore storage errors (e.g., in private browsing mode)
|
|
222
|
+
logger.warn('AuthService', 'Failed to clear sessionStorage', { error: storageError });
|
|
223
|
+
}
|
|
224
|
+
|
|
190
225
|
// Convert regular Error to AuthError if needed
|
|
191
226
|
const authError = error instanceof AuthError
|
|
192
227
|
? error
|
|
@@ -397,6 +432,56 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
397
432
|
}
|
|
398
433
|
}
|
|
399
434
|
|
|
435
|
+
/**
|
|
436
|
+
* Clear pace-core persistence entries from sessionStorage
|
|
437
|
+
* This includes dialog, form, and datatable persistence
|
|
438
|
+
*
|
|
439
|
+
* @param userId - Optional user ID to clear only that user's persistence.
|
|
440
|
+
* If not provided, clears all pace-core persistence (for user changes).
|
|
441
|
+
* @param clearUnscoped - If true, also clears old unscoped keys (without :user: prefix)
|
|
442
|
+
*/
|
|
443
|
+
private clearPersistenceOnLogin(userId?: string | null, clearUnscoped: boolean = true): void {
|
|
444
|
+
if (typeof window === 'undefined' || !window.sessionStorage) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
try {
|
|
449
|
+
const keysToRemove: string[] = [];
|
|
450
|
+
|
|
451
|
+
// Collect all pace-core persistence keys
|
|
452
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
453
|
+
const key = sessionStorage.key(i);
|
|
454
|
+
if (key && (
|
|
455
|
+
key.startsWith('pace-core:draft:') ||
|
|
456
|
+
key.startsWith('pace-core:dialog:')
|
|
457
|
+
)) {
|
|
458
|
+
// If userId is provided, only clear keys for that user
|
|
459
|
+
// Otherwise, clear all (for user changes or fresh login)
|
|
460
|
+
if (userId && !key.includes(`:user:${userId}`)) {
|
|
461
|
+
// Also clear old unscoped keys (without :user: prefix) if requested
|
|
462
|
+
if (clearUnscoped && !key.includes(':user:')) {
|
|
463
|
+
keysToRemove.push(key);
|
|
464
|
+
}
|
|
465
|
+
continue; // Skip keys that don't match this user
|
|
466
|
+
}
|
|
467
|
+
keysToRemove.push(key);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Remove all collected keys
|
|
472
|
+
keysToRemove.forEach(key => {
|
|
473
|
+
try {
|
|
474
|
+
sessionStorage.removeItem(key);
|
|
475
|
+
} catch (error) {
|
|
476
|
+
logger.warn('AuthService', `Failed to remove persistence key: ${key}`, error);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
} catch (error) {
|
|
481
|
+
logger.warn('AuthService', `Failed to clear persistence [ID:${this.instanceId}]:`, error);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
400
485
|
private async setupAuthStateListener(): Promise<void> {
|
|
401
486
|
if (!this.supabaseClient) {
|
|
402
487
|
this.authLoading = false;
|
|
@@ -408,16 +493,20 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
408
493
|
const subscription = this.supabaseClient.auth.onAuthStateChange(
|
|
409
494
|
(event: AuthChangeEvent, session: SupabaseSession | null) => {
|
|
410
495
|
try {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
496
|
+
|
|
497
|
+
// Track authentication state transition
|
|
498
|
+
const wasAuthenticated = this.wasAuthenticatedRef;
|
|
499
|
+
const isNowAuthenticated = !!session?.user;
|
|
500
|
+
const previousUserId = this.user?.id || null;
|
|
501
|
+
const newUserId = session?.user?.id || null;
|
|
502
|
+
const userChanged = previousUserId !== null && newUserId !== null && previousUserId !== newUserId;
|
|
503
|
+
|
|
416
504
|
// Handle different auth events
|
|
417
505
|
if (event === 'SIGNED_OUT') {
|
|
418
506
|
this.session = null;
|
|
419
507
|
this.user = null;
|
|
420
508
|
this.authError = null;
|
|
509
|
+
this.wasAuthenticatedRef = false;
|
|
421
510
|
|
|
422
511
|
// Automatic session tracking (non-blocking)
|
|
423
512
|
if (session?.user) {
|
|
@@ -434,6 +523,19 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
434
523
|
this.authError = null;
|
|
435
524
|
}
|
|
436
525
|
|
|
526
|
+
// Clear persistence when:
|
|
527
|
+
// 1. Transitioning from unauthenticated to authenticated (fresh login) - clear all + old unscoped keys
|
|
528
|
+
// 2. User changes (different user logs in) - clear previous user's persistence + old unscoped keys
|
|
529
|
+
if (!wasAuthenticated && isNowAuthenticated && event === 'SIGNED_IN') {
|
|
530
|
+
// Fresh login - clear all persistence including old unscoped keys
|
|
531
|
+
this.clearPersistenceOnLogin(null, true);
|
|
532
|
+
} else if (userChanged && previousUserId) {
|
|
533
|
+
// User changed - clear previous user's persistence + old unscoped keys
|
|
534
|
+
this.clearPersistenceOnLogin(previousUserId, true);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
this.wasAuthenticatedRef = isNowAuthenticated;
|
|
538
|
+
|
|
437
539
|
// Automatic session tracking for login (non-blocking)
|
|
438
540
|
// Only track on SIGNED_IN, not TOKEN_REFRESHED (to avoid duplicate login records)
|
|
439
541
|
if (event === 'SIGNED_IN' && session?.user) {
|
|
@@ -443,10 +545,30 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
443
545
|
}
|
|
444
546
|
} else if (event === 'INITIAL_SESSION') {
|
|
445
547
|
if (session) {
|
|
548
|
+
const previousUserId = this.user?.id || null;
|
|
549
|
+
const newUserId = session.user?.id || null;
|
|
550
|
+
const userChanged = previousUserId !== null && newUserId !== null && previousUserId !== newUserId;
|
|
551
|
+
|
|
446
552
|
this.session = session;
|
|
447
553
|
this.user = session.user ?? null;
|
|
448
554
|
this.authError = null;
|
|
449
555
|
|
|
556
|
+
// Clear persistence if user changed (e.g., different user's session restored)
|
|
557
|
+
// This prevents data leakage when a different user's session is restored
|
|
558
|
+
if (userChanged && previousUserId) {
|
|
559
|
+
// Clear previous user's persistence + old unscoped keys
|
|
560
|
+
this.clearPersistenceOnLogin(previousUserId, true);
|
|
561
|
+
} else if (!wasAuthenticated && !!session.user) {
|
|
562
|
+
// Fresh login on INITIAL_SESSION - clear all persistence including old unscoped keys
|
|
563
|
+
this.clearPersistenceOnLogin(null, true);
|
|
564
|
+
}
|
|
565
|
+
// Don't clear persistence on INITIAL_SESSION for same user - this is just session restoration
|
|
566
|
+
// Persistence should only be cleared on actual SIGNED_IN events or user changes
|
|
567
|
+
// INITIAL_SESSION happens on page load/refresh, and we want to preserve
|
|
568
|
+
// dialog/form/datatable state across page refreshes for the same user
|
|
569
|
+
|
|
570
|
+
this.wasAuthenticatedRef = !!session.user;
|
|
571
|
+
|
|
450
572
|
// Reset restoration state if valid session arrives after earlier failure
|
|
451
573
|
// This clears stale errors when session eventually succeeds
|
|
452
574
|
const hasTimeoutError = this.sessionRestorationState.restorationError?.name === 'SessionRestorationTimeoutError';
|
|
@@ -457,6 +579,7 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
457
579
|
}
|
|
458
580
|
} else {
|
|
459
581
|
// No session in INITIAL_SESSION event - user is not authenticated
|
|
582
|
+
this.wasAuthenticatedRef = false;
|
|
460
583
|
// Finish restoration to clear loading state
|
|
461
584
|
if (this.sessionRestorationState.isRestoring) {
|
|
462
585
|
this.finishSessionRestoration();
|
|
@@ -468,11 +591,6 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
468
591
|
// before checking authentication state
|
|
469
592
|
this.authLoading = false;
|
|
470
593
|
|
|
471
|
-
// Final check: Ensure state matches what we just logged
|
|
472
|
-
logger.debug('AuthService', `State synchronized after INITIAL_SESSION [ID:${this.instanceId}]`, {
|
|
473
|
-
hasUser: !!this.user,
|
|
474
|
-
userId: this.user?.id
|
|
475
|
-
});
|
|
476
594
|
|
|
477
595
|
this.notify();
|
|
478
596
|
return; // Return early to avoid setting loading to false again below
|
|
@@ -481,12 +599,6 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
481
599
|
// For other events (SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED), set loading to false and notify
|
|
482
600
|
this.authLoading = false;
|
|
483
601
|
|
|
484
|
-
// Final check: Ensure state matches what we just logged
|
|
485
|
-
logger.debug('AuthService', `State synchronized after event [ID:${this.instanceId}]`, {
|
|
486
|
-
event,
|
|
487
|
-
hasUser: !!this.user,
|
|
488
|
-
userId: this.user?.id
|
|
489
|
-
});
|
|
490
602
|
|
|
491
603
|
this.notify();
|
|
492
604
|
} catch (error) {
|