@jmruthers/pace-core 0.6.9 → 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/audit-tool/audits/02-project-structure.cjs +62 -0
- 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-SOAFXIWY.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-C7ZQ5O4C.js → chunk-KSNLMI7N.js} +3 -3
- package/dist/chunk-KYURMOQM.js +977 -0
- package/dist/{chunk-LX6U42O3.js → chunk-LNHFAF4X.js} +160 -58
- package/dist/{chunk-J2U36LHD.js → chunk-MPY44PWB.js} +620 -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-5HNSDQWH.js → chunk-TFIPNIPE.js} +865 -532
- 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/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 +11 -10
- package/dist/hooks.js +69 -44
- package/dist/index.d.ts +379 -31
- 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 +57 -213
- 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-0AyangqX.d.ts → timezone-K-ptz3HO.d.ts} +21 -22
- 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 +4 -4
- package/dist/{PublicPageProvider-CIGSujI2.d.ts → usePublicPageContext-vxBlEHO9.d.ts} +294 -151
- package/dist/{usePublicRouteParams-DQLrDqDb.d.ts → usePublicRouteParams-G3Ks53mk.d.ts} +7 -6
- package/dist/utils.d.ts +300 -136
- 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/implementation-guides/data-tables.md +190 -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 +16 -14
- 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 +128 -77
- 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 +15 -39
- 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 +1 -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 +0 -7
- 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 +0 -1
- 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/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 +2 -1
- 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 -10
- 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
|
@@ -198,6 +198,67 @@ function checkTestColocation(consumingAppPath) {
|
|
|
198
198
|
return issues;
|
|
199
199
|
}
|
|
200
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);
|
|
242
|
+
|
|
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) {
|
|
248
|
+
issues.push({
|
|
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]/`,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return issues;
|
|
260
|
+
}
|
|
261
|
+
|
|
201
262
|
/**
|
|
202
263
|
* Check Supabase structure
|
|
203
264
|
* Note: Consuming apps don't need migrations directory - only pace-core handles migrations
|
|
@@ -224,6 +285,7 @@ function runStandard2Audit(consumingAppPath) {
|
|
|
224
285
|
issues.push(...checkConfigFiles(consumingAppPath));
|
|
225
286
|
issues.push(...checkImportPaths(consumingAppPath));
|
|
226
287
|
issues.push(...checkTestColocation(consumingAppPath));
|
|
288
|
+
issues.push(...checkFeatureBasedOrganization(consumingAppPath));
|
|
227
289
|
issues.push(...checkSupabaseStructure(consumingAppPath));
|
|
228
290
|
} catch (error) {
|
|
229
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) {
|
|
@@ -14,7 +14,7 @@ This guide defines the standard folder structure and file organization for consu
|
|
|
14
14
|
## AI Agent Instructions
|
|
15
15
|
|
|
16
16
|
**When creating or organizing files, ALWAYS:**
|
|
17
|
-
1. **Organize by feature** - Group components, hooks, and utilities by feature
|
|
17
|
+
1. **Organize by feature** - Group components, hooks, and utilities by feature, not by type
|
|
18
18
|
2. **Colocate tests** - Place test files next to source files (`Component.test.tsx` next to `Component.tsx`)
|
|
19
19
|
3. **Follow naming conventions** - Components: `PascalCase.tsx`, Hooks: `use*.ts`, Utils: `camelCase.ts`
|
|
20
20
|
4. **Use absolute imports** - Use `@/` path alias for imports, never relative imports for distant files
|
|
@@ -74,7 +74,7 @@ your-app/
|
|
|
74
74
|
|
|
75
75
|
## MUST: Organize Components by Feature
|
|
76
76
|
|
|
77
|
-
**ALWAYS organize components by feature
|
|
77
|
+
**ALWAYS organize components by feature, not by type:**
|
|
78
78
|
|
|
79
79
|
```
|
|
80
80
|
src/
|
|
@@ -136,30 +136,6 @@ src/
|
|
|
136
136
|
- where migrations/RLS policies are managed
|
|
137
137
|
- whether the app is expected to add migrations in the future
|
|
138
138
|
|
|
139
|
-
## SHOULD: Organize by Domain
|
|
140
|
-
|
|
141
|
-
**For larger apps, SHOULD organize by domain/feature:**
|
|
142
|
-
|
|
143
|
-
```
|
|
144
|
-
src/
|
|
145
|
-
├── domains/
|
|
146
|
-
│ ├── events/
|
|
147
|
-
│ │ ├── components/
|
|
148
|
-
│ │ ├── hooks/
|
|
149
|
-
│ │ ├── services/
|
|
150
|
-
│ │ └── types.ts
|
|
151
|
-
│ └── users/
|
|
152
|
-
│ ├── components/
|
|
153
|
-
│ ├── hooks/
|
|
154
|
-
│ ├── services/
|
|
155
|
-
│ └── types.ts
|
|
156
|
-
├── shared/ # Shared across domains
|
|
157
|
-
│ ├── components/
|
|
158
|
-
│ ├── hooks/
|
|
159
|
-
│ └── utils/
|
|
160
|
-
└── App.tsx
|
|
161
|
-
```
|
|
162
|
-
|
|
163
139
|
## MUST: Keep Root Directory Clean
|
|
164
140
|
|
|
165
141
|
**Root directory SHOULD only contain:**
|