@jmruthers/pace-core 0.6.8 → 0.6.10
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 +3 -0
- package/audit-tool/audits/02-project-structure.cjs +97 -32
- package/audit-tool/audits/03-architecture.cjs +145 -19
- package/audit-tool/audits/04-code-quality.cjs +86 -1
- package/audit-tool/audits/06-security-rbac.cjs +109 -11
- package/cursor-rules/02-project-structure.mdc +2 -26
- package/cursor-rules/05-styling.mdc +84 -6
- package/cursor-rules/06-security-rbac.mdc +124 -1
- package/dist/{DataTable-6RMSCQJ6.js → DataTable-SAXFG4XI.js} +11 -13
- package/dist/{AuthService-DmfO5rGS.d.ts → InactivityServiceProvider-DHryoh6K.d.ts} +24 -249
- package/dist/UnifiedAuthProvider-BBD2PS3Q.js +7 -0
- package/dist/{UnifiedAuthProvider-CKvHP1MK.d.ts → UnifiedAuthProvider-CiBAl9-s.d.ts} +34 -22
- package/dist/{api-7P7DI652.js → api-F47QJ7FX.js} +3 -3
- package/dist/assets/app-icons/admin_favicon.svg +462 -0
- package/dist/assets/app-icons/base_favicon.svg +85 -0
- package/dist/assets/app-icons/cake_favicon.svg +68 -0
- package/dist/assets/app-icons/core_favicon.svg +256 -0
- package/dist/assets/app-icons/gear_favicon.svg +91 -0
- package/dist/assets/app-icons/medi_favicon.svg +92 -0
- package/dist/assets/app-icons/mint_favicon.svg +83 -0
- package/dist/assets/app-icons/pace_favicon.svg +49 -0
- package/dist/assets/app-icons/pump_favicon.svg +68 -0
- package/dist/assets/app-icons/seed_favicon.svg +91 -0
- package/dist/assets/app-icons/team_favicon.svg +67 -0
- package/dist/assets/app-icons/trac_favicon.svg +112 -0
- package/dist/assets/app-icons/trip_favicon.svg +102 -0
- package/dist/audit-Z6ZZBWLU.js +3 -0
- package/dist/chunk-3GWSPISD.js +61 -0
- package/dist/{chunk-4DDCYDQ3.js → chunk-66R6RLUZ.js} +12 -27
- package/dist/{chunk-FYHN4DD5.js → chunk-7YDC7LMU.js} +80 -8
- package/dist/{chunk-S7DKJPLT.js → chunk-BCTXBU6U.js} +22 -17
- package/dist/{chunk-TTRFSOKR.js → chunk-BTHN5MKC.js} +4 -4
- package/dist/{chunk-A3W6LW53.js → chunk-DDMPHZ3D.js} +6 -18
- package/dist/{chunk-MPBLMWVR.js → chunk-FBZ7U3ID.js} +140 -92
- package/dist/chunk-FN52B75D.js +246 -0
- package/dist/{chunk-5W2A3DRC.js → chunk-JJEYZ3DX.js} +5 -4
- package/dist/chunk-KPYQWGFQ.js +183 -0
- package/dist/{chunk-IUBRCBSY.js → chunk-KSNLMI7N.js} +14 -8
- package/dist/chunk-KYURMOQM.js +977 -0
- package/dist/{chunk-LX6U42O3.js → chunk-LNHFAF4X.js} +160 -58
- package/dist/{chunk-NKHKXPI4.js → chunk-MPY44PWB.js} +683 -627
- package/dist/{chunk-AHU7G2R5.js → chunk-NIU6DPQV.js} +10 -6
- package/dist/{chunk-HF6O3O37.js → chunk-RMLY6KB5.js} +1 -1
- package/dist/{chunk-6GLLNA6U.js → chunk-SACF5YSM.js} +1 -1
- package/dist/{chunk-EURB7QFZ.js → chunk-TFIPNIPE.js} +867 -534
- package/dist/{chunk-OJ4SKRSV.js → chunk-UZNAFKGW.js} +25 -5
- package/dist/chunk-W46INAVW.js +1216 -0
- package/dist/chunk-X5EAU5G7.js +793 -0
- package/dist/{chunk-T5CVK4R3.js → chunk-Y4PF6HIM.js} +110 -64
- package/dist/components.d.ts +8 -86
- package/dist/components.js +21 -55
- package/dist/{database.generated-CcnC_DRc.d.ts → database.generated-DT8JTZiP.d.ts} +12 -12
- package/dist/eslint-rules/rules/05-styling.cjs +507 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +10 -0
- package/dist/{event-CW5YB_2p.d.ts → event-WTAQuGcq.d.ts} +1 -1
- package/dist/{functions-lBy5L2ry.d.ts → functions-DH45k8ec.d.ts} +1 -1
- package/dist/hooks.d.ts +12 -11
- package/dist/hooks.js +69 -44
- package/dist/index.d.ts +380 -32
- package/dist/index.js +46 -32
- package/dist/papaparseLoader-WG2UXQ22.js +7 -0
- package/dist/providers.d.ts +28 -14
- package/dist/providers.js +5 -5
- package/dist/rbac/eslint-rules.js +2 -2
- package/dist/rbac/index.d.ts +58 -214
- package/dist/rbac/index.js +11 -11
- package/dist/theming/runtime.d.ts +9 -3
- package/dist/theming/runtime.js +2 -2
- package/dist/{timezone-BZe_eUxx.d.ts → timezone-K-ptz3HO.d.ts} +22 -23
- package/dist/{types-t9H8qKRw.d.ts → types-BE2sEHKd.d.ts} +1 -1
- package/dist/{types-BeoeWV5I.d.ts → types-CvOPXWWZ.d.ts} +6 -5
- package/dist/{types-DXstZpNI.d.ts → types-D05dCGma.d.ts} +56 -149
- package/dist/types-Dr8sNhER.d.ts +50 -0
- package/dist/types.d.ts +5 -5
- package/dist/{PublicPageProvider-CIGSujI2.d.ts → usePublicPageContext-vxBlEHO9.d.ts} +294 -151
- package/dist/{usePublicRouteParams-MamNgwqe.d.ts → usePublicRouteParams-G3Ks53mk.d.ts} +8 -7
- package/dist/utils.d.ts +301 -137
- package/dist/utils.js +42 -41
- package/dist/{validation-643vUDZW.d.ts → validation-g5n0hDkh.d.ts} +2 -2
- package/docs/api/modules.md +542 -549
- package/docs/api-reference/components.md +5 -5
- package/docs/api-reference/rpc-functions.md +3 -3
- package/docs/implementation-guides/data-tables.md +256 -8
- package/docs/rbac/RBAC_CONTRACT.md +0 -12
- package/docs/standards/2-project-structure-standards.md +12 -74
- package/docs/standards/6-security-rbac-standards.md +222 -7
- package/docs/standards/7-api-tech-stack-standards.md +91 -3
- package/docs/testing/README.md +10 -0
- package/docs/testing/test-setup-for-consumers.md +914 -0
- package/eslint-config-pace-core.cjs +4 -0
- package/package.json +1 -1
- package/scripts/eslint-audit.cjs +110 -11
- package/src/__mocks__/lucide-react.ts +0 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -2
- package/src/__tests__/index.test.ts +532 -0
- package/src/__tests__/integration/UserProfile.test.tsx +1 -1
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -8
- package/src/__tests__/rls-policies.test.ts +3 -2
- package/src/assets/app-icons/admin_favicon.svg +462 -0
- package/src/assets/app-icons/base_favicon.svg +85 -0
- package/src/assets/app-icons/cake_favicon.svg +68 -0
- package/src/assets/app-icons/core_favicon.svg +256 -0
- package/src/assets/app-icons/gear_favicon.svg +91 -0
- package/src/assets/app-icons/index.ts +83 -0
- package/src/assets/app-icons/medi_favicon.svg +92 -0
- package/src/assets/app-icons/mint_favicon.svg +83 -0
- package/src/assets/app-icons/pace_favicon.svg +49 -0
- package/src/assets/app-icons/pump_favicon.svg +68 -0
- package/src/assets/app-icons/seed_favicon.svg +91 -0
- package/src/assets/app-icons/team_favicon.svg +67 -0
- package/src/assets/app-icons/trac_favicon.svg +112 -0
- package/src/assets/app-icons/trip_favicon.svg +102 -0
- package/src/components/AddressField/AddressField.test.tsx +378 -3
- package/src/components/AddressField/AddressField.tsx +2 -2
- package/src/components/AddressField/types.ts +2 -2
- package/src/components/Alert/Alert.test.tsx +35 -25
- package/src/components/Alert/Alert.tsx +8 -8
- package/src/components/AppSwitcher/AppSwitcher.test.tsx +1250 -0
- package/src/components/AppSwitcher/AppSwitcher.tsx +315 -0
- package/src/components/Avatar/Avatar.test.tsx +11 -1
- package/src/components/Avatar/Avatar.tsx +3 -2
- package/src/components/Badge/Badge.test.tsx +11 -1
- package/src/components/Button/Button.test.tsx +13 -3
- package/src/components/Calendar/Calendar.test.tsx +523 -131
- package/src/components/Calendar/Calendar.tsx +107 -488
- package/src/components/Card/Card.test.tsx +220 -249
- package/src/components/Checkbox/Checkbox.test.tsx +58 -174
- package/src/components/ContextSelector/ContextSelector.tsx +3 -3
- package/src/components/ContextSelector/__tests__/ContextSelector.test.tsx +360 -0
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +1 -1
- package/src/components/DataTable/__tests__/DataTable.export.test.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +1 -1
- package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +485 -0
- package/src/components/DataTable/__tests__/DataTable.test.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +1 -1
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +76 -580
- package/src/components/DataTable/__tests__/README.md +1 -1
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +1 -1
- package/src/components/DataTable/__tests__/keyboard.test.tsx +1 -1
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +1 -3
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -6
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +14 -6
- package/src/components/DataTable/components/ActionButtons.tsx +9 -4
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +3 -3
- package/src/components/DataTable/components/ColumnFilter.tsx +2 -7
- package/src/components/DataTable/components/DataTableCore.tsx +44 -52
- package/src/components/DataTable/components/DataTableLayout.tsx +37 -26
- package/src/components/DataTable/components/DataTableModals.tsx +118 -30
- package/src/components/DataTable/components/DataTableToolbar.tsx +2 -2
- package/src/components/DataTable/components/EditFields.tsx +6 -47
- package/src/components/DataTable/components/EditableRow.tsx +8 -8
- package/src/components/DataTable/components/EmptyState.tsx +6 -3
- package/src/components/DataTable/components/FilterRow.tsx +18 -11
- package/src/components/DataTable/components/GroupingDropdown.tsx +0 -1
- package/src/components/DataTable/components/ImportModal.tsx +305 -133
- package/src/components/DataTable/components/LoadingState.tsx +2 -2
- package/src/components/DataTable/components/PaginationControls.tsx +0 -4
- package/src/components/DataTable/components/RowComponent.tsx +42 -22
- package/src/components/DataTable/components/UnifiedTableBody.tsx +52 -12
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +51 -463
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +122 -116
- package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +40 -68
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +9 -137
- package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +57 -17
- package/src/components/DataTable/components/__tests__/DataTableCore.test.tsx +792 -0
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +24 -65
- package/src/components/DataTable/components/__tests__/DataTableLayout.test.tsx +467 -0
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +8 -125
- package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +528 -56
- package/src/components/DataTable/components/__tests__/EditFields.test.tsx +526 -0
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +1 -68
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +8 -25
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +3 -62
- package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +9 -14
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +50 -186
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +39 -97
- package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +13 -103
- package/src/components/DataTable/components/__tests__/RowComponent.test.tsx +629 -0
- package/src/components/DataTable/components/__tests__/SortIndicator.test.tsx +135 -0
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +31 -171
- package/src/components/DataTable/components/__tests__/cellValueUtils.test.ts +453 -0
- package/src/components/DataTable/components/hooks/useImportModalFocus.test.ts +184 -0
- package/src/components/DataTable/components/hooks/usePermissionTracking.test.ts +381 -0
- package/src/components/DataTable/context/DataTableContext.tsx +9 -10
- package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +12 -26
- package/src/components/DataTable/core/ColumnFactory.ts +3 -3
- package/src/components/DataTable/core/ColumnManager.ts +0 -1
- package/src/components/DataTable/core/DataManager.ts +4 -2
- package/src/components/DataTable/core/LocalDataAdapter.ts +1 -1
- package/src/components/DataTable/core/PluginRegistry.ts +2 -2
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +114 -2
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +103 -5
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +57 -0
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +63 -0
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +42 -9
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +29 -7
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +58 -4
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +16 -21
- package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +93 -4
- package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +227 -54
- package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +215 -62
- package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +217 -39
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +101 -6
- package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +157 -27
- package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +80 -0
- package/src/components/DataTable/hooks/__tests__/useKeyboardNavigation.test.ts +787 -0
- package/src/components/DataTable/hooks/__tests__/useServerSideDataEffect.test.ts +258 -0
- package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +298 -23
- package/src/components/DataTable/hooks/__tests__/useTableHandlers.test.ts +440 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +1 -1
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +11 -22
- package/src/components/DataTable/hooks/useDataTableState.ts +20 -24
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +5 -5
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +13 -1
- package/src/components/DataTable/hooks/useTableColumns.ts +36 -38
- package/src/components/DataTable/hooks/useTableHandlers.ts +8 -20
- package/src/components/DataTable/index.ts +24 -2
- package/src/components/DataTable/types.ts +6 -3
- package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +3 -67
- package/src/components/DataTable/utils/__tests__/aggregationUtils.test.ts +288 -0
- package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +3 -60
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +1 -1
- package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +9 -21
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +102 -86
- package/src/components/DataTable/utils/__tests__/paginationUtils.test.ts +593 -0
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +33 -49
- package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +208 -0
- package/src/components/DataTable/utils/a11yUtils.ts +1 -1
- package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
- package/src/components/DataTable/utils/errorHandling.ts +3 -1
- package/src/components/DataTable/utils/exportUtils.ts +1 -1
- package/src/components/DataTable/utils/flexibleImport.ts +2 -2
- package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
- package/src/components/DataTable/utils/paginationUtils.ts +1 -1
- package/src/components/DataTable/utils/performanceUtils.ts +1 -1
- package/src/components/DataTable/utils/selectFieldUtils.ts +127 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +17 -24
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
- package/src/components/DateTimeField/DateTimeField.test.tsx +2 -15
- package/src/components/DateTimeField/DateTimeField.tsx +1 -1
- package/src/components/Dialog/Dialog.test.tsx +2007 -407
- package/src/components/Dialog/Dialog.tsx +97 -192
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +2 -62
- package/src/components/ErrorBoundary/ErrorBoundaryContext.context.ts +17 -0
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +2 -45
- package/src/components/ErrorBoundary/ErrorBoundaryContext.types.ts +41 -0
- package/src/components/ErrorBoundary/index.ts +3 -4
- package/src/components/ErrorBoundary/useErrorBoundaryContext.ts +20 -0
- package/src/components/FileDisplay/FileDisplay.test.tsx +454 -222
- package/src/components/FileDisplay/FileDisplay.tsx +14 -12
- package/src/components/FileDisplay/index.tsx +1 -1
- package/src/components/FileUpload/FileUpload.test.tsx +54 -18
- package/src/components/FileUpload/FileUpload.tsx +10 -7
- package/src/components/FileUpload/index.tsx +1 -1
- package/src/components/Footer/Footer.test.tsx +33 -114
- package/src/components/Form/Form.test.tsx +388 -68
- package/src/components/Form/Form.tsx +57 -42
- package/src/components/Header/Header.test.tsx +645 -154
- package/src/components/Header/Header.tsx +52 -43
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
- package/src/components/Input/Input.test.tsx +34 -120
- package/src/components/Label/Label.test.tsx +47 -46
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +9 -12
- package/src/components/LoginForm/LoginForm.test.tsx +0 -1
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +1399 -82
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
- package/src/components/NavigationMenu/__tests__/useNavigationFiltering.test.ts +1934 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +5 -15
- package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1307 -0
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +47 -46
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +87 -66
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +245 -39
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -20
- package/src/components/PaceAppLayout/README.md +0 -9
- package/src/components/PaceAppLayout/test-setup.tsx +15 -9
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +759 -3
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +2 -3
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
- package/src/components/Progress/Progress.test.tsx +127 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +24 -6
- package/src/components/PublicLayout/PublicLayout.test.tsx +1435 -14
- package/src/components/PublicLayout/PublicPageContext.ts +28 -0
- package/src/components/PublicLayout/PublicPageLayout.tsx +6 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +2 -41
- package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
- package/src/components/Select/Select.test.tsx +46 -9
- package/src/components/Select/Select.tsx +31 -24
- package/src/components/Select/__tests__/context.test.tsx +56 -0
- package/src/components/Select/hooks/__tests__/useSelectEvents.test.ts +279 -0
- package/src/components/Select/hooks/__tests__/useSelectSearch.test.tsx +295 -0
- package/src/components/Select/hooks/__tests__/useSelectState.test.ts +254 -0
- package/src/components/Select/hooks/useSelectState.ts +16 -16
- package/src/components/Select/types.ts +3 -0
- package/src/components/Select/utils/__tests__/text.test.tsx +104 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +28 -112
- package/src/components/Switch/Switch.test.tsx +57 -153
- package/src/components/Table/Table.test.tsx +47 -317
- package/src/components/Tabs/Tabs.tsx +3 -3
- package/src/components/Textarea/Textarea.test.tsx +11 -38
- package/src/components/Toast/Toast.test.tsx +78 -569
- package/src/components/Tooltip/Tooltip.test.tsx +4 -21
- package/src/components/UserMenu/UserMenu.test.tsx +1 -21
- package/src/components/UserMenu/UserMenu.tsx +3 -6
- package/src/components/__tests__/index.test.ts +346 -0
- package/src/components/index.ts +12 -1
- package/src/constants/__tests__/performance.test.ts +91 -0
- package/src/hooks/__tests__/ServiceHooks.test.tsx +239 -129
- package/src/hooks/__tests__/hooks.integration.test.tsx +4 -3
- package/src/hooks/__tests__/useApiFetch.unit.test.ts +1 -1
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +88 -29
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +282 -98
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +53 -109
- package/src/hooks/__tests__/useDataTableState.test.ts +143 -49
- package/src/hooks/__tests__/useDebounce.unit.test.ts +94 -19
- package/src/hooks/__tests__/useEvents.unit.test.ts +100 -125
- package/src/hooks/__tests__/useFileDisplay.test.ts +540 -0
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +1 -4
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +27 -247
- package/src/hooks/__tests__/useFileUrlCache.test.ts +246 -56
- package/src/hooks/__tests__/useFocusManagement.unit.test.ts +442 -68
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +345 -560
- package/src/hooks/__tests__/useFormDialog.test.ts +51 -222
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +1 -1
- package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +1 -4
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -1
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +1 -1
- package/src/hooks/__tests__/usePermissionCache.test.ts +506 -0
- package/src/hooks/__tests__/usePreventTabReload.test.ts +255 -36
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +17 -8
- package/src/hooks/__tests__/usePublicEvent.test.ts +16 -24
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +12 -4
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +3 -6
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +1 -2
- package/src/hooks/__tests__/useQueryCache.test.ts +313 -66
- package/src/hooks/__tests__/useSessionDraft.test.ts +496 -103
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +2 -2
- package/src/hooks/__tests__/useStorage.unit.test.ts +72 -102
- package/src/hooks/__tests__/useToast.test.ts +413 -0
- package/src/hooks/__tests__/useToast.unit.test.tsx +1 -1
- package/src/hooks/__tests__/useZodForm.unit.test.tsx +175 -21
- package/src/hooks/index.ts +13 -1
- package/src/hooks/public/usePublicEvent.test.ts +304 -0
- package/src/hooks/public/usePublicEvent.ts +11 -11
- package/src/hooks/public/usePublicEventLogo.test.ts +655 -120
- package/src/hooks/public/usePublicEventLogo.ts +2 -2
- package/src/hooks/public/usePublicFileDisplay.test.ts +723 -0
- package/src/hooks/public/usePublicFileDisplay.ts +79 -49
- package/src/hooks/public/usePublicRouteParams.test.ts +595 -0
- package/src/hooks/public/usePublicRouteParams.ts +2 -2
- package/src/hooks/services/useAuthService.ts +1 -1
- package/src/hooks/services/useEventService.ts +1 -1
- package/src/hooks/useAccessibleApps.test.ts +400 -0
- package/src/hooks/useAccessibleApps.ts +264 -0
- package/src/hooks/useAddressAutocomplete.test.ts +165 -42
- package/src/hooks/useAddressAutocomplete.ts +41 -28
- package/src/hooks/useAppConfig.ts +13 -3
- package/src/hooks/useDataTablePerformance.ts +13 -12
- package/src/hooks/useDataTableState.ts +5 -5
- package/src/hooks/useEventTheme.test.ts +66 -17
- package/src/hooks/useEventTheme.ts +1 -1
- package/src/hooks/useEvents.ts +8 -1
- package/src/hooks/useFileDisplay.ts +66 -33
- package/src/hooks/useFileReference.test.ts +365 -87
- package/src/hooks/useFileReference.ts +2 -6
- package/src/hooks/useFileUrlCache.ts +4 -1
- package/src/hooks/useFormDialog.ts +2 -2
- package/src/hooks/useInactivityTracker.ts +3 -3
- package/src/hooks/useOrganisationPermissions.test.ts +1 -2
- package/src/hooks/useOrganisationPermissions.ts +1 -4
- package/src/hooks/useOrganisationSecurity.test.ts +1 -30
- package/src/hooks/useOrganisationSecurity.ts +3 -3
- package/src/hooks/useOrganisations.ts +1 -1
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePermissionCache.ts +2 -6
- package/src/hooks/useQueryCache.ts +7 -7
- package/src/hooks/useSessionDraft.ts +14 -11
- package/src/hooks/useSessionRestoration.ts +1 -1
- package/src/hooks/useStorage.ts +75 -40
- package/src/hooks/useToast.ts +2 -2
- package/src/hooks/useZodForm.ts +3 -3
- package/src/icons/__tests__/index.test.ts +133 -0
- package/src/icons/index.ts +1 -1
- package/src/index.ts +43 -4
- package/src/providers/OrganisationProvider.test.tsx +40 -0
- package/src/providers/OrganisationProvider.tsx +5 -5
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
- package/src/providers/__tests__/AuthProvider.test.tsx +22 -91
- package/src/providers/__tests__/EventProvider.test.tsx +16 -80
- package/src/providers/__tests__/InactivityProvider.test.tsx +29 -173
- package/src/providers/__tests__/OrganisationProvider.test.tsx +4 -5
- package/src/providers/__tests__/OrganisationProvider.wrapper.test.tsx +591 -0
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +80 -196
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +40 -133
- package/src/providers/__tests__/index.test.ts +138 -0
- package/src/providers/services/AuthServiceContext.ts +27 -0
- package/src/providers/services/AuthServiceProvider.tsx +81 -20
- package/src/providers/services/EventServiceContext.ts +25 -0
- package/src/providers/services/EventServiceProvider.tsx +11 -20
- package/src/providers/services/InactivityServiceContext.ts +25 -0
- package/src/providers/services/InactivityServiceProvider.tsx +7 -17
- package/src/providers/services/OrganisationServiceContext.ts +25 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
- package/src/providers/services/UnifiedAuthContext.ts +99 -0
- package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +38 -143
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +61 -95
- package/src/providers/services/__tests__/AuthServiceProvider.test.tsx +638 -0
- package/src/providers/services/__tests__/EventServiceProvider.test.tsx +839 -0
- package/src/providers/services/__tests__/InactivityServiceProvider.test.tsx +662 -0
- package/src/providers/services/__tests__/OrganisationServiceProvider.test.tsx +440 -0
- package/src/providers/services/__tests__/UnifiedAuthProvider.advanced.test.tsx +435 -0
- package/src/providers/services/__tests__/UnifiedAuthProvider.appId.test.tsx +408 -0
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +55 -48
- package/src/providers/services/__tests__/contexts.test.tsx +281 -0
- package/src/providers/services/__tests__/useUnifiedAuth.test.tsx +251 -0
- package/src/providers/services/useUnifiedAuth.ts +29 -0
- package/src/rbac/README.md +5 -5
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +9 -14
- package/src/rbac/__tests__/audit-batched.test.ts +550 -0
- package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +1 -14
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +43 -12
- package/src/rbac/__tests__/cache-invalidation.test.ts +8 -14
- package/src/rbac/__tests__/engine.comprehensive.test.ts +2 -7
- package/src/rbac/__tests__/index.test.ts +107 -0
- package/src/rbac/__tests__/performance.test.ts +451 -0
- package/src/rbac/__tests__/rbac-core.test.tsx +2 -2
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -5
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +1 -7
- package/src/rbac/__tests__/rbac-functions.test.ts +0 -1
- package/src/rbac/__tests__/rbac-integration.test.ts +0 -1
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +21 -32
- package/src/rbac/adapters.test.tsx +654 -0
- package/src/rbac/adapters.tsx +24 -9
- package/src/rbac/api.test.ts +13 -217
- package/src/rbac/api.ts +85 -16
- package/src/rbac/audit-batched.ts +5 -4
- package/src/rbac/audit.test.ts +225 -28
- package/src/rbac/audit.ts +22 -17
- package/src/rbac/cache-invalidation.ts +18 -15
- package/src/rbac/cache.test.ts +123 -63
- package/src/rbac/cache.ts +3 -4
- package/src/rbac/components/AccessDenied.tsx +20 -18
- package/src/rbac/components/NavigationGuard.tsx +10 -8
- package/src/rbac/components/PagePermissionGuard.tsx +27 -25
- package/src/rbac/components/__tests__/AccessDenied.test.tsx +324 -0
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +242 -71
- package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +20 -37
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +18 -17
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +452 -129
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -13
- package/src/rbac/config.test.ts +131 -48
- package/src/rbac/config.ts +11 -8
- package/src/rbac/docs/event-based-apps.md +26 -13
- package/src/rbac/engine.test.ts +496 -146
- package/src/rbac/engine.ts +53 -13
- package/src/rbac/errors.test.ts +99 -87
- package/src/rbac/eslint-rules.js +2 -2
- package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -5
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +601 -1
- package/src/rbac/hooks/permissions/__tests__/useAccessLevel.test.ts +622 -0
- package/src/rbac/hooks/permissions/__tests__/useCan.test.ts +798 -0
- package/src/rbac/hooks/permissions/__tests__/useMultiplePermissions.test.ts +843 -0
- package/src/rbac/hooks/permissions/__tests__/usePermissions.test.ts +545 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +7 -8
- package/src/rbac/hooks/permissions/useCan.ts +12 -10
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +57 -8
- package/src/rbac/hooks/permissions/usePermissions.ts +15 -14
- package/src/rbac/hooks/useCan.test.ts +319 -3
- package/src/rbac/hooks/usePermissions.test.ts +426 -0
- package/src/rbac/hooks/usePermissions.ts +5 -7
- package/src/rbac/hooks/useRBAC.test.ts +1669 -2
- package/src/rbac/hooks/useRBAC.ts +7 -11
- package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
- package/src/rbac/hooks/useResolvedScope.ts +4 -1
- package/src/rbac/hooks/useResourcePermissions.test.ts +538 -1
- package/src/rbac/hooks/useResourcePermissions.ts +9 -7
- package/src/rbac/hooks/useRoleManagement.test.ts +659 -1
- package/src/rbac/hooks/useRoleManagement.ts +16 -12
- package/src/rbac/hooks/useSecureSupabase.ts +11 -12
- package/src/rbac/index.ts +32 -32
- package/src/rbac/permissions.test.ts +149 -68
- package/src/rbac/permissions.ts +0 -3
- package/src/rbac/request-deduplication.test.ts +347 -0
- package/src/rbac/secureClient.test.ts +112 -159
- package/src/rbac/secureClient.ts +46 -26
- package/src/rbac/security.test.ts +125 -44
- package/src/rbac/security.ts +7 -6
- package/src/rbac/types.test.ts +236 -0
- package/src/rbac/types.ts +7 -5
- package/src/rbac/utils/__tests__/clientSecurity.test.ts +192 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +1 -3
- package/src/rbac/utils/__tests__/deep-equal.test.ts +23 -0
- package/src/rbac/utils/__tests__/eventContext.test.ts +10 -57
- package/src/rbac/utils/clientSecurity.ts +6 -4
- package/src/rbac/utils/contextValidator.ts +1 -2
- package/src/rbac/utils/eventContext.ts +2 -2
- package/src/services/AuthService.ts +13 -11
- package/src/services/EventService.ts +4 -5
- package/src/services/OrganisationService.ts +13 -30
- package/src/services/__tests__/AuthService.edge-cases.test.ts +746 -0
- package/src/services/__tests__/AuthService.restoreSession.test.ts +23 -3
- package/src/services/__tests__/AuthService.test.ts +4 -8
- package/src/services/__tests__/BaseService.edge-cases.test.ts +506 -0
- package/src/services/__tests__/BaseService.test.ts +49 -0
- package/src/services/__tests__/EventService.edge-cases.test.ts +633 -0
- package/src/services/__tests__/EventService.eventColours.test.ts +0 -12
- package/src/services/__tests__/EventService.test.ts +0 -7
- package/src/services/__tests__/InactivityService.edge-cases.test.ts +492 -0
- package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -5
- package/src/services/__tests__/OrganisationService.edge-cases.test.ts +633 -0
- package/src/services/base/BaseService.test.ts +214 -0
- package/src/services/interfaces/IOrganisationService.ts +0 -1
- package/src/services/interfaces/__tests__/IAuthService.test.ts +190 -0
- package/src/services/interfaces/__tests__/IEventService.test.ts +176 -0
- package/src/services/interfaces/__tests__/IInactivityService.test.ts +183 -0
- package/src/services/interfaces/__tests__/IOrganisationService.test.ts +207 -0
- package/src/styles/core.css +1 -0
- package/src/theming/__tests__/runtime.test.ts +29 -94
- package/src/theming/parseEventColours.ts +18 -9
- package/src/theming/runtime.ts +1 -5
- package/src/types/__tests__/core.test.ts +397 -0
- package/src/types/__tests__/database-generated.test.ts +78 -0
- package/src/types/__tests__/file-reference.test.ts +270 -366
- package/src/types/__tests__/guards.test.ts +26 -26
- package/src/types/__tests__/index.test.ts +265 -0
- package/src/types/__tests__/type-validation.test.ts +3 -3
- package/src/types/__tests__/validation.test.ts +0 -2
- package/src/types/auth.ts +0 -1
- package/src/types/database.generated.ts +9 -9
- package/src/types/event.ts +1 -1
- package/src/types/rpc-responses.ts +33 -0
- package/src/types/supabase.ts +1 -2
- package/src/types/vitest-globals.d.ts +1 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +64 -77
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +13 -0
- package/src/utils/__tests__/formatDate.unit.test.ts +1 -1
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -1
- package/src/utils/__tests__/logger.unit.test.ts +1 -1
- package/src/utils/__tests__/performanceBenchmark.test.ts +1 -2
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +48 -13
- package/src/utils/__tests__/request-deduplication.test.ts +349 -0
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -1
- package/src/utils/__tests__/timezone.test.ts +1 -1
- package/src/utils/__tests__/validation.unit.test.ts +1 -2
- package/src/utils/__tests__/validationUtils.unit.test.ts +1 -1
- package/src/utils/app/appConfig.test.ts +235 -0
- package/src/utils/app/appIdResolver.test.ts +188 -20
- package/src/utils/app/appNameResolver.test.ts +18 -10
- package/src/utils/app/appNameResolver.ts +11 -9
- package/src/utils/app/appPortMap.test.ts +125 -0
- package/src/utils/app/appPortMap.ts +51 -0
- package/src/utils/app/buildAppUrl.test.ts +273 -0
- package/src/utils/app/buildAppUrl.ts +114 -0
- package/src/utils/audit/audit.test.ts +354 -39
- package/src/utils/context/organisationContext.test.ts +10 -4
- package/src/utils/context/organisationContext.ts +5 -5
- package/src/utils/context/sessionTracking.test.ts +354 -0
- package/src/utils/core/__tests__/cn.test.ts +66 -0
- package/src/utils/core/__tests__/debugLogger.test.ts +113 -0
- package/src/utils/core/__tests__/logger.test.ts +217 -0
- package/src/utils/core/debugLogger.ts +15 -8
- package/src/utils/core/logger.ts +20 -16
- package/src/utils/device/deviceFingerprint.test.ts +8 -5
- package/src/utils/device/deviceFingerprint.ts +3 -3
- package/src/utils/dynamic/__tests__/dynamicUtils.test.ts +185 -0
- package/src/utils/dynamic/__tests__/lazyLoad.test.tsx +156 -0
- package/src/utils/dynamic/createLazyComponent.tsx +38 -0
- package/src/utils/dynamic/dynamicUtils.ts +6 -6
- package/src/utils/dynamic/lazyLoad.tsx +8 -36
- package/src/utils/dynamic/papaparseLoader.ts +7 -0
- package/src/utils/file-reference/__tests__/file-reference.test.ts +583 -145
- package/src/utils/file-reference/index.ts +0 -1
- package/src/utils/formatting/formatDate.test.ts +22 -148
- package/src/utils/formatting/formatDateTime.test.ts +41 -119
- package/src/utils/formatting/formatDateTimeTimezone.test.ts +40 -84
- package/src/utils/formatting/formatNumber.test.ts +259 -0
- package/src/utils/formatting/formatTime.test.ts +36 -128
- package/src/utils/formatting/formatting.ts +1 -1
- package/src/utils/google-places/googlePlacesUtils.test.ts +72 -3
- package/src/utils/google-places/googlePlacesUtils.ts +15 -2
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +58 -1
- package/src/utils/google-places/loadGoogleMapsScript.ts +2 -1
- package/src/utils/index.ts +52 -11
- package/src/utils/location/location.test.ts +18 -115
- package/src/utils/performance/__tests__/bundleAnalysis.test.ts +148 -0
- package/src/utils/performance/__tests__/performanceBenchmark.test.ts +251 -0
- package/src/utils/performance/__tests__/performanceBudgets.test.ts +241 -0
- package/src/utils/performance/bundleAnalysis.ts +16 -22
- package/src/utils/performance/performanceBenchmark.ts +12 -4
- package/src/utils/performance/performanceBudgets.ts +9 -6
- package/src/utils/permissions/__tests__/permissionTypes.test.ts +149 -0
- package/src/utils/permissions/permissionUtils.test.ts +20 -42
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +180 -9
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +164 -16
- package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
- package/src/utils/request-deduplication.ts +6 -4
- package/src/utils/security/auth-utils.ts +7 -7
- package/src/utils/security/secureDataAccess.test.ts +22 -191
- package/src/utils/security/secureErrors.test.ts +163 -0
- package/src/utils/security/secureStorage.test.ts +156 -0
- package/src/utils/security/secureStorage.ts +1 -1
- package/src/utils/security/security.test.ts +204 -0
- package/src/utils/security/securityMonitor.test.ts +90 -0
- package/src/utils/security/securityMonitor.ts +1 -1
- package/src/utils/storage/__tests__/config.unit.test.ts +239 -0
- package/src/utils/storage/__tests__/index.unit.test.ts +64 -12
- package/src/utils/storage/helpers.test.ts +757 -430
- package/src/utils/storage/helpers.ts +1 -2
- package/src/utils/storage/{index.ts → storageUtils.ts} +1 -36
- package/src/utils/storage/types.ts +2 -2
- package/src/utils/supabase/createBaseClient.test.ts +201 -0
- package/src/utils/supabase/createBaseClient.ts +27 -8
- package/src/utils/timezone/timezone.test.ts +25 -43
- package/src/utils/validation/__tests__/common.test.ts +115 -0
- package/src/utils/validation/__tests__/csrf.test.ts +65 -0
- package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +27 -7
- package/src/utils/validation/__tests__/passwordSchema.test.ts +164 -0
- package/src/utils/validation/__tests__/schema.test.ts +127 -0
- package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +76 -3
- package/src/utils/validation/__tests__/user.test.ts +173 -0
- package/src/utils/validation/__tests__/validation.test.ts +197 -0
- package/src/utils/validation/__tests__/validationUtils.test.ts +265 -43
- package/src/utils/validation/htmlSanitization.ts +27 -31
- package/src/utils/validation/schema.ts +6 -3
- package/src/utils/validation/sqlInjectionProtection.ts +2 -2
- package/src/vite-env.d.ts +6 -0
- package/dist/DataTable-DRUIgtUH.d.ts +0 -166
- package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
- package/dist/audit-MYQXYZFU.js +0 -3
- package/dist/chunk-7ILTDCL2.js +0 -80
- package/dist/chunk-EF2UGZWY.js +0 -611
- package/dist/chunk-FEJLJNWA.js +0 -181
- package/dist/chunk-GS5672WG.js +0 -2003
- package/dist/chunk-S6ZQKDY6.js +0 -62
- package/dist/chunk-Z2FNRKF3.js +0 -994
- package/dist/useToast-AyaT-x7p.d.ts +0 -68
- package/src/components/DataTable/components/index.ts +0 -16
- package/src/components/DataTable/core/index.ts +0 -1
- package/src/components/DataTable/hooks/index.ts +0 -13
- package/src/components/DataTable/utils/index.ts +0 -9
- package/src/components/PublicLayout/index.ts +0 -32
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
- package/src/hooks/public/index.ts +0 -36
- package/src/hooks/usePermissionCache.test.ts +0 -536
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
- package/src/rbac/audit-enhanced.ts +0 -384
- package/src/rbac/compliance/database-validator.ts +0 -165
- package/src/rbac/compliance/index.ts +0 -48
- package/src/rbac/compliance/pattern-detector.ts +0 -553
- package/src/rbac/compliance/quick-fix-suggestions.ts +0 -209
- package/src/rbac/compliance/runtime-compliance.ts +0 -99
- package/src/rbac/compliance/setup-validator.ts +0 -131
- package/src/rbac/components/index.ts +0 -26
- package/src/rbac/hooks/index.ts +0 -34
- package/src/rbac/hooks/permissions/index.ts +0 -4
- package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
- package/src/utils/app/appNameResolver.simple.test.ts +0 -212
- package/src/utils/google-places/index.ts +0 -26
- package/src/utils/location/index.ts +0 -16
- package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
- package/src/utils/timezone/index.ts +0 -17
- package/src/utils/validation/index.ts +0 -73
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
- **DataTable: Automatic label display for select fields** - Select fields with `fieldType: 'select'` and `fieldOptions` now automatically display labels (e.g., "Mobile") instead of raw values (e.g., `1`) in read mode. This eliminates the need for custom cell renderers for common select field patterns. The feature supports simple options, grouped options, type coercion, and gracefully falls back to raw values when labels aren't found. Custom cell renderers are preserved if already defined.
|
|
12
|
+
|
|
10
13
|
### Breaking Changes
|
|
11
14
|
- **@supabase/supabase-js is now an included dependency (security enforcement)**: Moved from peer dependency to included dependency to enforce security rules. Consuming apps can no longer import `createClient` directly.
|
|
12
15
|
- **Action Required**:
|
|
@@ -127,6 +127,8 @@ function checkImportPaths(consumingAppPath) {
|
|
|
127
127
|
|
|
128
128
|
/**
|
|
129
129
|
* Check test file colocation
|
|
130
|
+
* Only flags tests that are in the wrong location (e.g., in __tests__/ when they should be colocated).
|
|
131
|
+
* Does not flag missing test files - use test coverage tools for that.
|
|
130
132
|
*/
|
|
131
133
|
function checkTestColocation(consumingAppPath) {
|
|
132
134
|
const issues = [];
|
|
@@ -136,9 +138,9 @@ function checkTestColocation(consumingAppPath) {
|
|
|
136
138
|
return issues;
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
// Find all
|
|
140
|
-
const
|
|
141
|
-
function
|
|
141
|
+
// Find all test files
|
|
142
|
+
const testFiles = [];
|
|
143
|
+
function findTestFiles(dir) {
|
|
142
144
|
if (!fs.existsSync(dir)) return;
|
|
143
145
|
|
|
144
146
|
const files = fs.readdirSync(dir);
|
|
@@ -148,49 +150,111 @@ function checkTestColocation(consumingAppPath) {
|
|
|
148
150
|
|
|
149
151
|
if (stat.isDirectory()) {
|
|
150
152
|
if (!['node_modules', 'dist', 'build', '.git'].includes(file)) {
|
|
151
|
-
|
|
153
|
+
findTestFiles(filePath);
|
|
152
154
|
}
|
|
153
|
-
} else if (/\.(ts|tsx|js|jsx)$/.test(file) &&
|
|
154
|
-
|
|
155
|
+
} else if (/\.(ts|tsx|js|jsx)$/.test(file) && (file.includes('.test.') || file.includes('.spec.'))) {
|
|
156
|
+
testFiles.push(filePath);
|
|
155
157
|
}
|
|
156
158
|
});
|
|
157
159
|
}
|
|
158
160
|
|
|
159
|
-
|
|
161
|
+
findTestFiles(srcDir);
|
|
160
162
|
|
|
161
|
-
// Check if test files are
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
const ext = path.extname(sourceFile);
|
|
163
|
+
// Check if test files are in the wrong location
|
|
164
|
+
testFiles.forEach(testFile => {
|
|
165
|
+
const testDir = path.dirname(testFile);
|
|
166
|
+
const testBasename = path.basename(testFile);
|
|
166
167
|
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
const
|
|
168
|
+
// Extract the source file name from the test file name
|
|
169
|
+
// e.g., "Component.test.tsx" -> "Component.tsx"
|
|
170
|
+
const sourceBasename = testBasename.replace(/\.(test|spec)\./, '.');
|
|
171
|
+
const sourceFile = path.join(testDir, sourceBasename);
|
|
170
172
|
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
173
|
+
// Check if test is in a __tests__ or tests directory
|
|
174
|
+
const isInTestDirectory = testDir.includes('/__tests__/') || testDir.includes('/tests/') ||
|
|
175
|
+
testDir.endsWith('/__tests__') || testDir.endsWith('/tests');
|
|
175
176
|
|
|
176
|
-
//
|
|
177
|
-
if (
|
|
178
|
-
|
|
177
|
+
// If test is in a test directory, check if the source file exists in the parent directory
|
|
178
|
+
if (isInTestDirectory) {
|
|
179
|
+
// Get the parent directory (should be where the source file is)
|
|
180
|
+
const parentDir = path.dirname(testDir);
|
|
181
|
+
const expectedSourceFile = path.join(parentDir, sourceBasename);
|
|
182
|
+
|
|
183
|
+
// If source file exists in parent, flag that test should be colocated
|
|
184
|
+
if (fileExists(expectedSourceFile)) {
|
|
185
|
+
const relativePath = getRelativePath(testFile, consumingAppPath);
|
|
186
|
+
issues.push({
|
|
187
|
+
type: 'testColocation',
|
|
188
|
+
file: relativePath,
|
|
189
|
+
line: 1,
|
|
190
|
+
message: `Test file is in a test directory but should be colocated with source file. Move to ${parentDir}`,
|
|
191
|
+
severity: 'warning',
|
|
192
|
+
fix: `Move test file to ${parentDir} to colocate with source file`,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
179
195
|
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return issues;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Check for domain-based organization (Pattern 2 - not allowed)
|
|
203
|
+
* Feature-based organization is the only standard
|
|
204
|
+
*/
|
|
205
|
+
function checkFeatureBasedOrganization(consumingAppPath) {
|
|
206
|
+
const issues = [];
|
|
207
|
+
|
|
208
|
+
const srcDir = path.join(consumingAppPath, 'src');
|
|
209
|
+
if (!directoryExists(srcDir)) {
|
|
210
|
+
return issues;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check for src/domains/ directory (Pattern 2 structure - not allowed)
|
|
214
|
+
const domainsDir = path.join(srcDir, 'domains');
|
|
215
|
+
if (directoryExists(domainsDir)) {
|
|
216
|
+
issues.push({
|
|
217
|
+
type: 'organizationPattern',
|
|
218
|
+
file: 'src/domains',
|
|
219
|
+
line: 0,
|
|
220
|
+
message: 'Domain-based organization (src/domains/) is not allowed. Use feature-based organization instead (src/components/[feature]/, src/hooks/[feature]/, etc.)',
|
|
221
|
+
severity: 'error',
|
|
222
|
+
fix: 'Reorganize to feature-based structure: move components from src/domains/[domain]/components/ to src/components/[domain]/, hooks to src/hooks/[domain]/, etc.',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check components directory for type-based organization (anti-pattern)
|
|
227
|
+
const componentsDir = path.join(srcDir, 'components');
|
|
228
|
+
if (directoryExists(componentsDir)) {
|
|
229
|
+
const componentDirs = [];
|
|
230
|
+
function scanComponentsDir(dir) {
|
|
231
|
+
if (!fs.existsSync(dir)) return;
|
|
232
|
+
const entries = fs.readdirSync(dir);
|
|
233
|
+
entries.forEach(entry => {
|
|
234
|
+
const entryPath = path.join(dir, entry);
|
|
235
|
+
const stat = fs.statSync(entryPath);
|
|
236
|
+
if (stat.isDirectory()) {
|
|
237
|
+
componentDirs.push(entry);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
scanComponentsDir(componentsDir);
|
|
180
242
|
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
|
|
243
|
+
// Common type-based directory names that should not exist
|
|
244
|
+
const typeBasedPatterns = ['buttons', 'button', 'inputs', 'input', 'cards', 'card', 'forms', 'form', 'modals', 'modal', 'dialogs', 'dialog'];
|
|
245
|
+
const foundTypeBased = componentDirs.filter(dir => typeBasedPatterns.includes(dir.toLowerCase()));
|
|
246
|
+
|
|
247
|
+
if (foundTypeBased.length > 0) {
|
|
184
248
|
issues.push({
|
|
185
|
-
type: '
|
|
186
|
-
file:
|
|
187
|
-
line:
|
|
188
|
-
message: `
|
|
189
|
-
severity: '
|
|
190
|
-
fix: `
|
|
249
|
+
type: 'organizationPattern',
|
|
250
|
+
file: `src/components/${foundTypeBased[0]}`,
|
|
251
|
+
line: 0,
|
|
252
|
+
message: `Components organized by type (${foundTypeBased.join(', ')}) instead of feature. Use pace-core components for UI primitives and organize app components by feature.`,
|
|
253
|
+
severity: 'error',
|
|
254
|
+
fix: `Reorganize components by feature. Use pace-core for ${foundTypeBased.join(', ')} components. Move feature-specific components to src/components/[feature]/`,
|
|
191
255
|
});
|
|
192
256
|
}
|
|
193
|
-
}
|
|
257
|
+
}
|
|
194
258
|
|
|
195
259
|
return issues;
|
|
196
260
|
}
|
|
@@ -221,6 +285,7 @@ function runStandard2Audit(consumingAppPath) {
|
|
|
221
285
|
issues.push(...checkConfigFiles(consumingAppPath));
|
|
222
286
|
issues.push(...checkImportPaths(consumingAppPath));
|
|
223
287
|
issues.push(...checkTestColocation(consumingAppPath));
|
|
288
|
+
issues.push(...checkFeatureBasedOrganization(consumingAppPath));
|
|
224
289
|
issues.push(...checkSupabaseStructure(consumingAppPath));
|
|
225
290
|
} catch (error) {
|
|
226
291
|
return {
|
|
@@ -13,6 +13,66 @@ const path = require('path');
|
|
|
13
13
|
const { findSourceFiles, readFileSafe, getRelativePath } = require('../utils/file-utils.cjs');
|
|
14
14
|
const { getLineNumber, getCodeSnippet, isInCommentOrString, parseImports } = require('../utils/code-utils.cjs');
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Check if a file is a test file or test utility file
|
|
18
|
+
* @param {string} filePath - Full path to the file
|
|
19
|
+
* @returns {boolean} - True if file is a test file
|
|
20
|
+
*/
|
|
21
|
+
function isTestFile(filePath) {
|
|
22
|
+
const fileName = path.basename(filePath, path.extname(filePath));
|
|
23
|
+
const fileNameLower = fileName.toLowerCase();
|
|
24
|
+
const filePathLower = filePath.toLowerCase();
|
|
25
|
+
|
|
26
|
+
// Standard test file patterns
|
|
27
|
+
if (filePath.includes('.test.') || filePath.includes('.spec.')) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Files in test directories
|
|
32
|
+
if (filePath.includes('/test/') ||
|
|
33
|
+
filePath.includes('/tests/') ||
|
|
34
|
+
filePath.includes('__tests__') ||
|
|
35
|
+
filePath.includes('__test__')) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Files starting with "test" prefix
|
|
40
|
+
if (fileNameLower.startsWith('test')) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Test utility patterns in filename
|
|
45
|
+
const testUtilityPatterns = [
|
|
46
|
+
'testassertion', 'testverifier', 'testhelper', 'testutility',
|
|
47
|
+
'testsetup', 'testteardown', 'testmock', 'testfixture',
|
|
48
|
+
'testdata', 'testutil', 'testhelper', 'testutils'
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
if (testUtilityPatterns.some(pattern => fileNameLower.includes(pattern))) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Test utility patterns in path
|
|
56
|
+
const testPathPatterns = [
|
|
57
|
+
'testutils', 'testhelpers', 'testassertions', 'testverifiers',
|
|
58
|
+
'testsetup', 'testmocks', 'testfixtures', 'testdata'
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
if (testPathPatterns.some(pattern => filePathLower.includes(pattern))) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Files ending with test utility suffixes
|
|
66
|
+
if (fileNameLower.endsWith('testutil') ||
|
|
67
|
+
fileNameLower.endsWith('testhelper') ||
|
|
68
|
+
fileNameLower.endsWith('testassertion') ||
|
|
69
|
+
fileNameLower.endsWith('testverifier')) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
16
76
|
/**
|
|
17
77
|
* Check for component boundary violations (domain logic in components)
|
|
18
78
|
*/
|
|
@@ -25,29 +85,37 @@ function checkComponentBoundaries(consumingAppPath) {
|
|
|
25
85
|
}
|
|
26
86
|
|
|
27
87
|
const componentFiles = findSourceFiles(srcDir).filter(file => {
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
const isUtilityFile = file.endsWith('Utils.ts') ||
|
|
31
|
-
file.endsWith('Utils.tsx') ||
|
|
32
|
-
file.endsWith('Helpers.ts') ||
|
|
33
|
-
file.endsWith('Helpers.tsx') ||
|
|
34
|
-
file.includes('testUtils') ||
|
|
35
|
-
file.includes('testHelpers') ||
|
|
36
|
-
file.includes('testAssertions') ||
|
|
37
|
-
file.includes('testSetup');
|
|
38
|
-
|
|
39
|
-
if (isTestFile || isUtilityFile) {
|
|
88
|
+
// Skip all test files
|
|
89
|
+
if (isTestFile(file)) {
|
|
40
90
|
return false;
|
|
41
91
|
}
|
|
42
92
|
|
|
43
93
|
const dir = path.dirname(file);
|
|
44
94
|
const isComponentDir = dir.includes('/components/') || dir.includes('/pages/');
|
|
45
|
-
|
|
95
|
+
// Only check .tsx files for component boundaries (React components)
|
|
96
|
+
// .ts files are utilities, hooks, or test files and shouldn't be checked
|
|
97
|
+
const isComponentFile = file.endsWith('.tsx');
|
|
46
98
|
|
|
47
99
|
return isComponentDir && isComponentFile;
|
|
48
100
|
});
|
|
49
101
|
|
|
50
|
-
|
|
102
|
+
// For data fetching checks, also include .ts files (to catch data fetching in hooks)
|
|
103
|
+
const filesForDataFetchingCheck = findSourceFiles(srcDir).filter(file => {
|
|
104
|
+
// Skip all test files
|
|
105
|
+
if (isTestFile(file)) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const dir = path.dirname(file);
|
|
110
|
+
const isComponentDir = dir.includes('/components/') || dir.includes('/pages/');
|
|
111
|
+
const isComponentFile = file.endsWith('.tsx') || file.endsWith('.ts');
|
|
112
|
+
|
|
113
|
+
return isComponentDir && isComponentFile;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Check for data fetching in components (should be in hooks)
|
|
117
|
+
// Check both .tsx and .ts files for data fetching
|
|
118
|
+
filesForDataFetchingCheck.forEach(filePath => {
|
|
51
119
|
const content = readFileSafe(filePath);
|
|
52
120
|
if (!content) {
|
|
53
121
|
return;
|
|
@@ -55,7 +123,6 @@ function checkComponentBoundaries(consumingAppPath) {
|
|
|
55
123
|
|
|
56
124
|
const relativePath = getRelativePath(filePath, consumingAppPath);
|
|
57
125
|
|
|
58
|
-
// Check for data fetching in components (should be in hooks)
|
|
59
126
|
const dataFetchingPatterns = [
|
|
60
127
|
/\.from\s*\([^)]*\)\s*\.select/i,
|
|
61
128
|
/\.rpc\s*\(/i,
|
|
@@ -87,8 +154,29 @@ function checkComponentBoundaries(consumingAppPath) {
|
|
|
87
154
|
}
|
|
88
155
|
}
|
|
89
156
|
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Check for complex business logic in components
|
|
160
|
+
// Only check .tsx files (React components), not .ts files (utilities/hooks/test files)
|
|
161
|
+
componentFiles.forEach(filePath => {
|
|
162
|
+
// Safety check: explicitly skip .ts files (should already be filtered, but double-check)
|
|
163
|
+
if (!filePath.endsWith('.tsx')) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Also skip test files as an extra safety measure
|
|
168
|
+
if (isTestFile(filePath)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const content = readFileSafe(filePath);
|
|
173
|
+
if (!content) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const relativePath = getRelativePath(filePath, consumingAppPath);
|
|
90
178
|
|
|
91
|
-
//
|
|
179
|
+
// All files in componentFiles are .tsx files, so we can check them directly
|
|
92
180
|
// Only flag actual business logic patterns, not UI text or comments
|
|
93
181
|
const businessLogicPatterns = [
|
|
94
182
|
/if\s*\([^)]*permission[^)]*\)/i, // Permission checks
|
|
@@ -98,10 +186,43 @@ function checkComponentBoundaries(consumingAppPath) {
|
|
|
98
186
|
/process\w+\s*\(/i, // Process functions (not "Processing..." text)
|
|
99
187
|
];
|
|
100
188
|
|
|
189
|
+
// Exclude test utility functions - functions that start with test/verify/get/create
|
|
190
|
+
// These are test helpers, not business logic
|
|
191
|
+
const isTestUtilityFunction = (match, content, index) => {
|
|
192
|
+
// Look backwards to extract the function name being called
|
|
193
|
+
const beforeMatch = content.substring(Math.max(0, index - 200), index);
|
|
194
|
+
|
|
195
|
+
// Extract identifier before the match (function name)
|
|
196
|
+
// Look for word characters, dots, and spaces before the match
|
|
197
|
+
const identifierMatch = beforeMatch.match(/(\w+)\s*$/);
|
|
198
|
+
if (identifierMatch) {
|
|
199
|
+
const functionName = identifierMatch[1];
|
|
200
|
+
// Check if function name starts with test utility prefixes
|
|
201
|
+
const testUtilityPrefixes = /^(verify|get|test|create|assert|check|expect|mock|setup|teardown)/i;
|
|
202
|
+
if (testUtilityPrefixes.test(functionName)) {
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Also check if it's a function definition with test utility prefix
|
|
208
|
+
return /(?:export\s+)?(?:async\s+)?function\s+(verify|get|test|create|assert|check|expect|mock|setup|teardown)\w*\s*\(/i.test(beforeMatch) ||
|
|
209
|
+
// Check if it's a const/let/var assignment
|
|
210
|
+
/(?:export\s+)?(?:const|let|var)\s+(verify|get|test|create|assert|check|expect|mock|setup|teardown)\w*\s*=\s*(?:async\s+)?\(/i.test(beforeMatch);
|
|
211
|
+
};
|
|
212
|
+
|
|
101
213
|
// This is a heuristic - look for complex logic that should be in hooks/utils
|
|
102
214
|
// Exclude common UI text patterns
|
|
103
215
|
const hasUIOnlyText = /Processing\.\.\.|processing\.\.\./i.test(content);
|
|
104
|
-
const hasComplexLogic = businessLogicPatterns.some(pattern =>
|
|
216
|
+
const hasComplexLogic = businessLogicPatterns.some(pattern => {
|
|
217
|
+
const matches = content.matchAll(new RegExp(pattern.source, pattern.flags + 'g'));
|
|
218
|
+
for (const match of matches) {
|
|
219
|
+
const index = match.index;
|
|
220
|
+
if (!isInCommentOrString(content, index) && !isTestUtilityFunction(match, content, index)) {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return false;
|
|
225
|
+
});
|
|
105
226
|
|
|
106
227
|
if (hasComplexLogic && !hasUIOnlyText) {
|
|
107
228
|
// Check if logic is in a hook call (acceptable)
|
|
@@ -155,6 +276,11 @@ function checkApiResultUsage(consumingAppPath) {
|
|
|
155
276
|
|
|
156
277
|
// Check for API functions that don't use ApiResult
|
|
157
278
|
sourceFiles.forEach(filePath => {
|
|
279
|
+
// Skip test files
|
|
280
|
+
if (isTestFile(filePath)) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
158
284
|
const content = readFileSafe(filePath);
|
|
159
285
|
if (!content) {
|
|
160
286
|
return;
|
|
@@ -168,8 +294,8 @@ function checkApiResultUsage(consumingAppPath) {
|
|
|
168
294
|
const functionName = match[1];
|
|
169
295
|
const functionIndex = match.index;
|
|
170
296
|
|
|
171
|
-
// Skip if it's a
|
|
172
|
-
if (
|
|
297
|
+
// Skip if it's a hook
|
|
298
|
+
if (functionName.startsWith('use')) {
|
|
173
299
|
return;
|
|
174
300
|
}
|
|
175
301
|
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
const path = require('path');
|
|
14
|
-
const { findConfigFiles, readFileSafe, getRelativePath } = require('../utils/file-utils.cjs');
|
|
14
|
+
const { findConfigFiles, readFileSafe, getRelativePath, findSourceFiles } = require('../utils/file-utils.cjs');
|
|
15
|
+
const { getLineNumber, isInCommentOrString } = require('../utils/code-utils.cjs');
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Check TypeScript configuration
|
|
@@ -120,6 +121,89 @@ function checkTestCoverageConfig(consumingAppPath) {
|
|
|
120
121
|
return issues;
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Check for excessive console logging in production code
|
|
126
|
+
*/
|
|
127
|
+
function checkConsoleLogging(consumingAppPath) {
|
|
128
|
+
const issues = [];
|
|
129
|
+
|
|
130
|
+
const srcDir = path.join(consumingAppPath, 'src');
|
|
131
|
+
if (!require('fs').existsSync(srcDir)) {
|
|
132
|
+
return issues;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const sourceFiles = findSourceFiles(srcDir).filter(file => {
|
|
136
|
+
// Skip test files
|
|
137
|
+
const isTestFile = file.includes('.test.') ||
|
|
138
|
+
file.includes('.spec.') ||
|
|
139
|
+
file.includes('/test/') ||
|
|
140
|
+
file.includes('/tests/') ||
|
|
141
|
+
file.includes('__tests__') ||
|
|
142
|
+
file.includes('__test__') ||
|
|
143
|
+
path.basename(file, path.extname(file)).toLowerCase().startsWith('test');
|
|
144
|
+
|
|
145
|
+
return !isTestFile;
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
sourceFiles.forEach(filePath => {
|
|
149
|
+
const content = readFileSafe(filePath);
|
|
150
|
+
if (!content) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const relativePath = getRelativePath(filePath, consumingAppPath);
|
|
155
|
+
|
|
156
|
+
// Count console.log, console.debug, console.warn, console.error statements
|
|
157
|
+
const consolePatterns = [
|
|
158
|
+
{ pattern: /console\.(log|debug)\s*\(/gi, name: 'console.log/debug', threshold: 3 },
|
|
159
|
+
{ pattern: /console\.warn\s*\(/gi, name: 'console.warn', threshold: 5 },
|
|
160
|
+
{ pattern: /console\.error\s*\(/gi, name: 'console.error', threshold: 10 }, // Errors are usually OK
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
consolePatterns.forEach(({ pattern, name, threshold }) => {
|
|
164
|
+
const matches = [...content.matchAll(pattern)];
|
|
165
|
+
const validMatches = matches.filter(match => {
|
|
166
|
+
const index = match.index;
|
|
167
|
+
return !isInCommentOrString(content, index);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (validMatches.length > threshold) {
|
|
171
|
+
const firstMatch = validMatches[0];
|
|
172
|
+
issues.push({
|
|
173
|
+
type: 'excessiveConsoleLogging',
|
|
174
|
+
file: relativePath,
|
|
175
|
+
line: getLineNumber(content, firstMatch.index),
|
|
176
|
+
message: `Excessive ${name} usage detected (${validMatches.length} instances). Consider removing debug logs or using a proper logging library.`,
|
|
177
|
+
severity: 'warning',
|
|
178
|
+
fix: `Remove or replace ${name} statements. Use a logging library or remove debug logs before production.`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Special check for DEBUG logging (common pattern)
|
|
184
|
+
const debugLogPattern = /console\.(log|debug)\s*\(\s*['"`]\[DEBUG\]/gi;
|
|
185
|
+
const debugMatches = [...content.matchAll(debugLogPattern)];
|
|
186
|
+
const validDebugMatches = debugMatches.filter(match => {
|
|
187
|
+
const index = match.index;
|
|
188
|
+
return !isInCommentOrString(content, index);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
if (validDebugMatches.length > 0) {
|
|
192
|
+
const firstMatch = validDebugMatches[0];
|
|
193
|
+
issues.push({
|
|
194
|
+
type: 'debugConsoleLogging',
|
|
195
|
+
file: relativePath,
|
|
196
|
+
line: getLineNumber(content, firstMatch.index),
|
|
197
|
+
message: `Debug console logging detected (${validDebugMatches.length} instances). Debug logs should be removed or disabled in production.`,
|
|
198
|
+
severity: 'warning',
|
|
199
|
+
fix: 'Remove debug console.log statements or use environment-based logging that can be disabled in production.',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
return issues;
|
|
205
|
+
}
|
|
206
|
+
|
|
123
207
|
/**
|
|
124
208
|
* Run audit for Standard 4: Code Quality
|
|
125
209
|
* @param {string} consumingAppPath - Path to consuming app
|
|
@@ -131,6 +215,7 @@ function runStandard4Audit(consumingAppPath) {
|
|
|
131
215
|
try {
|
|
132
216
|
issues.push(...checkTypeScriptConfig(consumingAppPath));
|
|
133
217
|
issues.push(...checkTestCoverageConfig(consumingAppPath));
|
|
218
|
+
issues.push(...checkConsoleLogging(consumingAppPath));
|
|
134
219
|
} catch (error) {
|
|
135
220
|
return {
|
|
136
221
|
standard: '04-code-quality',
|
|
@@ -293,16 +293,36 @@ function checkRLSPolicies(consumingAppPath) {
|
|
|
293
293
|
});
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
// Check if function queries RLS-protected tables to determine if SECURITY DEFINER is needed
|
|
297
|
+
const funcBodyStart = content.indexOf('AS $$', funcStart);
|
|
298
|
+
let needsSecurityDefiner = false;
|
|
299
|
+
if (funcBodyStart !== -1) {
|
|
300
|
+
const funcBodyEnd = content.indexOf('$$;', funcBodyStart);
|
|
301
|
+
if (funcBodyEnd !== -1) {
|
|
302
|
+
const funcBody = content.substring(funcBodyStart, funcBodyEnd);
|
|
303
|
+
const rlsProtectedTables = ['rbac_organisation_roles', 'rbac_global_roles', 'rbac_event_app_roles', 'rbac_user_profiles', 'rbac_apps', 'rbac_app_pages', 'rbac_page_permissions'];
|
|
304
|
+
needsSecurityDefiner = rlsProtectedTables.some(table => {
|
|
305
|
+
const tablePattern = new RegExp(`\\b${table}\\b`, 'i');
|
|
306
|
+
return tablePattern.test(funcBody);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
296
311
|
if (!hasSecurityDefiner) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
312
|
+
if (needsSecurityDefiner) {
|
|
313
|
+
issues.push({
|
|
314
|
+
type: 'rlsPolicy',
|
|
315
|
+
file: relativePath,
|
|
316
|
+
line: getLineNumber(content, funcStart),
|
|
317
|
+
message: `Helper function '${funcName}' queries RLS-protected tables but missing SECURITY DEFINER attribute. REQUIRED: SECURITY DEFINER is needed to bypass RLS and avoid circular dependencies when querying RLS-protected tables from within RLS policies.`,
|
|
318
|
+
code: getCodeSnippet(content, funcStart, 0, 150),
|
|
319
|
+
severity: 'error',
|
|
320
|
+
fix: 'Add SECURITY DEFINER attribute to function definition. Also ensure SET search_path TO public is present.',
|
|
321
|
+
});
|
|
322
|
+
} else {
|
|
323
|
+
// Function doesn't query RLS-protected tables, SECURITY DEFINER might not be needed
|
|
324
|
+
// This is just informational, not an error
|
|
325
|
+
}
|
|
306
326
|
}
|
|
307
327
|
|
|
308
328
|
if (!hasSearchPath) {
|
|
@@ -310,12 +330,90 @@ function checkRLSPolicies(consumingAppPath) {
|
|
|
310
330
|
type: 'rlsPolicy',
|
|
311
331
|
file: relativePath,
|
|
312
332
|
line: getLineNumber(content, funcStart),
|
|
313
|
-
message: `Helper function '${funcName}' missing SET search_path TO public.
|
|
333
|
+
message: `Helper function '${funcName}' missing SET search_path TO public. MANDATORY for SECURITY DEFINER functions to prevent search path hijacking attacks.`,
|
|
314
334
|
code: getCodeSnippet(content, funcStart, 0, 150),
|
|
315
335
|
severity: 'error',
|
|
316
|
-
fix: 'Add SET search_path TO public to function definition.',
|
|
336
|
+
fix: 'Add SET search_path TO public to function definition. This is MANDATORY for SECURITY DEFINER functions to prevent search path hijacking.',
|
|
317
337
|
});
|
|
318
338
|
}
|
|
339
|
+
|
|
340
|
+
// Additional security checks for SECURITY DEFINER functions
|
|
341
|
+
if (hasSecurityDefiner) {
|
|
342
|
+
// Get function body to check for unqualified references
|
|
343
|
+
const funcBodyStart = content.indexOf('AS $$', funcStart);
|
|
344
|
+
if (funcBodyStart !== -1) {
|
|
345
|
+
const funcBodyEnd = content.indexOf('$$;', funcBodyStart);
|
|
346
|
+
if (funcBodyEnd !== -1) {
|
|
347
|
+
const funcBody = content.substring(funcBodyStart, funcBodyEnd);
|
|
348
|
+
|
|
349
|
+
// Check for unqualified table references (security risk)
|
|
350
|
+
// Look for FROM/JOIN without schema qualification
|
|
351
|
+
const unqualifiedTablePattern = /(?:FROM|JOIN)\s+([a-z_][a-z0-9_]*)\s+(?!WHERE|ON|,|$)/gi;
|
|
352
|
+
let tableMatch;
|
|
353
|
+
const rlsProtectedTables = ['rbac_organisation_roles', 'rbac_global_roles', 'rbac_event_app_roles', 'rbac_user_profiles', 'rbac_apps', 'rbac_app_pages', 'rbac_page_permissions'];
|
|
354
|
+
|
|
355
|
+
while ((tableMatch = unqualifiedTablePattern.exec(funcBody)) !== null) {
|
|
356
|
+
const tableName = tableMatch[1].toLowerCase();
|
|
357
|
+
// Check if it's a known RLS-protected table without schema qualification
|
|
358
|
+
if (rlsProtectedTables.some(t => t.toLowerCase() === tableName)) {
|
|
359
|
+
// Check if it's already schema-qualified (public.table_name)
|
|
360
|
+
const beforeMatch = funcBody.substring(Math.max(0, tableMatch.index - 20), tableMatch.index);
|
|
361
|
+
if (!/public\./i.test(beforeMatch)) {
|
|
362
|
+
issues.push({
|
|
363
|
+
type: 'rlsPolicy',
|
|
364
|
+
file: relativePath,
|
|
365
|
+
line: getLineNumber(content, funcStart + tableMatch.index),
|
|
366
|
+
message: `SECURITY DEFINER function '${funcName}' uses unqualified table reference '${tableName}'. MANDATORY: All table references in SECURITY DEFINER functions must be schema-qualified (e.g., public.${tableName}) to prevent search path hijacking.`,
|
|
367
|
+
code: getCodeSnippet(content, funcStart + tableMatch.index, 0, 100),
|
|
368
|
+
severity: 'error',
|
|
369
|
+
fix: `Schema-qualify the table reference: public.${tableName}`,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Check if function queries RLS-protected tables (justification for SECURITY DEFINER)
|
|
376
|
+
const queriesRLSProtected = rlsProtectedTables.some(table => {
|
|
377
|
+
const tablePattern = new RegExp(`\\b${table}\\b`, 'i');
|
|
378
|
+
return tablePattern.test(funcBody);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// Check if function has any table queries at all
|
|
382
|
+
const hasTableQueries = /(?:FROM|JOIN|INTO|UPDATE)\s+\w+/i.test(funcBody);
|
|
383
|
+
|
|
384
|
+
if (!queriesRLSProtected && hasTableQueries) {
|
|
385
|
+
// Function has SECURITY DEFINER but doesn't query RLS-protected tables
|
|
386
|
+
// This might be okay if it needs elevated privileges, but should be documented
|
|
387
|
+
const hasComment = /COMMENT\s+ON\s+FUNCTION.*SECURITY\s+DEFINER/i.test(content.substring(funcStart, funcStart + 2000));
|
|
388
|
+
if (!hasComment) {
|
|
389
|
+
issues.push({
|
|
390
|
+
type: 'rlsPolicy',
|
|
391
|
+
file: relativePath,
|
|
392
|
+
line: getLineNumber(content, funcStart),
|
|
393
|
+
message: `SECURITY DEFINER function '${funcName}' does not query RLS-protected tables. If SECURITY DEFINER is needed for elevated privileges, document why in a COMMENT. Otherwise, consider removing SECURITY DEFINER if not needed.`,
|
|
394
|
+
code: getCodeSnippet(content, funcStart, 0, 200),
|
|
395
|
+
severity: 'warning',
|
|
396
|
+
fix: 'Add COMMENT ON FUNCTION explaining why SECURITY DEFINER is needed, or remove SECURITY DEFINER if not required.',
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
} else if (queriesRLSProtected && !hasComment) {
|
|
400
|
+
// Function queries RLS-protected tables but doesn't document why SECURITY DEFINER is needed
|
|
401
|
+
const hasComment = /COMMENT\s+ON\s+FUNCTION/i.test(content.substring(funcStart, funcStart + 2000));
|
|
402
|
+
if (!hasComment) {
|
|
403
|
+
issues.push({
|
|
404
|
+
type: 'rlsPolicy',
|
|
405
|
+
file: relativePath,
|
|
406
|
+
line: getLineNumber(content, funcStart),
|
|
407
|
+
message: `SECURITY DEFINER function '${funcName}' queries RLS-protected tables but lacks documentation. REQUIRED: Add COMMENT ON FUNCTION explaining why SECURITY DEFINER is needed (to avoid circular RLS dependencies).`,
|
|
408
|
+
code: getCodeSnippet(content, funcStart, 0, 200),
|
|
409
|
+
severity: 'warning',
|
|
410
|
+
fix: 'Add COMMENT ON FUNCTION documenting that SECURITY DEFINER is required to avoid circular RLS dependencies when querying RLS-protected tables.',
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
319
417
|
}
|
|
320
418
|
}
|
|
321
419
|
} catch (error) {
|