@jmruthers/pace-core 0.6.4 → 0.6.6
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/core-usage-manifest.json +93 -0
- package/cursor-rules/00-pace-core-compliance.mdc +128 -26
- package/cursor-rules/01-standards-compliance.mdc +49 -8
- package/cursor-rules/02-project-structure.mdc +6 -0
- package/cursor-rules/03-solid-principles.mdc +2 -0
- package/cursor-rules/04-testing-standards.mdc +2 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +2 -0
- package/cursor-rules/06-code-quality.mdc +2 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +2 -0
- package/cursor-rules/08-markup-quality.mdc +52 -27
- package/cursor-rules/09-rbac-compliance.mdc +462 -0
- package/cursor-rules/10-error-handling-patterns.mdc +179 -0
- package/cursor-rules/11-performance-optimization.mdc +169 -0
- package/cursor-rules/12-ci-cd-integration.mdc +150 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-2N_tqbfq.d.ts} +1 -1
- package/dist/DataTable-LRJL4IRV.js +15 -0
- package/dist/{PublicPageProvider-DEMpysFR.d.ts → PublicPageProvider-BBH6Vqg7.d.ts} +72 -139
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-J36DSWQK.js → chunk-2HGJFNAH.js} +8 -28
- package/dist/{chunk-OEWDTMG7.js → chunk-3O3WHILE.js} +38 -121
- package/dist/{chunk-M43Y4SSO.js → chunk-3QC3KRHK.js} +1 -14
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/chunk-4T7OBVTU.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-NN6WWZ5U.js → chunk-7TYHROIV.js} +579 -563
- package/dist/{chunk-M7MPQISP.js → chunk-A55DK444.js} +9 -16
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-L4OXEN46.js → chunk-BVP2BCJF.js} +2 -16
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-YKRAFF5K.js → chunk-FENMYN2U.js} +73 -149
- package/dist/{chunk-AVMLPIM7.js → chunk-FTCRZOG2.js} +284 -432
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-I6DAQMWX.js → chunk-LAZMKTTF.js} +930 -891
- package/dist/{chunk-5EC5MEWX.js → chunk-MAGBIDNS.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/chunk-OHIK3MIO.js +994 -0
- package/dist/{chunk-6SOIHG6Z.js → chunk-S7DKJPLT.js} +115 -44
- package/dist/{chunk-FMUCXFII.js → chunk-SD6WQY43.js} +1 -5
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-FFQEQTNW.js → chunk-UIYSCEV7.js} +134 -45
- package/dist/{chunk-3LPHPB62.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-7JPAB3T5.js → chunk-ZS5VO5JB.js} +1989 -1283
- package/dist/components.d.ts +6 -6
- package/dist/components.js +57 -267
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +22 -0
- package/dist/eslint-rules/rules/compliance.cjs +348 -0
- package/dist/eslint-rules/rules/components.cjs +113 -0
- package/dist/eslint-rules/rules/imports.cjs +102 -0
- package/dist/eslint-rules/rules/rbac.cjs +790 -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 +5 -5
- package/dist/hooks.js +62 -270
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +36 -26
- package/dist/index.js +87 -690
- 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 +124 -594
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- 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-B-K_5VnO.d.ts} +4 -0
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-i3qtoBgg.d.ts → usePublicRouteParams-COZ28Mvq.d.ts} +9 -9
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +16 -6
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +454 -930
- package/docs/api-reference/components.md +3 -1
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/best-practices/accessibility.md +6 -3
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/permission-enforcement.md +4 -0
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +12 -3
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/secure-client-protection.md +0 -34
- package/docs/standards/00-pace-core-compliance.md +967 -0
- package/docs/standards/01-standards-compliance.md +188 -0
- package/docs/standards/02-project-structure.md +985 -0
- package/docs/standards/03-solid-principles.md +39 -0
- package/docs/standards/04-testing-standards.md +36 -0
- package/docs/standards/05-bug-reports-and-features.md +27 -0
- package/docs/standards/{04-code-style-standard.md → 06-code-quality.md} +2 -0
- package/docs/standards/07-tech-stack-compliance.md +30 -0
- package/docs/standards/08-markup-quality.md +345 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 09-rbac-compliance.md} +149 -54
- package/docs/standards/10-error-handling-patterns.md +401 -0
- package/docs/standards/11-performance-optimization.md +348 -0
- package/docs/standards/12-ci-cd-integration.md +370 -0
- package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +192 -0
- package/docs/standards/README.md +62 -33
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +20 -4
- package/package.json +31 -21
- package/scripts/audit/audit-compliance.cjs +1295 -0
- package/scripts/audit/audit-components.cjs +260 -0
- package/scripts/audit/audit-dependencies.cjs +395 -0
- package/scripts/audit/audit-rbac.cjs +954 -0
- package/scripts/audit/audit-standards.cjs +1268 -0
- package/scripts/audit/index.cjs +1898 -194
- package/scripts/install-cursor-rules.cjs +259 -8
- package/scripts/validate-master.js +1 -1
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +1 -1
- 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-utils.test.tsx +3 -3
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/ContextSelector/ContextSelector.tsx +42 -39
- package/src/components/DataTable/__tests__/keyboard.test.tsx +15 -2
- package/src/components/DataTable/components/DataTableBody.tsx +55 -31
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableLayout.tsx +30 -5
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +7 -2
- package/src/components/DataTable/components/ImportModal.tsx +4 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/ViewRowModal.tsx +4 -4
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +455 -96
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +122 -58
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/core/DataTableContext.tsx +1 -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 -0
- package/src/components/DateTimeField/DateTimeField.tsx +20 -20
- package/src/components/DateTimeField/README.md +5 -2
- package/src/components/Dialog/Dialog.test.tsx +361 -318
- package/src/components/Dialog/Dialog.tsx +1154 -323
- package/src/components/Dialog/index.ts +3 -3
- package/src/components/FileDisplay/FileDisplay.test.tsx +45 -2
- package/src/components/FileDisplay/FileDisplay.tsx +28 -22
- package/src/components/Form/Form.test.tsx +9 -10
- package/src/components/Form/Form.tsx +369 -9
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +28 -28
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +40 -54
- package/src/components/LoginForm/LoginForm.tsx +2 -2
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +30 -41
- package/src/components/PaceAppLayout/README.md +10 -9
- package/src/components/PaceAppLayout/test-setup.tsx +40 -31
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +61 -0
- package/src/components/PasswordChange/PasswordChangeForm.tsx +20 -13
- package/src/components/PublicLayout/PublicLayout.test.tsx +7 -3
- package/src/components/PublicLayout/PublicPageLayout.tsx +5 -8
- package/src/components/Select/Select.tsx +23 -21
- package/src/components/Select/types.ts +1 -1
- package/src/components/UserMenu/UserMenu.test.tsx +38 -6
- package/src/components/UserMenu/UserMenu.tsx +39 -34
- package/src/components/index.ts +3 -4
- package/src/eslint-rules/index.cjs +22 -0
- package/src/eslint-rules/rules/compliance.cjs +348 -0
- package/src/eslint-rules/rules/components.cjs +113 -0
- package/src/eslint-rules/rules/imports.cjs +102 -0
- package/src/eslint-rules/rules/rbac.cjs +790 -0
- package/src/eslint-rules/utils/helpers.cjs +42 -0
- package/src/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/src/hooks/__tests__/hooks.integration.test.tsx +6 -8
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- 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 +62 -190
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +19 -9
- package/src/hooks/useAppConfig.ts +26 -24
- package/src/hooks/useEventTheme.test.ts +211 -233
- package/src/hooks/useEventTheme.ts +19 -28
- package/src/hooks/useEvents.ts +11 -7
- package/src/hooks/useKeyboardShortcuts.ts +1 -1
- package/src/hooks/useOrganisationPermissions.ts +9 -11
- package/src/hooks/useOrganisations.ts +13 -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 +16 -1
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -0
- package/src/rbac/README.md +20 -20
- 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/useRBAC.test.ts +21 -3
- package/src/rbac/hooks/useRBAC.ts +4 -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 +241 -60
- package/src/rbac/hooks/useResourcePermissions.ts +182 -63
- 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/permissions.ts +17 -17
- package/src/rbac/utils/contextValidator.ts +45 -7
- package/src/services/AuthService.ts +132 -23
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +155 -58
- 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 +4 -0
- package/src/types/database.generated.ts +4733 -3809
- 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/formatting/formatTime.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/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/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-E7YQZD7D.js +0 -175
- package/dist/DataTable-E7YQZD7D.js.map +0 -1
- package/dist/UnifiedAuthProvider-QPXO24B4.js +0 -18
- package/dist/UnifiedAuthProvider-QPXO24B4.js.map +0 -1
- package/dist/api-6LVZTHDS.js +0 -52
- package/dist/api-6LVZTHDS.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-36LVWXB2.js +0 -227
- package/dist/chunk-36LVWXB2.js.map +0 -1
- package/dist/chunk-3LPHPB62.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-5EC5MEWX.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6SOIHG6Z.js.map +0 -1
- package/dist/chunk-7JPAB3T5.js.map +0 -1
- package/dist/chunk-ATKZM7RX.js +0 -2053
- package/dist/chunk-ATKZM7RX.js.map +0 -1
- package/dist/chunk-AVMLPIM7.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.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-I6DAQMWX.js.map +0 -1
- package/dist/chunk-J36DSWQK.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-NN6WWZ5U.js.map +0 -1
- package/dist/chunk-OEWDTMG7.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-YKRAFF5K.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-OOPCLPZW.js +0 -9
- package/dist/contextValidator-OOPCLPZW.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/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/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/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/eslint-rules/pace-core-compliance.cjs +0 -510
- 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
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* // Automatically applies event colors when event is selected via EventProvider
|
|
21
21
|
* useEventTheme();
|
|
22
22
|
*
|
|
23
|
-
* return <
|
|
23
|
+
* return <main>Your app content</main>;
|
|
24
24
|
* }
|
|
25
25
|
* ```
|
|
26
26
|
*
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
* // Applies event colors directly from event prop
|
|
34
34
|
* useEventTheme(event);
|
|
35
35
|
*
|
|
36
|
-
* return <
|
|
36
|
+
* return <main>Public page content</main>;
|
|
37
37
|
* }
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
import { useEffect } from 'react';
|
|
41
|
+
import { useEffect, useContext } from 'react';
|
|
42
42
|
import { useLocation } from 'react-router-dom';
|
|
43
|
-
import {
|
|
43
|
+
import { EventServiceContext } from '../providers/services/EventServiceProvider';
|
|
44
44
|
import { applyPalette, clearPalette } from '../theming/runtime';
|
|
45
45
|
import { parseAndNormalizeEventColours } from '../theming/parseEventColours';
|
|
46
46
|
import type { Event } from '../types/event';
|
|
@@ -79,7 +79,7 @@ const log = createLogger('useEventTheme');
|
|
|
79
79
|
* // Authenticated mode - uses EventProvider
|
|
80
80
|
* function MyApp() {
|
|
81
81
|
* useEventTheme(); // Watches selectedEvent from EventProvider
|
|
82
|
-
* return <
|
|
82
|
+
* return <main>App content</main>;
|
|
83
83
|
* }
|
|
84
84
|
* ```
|
|
85
85
|
*
|
|
@@ -88,7 +88,7 @@ const log = createLogger('useEventTheme');
|
|
|
88
88
|
* // Public page mode - uses event prop
|
|
89
89
|
* function PublicPageLayout({ event }) {
|
|
90
90
|
* useEventTheme(event); // Uses event prop directly
|
|
91
|
-
* return <
|
|
91
|
+
* return <main>Public content</main>;
|
|
92
92
|
* }
|
|
93
93
|
* ```
|
|
94
94
|
*/
|
|
@@ -99,30 +99,21 @@ const log = createLogger('useEventTheme');
|
|
|
99
99
|
* @param event - Optional event object for public page mode. If not provided, uses EventProvider context.
|
|
100
100
|
*/
|
|
101
101
|
export function useEventTheme(event?: Event | null): void {
|
|
102
|
+
// Call all hooks unconditionally at the top level
|
|
103
|
+
// Hooks must be called in the same order on every render
|
|
102
104
|
const location = useLocation();
|
|
103
105
|
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
} catch (error) {
|
|
117
|
-
// useEvents() throws if EventProvider is not available
|
|
118
|
-
// This is expected for public pages - use the event prop if provided
|
|
119
|
-
if (event !== undefined) {
|
|
120
|
-
selectedEvent = event;
|
|
121
|
-
} else {
|
|
122
|
-
// No event prop and no EventProvider - can't apply theme
|
|
123
|
-
selectedEvent = null;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
106
|
+
// Use EventServiceContext directly to handle missing provider gracefully
|
|
107
|
+
// This allows the hook to work in public pages without EventProvider
|
|
108
|
+
const eventServiceContext = useContext(EventServiceContext);
|
|
109
|
+
const eventsContextSelectedEvent = eventServiceContext?.eventService?.getSelectedEvent() ?? null;
|
|
110
|
+
|
|
111
|
+
// Determine which event to use: event prop (public mode) or EventProvider context (authenticated mode)
|
|
112
|
+
// If event prop is provided, use it (public page mode)
|
|
113
|
+
// Otherwise, use EventProvider context (authenticated mode)
|
|
114
|
+
const selectedEvent = event !== undefined
|
|
115
|
+
? event // Public page mode - use event prop
|
|
116
|
+
: eventsContextSelectedEvent; // Authenticated mode - use EventProvider context
|
|
126
117
|
|
|
127
118
|
useEffect(() => {
|
|
128
119
|
// Skip theme application when on login route
|
package/src/hooks/useEvents.ts
CHANGED
|
@@ -37,13 +37,17 @@ export interface EventContextType {
|
|
|
37
37
|
* const { events, selectedEvent, setSelectedEvent } = useEvents();
|
|
38
38
|
*
|
|
39
39
|
* return (
|
|
40
|
-
* <
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* {event.
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
40
|
+
* <nav>
|
|
41
|
+
* <ul>
|
|
42
|
+
* {events.map(event => (
|
|
43
|
+
* <li key={event.id}>
|
|
44
|
+
* <button onClick={() => setSelectedEvent(event)}>
|
|
45
|
+
* {event.event_name}
|
|
46
|
+
* </button>
|
|
47
|
+
* </li>
|
|
48
|
+
* ))}
|
|
49
|
+
* </ul>
|
|
50
|
+
* </nav>
|
|
47
51
|
* );
|
|
48
52
|
* }
|
|
49
53
|
* ```
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
* } = useOrganisationPermissions();
|
|
19
19
|
*
|
|
20
20
|
* return (
|
|
21
|
-
* <
|
|
21
|
+
* <section>
|
|
22
22
|
* {isOrgAdmin && <AdminPanel />}
|
|
23
23
|
* {canManageMembers && <MemberManagement />}
|
|
24
24
|
* <p>Your role: {userRole}</p>
|
|
25
|
-
* </
|
|
25
|
+
* </section>
|
|
26
26
|
* );
|
|
27
27
|
* }
|
|
28
28
|
*
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
* const permissions = useOrganisationPermissions('org-123');
|
|
32
32
|
*
|
|
33
33
|
* if (!permissions.hasOrganisationAccess) {
|
|
34
|
-
* return <
|
|
34
|
+
* return <main>No access to this organisation</main>;
|
|
35
35
|
* }
|
|
36
36
|
*
|
|
37
|
-
* return <
|
|
37
|
+
* return <main>Role in org-123: {permissions.userRole}</main>;
|
|
38
38
|
* }
|
|
39
39
|
* ```
|
|
40
40
|
*
|
|
@@ -106,13 +106,11 @@ export function useOrganisationPermissions(orgId?: string): UseOrganisationPermi
|
|
|
106
106
|
ensureOrganisationContext
|
|
107
107
|
} = useOrganisations();
|
|
108
108
|
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// Not available in this context, default to false
|
|
115
|
-
}
|
|
109
|
+
// Call hook unconditionally - if provider is missing, it will throw
|
|
110
|
+
// Errors should be handled by error boundaries at a higher level
|
|
111
|
+
// We cannot conditionally call hooks
|
|
112
|
+
const organisationSecurity = useOrganisationSecurity();
|
|
113
|
+
const superAdminContext = organisationSecurity.superAdminContext;
|
|
116
114
|
|
|
117
115
|
const organisationId = useMemo(() => {
|
|
118
116
|
if (orgId) {
|
|
@@ -26,14 +26,20 @@ import { Organisation, OrganisationMembership, OrganisationContextType } from '.
|
|
|
26
26
|
* const { selectedOrganisation, organisations, switchOrganisation } = useOrganisations();
|
|
27
27
|
*
|
|
28
28
|
* return (
|
|
29
|
-
* <
|
|
29
|
+
* <main>
|
|
30
30
|
* <h1>Current Organisation: {selectedOrganisation.display_name}</h1>
|
|
31
|
-
*
|
|
32
|
-
* <
|
|
33
|
-
* {org
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
31
|
+
* <nav>
|
|
32
|
+
* <ul>
|
|
33
|
+
* {organisations.map(org => (
|
|
34
|
+
* <li key={org.id}>
|
|
35
|
+
* <button onClick={() => switchOrganisation(org.id)}>
|
|
36
|
+
* {org.display_name}
|
|
37
|
+
* </button>
|
|
38
|
+
* </li>
|
|
39
|
+
* ))}
|
|
40
|
+
* </ul>
|
|
41
|
+
* </nav>
|
|
42
|
+
* </main>
|
|
37
43
|
* );
|
|
38
44
|
* }
|
|
39
45
|
* ```
|
|
@@ -50,7 +50,6 @@ function runCacheCleanup() {
|
|
|
50
50
|
// Initialize cleanup timer once
|
|
51
51
|
if (typeof window !== 'undefined' && !cleanupTimer) {
|
|
52
52
|
cleanupTimer = setInterval(runCacheCleanup, CLEANUP_INTERVAL_MS);
|
|
53
|
-
log.debug('Query cache cleanup initialized.');
|
|
54
53
|
|
|
55
54
|
// Cleanup on page unload to prevent memory leaks
|
|
56
55
|
window.addEventListener('beforeunload', () => {
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Session Draft Persistence Hook
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Hooks/Persistence
|
|
5
|
+
* @since 1.0.0
|
|
6
|
+
*
|
|
7
|
+
* A React hook for persisting component state to sessionStorage.
|
|
8
|
+
* Provides automatic persistence that survives tab switching but clears on tab close.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Debounced writes to prevent excessive storage operations
|
|
12
|
+
* - Automatic restoration on mount
|
|
13
|
+
* - Versioning support for schema migrations
|
|
14
|
+
* - Safe JSON serialization with error handling
|
|
15
|
+
* - Type-safe with generics
|
|
16
|
+
* - Handles storage quota errors gracefully
|
|
17
|
+
* - Optional cleanup on unmount
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* const { state, setState, clearDraft } = useSessionDraft('my-component', {
|
|
22
|
+
* filters: [],
|
|
23
|
+
* sortBy: 'name'
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // State is automatically persisted on changes
|
|
27
|
+
* setState({ filters: ['active'], sortBy: 'email' });
|
|
28
|
+
*
|
|
29
|
+
* // Clear draft when done
|
|
30
|
+
* clearDraft();
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @dependencies
|
|
34
|
+
* - React 19+ - Hooks and effects
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Schema version for stored drafts
|
|
41
|
+
* Increment when the data structure changes
|
|
42
|
+
*/
|
|
43
|
+
const DRAFT_SCHEMA_VERSION = 1;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Default debounce delay in milliseconds
|
|
47
|
+
*/
|
|
48
|
+
const DEFAULT_DEBOUNCE_MS = 300;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Stored draft payload structure
|
|
52
|
+
*/
|
|
53
|
+
interface StoredDraft<T> {
|
|
54
|
+
version: number;
|
|
55
|
+
data: T;
|
|
56
|
+
timestamp: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Options for useSessionDraft hook
|
|
61
|
+
*/
|
|
62
|
+
export interface UseSessionDraftOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Debounce delay in milliseconds
|
|
65
|
+
* @default 300
|
|
66
|
+
*/
|
|
67
|
+
debounceMs?: number;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Whether to clear draft on component unmount
|
|
71
|
+
* @default false
|
|
72
|
+
*/
|
|
73
|
+
clearOnUnmount?: boolean;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Whether to enable persistence (useful for conditional persistence)
|
|
77
|
+
* @default true
|
|
78
|
+
*/
|
|
79
|
+
enabled?: boolean;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Custom storage key prefix
|
|
83
|
+
* @default 'pace-core:draft'
|
|
84
|
+
*/
|
|
85
|
+
keyPrefix?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Return value of useSessionDraft hook
|
|
90
|
+
*/
|
|
91
|
+
export interface UseSessionDraftReturn<T> {
|
|
92
|
+
/**
|
|
93
|
+
* Current draft state
|
|
94
|
+
*/
|
|
95
|
+
state: T;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Update draft state (triggers debounced save)
|
|
99
|
+
*/
|
|
100
|
+
setState: (newState: T | ((prev: T) => T)) => void;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Clear the draft from storage
|
|
104
|
+
*/
|
|
105
|
+
clearDraft: () => void;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Force immediate save (bypasses debounce)
|
|
109
|
+
*/
|
|
110
|
+
saveImmediately: () => void;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Whether the draft was restored from storage
|
|
114
|
+
*/
|
|
115
|
+
wasRestored: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Debounce function that matches lodash debounce API
|
|
120
|
+
*/
|
|
121
|
+
function debounce<T extends (...args: any[]) => void>(
|
|
122
|
+
func: T,
|
|
123
|
+
wait: number
|
|
124
|
+
): T & { cancel: () => void } {
|
|
125
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
126
|
+
|
|
127
|
+
const debounced = ((...args: Parameters<T>) => {
|
|
128
|
+
if (timeoutId !== null) {
|
|
129
|
+
clearTimeout(timeoutId);
|
|
130
|
+
}
|
|
131
|
+
timeoutId = setTimeout(() => {
|
|
132
|
+
func(...args);
|
|
133
|
+
}, wait);
|
|
134
|
+
}) as T & { cancel: () => void };
|
|
135
|
+
|
|
136
|
+
debounced.cancel = () => {
|
|
137
|
+
if (timeoutId !== null) {
|
|
138
|
+
clearTimeout(timeoutId);
|
|
139
|
+
timeoutId = null;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return debounced;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get the full storage key with namespace
|
|
148
|
+
*/
|
|
149
|
+
function getStorageKey(key: string, prefix: string): string {
|
|
150
|
+
return `${prefix}:${key}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Save draft to sessionStorage
|
|
155
|
+
*/
|
|
156
|
+
function saveDraft<T>(storageKey: string, data: T): boolean {
|
|
157
|
+
if (typeof window === 'undefined' || !window.sessionStorage) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
const payload: StoredDraft<T> = {
|
|
163
|
+
version: DRAFT_SCHEMA_VERSION,
|
|
164
|
+
data,
|
|
165
|
+
timestamp: Date.now(),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const serialized = JSON.stringify(payload);
|
|
169
|
+
window.sessionStorage.setItem(storageKey, serialized);
|
|
170
|
+
return true;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
// Handle quota exceeded or other storage errors
|
|
173
|
+
if (error instanceof DOMException && error.name === 'QuotaExceededError') {
|
|
174
|
+
console.warn(`[useSessionDraft] Storage quota exceeded for key: ${storageKey}`);
|
|
175
|
+
} else {
|
|
176
|
+
console.warn(`[useSessionDraft] Failed to save draft for key: ${storageKey}`, error);
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Load draft from sessionStorage
|
|
184
|
+
*/
|
|
185
|
+
function loadDraft<T>(storageKey: string): T | null {
|
|
186
|
+
if (typeof window === 'undefined' || !window.sessionStorage) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const stored = window.sessionStorage.getItem(storageKey);
|
|
192
|
+
if (!stored) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const parsed = JSON.parse(stored) as StoredDraft<T>;
|
|
197
|
+
|
|
198
|
+
// Validate version
|
|
199
|
+
if (parsed.version !== DRAFT_SCHEMA_VERSION) {
|
|
200
|
+
console.warn(
|
|
201
|
+
`[useSessionDraft] Schema version mismatch for key: ${storageKey}. Expected ${DRAFT_SCHEMA_VERSION}, got ${parsed.version}`
|
|
202
|
+
);
|
|
203
|
+
// Clear outdated draft
|
|
204
|
+
window.sessionStorage.removeItem(storageKey);
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Validate structure
|
|
209
|
+
if (!parsed.data) {
|
|
210
|
+
console.warn(`[useSessionDraft] Invalid draft structure for key: ${storageKey}`);
|
|
211
|
+
window.sessionStorage.removeItem(storageKey);
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return parsed.data;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.warn(`[useSessionDraft] Failed to load draft for key: ${storageKey}`, error);
|
|
218
|
+
// Clear corrupted draft
|
|
219
|
+
try {
|
|
220
|
+
if (typeof window !== 'undefined' && window.sessionStorage) {
|
|
221
|
+
window.sessionStorage.removeItem(storageKey);
|
|
222
|
+
}
|
|
223
|
+
} catch {
|
|
224
|
+
// Ignore cleanup errors
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Remove draft from sessionStorage
|
|
232
|
+
*/
|
|
233
|
+
function removeDraft(storageKey: string): void {
|
|
234
|
+
if (typeof window === 'undefined' || !window.sessionStorage) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
window.sessionStorage.removeItem(storageKey);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.warn(`[useSessionDraft] Failed to remove draft for key: ${storageKey}`, error);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Hook for persisting component state to sessionStorage
|
|
247
|
+
*
|
|
248
|
+
* Automatically saves state changes with debouncing and restores state on mount.
|
|
249
|
+
* Data persists across tab switches but clears when the tab closes.
|
|
250
|
+
*
|
|
251
|
+
* @template T - The type of state to persist
|
|
252
|
+
* @param key - Unique key for this draft (will be namespaced automatically)
|
|
253
|
+
* @param initialState - Initial state if no draft exists
|
|
254
|
+
* @param options - Configuration options
|
|
255
|
+
* @returns Object containing state, setState, clearDraft, and utility functions
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```tsx
|
|
259
|
+
* const { state, setState } = useSessionDraft('user-filters', {
|
|
260
|
+
* search: '',
|
|
261
|
+
* status: 'all'
|
|
262
|
+
* });
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
export function useSessionDraft<T>(
|
|
266
|
+
key: string,
|
|
267
|
+
initialState: T,
|
|
268
|
+
options: UseSessionDraftOptions = {}
|
|
269
|
+
): UseSessionDraftReturn<T> {
|
|
270
|
+
const {
|
|
271
|
+
debounceMs = DEFAULT_DEBOUNCE_MS,
|
|
272
|
+
clearOnUnmount = false,
|
|
273
|
+
enabled = true,
|
|
274
|
+
keyPrefix = 'pace-core:draft',
|
|
275
|
+
} = options;
|
|
276
|
+
|
|
277
|
+
const storageKey = getStorageKey(key, keyPrefix);
|
|
278
|
+
const wasRestoredRef = useRef(false);
|
|
279
|
+
|
|
280
|
+
// Load draft on mount
|
|
281
|
+
const [state, setStateInternal] = useState<T>(() => {
|
|
282
|
+
if (!enabled) {
|
|
283
|
+
return initialState;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const restored = loadDraft<T>(storageKey);
|
|
287
|
+
if (restored !== null) {
|
|
288
|
+
wasRestoredRef.current = true;
|
|
289
|
+
return restored;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return initialState;
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Create debounced save function
|
|
296
|
+
const debouncedSaveRef = useRef<ReturnType<typeof debounce> | null>(null);
|
|
297
|
+
|
|
298
|
+
useEffect(() => {
|
|
299
|
+
if (!enabled) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Create debounced save function
|
|
304
|
+
const debouncedSave = debounce((data: T) => {
|
|
305
|
+
saveDraft(storageKey, data);
|
|
306
|
+
}, debounceMs);
|
|
307
|
+
|
|
308
|
+
debouncedSaveRef.current = debouncedSave;
|
|
309
|
+
|
|
310
|
+
return () => {
|
|
311
|
+
// Cancel pending saves on unmount
|
|
312
|
+
debouncedSave.cancel();
|
|
313
|
+
};
|
|
314
|
+
}, [storageKey, debounceMs, enabled]);
|
|
315
|
+
|
|
316
|
+
// Save state changes
|
|
317
|
+
const setState = useCallback(
|
|
318
|
+
(newState: T | ((prev: T) => T)) => {
|
|
319
|
+
setStateInternal((prev) => {
|
|
320
|
+
const updated = typeof newState === 'function' ? (newState as (prev: T) => T)(prev) : newState;
|
|
321
|
+
|
|
322
|
+
// Save to storage (debounced)
|
|
323
|
+
if (enabled && debouncedSaveRef.current) {
|
|
324
|
+
debouncedSaveRef.current(updated);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return updated;
|
|
328
|
+
});
|
|
329
|
+
},
|
|
330
|
+
[enabled]
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
// Force immediate save
|
|
334
|
+
const saveImmediately = useCallback(() => {
|
|
335
|
+
if (!enabled) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Cancel any pending debounced save
|
|
340
|
+
if (debouncedSaveRef.current) {
|
|
341
|
+
debouncedSaveRef.current.cancel();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Save immediately
|
|
345
|
+
saveDraft(storageKey, state);
|
|
346
|
+
}, [storageKey, state, enabled]);
|
|
347
|
+
|
|
348
|
+
// Clear draft
|
|
349
|
+
const clearDraft = useCallback(() => {
|
|
350
|
+
removeDraft(storageKey);
|
|
351
|
+
setStateInternal(initialState);
|
|
352
|
+
wasRestoredRef.current = false;
|
|
353
|
+
}, [storageKey, initialState]);
|
|
354
|
+
|
|
355
|
+
// Cleanup on unmount if configured
|
|
356
|
+
useEffect(() => {
|
|
357
|
+
if (!enabled || !clearOnUnmount) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return () => {
|
|
362
|
+
// Cancel pending saves
|
|
363
|
+
if (debouncedSaveRef.current) {
|
|
364
|
+
debouncedSaveRef.current.cancel();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Clear draft
|
|
368
|
+
removeDraft(storageKey);
|
|
369
|
+
};
|
|
370
|
+
}, [storageKey, clearOnUnmount, enabled]);
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
state,
|
|
374
|
+
setState,
|
|
375
|
+
clearDraft,
|
|
376
|
+
saveImmediately,
|
|
377
|
+
wasRestored: wasRestoredRef.current,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
@@ -16,7 +16,9 @@ import { createLogger } from '../utils/core/logger';
|
|
|
16
16
|
|
|
17
17
|
const log = createLogger('useSessionRestoration');
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// Increased timeout to handle hard refresh scenarios where localStorage access
|
|
20
|
+
// and session restoration may take longer, especially with organisation/event context
|
|
21
|
+
const SESSION_RESTORATION_TIMEOUT_MS = 10000; // 10 seconds (increased from 5)
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Return value of the useSessionRestoration hook.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Icon Exports
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Icons
|
|
5
|
+
* @since 0.6.6
|
|
6
|
+
*
|
|
7
|
+
* Icon exports from lucide-react.
|
|
8
|
+
* Import icons from '@jmruthers/pace-core/icons' instead of 'lucide-react'.
|
|
9
|
+
*
|
|
10
|
+
* All lucide-react icons are exported to ensure consuming apps have access
|
|
11
|
+
* to all icons while enforcing consistent usage through pace-core.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* // ✅ CORRECT: Import icons from pace-core
|
|
16
|
+
* import { ChevronDown, Edit, Trash } from '@jmruthers/pace-core/icons';
|
|
17
|
+
*
|
|
18
|
+
* // ❌ WRONG: Direct import from lucide-react (will fail - lucide-react is not a peer dependency)
|
|
19
|
+
* import { ChevronDown } from 'lucide-react';
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
// Export all icons from lucide-react
|
|
24
|
+
// This ensures consuming apps have access to all icons while enforcing
|
|
25
|
+
// consistent usage through pace-core's icon export system
|
|
26
|
+
export * from 'lucide-react';
|
|
27
|
+
|
package/src/index.ts
CHANGED
|
@@ -101,7 +101,6 @@ export type { ProgressProps } from './components/Progress';
|
|
|
101
101
|
export {
|
|
102
102
|
Dialog,
|
|
103
103
|
DialogPortal,
|
|
104
|
-
DialogOverlay,
|
|
105
104
|
DialogTrigger,
|
|
106
105
|
DialogClose,
|
|
107
106
|
DialogContent,
|
|
@@ -111,6 +110,17 @@ export {
|
|
|
111
110
|
DialogTitle,
|
|
112
111
|
DialogDescription,
|
|
113
112
|
} from './components/Dialog/Dialog';
|
|
113
|
+
export type {
|
|
114
|
+
DialogProps,
|
|
115
|
+
DialogTriggerProps,
|
|
116
|
+
DialogContentProps,
|
|
117
|
+
DialogPortalProps,
|
|
118
|
+
DialogCloseProps,
|
|
119
|
+
DialogHeaderProps,
|
|
120
|
+
DialogFooterProps,
|
|
121
|
+
DialogBodyProps,
|
|
122
|
+
DialogSize
|
|
123
|
+
} from './components/Dialog/Dialog';
|
|
114
124
|
|
|
115
125
|
// Dropdown Menu exports
|
|
116
126
|
// DropdownMenu components have been merged into Select components
|
|
@@ -202,6 +212,8 @@ export { z } from 'zod';
|
|
|
202
212
|
// Common validation schemas
|
|
203
213
|
export { emailSchema, nameSchema, phoneSchema, urlSchema } from './utils/validation/common';
|
|
204
214
|
export { passwordSchema } from './utils/validation/passwordSchema';
|
|
215
|
+
// Sanitization utilities
|
|
216
|
+
export { sanitizeUserInput, sanitizeFormData, sanitizeHtml } from './utils/validation/sanitization';
|
|
205
217
|
|
|
206
218
|
// LAYOUT COMPONENTS
|
|
207
219
|
export { Header } from './components/Header/Header';
|
|
@@ -250,6 +262,9 @@ export { useEventTheme } from './hooks/useEventTheme';
|
|
|
250
262
|
export { cn } from './utils/core/cn';
|
|
251
263
|
export { setAppConfig, getAppConfig, getCurrentAppName, getCurrentAppId } from './utils/app/appConfig';
|
|
252
264
|
|
|
265
|
+
// Supabase client creation (restricted wrapper - ONLY for base client creation)
|
|
266
|
+
export { createBaseClient } from './utils/supabase/createBaseClient';
|
|
267
|
+
|
|
253
268
|
// LOGGING UTILITIES
|
|
254
269
|
export { Logger, logger, createLogger, LogLevel } from './utils/core/logger';
|
|
255
270
|
export type { LoggerConfig } from './utils/core/logger';
|