@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
|
@@ -15,19 +15,19 @@ import { renderWithProviders } from '../../__tests__/helpers/test-utils';
|
|
|
15
15
|
|
|
16
16
|
// Mock the DataTable component
|
|
17
17
|
vi.mock('../../components/DataTable', () => ({
|
|
18
|
-
DataTable: vi.fn(() => <
|
|
18
|
+
DataTable: vi.fn(() => <section data-testid="data-table">DataTable Component</section>)
|
|
19
19
|
}));
|
|
20
20
|
|
|
21
21
|
// Mock the LoadingSpinner component
|
|
22
22
|
vi.mock('../../components/LoadingSpinner/LoadingSpinner', () => ({
|
|
23
|
-
LoadingSpinner: vi.fn(() => <
|
|
23
|
+
LoadingSpinner: vi.fn(() => <p data-testid="loading-spinner">Loading...</p>)
|
|
24
24
|
}));
|
|
25
25
|
|
|
26
26
|
describe('LazyLoad Utility', () => {
|
|
27
27
|
describe('createLazyComponent', () => {
|
|
28
28
|
it('should create a lazy component with default fallback', async () => {
|
|
29
29
|
const mockImportFn = vi.fn().mockResolvedValue({
|
|
30
|
-
default: () => <
|
|
30
|
+
default: () => <section data-testid="lazy-component">Lazy Component</section>
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
const LazyTestComponent = createLazyComponent(
|
|
@@ -38,11 +38,11 @@ describe('LazyLoad Utility', () => {
|
|
|
38
38
|
renderWithProviders(<LazyTestComponent />);
|
|
39
39
|
|
|
40
40
|
// Should show loading spinner initially
|
|
41
|
-
expect(screen.getByTestId('loading-spinner')).
|
|
41
|
+
expect(screen.getByTestId('loading-spinner')).toBeDefined();
|
|
42
42
|
|
|
43
43
|
// Wait for the component to load
|
|
44
44
|
await waitFor(() => {
|
|
45
|
-
expect(screen.getByTestId('lazy-component')).
|
|
45
|
+
expect(screen.getByTestId('lazy-component')).toBeDefined();
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
expect(mockImportFn).toHaveBeenCalledTimes(1);
|
|
@@ -50,10 +50,10 @@ describe('LazyLoad Utility', () => {
|
|
|
50
50
|
|
|
51
51
|
it('should create a lazy component with custom fallback', async () => {
|
|
52
52
|
const mockImportFn = vi.fn().mockResolvedValue({
|
|
53
|
-
default: () => <
|
|
53
|
+
default: () => <section data-testid="lazy-component">Lazy Component</section>
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
const customFallback = <
|
|
56
|
+
const customFallback = <p data-testid="custom-fallback">Custom Loading...</p>;
|
|
57
57
|
|
|
58
58
|
const LazyTestComponent = createLazyComponent(
|
|
59
59
|
mockImportFn,
|
|
@@ -64,21 +64,21 @@ describe('LazyLoad Utility', () => {
|
|
|
64
64
|
renderWithProviders(<LazyTestComponent />);
|
|
65
65
|
|
|
66
66
|
// Should show custom fallback initially
|
|
67
|
-
expect(screen.getByTestId('custom-fallback')).
|
|
67
|
+
expect(screen.getByTestId('custom-fallback')).toBeDefined();
|
|
68
68
|
|
|
69
69
|
// Wait for the component to load
|
|
70
70
|
await waitFor(() => {
|
|
71
|
-
expect(screen.getByTestId('lazy-component')).
|
|
71
|
+
expect(screen.getByTestId('lazy-component')).toBeDefined();
|
|
72
72
|
});
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
it('should create a lazy component with error boundary', async () => {
|
|
76
76
|
const mockImportFn = vi.fn().mockResolvedValue({
|
|
77
|
-
default: () => <
|
|
77
|
+
default: () => <section data-testid="lazy-component">Lazy Component</section>
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
const ErrorBoundary = ({ children }: { children: React.ReactNode }) => (
|
|
81
|
-
<
|
|
81
|
+
<section data-testid="error-boundary">{children}</section>
|
|
82
82
|
);
|
|
83
83
|
|
|
84
84
|
const LazyTestComponent = createLazyComponent(
|
|
@@ -90,15 +90,15 @@ describe('LazyLoad Utility', () => {
|
|
|
90
90
|
renderWithProviders(<LazyTestComponent />);
|
|
91
91
|
|
|
92
92
|
// Should show loading spinner initially
|
|
93
|
-
expect(screen.getByTestId('loading-spinner')).
|
|
93
|
+
expect(screen.getByTestId('loading-spinner')).toBeDefined();
|
|
94
94
|
|
|
95
95
|
// Wait for the component to load
|
|
96
96
|
await waitFor(() => {
|
|
97
|
-
expect(screen.getByTestId('lazy-component')).
|
|
97
|
+
expect(screen.getByTestId('lazy-component')).toBeDefined();
|
|
98
98
|
});
|
|
99
99
|
|
|
100
100
|
// Error boundary should wrap the component
|
|
101
|
-
expect(screen.getByTestId('error-boundary')).
|
|
101
|
+
expect(screen.getByTestId('error-boundary')).toBeDefined();
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
it('should handle import errors gracefully', async () => {
|
|
@@ -121,7 +121,7 @@ describe('LazyLoad Utility', () => {
|
|
|
121
121
|
|
|
122
122
|
render() {
|
|
123
123
|
if (this.state.hasError) {
|
|
124
|
-
return <
|
|
124
|
+
return <p data-testid="error-display">Error occurred</p>;
|
|
125
125
|
}
|
|
126
126
|
return this.props.children;
|
|
127
127
|
}
|
|
@@ -139,11 +139,11 @@ describe('LazyLoad Utility', () => {
|
|
|
139
139
|
renderWithProviders(<LazyTestComponent />);
|
|
140
140
|
|
|
141
141
|
// Should show loading spinner initially
|
|
142
|
-
expect(screen.getByTestId('loading-spinner')).
|
|
142
|
+
expect(screen.getByTestId('loading-spinner')).toBeDefined();
|
|
143
143
|
|
|
144
144
|
// Wait for error to be caught and displayed
|
|
145
145
|
await waitFor(() => {
|
|
146
|
-
expect(screen.getByTestId('error-display')).
|
|
146
|
+
expect(screen.getByTestId('error-display')).toBeDefined();
|
|
147
147
|
}, { timeout: 2000 });
|
|
148
148
|
|
|
149
149
|
// The import function should have been called
|
|
@@ -154,7 +154,7 @@ describe('LazyLoad Utility', () => {
|
|
|
154
154
|
|
|
155
155
|
it('should set display name correctly', async () => {
|
|
156
156
|
const mockImportFn = vi.fn().mockResolvedValue({
|
|
157
|
-
default: () => <
|
|
157
|
+
default: () => <section data-testid="lazy-component">Lazy Component</section>
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
const LazyTestComponent = createLazyComponent(
|
|
@@ -168,9 +168,9 @@ describe('LazyLoad Utility', () => {
|
|
|
168
168
|
it('should pass props to the lazy component', async () => {
|
|
169
169
|
const mockImportFn = vi.fn().mockResolvedValue({
|
|
170
170
|
default: ({ title, count }: { title: string; count: number }) => (
|
|
171
|
-
<
|
|
171
|
+
<section data-testid="lazy-component">
|
|
172
172
|
{title}: {count}
|
|
173
|
-
</
|
|
173
|
+
</section>
|
|
174
174
|
)
|
|
175
175
|
});
|
|
176
176
|
|
|
@@ -189,7 +189,7 @@ describe('LazyLoad Utility', () => {
|
|
|
189
189
|
it('should handle multiple instances of the same lazy component', async () => {
|
|
190
190
|
const mockImportFn = vi.fn().mockResolvedValue({
|
|
191
191
|
default: ({ id }: { id: string }) => (
|
|
192
|
-
<
|
|
192
|
+
<section data-testid={`lazy-component-${id}`}>Component {id}</section>
|
|
193
193
|
)
|
|
194
194
|
});
|
|
195
195
|
|
|
@@ -199,15 +199,15 @@ describe('LazyLoad Utility', () => {
|
|
|
199
199
|
);
|
|
200
200
|
|
|
201
201
|
renderWithProviders(
|
|
202
|
-
<
|
|
202
|
+
<section>
|
|
203
203
|
<LazyTestComponent id="1" />
|
|
204
204
|
<LazyTestComponent id="2" />
|
|
205
|
-
</
|
|
205
|
+
</section>
|
|
206
206
|
);
|
|
207
207
|
|
|
208
208
|
await waitFor(() => {
|
|
209
|
-
expect(screen.getByTestId('lazy-component-1')).
|
|
210
|
-
expect(screen.getByTestId('lazy-component-2')).
|
|
209
|
+
expect(screen.getByTestId('lazy-component-1')).toBeDefined();
|
|
210
|
+
expect(screen.getByTestId('lazy-component-2')).toBeDefined();
|
|
211
211
|
});
|
|
212
212
|
|
|
213
213
|
// Import function should only be called once due to React.lazy caching
|
|
@@ -219,37 +219,40 @@ describe('LazyLoad Utility', () => {
|
|
|
219
219
|
it('should render the DataTable component when loaded', async () => {
|
|
220
220
|
const mockProps = {
|
|
221
221
|
data: [{ id: 1, name: 'Test' }],
|
|
222
|
-
columns: [{ id: 'name', accessorKey: 'name', header: 'Name' }]
|
|
222
|
+
columns: [{ id: 'name', accessorKey: 'name', header: 'Name' }],
|
|
223
|
+
rbac: { pageName: 'test-page' }
|
|
223
224
|
};
|
|
224
225
|
|
|
225
226
|
renderWithProviders(<LazyDataTable {...mockProps} />);
|
|
226
227
|
|
|
227
228
|
// Should show loading spinner initially
|
|
228
|
-
expect(screen.getByTestId('loading-spinner')).
|
|
229
|
+
expect(screen.getByTestId('loading-spinner')).toBeDefined();
|
|
229
230
|
|
|
230
231
|
// Wait for the DataTable to load
|
|
231
232
|
await waitFor(() => {
|
|
232
|
-
expect(screen.getByTestId('data-table')).
|
|
233
|
+
expect(screen.getByTestId('data-table')).toBeDefined();
|
|
233
234
|
});
|
|
234
235
|
});
|
|
235
236
|
|
|
236
237
|
it('should pass props to the DataTable component', async () => {
|
|
237
238
|
const mockProps = {
|
|
238
239
|
data: [{ id: 1, name: 'Test' }],
|
|
239
|
-
columns: [{ id: 'name', accessorKey: 'name', header: 'Name' }]
|
|
240
|
+
columns: [{ id: 'name', accessorKey: 'name', header: 'Name' }],
|
|
241
|
+
rbac: { pageName: 'test-page' }
|
|
240
242
|
};
|
|
241
243
|
|
|
242
244
|
renderWithProviders(<LazyDataTable {...mockProps} />);
|
|
243
245
|
|
|
244
246
|
await waitFor(() => {
|
|
245
|
-
expect(screen.getByTestId('data-table')).
|
|
247
|
+
expect(screen.getByTestId('data-table')).toBeDefined();
|
|
246
248
|
});
|
|
247
249
|
});
|
|
248
250
|
|
|
249
251
|
it('should render without errors', () => {
|
|
250
252
|
const mockProps = {
|
|
251
253
|
data: [{ id: 1, name: 'Test' }],
|
|
252
|
-
columns: [{ id: 'name', accessorKey: 'name', header: 'Name' }]
|
|
254
|
+
columns: [{ id: 'name', accessorKey: 'name', header: 'Name' }],
|
|
255
|
+
rbac: { pageName: 'test-page' }
|
|
253
256
|
};
|
|
254
257
|
|
|
255
258
|
expect(() => renderWithProviders(<LazyDataTable {...mockProps} />)).not.toThrow();
|
|
@@ -259,7 +262,7 @@ describe('LazyLoad Utility', () => {
|
|
|
259
262
|
describe('Component Integration', () => {
|
|
260
263
|
it('should work with React Suspense boundaries', async () => {
|
|
261
264
|
const mockImportFn = vi.fn().mockResolvedValue({
|
|
262
|
-
default: () => <
|
|
265
|
+
default: () => <section data-testid="lazy-component">Lazy Component</section>
|
|
263
266
|
});
|
|
264
267
|
|
|
265
268
|
const LazyTestComponent = createLazyComponent(
|
|
@@ -268,23 +271,23 @@ describe('LazyLoad Utility', () => {
|
|
|
268
271
|
);
|
|
269
272
|
|
|
270
273
|
const TestWrapper = () => (
|
|
271
|
-
<
|
|
274
|
+
<section data-testid="wrapper">
|
|
272
275
|
<LazyTestComponent />
|
|
273
|
-
</
|
|
276
|
+
</section>
|
|
274
277
|
);
|
|
275
278
|
|
|
276
279
|
renderWithProviders(<TestWrapper />);
|
|
277
280
|
|
|
278
281
|
// Should show loading spinner initially
|
|
279
|
-
expect(screen.getByTestId('loading-spinner')).
|
|
282
|
+
expect(screen.getByTestId('loading-spinner')).toBeDefined();
|
|
280
283
|
|
|
281
284
|
// Wait for the component to load
|
|
282
285
|
await waitFor(() => {
|
|
283
|
-
expect(screen.getByTestId('lazy-component')).
|
|
286
|
+
expect(screen.getByTestId('lazy-component')).toBeDefined();
|
|
284
287
|
});
|
|
285
288
|
|
|
286
289
|
// Wrapper should still be present
|
|
287
|
-
expect(screen.getByTestId('wrapper')).
|
|
290
|
+
expect(screen.getByTestId('wrapper')).toBeDefined();
|
|
288
291
|
});
|
|
289
292
|
|
|
290
293
|
it('should handle unmounting before load completes', async () => {
|
|
@@ -292,7 +295,7 @@ describe('LazyLoad Utility', () => {
|
|
|
292
295
|
new Promise(resolve => {
|
|
293
296
|
setTimeout(() => {
|
|
294
297
|
resolve({
|
|
295
|
-
default: () => <
|
|
298
|
+
default: () => <section data-testid="lazy-component">Lazy Component</section>
|
|
296
299
|
});
|
|
297
300
|
}, 100);
|
|
298
301
|
})
|
|
@@ -306,7 +309,7 @@ describe('LazyLoad Utility', () => {
|
|
|
306
309
|
const { unmount } = renderWithProviders(<LazyTestComponent />);
|
|
307
310
|
|
|
308
311
|
// Should show loading spinner initially
|
|
309
|
-
expect(screen.getByTestId('loading-spinner')).
|
|
312
|
+
expect(screen.getByTestId('loading-spinner')).toBeDefined();
|
|
310
313
|
|
|
311
314
|
// Unmount before load completes
|
|
312
315
|
unmount();
|
|
@@ -24,16 +24,16 @@ describe('organisationContext', () => {
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
describe('setOrganisationContext', () => {
|
|
27
|
-
it('should set organisation context successfully', async () => {
|
|
27
|
+
it('should set organisation context successfully (no-op)', async () => {
|
|
28
28
|
const organisationId = 'org-123';
|
|
29
29
|
const mockRpc = vi.fn().mockResolvedValue({ error: null });
|
|
30
30
|
mockSupabase.rpc = mockRpc;
|
|
31
31
|
|
|
32
|
+
// setOrganisationContext is now a no-op (deprecated)
|
|
32
33
|
await setOrganisationContext(mockSupabase, organisationId);
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
35
|
+
// Should not call rpc since it's a no-op
|
|
36
|
+
expect(mockRpc).not.toHaveBeenCalled();
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
it('should handle missing supabase client gracefully', async () => {
|
|
@@ -153,14 +153,13 @@ describe('organisationContext', () => {
|
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
describe('isOrganisationContextAvailable', () => {
|
|
156
|
-
it('should return
|
|
157
|
-
|
|
158
|
-
mockSupabase.rpc = mockRpc;
|
|
159
|
-
|
|
156
|
+
it('should return false (deprecated function)', async () => {
|
|
157
|
+
// isOrganisationContextAvailable is deprecated and always returns false
|
|
160
158
|
const result = await isOrganisationContextAvailable(mockSupabase);
|
|
161
159
|
|
|
162
|
-
|
|
163
|
-
expect(
|
|
160
|
+
// Should not call RPC since function is deprecated
|
|
161
|
+
expect(mockSupabase.rpc).not.toHaveBeenCalled();
|
|
162
|
+
expect(result).toBe(false);
|
|
164
163
|
});
|
|
165
164
|
|
|
166
165
|
it('should return false when supabase client is missing', async () => {
|
|
@@ -29,17 +29,13 @@ describe('Organisation Context', () => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
describe('setOrganisationContext', () => {
|
|
32
|
-
it('sets organisation context successfully', async () => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
error: null
|
|
36
|
-
});
|
|
37
|
-
|
|
32
|
+
it('sets organisation context successfully (no-op)', async () => {
|
|
33
|
+
// setOrganisationContext is now a no-op (deprecated)
|
|
34
|
+
// It should complete without calling rpc
|
|
38
35
|
await setOrganisationContext(mockSupabase, 'org-123');
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
37
|
+
// Should not call rpc since it's a no-op
|
|
38
|
+
expect(mockSupabase.rpc).not.toHaveBeenCalled();
|
|
43
39
|
});
|
|
44
40
|
|
|
45
41
|
it('handles missing supabase client gracefully', async () => {
|
|
@@ -180,20 +176,14 @@ describe('Organisation Context', () => {
|
|
|
180
176
|
});
|
|
181
177
|
|
|
182
178
|
it('handles complete workflow', async () => {
|
|
183
|
-
//
|
|
184
|
-
mockSupabase.rpc.mockResolvedValueOnce({
|
|
185
|
-
data: null,
|
|
186
|
-
error: null
|
|
187
|
-
});
|
|
188
|
-
|
|
179
|
+
// setOrganisationContext is now a no-op, so it won't call rpc
|
|
189
180
|
await setOrganisationContext(mockSupabase, 'org-123');
|
|
190
181
|
|
|
191
|
-
//
|
|
182
|
+
// getOrganisationContext always returns null (deprecated)
|
|
192
183
|
const result = await getOrganisationContext(mockSupabase);
|
|
193
|
-
|
|
194
184
|
expect(result).toBeNull();
|
|
195
185
|
|
|
196
|
-
//
|
|
186
|
+
// clearOrganisationContext still calls rpc
|
|
197
187
|
mockSupabase.rpc.mockResolvedValueOnce({
|
|
198
188
|
data: null,
|
|
199
189
|
error: null
|
|
@@ -201,25 +191,20 @@ describe('Organisation Context', () => {
|
|
|
201
191
|
|
|
202
192
|
await clearOrganisationContext(mockSupabase);
|
|
203
193
|
|
|
204
|
-
|
|
194
|
+
// Only clearOrganisationContext should call rpc (once)
|
|
195
|
+
expect(mockSupabase.rpc).toHaveBeenCalledTimes(1);
|
|
205
196
|
});
|
|
206
197
|
|
|
207
198
|
it('handles multiple organisation switches', async () => {
|
|
208
199
|
const organisations = ['org-123', 'org-456', 'org-789'];
|
|
209
200
|
|
|
201
|
+
// setOrganisationContext is now a no-op, so it won't call rpc
|
|
210
202
|
for (const orgId of organisations) {
|
|
211
|
-
mockSupabase.rpc.mockResolvedValue({
|
|
212
|
-
data: null,
|
|
213
|
-
error: null
|
|
214
|
-
});
|
|
215
|
-
|
|
216
203
|
await setOrganisationContext(mockSupabase, orgId);
|
|
217
204
|
}
|
|
218
205
|
|
|
219
|
-
|
|
220
|
-
expect(mockSupabase.rpc).
|
|
221
|
-
expect(mockSupabase.rpc).toHaveBeenCalledWith('set_organisation_context', { org_id: 'org-456' });
|
|
222
|
-
expect(mockSupabase.rpc).toHaveBeenCalledWith('set_organisation_context', { org_id: 'org-789' });
|
|
206
|
+
// setOrganisationContext is deprecated and doesn't call rpc anymore
|
|
207
|
+
expect(mockSupabase.rpc).not.toHaveBeenCalled();
|
|
223
208
|
});
|
|
224
209
|
});
|
|
225
210
|
|
|
@@ -16,47 +16,25 @@ const log = createLogger('organisationContext');
|
|
|
16
16
|
/**
|
|
17
17
|
* Set organisation context in the database session
|
|
18
18
|
*
|
|
19
|
-
* This function
|
|
20
|
-
*
|
|
19
|
+
* @deprecated This function is a no-op. Organisation context is now handled via:
|
|
20
|
+
* - Secure Supabase client headers (useSecureSupabase hook)
|
|
21
|
+
* - Explicit p_organisation_id parameters in RPC calls
|
|
22
|
+
* - RLS policies that use auth.uid() and organisation_id columns
|
|
21
23
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* @
|
|
24
|
+
* This function is kept for backward compatibility but does nothing.
|
|
25
|
+
*
|
|
26
|
+
* @param supabase - Supabase client instance (unused)
|
|
27
|
+
* @param organisationId - The organisation ID (unused)
|
|
28
|
+
* @returns Promise that resolves immediately
|
|
25
29
|
*/
|
|
26
30
|
export async function setOrganisationContext(
|
|
27
31
|
supabase: SupabaseClient,
|
|
28
32
|
organisationId: string
|
|
29
33
|
): Promise<void> {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
// Add timeout to prevent hanging RPC calls
|
|
37
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
38
|
-
setTimeout(() => reject(new Error('RPC timeout after 3 seconds')), 3000);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// Call the database function to set organisation context
|
|
42
|
-
const rpcPromise = supabase.rpc('set_organisation_context', {
|
|
43
|
-
org_id: organisationId
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const { error } = await Promise.race([rpcPromise, timeoutPromise]) as any;
|
|
47
|
-
|
|
48
|
-
if (error) {
|
|
49
|
-
// Function might not exist yet - this is expected during migration
|
|
50
|
-
// Silent fail - will fall back to client-side filtering
|
|
51
|
-
log.debug('RPC function not available or failed, continuing without database context');
|
|
52
|
-
} else {
|
|
53
|
-
log.debug('Organisation context set in database successfully');
|
|
54
|
-
}
|
|
55
|
-
} catch (error) {
|
|
56
|
-
// Handle any other errors gracefully
|
|
57
|
-
// Silent fail - will fall back to client-side filtering
|
|
58
|
-
log.debug('Failed to set database context, continuing without it:', error);
|
|
59
|
-
}
|
|
34
|
+
// No-op: Organisation context is now handled via secure client and explicit parameters
|
|
35
|
+
// This function is kept for backward compatibility
|
|
36
|
+
log.debug('setOrganisationContext called but is a no-op - context handled via secure client');
|
|
37
|
+
return Promise.resolve();
|
|
60
38
|
}
|
|
61
39
|
|
|
62
40
|
/**
|
|
@@ -132,25 +110,16 @@ export async function getOrganisationContext(
|
|
|
132
110
|
/**
|
|
133
111
|
* Check if organisation context functions are available in the database
|
|
134
112
|
*
|
|
135
|
-
* @
|
|
136
|
-
*
|
|
113
|
+
* @deprecated This function always returns false. Organisation context functions have been removed.
|
|
114
|
+
* Organisation context is now handled via secure client and explicit parameters.
|
|
115
|
+
*
|
|
116
|
+
* @param supabase - Supabase client instance (unused)
|
|
117
|
+
* @returns Promise that resolves to false
|
|
137
118
|
*/
|
|
138
119
|
export async function isOrganisationContextAvailable(
|
|
139
120
|
supabase: SupabaseClient
|
|
140
121
|
): Promise<boolean> {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
const { error } = await supabase.rpc('get_organisation_context');
|
|
147
|
-
|
|
148
|
-
if (error) {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return true;
|
|
153
|
-
} catch (error) {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
122
|
+
// Always return false - organisation context functions have been removed
|
|
123
|
+
// Context is now handled via secure client and explicit parameters
|
|
124
|
+
return false;
|
|
156
125
|
}
|
|
@@ -73,7 +73,7 @@ export const loadFormUtils = async (): Promise<{
|
|
|
73
73
|
|
|
74
74
|
// Dynamic CSV utilities
|
|
75
75
|
export const loadCSVUtils = async (): Promise<unknown> => {
|
|
76
|
-
// @ts-ignore - papaparse is
|
|
76
|
+
// @ts-ignore - papaparse is a dependency of pace-core, should be available via node_modules
|
|
77
77
|
const papaparse = await import('papaparse');
|
|
78
78
|
return papaparse.default;
|
|
79
79
|
};
|
|
@@ -87,6 +87,26 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
87
87
|
log.debug('Using authenticated user ID for user-scoped file upload', { userId: authenticatedUserId });
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
// CRITICAL: Check super admin status in application layer (consistent with pace-core pattern)
|
|
91
|
+
// Super admins bypass all permission checks - this is handled in the application layer,
|
|
92
|
+
// not in RLS policies. The RPC function still validates input and handles the insert,
|
|
93
|
+
// but permission checks are bypassed for super admins.
|
|
94
|
+
let isSuperAdminUser = false;
|
|
95
|
+
const userIdForCheck = authenticatedUserId || options.userId;
|
|
96
|
+
if (userIdForCheck) {
|
|
97
|
+
try {
|
|
98
|
+
// Import isSuperAdmin from rbac/api - this is the standard way to check super admin
|
|
99
|
+
const { isSuperAdmin } = await import('../../rbac/api');
|
|
100
|
+
isSuperAdminUser = await isSuperAdmin(userIdForCheck);
|
|
101
|
+
if (isSuperAdminUser) {
|
|
102
|
+
log.debug('Super admin detected - bypassing permission checks', { userId: userIdForCheck });
|
|
103
|
+
}
|
|
104
|
+
} catch (superAdminCheckError) {
|
|
105
|
+
// If super admin check fails, continue with normal permission flow
|
|
106
|
+
log.warn('Failed to check super-admin status, proceeding with normal permission checks', superAdminCheckError);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
90
110
|
// Step 1: Upload file to storage bucket first
|
|
91
111
|
// This generates a unique path: {orgId}/{folder}/{timestamp-uuid-filename} or users/{auth.uid()}/{folder}/{timestamp-uuid-filename}
|
|
92
112
|
// Bucket is automatically selected based on is_public flag
|
|
@@ -123,6 +143,22 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
123
143
|
|
|
124
144
|
// Step 4: Create file reference in database using RPC function
|
|
125
145
|
// This links the storage path to the record in core_file_references table
|
|
146
|
+
// CRITICAL: Always pass the authenticated user ID to SECURITY DEFINER functions
|
|
147
|
+
// In SECURITY DEFINER functions, auth.uid() returns the function owner's ID,
|
|
148
|
+
// not the caller's ID, so we must explicitly pass the user ID
|
|
149
|
+
let rpcUserId: string | null = null;
|
|
150
|
+
if (authenticatedUserId) {
|
|
151
|
+
rpcUserId = authenticatedUserId;
|
|
152
|
+
} else if (options.userId) {
|
|
153
|
+
rpcUserId = options.userId;
|
|
154
|
+
} else {
|
|
155
|
+
// Get authenticated user ID from session as fallback
|
|
156
|
+
const { data: { user: authUser } } = await this.supabase.auth.getUser();
|
|
157
|
+
if (authUser) {
|
|
158
|
+
rpcUserId = authUser.id;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
126
162
|
const { data, error } = await this.supabase
|
|
127
163
|
.rpc('data_file_reference_create', {
|
|
128
164
|
p_table_name: options.table_name,
|
|
@@ -141,7 +177,7 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
141
177
|
...options.custom_metadata
|
|
142
178
|
},
|
|
143
179
|
p_is_public: options.is_public || false,
|
|
144
|
-
p_user_id:
|
|
180
|
+
p_user_id: rpcUserId // Always pass authenticated user ID for SECURITY DEFINER functions
|
|
145
181
|
});
|
|
146
182
|
|
|
147
183
|
// Step 5: Rollback - if database insert fails, clean up uploaded file
|
|
@@ -152,19 +188,6 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
152
188
|
|
|
153
189
|
// Check if RPC returned null (permission denied or other failure)
|
|
154
190
|
if (!data || data === null) {
|
|
155
|
-
// Before throwing permission error, check if user is super-admin
|
|
156
|
-
// If super-admin, the RPC should have allowed the upload, so this is likely a different issue
|
|
157
|
-
let isSuperAdminUser = false;
|
|
158
|
-
try {
|
|
159
|
-
const { data: { user: authUser } } = await this.supabase.auth.getUser();
|
|
160
|
-
if (authUser) {
|
|
161
|
-
isSuperAdminUser = await isSuperAdmin(authUser.id);
|
|
162
|
-
}
|
|
163
|
-
} catch (superAdminCheckError) {
|
|
164
|
-
// If super-admin check fails, continue with permission error
|
|
165
|
-
log.warn('Failed to check super-admin status', superAdminCheckError);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
191
|
// Clean up the uploaded file since DB insert failed
|
|
169
192
|
await deleteFile(this.supabase, filePath, options.is_public || false);
|
|
170
193
|
|
|
@@ -173,7 +196,8 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
173
196
|
const pageContextDisplay = options.pageContext || 'undefined';
|
|
174
197
|
|
|
175
198
|
if (isSuperAdminUser) {
|
|
176
|
-
// Super-admin should have been allowed - this suggests a
|
|
199
|
+
// Super-admin should have been allowed - this suggests a database or RPC function issue
|
|
200
|
+
// Since we already checked super admin in the application layer, this is unexpected
|
|
177
201
|
throw new Error(
|
|
178
202
|
`File upload failed for super-admin user. This may indicate a database issue. ` +
|
|
179
203
|
`Page context: '${pageContextDisplay}', App: '${appName}'. ` +
|
|
@@ -161,8 +161,9 @@ describe('formatDateTime Utility', () => {
|
|
|
161
161
|
}
|
|
162
162
|
const end = performance.now();
|
|
163
163
|
|
|
164
|
-
// Should complete in reasonable time (less than
|
|
165
|
-
|
|
164
|
+
// Should complete in reasonable time (less than 200ms for 1000 calls in test environment)
|
|
165
|
+
// Increased threshold to account for test environment overhead
|
|
166
|
+
expect(end - start).toBeLessThan(200);
|
|
166
167
|
});
|
|
167
168
|
});
|
|
168
169
|
|
|
@@ -127,13 +127,19 @@ export function loadGoogleMapsScript(
|
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
// Check if script is already being loaded
|
|
130
|
+
// Check if script is already being loaded or exists
|
|
131
131
|
const existingScript = document.querySelector(
|
|
132
132
|
`script[src*="maps.googleapis.com/maps/api/js"]`
|
|
133
133
|
);
|
|
134
134
|
if (existingScript) {
|
|
135
|
+
// If Google Maps is already loaded, resolve immediately
|
|
136
|
+
if (window.google?.maps?.places) {
|
|
137
|
+
resolve();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
135
141
|
// Wait for existing script to load
|
|
136
|
-
|
|
142
|
+
const handleLoad = () => {
|
|
137
143
|
// Wait for the library to initialize with multiple retries
|
|
138
144
|
let attempts = 0;
|
|
139
145
|
const maxAttempts = 20; // 2 seconds total
|
|
@@ -150,10 +156,26 @@ export function loadGoogleMapsScript(
|
|
|
150
156
|
};
|
|
151
157
|
|
|
152
158
|
checkPlaces();
|
|
153
|
-
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Check if script already loaded
|
|
162
|
+
const scriptElement = existingScript as HTMLScriptElement;
|
|
163
|
+
if (scriptElement.getAttribute('data-loaded') === 'true') {
|
|
164
|
+
handleLoad();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Check if script is already complete (using type assertion for readyState which exists at runtime)
|
|
169
|
+
const scriptReadyState = (scriptElement as any).readyState;
|
|
170
|
+
if (scriptReadyState === 'complete' || scriptReadyState === 'loaded') {
|
|
171
|
+
handleLoad();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
existingScript.addEventListener('load', handleLoad, { once: true });
|
|
154
176
|
existingScript.addEventListener('error', () => {
|
|
155
177
|
reject(new Error('Failed to load Google Maps script'));
|
|
156
|
-
});
|
|
178
|
+
}, { once: true });
|
|
157
179
|
return;
|
|
158
180
|
}
|
|
159
181
|
|
|
@@ -164,6 +186,9 @@ export function loadGoogleMapsScript(
|
|
|
164
186
|
script.defer = true;
|
|
165
187
|
|
|
166
188
|
script.onload = () => {
|
|
189
|
+
// Mark script as loaded
|
|
190
|
+
script.setAttribute('data-loaded', 'true');
|
|
191
|
+
|
|
167
192
|
// Wait for the library to initialize with multiple retries
|
|
168
193
|
let attempts = 0;
|
|
169
194
|
const maxAttempts = 20; // 2 seconds total (20 * 100ms)
|