@jmruthers/pace-core 0.6.9 → 0.6.11
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 +21 -0
- package/audit-tool/00-dependencies.cjs +46 -13
- package/audit-tool/audits/01-pace-core-compliance.cjs +96 -21
- package/audit-tool/audits/02-project-structure.cjs +74 -2
- package/audit-tool/audits/03-architecture.cjs +220 -20
- package/audit-tool/audits/04-code-quality.cjs +95 -3
- package/audit-tool/audits/05-styling.cjs +19 -7
- package/audit-tool/audits/06-security-rbac.cjs +214 -25
- package/audit-tool/audits/07-api-tech-stack.cjs +31 -15
- package/audit-tool/audits/08-testing-documentation.cjs +11 -3
- package/audit-tool/audits/09-operations.cjs +19 -7
- package/audit-tool/index.cjs +22 -11
- package/audit-tool/utils/report-utils.cjs +4 -0
- package/cursor-rules/01-pace-core-compliance.mdc +1 -0
- package/cursor-rules/02-project-structure.mdc +3 -26
- package/cursor-rules/03-architecture.mdc +3 -1
- package/cursor-rules/04-code-quality.mdc +1 -0
- package/cursor-rules/05-styling.mdc +120 -8
- package/cursor-rules/06-security-rbac.mdc +126 -2
- package/cursor-rules/07-api-tech-stack.mdc +1 -0
- package/cursor-rules/08-testing-documentation.mdc +1 -0
- package/cursor-rules/09-operations.mdc +1 -0
- package/dist/DataTable-EFYP2QLE.js +16 -0
- package/dist/InactivityServiceProvider-BbxwwDz1.d.ts +308 -0
- package/dist/UnifiedAuthProvider-Bkt_tzdS.d.ts +183 -0
- package/dist/api-BZR2CYXL.js +5 -0
- package/dist/api-result-USV1Czr-.d.ts +51 -0
- 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-HI2DHUVU.js +4 -0
- package/dist/auth-JvdRVaud.d.ts +49 -0
- package/dist/chunk-2DL2WSOE.js +327 -0
- package/dist/chunk-2OEVOGGR.js +9598 -0
- package/dist/chunk-44CNXN4P.js +15 -0
- package/dist/chunk-4R3T5ENU.js +2943 -0
- package/dist/chunk-7A6IMHH2.js +2321 -0
- package/dist/chunk-BTHN5MKC.js +121 -0
- package/dist/chunk-CU2BU2MQ.js +2 -0
- package/dist/chunk-D6BMFMQZ.js +200 -0
- package/dist/chunk-DDMPHZ3D.js +58 -0
- package/dist/chunk-ENLXB7GP.js +721 -0
- package/dist/chunk-J2KQK6DG.js +2159 -0
- package/dist/chunk-KJXRL3XE.js +6434 -0
- package/dist/chunk-L5LFKKLJ.js +61 -0
- package/dist/chunk-PCSHBLPB.js +811 -0
- package/dist/chunk-QRYSEPHB.js +429 -0
- package/dist/chunk-RMLY6KB5.js +187 -0
- package/dist/chunk-SACF5YSM.js +31 -0
- package/dist/chunk-UZNAFKGW.js +125 -0
- package/dist/chunk-V7FTM2LU.js +1080 -0
- package/dist/chunk-WY6Y7KC3.js +264 -0
- package/dist/chunk-XOJME5T7.js +407 -0
- package/dist/chunk-XPFVT3GN.js +492 -0
- package/dist/chunk-YFTFFJIV.js +529 -0
- package/dist/chunk-YYTWKVHO.js +1334 -0
- package/dist/components.d.ts +12 -89
- package/dist/components.js +23 -55
- package/dist/database.generated-qkdoiVrJ.d.ts +9441 -0
- package/dist/eslint-rules/index.cjs +3 -0
- package/dist/eslint-rules/rules/03-architecture.cjs +74 -0
- package/dist/eslint-rules/rules/05-styling.cjs +507 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +84 -0
- package/dist/event-BfCox3N2.d.ts +265 -0
- package/dist/file-reference-DU1hcawx.d.ts +164 -0
- package/dist/functions-DH45k8ec.d.ts +208 -0
- package/dist/hooks.d.ts +28 -14
- package/dist/hooks.js +90 -56
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +392 -155
- package/dist/index.js +337 -347
- package/dist/pagination-BW1mqywp.d.ts +201 -0
- package/dist/papaparseLoader-WG2UXQ22.js +7 -0
- package/dist/providers.d.ts +29 -14
- package/dist/providers.js +7 -5
- package/dist/rbac/eslint-rules.js +2 -2
- package/dist/rbac/index.d.ts +180 -351
- package/dist/rbac/index.js +13 -11
- package/dist/theming/runtime.d.ts +28 -5
- package/dist/theming/runtime.js +2 -2
- package/dist/timezone-BTWWXKVY.d.ts +696 -0
- package/dist/types-BE2sEHKd.d.ts +55 -0
- package/dist/types-CvOPXWWZ.d.ts +111 -0
- package/dist/types-Dr8sNhER.d.ts +50 -0
- package/dist/types.d.ts +20 -13
- package/dist/types.js +1 -0
- package/dist/usePublicPageContext-B91dGYW1.d.ts +4367 -0
- package/dist/usePublicRouteParams-BgV6VhMi.d.ts +946 -0
- package/dist/utils.d.ts +338 -156
- package/dist/utils.js +78 -60
- package/dist/validation-g5n0hDkh.d.ts +177 -0
- package/docs/api/modules.md +1226 -1094
- package/docs/api-reference/components.md +5 -5
- package/docs/api-reference/rpc-functions.md +12 -3
- package/docs/core-concepts/rbac-system.md +8 -0
- package/docs/getting-started/cursor-rules.md +17 -20
- package/docs/getting-started/dependencies.md +1 -1
- package/docs/getting-started/setup.md +235 -0
- package/docs/implementation-guides/authentication.md +27 -0
- package/docs/implementation-guides/data-tables.md +365 -10
- package/docs/migration/ApiResult-migration.md +25 -0
- package/docs/rbac/RBAC_CONTRACT.md +0 -12
- package/docs/rbac/api-reference.md +33 -31
- package/docs/standards/0-standards-overview.md +50 -15
- package/docs/standards/1-pace-core-compliance-standards.md +62 -57
- package/docs/standards/2-project-structure-standards.md +45 -90
- package/docs/standards/3-architecture-standards.md +41 -1
- package/docs/standards/4-code-quality-standards.md +26 -6
- package/docs/standards/5-styling-standards.md +35 -1
- package/docs/standards/6-security-rbac-standards.md +288 -7
- package/docs/standards/7-api-tech-stack-standards.md +116 -17
- package/docs/standards/8-testing-documentation-standards.md +31 -0
- package/docs/standards/9-operations-standards.md +19 -0
- package/docs/standards/README.md +20 -201
- package/docs/testing/README.md +10 -0
- package/docs/testing/test-setup-for-consumers.md +916 -0
- package/docs/troubleshooting/common-issues.md +17 -1
- package/docs/troubleshooting/organisation-context-setup.md +8 -0
- package/docs/troubleshooting/print-event-name-css-variable-analysis.md +217 -0
- package/eslint-config-pace-core.cjs +24 -0
- package/package.json +14 -20
- package/scripts/build-docs.js +180 -0
- package/scripts/setup.cjs +536 -0
- package/scripts/validate.cjs +480 -0
- package/src/__mocks__/lucide-react.ts +0 -2
- package/src/__tests__/helpers/component-test-utils.test.tsx +260 -0
- package/src/__tests__/helpers/optimized-test-setup.test.ts +224 -0
- package/src/__tests__/helpers/supabaseMock.test.ts +273 -0
- package/src/__tests__/helpers/test-providers.test.tsx +99 -0
- package/src/__tests__/helpers/test-providers.tsx +37 -39
- package/src/__tests__/helpers/test-utils.test.tsx +447 -0
- package/src/__tests__/helpers/timer-utils.test.ts +371 -0
- 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.test.ts +304 -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 +379 -4
- package/src/components/AddressField/AddressField.tsx +239 -213
- 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/Button/Button.tsx +1 -1
- package/src/components/Calendar/Calendar.test.tsx +523 -131
- package/src/components/Calendar/Calendar.tsx +107 -488
- package/src/components/Card/Card.test.tsx +384 -258
- package/src/components/Card/Card.tsx +19 -10
- package/src/components/Checkbox/Checkbox.test.tsx +58 -174
- package/src/components/ContextSelector/ContextSelector.internals.tsx +204 -0
- package/src/components/ContextSelector/ContextSelector.test.tsx +360 -0
- package/src/components/ContextSelector/ContextSelector.tsx +66 -280
- package/src/components/ContextSelector/ContextSelector.types.ts +35 -0
- package/src/components/ContextSelector/useContextSelectorState.tsx +195 -0
- package/src/components/DataTable/AUDIT_REPORT.md +59 -44
- package/src/components/DataTable/DataTable.comprehensive.test.tsx +759 -0
- package/src/components/DataTable/DataTable.default-state.test.tsx +524 -0
- package/src/components/DataTable/DataTable.export.test.tsx +705 -0
- package/src/components/DataTable/DataTable.grouping-aggregation.test.tsx +658 -0
- package/src/components/DataTable/DataTable.hooks.test.tsx +192 -0
- package/src/components/DataTable/DataTable.select-label-display.test.tsx +485 -0
- package/src/components/DataTable/DataTable.test.tsx +787 -416
- package/src/components/DataTable/DataTable.tsx +14 -14
- package/src/components/DataTable/DataTableCore.integration.test.tsx +458 -0
- package/src/components/DataTable/DataTableCore.test-setup.ts +221 -0
- package/src/components/DataTable/DataTableCore.test.tsx +970 -0
- package/src/components/DataTable/README.md +155 -0
- package/src/components/DataTable/TESTING.md +101 -0
- package/src/components/DataTable/a11y.basic.test.tsx +788 -0
- package/src/components/DataTable/components/DataTableCore.tsx +126 -894
- package/src/components/DataTable/components/GroupingDropdown.test.tsx +621 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +2 -3
- package/src/components/DataTable/components/ImportModal.tsx +82 -408
- package/src/components/DataTable/components/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/context/DataTableContext.test.tsx +328 -0
- package/src/components/DataTable/context/DataTableContext.tsx +13 -13
- package/src/components/DataTable/core/ColumnFactory.test.ts +403 -0
- package/src/components/DataTable/core/ColumnFactory.ts +3 -3
- package/src/components/DataTable/hooks/useColumnOrderPersistence.test.ts +516 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.test.ts +256 -0
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useDataTableConfiguration.test.ts +297 -0
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +15 -3
- package/src/components/DataTable/hooks/useDataTableDataPipeline.test.ts +270 -0
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.test.ts +127 -0
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.ts +106 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.test.ts +461 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.ts +238 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.test.ts +296 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.ts +175 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.test.ts +203 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.ts +109 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.test.ts +280 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +81 -260
- package/src/components/DataTable/hooks/useDataTablePipeline.test.tsx +219 -0
- package/src/components/DataTable/hooks/useDataTablePipeline.tsx +239 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.test.tsx +316 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.tsx +195 -0
- package/src/components/DataTable/hooks/useDataTableScope.test.ts +110 -0
- package/src/components/DataTable/hooks/useDataTableScope.ts +123 -0
- package/src/components/DataTable/hooks/useDataTableState.test.ts +733 -0
- package/src/components/DataTable/hooks/useDataTableState.ts +161 -114
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.test.ts +277 -0
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.ts +222 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.test.ts +93 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.ts +86 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.test.ts +185 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.ts +178 -0
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.test.ts +183 -0
- package/src/components/DataTable/hooks/useHierarchicalState.test.ts +294 -0
- package/src/components/DataTable/hooks/useImportModalFocus.test.ts +184 -0
- package/src/components/DataTable/hooks/useImportModalFocus.ts +53 -0
- package/src/components/DataTable/hooks/useImportModalState.test.ts +390 -0
- package/src/components/DataTable/hooks/useImportModalState.ts +345 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.test.ts +787 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +311 -271
- package/src/components/DataTable/hooks/usePermissionTracking.test.ts +381 -0
- package/src/components/DataTable/hooks/usePermissionTracking.ts +122 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.test.ts +258 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +27 -4
- package/src/components/DataTable/hooks/useTableColumns.test.ts +499 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +15 -39
- package/src/components/DataTable/hooks/useTableHandlers.test.ts +461 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +13 -22
- package/src/components/DataTable/index.ts +28 -5
- package/src/components/DataTable/keyboard.test.tsx +734 -0
- package/src/components/DataTable/mocks/MockRBACProvider.tsx +66 -0
- package/src/components/DataTable/pagination.modes.test.tsx +728 -0
- package/src/components/DataTable/ssr.strict-mode.test.tsx +319 -0
- package/src/components/DataTable/styles.test.ts +379 -0
- package/src/components/DataTable/styles.ts +0 -1
- package/src/components/DataTable/test-utils/MockDataTableComponents.tsx +55 -0
- package/src/components/DataTable/test-utils/dataFactories.ts +103 -0
- package/src/components/DataTable/test-utils/featureConfig.ts +10 -0
- package/src/components/DataTable/test-utils/sharedTestUtils.ts +419 -0
- package/src/components/DataTable/test-utils.ts +94 -0
- package/src/components/DataTable/types/actions.ts +71 -0
- package/src/components/DataTable/types/base.ts +39 -0
- package/src/components/DataTable/types/columns.ts +125 -0
- package/src/components/DataTable/types/export.ts +32 -0
- package/src/components/DataTable/types/features.ts +81 -0
- package/src/components/DataTable/types/hierarchical.ts +44 -0
- package/src/components/DataTable/types/index.ts +43 -0
- package/src/components/DataTable/types/pagination.ts +85 -0
- package/src/components/DataTable/types/performance.ts +47 -0
- package/src/components/DataTable/types/props.ts +62 -0
- package/src/components/DataTable/types/rbac.ts +45 -0
- package/src/components/DataTable/ui/layout/DataTableCore.test.tsx +1194 -0
- package/src/components/DataTable/ui/layout/DataTableCore.tsx +345 -0
- package/src/components/DataTable/ui/layout/DataTableErrorBoundary.test.tsx +438 -0
- package/src/components/DataTable/ui/layout/DataTableErrorBoundary.tsx +225 -0
- package/src/components/DataTable/ui/layout/DataTableLayout.test.tsx +1352 -0
- package/src/components/DataTable/ui/layout/DataTableLayout.tsx +661 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.test.tsx +91 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.tsx +43 -0
- package/src/components/DataTable/ui/modals/DataTableModals.test.tsx +749 -0
- package/src/components/DataTable/ui/modals/DataTableModals.tsx +341 -0
- package/src/components/DataTable/ui/modals/ImportModal.test.tsx +1834 -0
- package/src/components/DataTable/ui/modals/ImportModal.tsx +197 -0
- package/src/components/DataTable/ui/modals/ImportModalFailedRowsSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/ui/modals/ImportModalPreviewSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalSummarySection.tsx +59 -0
- package/src/components/DataTable/ui/modals/importModalPersistence.ts +73 -0
- package/src/components/DataTable/ui/shared/AccessDeniedPage.test.tsx +245 -0
- package/src/components/DataTable/ui/shared/AccessDeniedPage.tsx +159 -0
- package/src/components/DataTable/ui/shared/ActionButtons.test.tsx +921 -0
- package/src/components/DataTable/ui/shared/ActionButtons.tsx +195 -0
- package/src/components/DataTable/ui/shared/ColumnFilter.test.tsx +497 -0
- package/src/components/DataTable/ui/shared/ColumnFilter.tsx +113 -0
- package/src/components/DataTable/ui/shared/PaginationControls.test.tsx +451 -0
- package/src/components/DataTable/ui/shared/PaginationControls.tsx +291 -0
- package/src/components/DataTable/ui/shared/SortIndicator.test.tsx +135 -0
- package/src/components/DataTable/ui/shared/SortIndicator.tsx +50 -0
- package/src/components/DataTable/ui/table/EditFields.test.tsx +526 -0
- package/src/components/DataTable/ui/table/EditFields.tsx +355 -0
- package/src/components/DataTable/ui/table/EditableRow.test.tsx +1003 -0
- package/src/components/DataTable/ui/table/EditableRow.tsx +444 -0
- package/src/components/DataTable/ui/table/EmptyState.test.tsx +360 -0
- package/src/components/DataTable/ui/table/EmptyState.tsx +74 -0
- package/src/components/DataTable/ui/table/FilterRow.test.tsx +416 -0
- package/src/components/DataTable/ui/table/FilterRow.tsx +148 -0
- package/src/components/DataTable/ui/table/LoadingState.test.tsx +77 -0
- package/src/components/DataTable/ui/table/LoadingState.tsx +17 -0
- package/src/components/DataTable/ui/table/RowComponent.test.tsx +1024 -0
- package/src/components/DataTable/ui/table/RowComponent.tsx +429 -0
- package/src/components/DataTable/ui/table/UnifiedTableBody.test.tsx +1273 -0
- package/src/components/DataTable/ui/table/UnifiedTableBody.tsx +440 -0
- package/src/components/DataTable/ui/table/cellValueUtils.test.ts +453 -0
- package/src/components/DataTable/ui/table/cellValueUtils.ts +40 -0
- package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.test.tsx +551 -0
- package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.tsx +160 -0
- package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.test.tsx +751 -0
- package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.tsx +114 -0
- package/src/components/DataTable/ui/toolbar/DataTableToolbar.test.tsx +629 -0
- package/src/components/DataTable/ui/toolbar/DataTableToolbar.tsx +271 -0
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.test.tsx +621 -0
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.tsx +107 -0
- package/src/components/DataTable/utils/a11yUtils.test.ts +548 -0
- package/src/components/DataTable/utils/a11yUtils.ts +1 -1
- package/src/components/DataTable/utils/aggregationUtils.test.ts +288 -0
- package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
- package/src/components/DataTable/utils/columnUtils.test.ts +94 -0
- package/src/components/DataTable/utils/csvParse.test.ts +74 -0
- package/src/components/DataTable/utils/csvParse.ts +65 -0
- package/src/components/DataTable/utils/errorHandling.test.ts +209 -0
- package/src/components/DataTable/utils/errorHandling.ts +3 -1
- package/src/components/DataTable/utils/exportUtils.test.ts +954 -0
- package/src/components/DataTable/utils/exportUtils.ts +1 -1
- package/src/components/DataTable/utils/flexibleImport.test.ts +573 -0
- package/src/components/DataTable/utils/flexibleImport.ts +3 -186
- package/src/components/DataTable/utils/hierarchicalSorting.test.ts +235 -0
- package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
- package/src/components/DataTable/utils/hierarchicalUtils.test.ts +586 -0
- package/src/components/DataTable/utils/importDateParser.test.ts +162 -0
- package/src/components/DataTable/utils/importDateParser.ts +114 -0
- package/src/components/DataTable/utils/importValueParser.test.ts +138 -0
- package/src/components/DataTable/utils/importValueParser.ts +91 -0
- package/src/components/DataTable/utils/paginationUtils.test.ts +593 -0
- package/src/components/DataTable/utils/paginationUtils.ts +7 -4
- package/src/components/DataTable/utils/performanceUtils.test.ts +470 -0
- package/src/components/DataTable/utils/performanceUtils.ts +1 -1
- package/src/components/DataTable/utils/rowUtils.test.ts +235 -0
- package/src/components/DataTable/utils/selectFieldUtils.test.ts +271 -0
- package/src/components/DataTable/utils/selectFieldUtils.ts +97 -67
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +18 -25
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
- package/src/components/DateTimeField/DateTimeField.test.tsx +3 -16
- package/src/components/DateTimeField/DateTimeField.tsx +1 -1
- package/src/components/Dialog/Dialog.test-utils.ts +49 -0
- package/src/components/Dialog/Dialog.test.tsx +2865 -458
- package/src/components/Dialog/Dialog.tsx +183 -986
- package/src/components/Dialog/dialogLock.test.ts +238 -0
- package/src/components/Dialog/dialogLock.ts +98 -0
- package/src/components/Dialog/index.ts +2 -0
- package/src/components/Dialog/useDialogDimensions.test.ts +163 -0
- package/src/components/Dialog/useDialogDimensions.ts +140 -0
- package/src/components/Dialog/useDialogLifecycle.test.ts +358 -0
- package/src/components/Dialog/useDialogLifecycle.ts +135 -0
- package/src/components/Dialog/useDialogPersistence.test.ts +381 -0
- package/src/components/Dialog/useDialogPersistence.ts +357 -0
- 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 +479 -247
- package/src/components/FileDisplay/FileDisplay.tsx +29 -659
- package/src/components/FileDisplay/FileDisplayContent.test.tsx +395 -0
- package/src/components/FileDisplay/FileDisplayContent.tsx +242 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.test.tsx +74 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.tsx +38 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.test.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayErrorView.test.tsx +71 -0
- package/src/components/FileDisplay/FileDisplayErrorView.tsx +50 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.test.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.test.tsx +21 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.tsx +23 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.test.tsx +101 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.tsx +109 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.test.tsx +58 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.tsx +48 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.test.tsx +111 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.tsx +270 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.test.tsx +78 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.tsx +67 -0
- package/src/components/FileDisplay/fallbackUtils.test.ts +50 -0
- package/src/components/FileDisplay/fallbackUtils.ts +44 -0
- package/src/components/FileDisplay/fetchFileDisplayData.ts +24 -0
- package/src/components/FileDisplay/fetchFileDisplayData.unit.test.ts +183 -0
- package/src/components/FileDisplay/fileDisplayUtils.test.ts +58 -0
- package/src/components/FileDisplay/fileDisplayUtils.ts +24 -0
- package/src/components/FileDisplay/index.tsx +1 -1
- package/src/components/FileDisplay/useFileDisplay.test.ts +538 -0
- package/src/components/FileDisplay/useFileDisplay.ts +515 -0
- package/src/components/FileDisplay/useFileDisplay.unit.test.ts +1438 -0
- package/src/components/FileDisplay/useFileDisplayData.ts +126 -0
- package/src/components/FileDisplay/usePublicFileDisplay.test.ts +729 -0
- package/src/components/FileDisplay/usePublicFileDisplay.ts +579 -0
- package/src/components/FileUpload/FileUpload.test.tsx +69 -27
- package/src/components/FileUpload/FileUpload.tsx +112 -527
- package/src/components/FileUpload/FileUploadDropZone.tsx +112 -0
- package/src/components/FileUpload/FileUploadProgressItem.tsx +86 -0
- package/src/components/FileUpload/FileUploadProgressList.tsx +40 -0
- package/src/components/FileUpload/index.tsx +1 -1
- package/src/components/FileUpload/useFileUploadManager.test.ts +308 -0
- package/src/components/FileUpload/useFileUploadManager.ts +454 -0
- package/src/components/FileUpload/useResolvedAppId.test.ts +102 -0
- package/src/components/FileUpload/useResolvedAppId.ts +77 -0
- package/src/components/Footer/Footer.test.tsx +15 -382
- package/src/components/Footer/Footer.tsx +8 -125
- package/src/components/Form/Form.test.tsx +425 -88
- package/src/components/Form/Form.tsx +91 -299
- package/src/components/Form/useFormPersistence.ts +257 -0
- package/src/components/Header/Header.test.tsx +653 -163
- package/src/components/Header/Header.tsx +62 -44
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
- package/src/components/Input/Input.test.tsx +34 -120
- package/src/components/Input/Input.tsx +1 -1
- package/src/components/Label/Label.test.tsx +46 -45
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +8 -11
- package/src/components/LoginForm/LoginForm.test.tsx +0 -1
- package/src/components/NavigationMenu/HierarchicalNavItem.tsx +104 -0
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +2422 -102
- package/src/components/NavigationMenu/NavigationMenu.tsx +62 -362
- package/src/components/NavigationMenu/index.ts +6 -1
- package/src/components/NavigationMenu/navigationPermissionHelper.ts +188 -0
- package/src/components/NavigationMenu/useNavigationFiltering.test.ts +1949 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +199 -308
- package/src/components/NavigationMenu/useNavigationScope.ts +125 -0
- package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1322 -0
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +50 -49
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +103 -85
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +774 -44
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +282 -764
- package/src/components/PaceAppLayout/README.md +0 -9
- package/src/components/PaceAppLayout/test-setup.tsx +15 -9
- package/src/components/PaceAppLayout/useFilteredNavItems.ts +304 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutConfig.ts +142 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutGate.tsx +150 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutPermissions.ts +162 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutScope.ts +79 -0
- package/src/components/PaceAppLayout/useRoleBasedRouteAccess.ts +157 -0
- package/src/components/PaceAppLayout/useSuperAdminFallback.ts +58 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +782 -20
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +33 -125
- package/src/components/PaceLoginPage/useLoginAppAccess.ts +153 -0
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
- package/src/components/Progress/Progress.test.tsx +127 -1
- package/src/components/Progress/Progress.tsx +1 -2
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +29 -217
- package/src/components/ProtectedRoute/useProtectedRouteState.ts +128 -0
- package/src/components/ProtectedRoute/useVisibilityRedirectGrace.ts +89 -0
- package/src/components/PublicLayout/PublicLayout.test.tsx +1640 -38
- package/src/components/PublicLayout/PublicPageContext.ts +28 -0
- package/src/components/PublicLayout/PublicPageLayout.tsx +134 -75
- package/src/components/PublicLayout/PublicPageProvider.tsx +7 -42
- package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
- package/src/components/Select/Select.test.tsx +45 -8
- package/src/components/Select/Select.tsx +57 -40
- package/src/components/Select/context.test.tsx +56 -0
- package/src/components/Select/text.test.tsx +104 -0
- package/src/components/Select/text.ts +26 -0
- package/src/components/Select/types.ts +3 -0
- package/src/components/Select/useSelectEvents.test.ts +279 -0
- package/src/components/Select/useSelectEvents.ts +87 -0
- package/src/components/Select/useSelectSearch.test.tsx +295 -0
- package/src/components/Select/useSelectSearch.ts +91 -0
- package/src/components/Select/useSelectState.test.ts +268 -0
- package/src/components/Select/useSelectState.ts +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 +395 -317
- package/src/components/Tabs/Tabs.test.tsx +270 -0
- package/src/components/Tabs/Tabs.tsx +4 -4
- package/src/components/Textarea/Textarea.test.tsx +11 -38
- package/src/components/Toast/Toast.test.tsx +425 -496
- 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/index.test.ts +346 -0
- package/src/components/index.ts +12 -1
- package/src/constants/performance.test.ts +91 -0
- package/src/hooks/ServiceHooks.test.tsx +725 -0
- package/src/hooks/hooks.integration.test.tsx +608 -0
- package/src/hooks/index.ts +18 -3
- package/src/hooks/index.unit.test.ts +220 -0
- 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/usePublicRouteParams.test.ts +595 -0
- package/src/hooks/public/usePublicRouteParams.ts +2 -2
- package/src/hooks/services/useAuth.ts +9 -7
- 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 +170 -47
- package/src/hooks/useAddressAutocomplete.ts +109 -81
- package/src/hooks/useApiFetch.unit.test.ts +111 -0
- package/src/hooks/useAppConfig.ts +13 -3
- package/src/hooks/useAppConfig.unit.test.ts +712 -0
- package/src/hooks/useComponentPerformance.unit.test.tsx +314 -0
- package/src/hooks/useDataTablePerformance.ts +111 -130
- package/src/hooks/useDataTablePerformance.unit.test.ts +720 -0
- package/src/hooks/useDataTableState.test.ts +170 -0
- package/src/hooks/useDataTableState.ts +5 -5
- package/src/hooks/useDebounce.unit.test.ts +157 -0
- package/src/hooks/useEventTheme.test.ts +70 -18
- package/src/hooks/useEventTheme.ts +50 -22
- package/src/hooks/useEvents.ts +49 -2
- package/src/hooks/useEvents.unit.test.ts +227 -0
- package/src/hooks/useFileReference.test.ts +388 -107
- package/src/hooks/useFileReference.ts +184 -179
- package/src/hooks/useFileUrl.ts +1 -1
- package/src/hooks/useFileUrl.unit.test.ts +686 -0
- package/src/hooks/useFileUrlCache.test.ts +319 -0
- package/src/hooks/useFileUrlCache.ts +5 -2
- package/src/hooks/useFocusManagement.unit.test.ts +604 -0
- package/src/hooks/useFocusTrap.unit.test.tsx +613 -0
- package/src/hooks/useFormDialog.test.ts +307 -0
- package/src/hooks/useFormDialog.ts +2 -2
- package/src/hooks/useInactivityTracker.ts +141 -134
- package/src/hooks/useInactivityTracker.unit.test.ts +446 -0
- package/src/hooks/useIsMobile.unit.test.ts +317 -0
- package/src/hooks/useIsPrint.ts +62 -0
- package/src/hooks/useIsPrint.unit.test.ts +545 -0
- package/src/hooks/useKeyboardShortcuts.unit.test.ts +907 -0
- package/src/hooks/useOrganisationPermissions.test.ts +1 -2
- package/src/hooks/useOrganisationPermissions.ts +1 -4
- package/src/hooks/useOrganisationPermissions.unit.test.tsx +293 -0
- package/src/hooks/useOrganisationSecurity.test.ts +4 -33
- package/src/hooks/useOrganisationSecurity.ts +192 -203
- package/src/hooks/useOrganisationSecurity.unit.test.tsx +959 -0
- package/src/hooks/useOrganisations.ts +1 -1
- package/src/hooks/useOrganisations.unit.test.ts +369 -0
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePerformanceMonitor.unit.test.ts +693 -0
- package/src/hooks/usePermissionCache.test.ts +298 -329
- package/src/hooks/usePermissionCache.ts +277 -276
- package/src/hooks/usePreventTabReload.test.ts +307 -0
- package/src/hooks/usePublicEvent.simple.test.ts +794 -0
- package/src/hooks/usePublicEvent.test.ts +670 -0
- package/src/hooks/usePublicEvent.unit.test.ts +638 -0
- package/src/hooks/usePublicFileDisplay.test.ts +948 -0
- package/src/hooks/usePublicRouteParams.unit.test.ts +442 -0
- package/src/hooks/useQueryCache.test.ts +391 -0
- package/src/hooks/useQueryCache.ts +7 -9
- package/src/hooks/useRBAC.unit.test.ts +253 -0
- package/src/hooks/useSessionDraft.test.ts +556 -0
- package/src/hooks/useSessionDraft.ts +14 -11
- package/src/hooks/useSessionRestoration.ts +1 -1
- package/src/hooks/useSessionRestoration.unit.test.tsx +381 -0
- package/src/hooks/useStorage.ts +94 -54
- package/src/hooks/useStorage.unit.test.ts +684 -0
- package/src/hooks/useToast.test.ts +413 -0
- package/src/hooks/useToast.ts +2 -2
- package/src/hooks/useToast.unit.test.tsx +481 -0
- package/src/hooks/useZodForm.ts +3 -3
- package/src/hooks/useZodForm.unit.test.tsx +191 -0
- package/src/icons/index.test.ts +133 -0
- package/src/icons/index.ts +3 -1
- package/src/index.test.ts +528 -0
- package/src/index.ts +56 -9
- package/src/providers/AuthProvider.test.tsx +218 -0
- package/src/providers/EventProvider.test.tsx +487 -0
- package/src/providers/InactivityProvider.test-helper.tsx +40 -0
- package/src/providers/InactivityProvider.test.tsx +421 -0
- package/src/providers/ProviderLifecycle.test.tsx +308 -0
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
- package/src/providers/UnifiedAuthProvider.test.tsx +503 -0
- package/src/providers/index.test.ts +138 -0
- package/src/providers/services/AuthServiceContext.ts +27 -0
- package/src/providers/services/AuthServiceProvider.integration.test.tsx +229 -0
- package/src/providers/services/AuthServiceProvider.test.tsx +638 -0
- package/src/providers/services/AuthServiceProvider.tsx +81 -20
- package/src/providers/services/EventServiceContext.ts +25 -0
- package/src/providers/services/EventServiceProvider.test.tsx +839 -0
- package/src/providers/services/EventServiceProvider.tsx +11 -20
- package/src/providers/services/InactivityServiceContext.ts +25 -0
- package/src/providers/services/InactivityServiceProvider.test.tsx +662 -0
- package/src/providers/services/InactivityServiceProvider.tsx +7 -17
- package/src/providers/services/OrganisationServiceContext.ts +25 -0
- package/src/providers/services/OrganisationServiceProvider.test.tsx +440 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
- package/src/providers/services/UnifiedAuthContext.ts +102 -0
- package/src/providers/services/UnifiedAuthProvider.advanced.test.tsx +434 -0
- package/src/providers/services/UnifiedAuthProvider.appId.test.tsx +408 -0
- package/src/providers/services/UnifiedAuthProvider.integration.test.tsx +304 -0
- package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +147 -497
- package/src/providers/services/contexts.test.tsx +281 -0
- package/src/providers/services/useUnifiedAuth.test.tsx +251 -0
- package/src/providers/services/useUnifiedAuth.ts +29 -0
- package/src/providers/services/useUnifiedAuthContextValue.ts +279 -0
- package/src/providers/useInactivity.test-helper.ts +27 -0
- package/src/rbac/README.md +5 -5
- package/src/rbac/adapters.comprehensive.test.tsx +429 -0
- package/src/rbac/adapters.test.tsx +654 -0
- package/src/rbac/adapters.tsx +53 -38
- package/src/rbac/api.test.ts +986 -259
- package/src/rbac/api.ts +260 -216
- package/src/rbac/audit-batched.test.ts +550 -0
- package/src/rbac/audit-batched.ts +5 -4
- package/src/rbac/audit.test.ts +225 -28
- package/src/rbac/audit.ts +26 -18
- package/src/rbac/auth-rbac-security.integration.test.tsx +300 -0
- package/src/rbac/auth-rbac.e2e.test.tsx +510 -0
- package/src/rbac/cache-invalidation.test.ts +715 -0
- 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.test.tsx +324 -0
- package/src/rbac/components/AccessDenied.tsx +20 -18
- package/src/rbac/components/NavigationGuard.test.tsx +1148 -0
- package/src/rbac/components/NavigationGuard.tsx +10 -8
- package/src/rbac/components/PagePermissionGuard.guard.test.tsx +236 -0
- package/src/rbac/components/PagePermissionGuard.performance.test.tsx +252 -0
- package/src/rbac/components/PagePermissionGuard.race-condition.test.tsx +243 -0
- package/src/rbac/components/PagePermissionGuard.test.tsx +1430 -0
- package/src/rbac/components/PagePermissionGuard.tsx +188 -381
- package/src/rbac/components/PagePermissionGuard.verification.test.tsx +185 -0
- package/src/rbac/config.test.ts +131 -48
- package/src/rbac/config.ts +69 -26
- package/src/rbac/docs/event-based-apps.md +26 -13
- package/src/rbac/engine.comprehensive.test.ts +808 -0
- package/src/rbac/engine.test.ts +974 -130
- package/src/rbac/engine.ts +53 -13
- package/src/rbac/errors.test.ts +99 -87
- package/src/rbac/errors.ts +89 -55
- package/src/rbac/eslint-rules.js +2 -2
- package/src/rbac/hooks/permissions/runPermissionCheck.ts +77 -0
- package/src/rbac/hooks/permissions/useAccessLevel.test.ts +622 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +23 -14
- package/src/rbac/hooks/permissions/useCan.test.ts +798 -0
- package/src/rbac/hooks/permissions/useCan.ts +173 -253
- package/src/rbac/hooks/permissions/useMultiplePermissions.test.ts +843 -0
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +63 -10
- package/src/rbac/hooks/permissions/usePermissions.test.ts +543 -0
- package/src/rbac/hooks/permissions/usePermissions.ts +50 -78
- package/src/rbac/hooks/useCan.test.ts +348 -32
- package/src/rbac/hooks/usePageAccessLogging.ts +160 -0
- package/src/rbac/hooks/usePageGuardScope.ts +117 -0
- package/src/rbac/hooks/usePagePermissionCheck.ts +67 -0
- package/src/rbac/hooks/usePermissions.integration.test.ts +427 -0
- package/src/rbac/hooks/usePermissions.stability.test.ts +268 -0
- package/src/rbac/hooks/usePermissions.test.ts +459 -33
- package/src/rbac/hooks/usePermissions.ts +5 -7
- package/src/rbac/hooks/useRBAC.test.ts +1784 -21
- package/src/rbac/hooks/useRBAC.ts +148 -88
- package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
- package/src/rbac/hooks/useResolvedScope.ts +4 -1
- package/src/rbac/hooks/useResourcePermissions.test.ts +561 -24
- package/src/rbac/hooks/useResourcePermissions.ts +76 -140
- package/src/rbac/hooks/useResourcePermissionsSuperAdmin.ts +67 -0
- package/src/rbac/hooks/useRoleManagement.test.ts +634 -61
- package/src/rbac/hooks/useRoleManagement.ts +158 -586
- package/src/rbac/hooks/useSecureSupabase.test.ts +1179 -0
- package/src/rbac/hooks/useSecureSupabase.ts +21 -14
- package/src/rbac/hooks/useSuperAdminCheck.ts +80 -0
- package/src/rbac/index.test.ts +107 -0
- package/src/rbac/index.ts +32 -32
- package/src/rbac/performance.test.ts +451 -0
- package/src/rbac/permissions.test.ts +149 -68
- package/src/rbac/permissions.ts +0 -3
- package/src/rbac/rbac-core.test.tsx +276 -0
- package/src/rbac/rbac-engine-core-logic.test.ts +387 -0
- package/src/rbac/rbac-engine-simplified.test.ts +252 -0
- package/src/rbac/rbac-functions.test.ts +703 -0
- package/src/rbac/rbac-integration.test.ts +523 -0
- package/src/rbac/rbac-role-isolation.test.ts +456 -0
- package/src/rbac/request-deduplication.test.ts +352 -0
- package/src/rbac/request-deduplication.ts +5 -4
- package/src/rbac/scenarios.user-role.test.tsx +271 -0
- package/src/rbac/secureClient.test.ts +499 -115
- package/src/rbac/secureClient.ts +54 -28
- package/src/rbac/security.test.ts +448 -44
- package/src/rbac/security.ts +7 -6
- package/src/rbac/types/roleManagement.ts +66 -0
- package/src/rbac/types.test.ts +236 -0
- package/src/rbac/types.ts +7 -5
- package/src/rbac/utils/clientSecurity.test.ts +192 -0
- package/src/rbac/utils/clientSecurity.ts +6 -4
- package/src/rbac/utils/contextValidator.test.ts +126 -0
- package/src/rbac/utils/contextValidator.ts +6 -3
- package/src/rbac/utils/deep-equal.test.ts +76 -0
- package/src/rbac/utils/eventContext.test.ts +401 -0
- package/src/rbac/utils/eventContext.ts +38 -34
- package/src/rbac/utils/fetchPermissionMap.ts +13 -0
- package/src/rbac/utils/permissionMapHelpers.ts +34 -0
- package/src/rbac/utils/roleManagementRpc.ts +303 -0
- package/src/services/AuthService.edge-cases.test.ts +746 -0
- package/src/services/AuthService.restoreSession.test.ts +59 -0
- package/src/services/AuthService.test.ts +1362 -0
- package/src/services/AuthService.ts +197 -216
- package/src/services/BaseService.edge-cases.test.ts +506 -0
- package/src/services/BaseService.test.ts +363 -0
- package/src/services/EventService.edge-cases.test.ts +636 -0
- package/src/services/EventService.eventColours.test.ts +64 -0
- package/src/services/EventService.test.ts +1250 -0
- package/src/services/EventService.ts +244 -315
- package/src/services/InactivityService.edge-cases.test.ts +492 -0
- package/src/services/InactivityService.lifecycle.test.ts +406 -0
- package/src/services/InactivityService.test.ts +829 -0
- package/src/services/InactivityService.ts +172 -213
- package/src/services/OrganisationService.edge-cases.test.ts +633 -0
- package/src/services/OrganisationService.pagination.test.ts +409 -0
- package/src/services/OrganisationService.test.ts +1579 -0
- package/src/services/OrganisationService.ts +186 -257
- package/src/services/base/BaseService.test.ts +214 -0
- package/src/services/interfaces/IAuthService.test.ts +184 -0
- package/src/services/interfaces/IAuthService.ts +10 -9
- package/src/services/interfaces/IEventService.test.ts +176 -0
- package/src/services/interfaces/IInactivityService.test.ts +183 -0
- package/src/services/interfaces/IOrganisationService.test.ts +207 -0
- package/src/services/interfaces/IOrganisationService.ts +0 -1
- package/src/styles/core.css +244 -12
- package/src/theming/parseEventColours.test.ts +321 -0
- package/src/theming/parseEventColours.ts +18 -9
- package/src/theming/runtime.test.ts +495 -0
- package/src/theming/runtime.ts +72 -7
- package/src/types/api-result.ts +53 -0
- package/src/types/auth.ts +0 -1
- package/src/types/core.test.ts +397 -0
- package/src/types/database-generated.test.ts +78 -0
- package/src/types/database.generated.ts +45 -10
- package/src/types/event.ts +39 -19
- package/src/types/file-reference.test.ts +351 -0
- package/src/types/file-reference.ts +37 -12
- package/src/types/guards.test.ts +246 -0
- package/src/types/index.test.ts +265 -0
- package/src/types/index.ts +3 -0
- package/src/types/organisation.roles.test.ts +55 -0
- package/src/types/organisation.test.ts +1105 -0
- package/src/types/organisation.ts +15 -15
- package/src/types/rpc-responses.ts +33 -0
- package/src/types/supabase.ts +14 -6
- package/src/types/theme.test.ts +830 -0
- package/src/types/type-validation.test.ts +526 -0
- package/src/types/validation.test.ts +729 -0
- package/src/types/vitest-globals.d.ts +1 -1
- package/src/utils/app/appConfig.test.ts +235 -0
- package/src/utils/app/appIdResolver.test.ts +252 -57
- package/src/utils/app/appIdResolver.ts +31 -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/appConfig.unit.test.ts +55 -0
- package/src/utils/audit/audit.test.ts +354 -39
- package/src/utils/audit.unit.test.ts +69 -0
- package/src/utils/auth-utils.unit.test.ts +69 -0
- package/src/utils/bundleAnalysis.unit.test.ts +326 -0
- package/src/utils/cn.unit.test.ts +34 -0
- package/src/utils/context/organisationContext.test.ts +115 -95
- package/src/utils/context/organisationContext.ts +32 -43
- package/src/utils/context/sessionTracking.test.ts +354 -0
- package/src/utils/core/cn.test.ts +66 -0
- package/src/utils/core/debugLogger.test.ts +113 -0
- package/src/utils/core/debugLogger.ts +15 -8
- package/src/utils/core/logger.test.ts +217 -0
- package/src/utils/core/logger.ts +20 -16
- package/src/utils/core/mergeRefs.ts +24 -0
- package/src/utils/debugLogger.test.ts +417 -0
- package/src/utils/device/deviceFingerprint.test.ts +8 -5
- package/src/utils/device/deviceFingerprint.ts +3 -3
- package/src/utils/deviceFingerprint.unit.test.ts +818 -0
- package/src/utils/dynamic/createLazyComponent.tsx +46 -0
- package/src/utils/dynamic/dynamicUtils.test.ts +185 -0
- package/src/utils/dynamic/dynamicUtils.ts +6 -6
- package/src/utils/dynamic/lazyLoad.test.tsx +156 -0
- package/src/utils/dynamic/lazyLoad.tsx +8 -36
- package/src/utils/dynamic/papaparseLoader.ts +7 -0
- package/src/utils/dynamicUtils.unit.test.ts +331 -0
- package/src/utils/file-reference/file-reference.test.ts +1238 -0
- package/src/utils/file-reference/index.ts +330 -348
- package/src/utils/formatDate.unit.test.ts +109 -0
- 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 +41 -85
- 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/formatting.unit.test.ts +99 -0
- package/src/utils/google-places/googlePlacesUtils.test.ts +127 -36
- package/src/utils/google-places/googlePlacesUtils.ts +67 -86
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +68 -8
- package/src/utils/google-places/loadGoogleMapsScript.ts +140 -118
- package/src/utils/index.ts +52 -11
- package/src/utils/index.unit.test.ts +251 -0
- package/src/utils/lazyLoad.unit.test.tsx +319 -0
- package/src/utils/location/location.test.ts +19 -116
- package/src/utils/logger.unit.test.ts +398 -0
- package/src/utils/organisationContext.unit.test.ts +180 -0
- package/src/utils/performance/bundleAnalysis.test.ts +148 -0
- package/src/utils/performance/bundleAnalysis.ts +16 -22
- package/src/utils/performance/performanceBenchmark.test.ts +251 -0
- package/src/utils/performance/performanceBenchmark.ts +12 -4
- package/src/utils/performance/performanceBudgets.test.ts +241 -0
- package/src/utils/performance/performanceBudgets.ts +9 -6
- package/src/utils/performanceBenchmark.test.ts +174 -0
- package/src/utils/performanceBudgets.unit.test.ts +288 -0
- package/src/utils/permissionTypes.unit.test.ts +250 -0
- package/src/utils/permissionUtils.unit.test.ts +362 -0
- package/src/utils/permissions/permissionTypes.test.ts +149 -0
- package/src/utils/permissions/permissionUtils.test.ts +20 -42
- package/src/utils/persistence/keyDerivation.test.ts +306 -0
- package/src/utils/persistence/sensitiveFieldDetection.test.ts +271 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
- package/src/utils/request-deduplication.test.ts +349 -0
- package/src/utils/request-deduplication.ts +6 -4
- package/src/utils/sanitization.unit.test.ts +346 -0
- package/src/utils/schemaUtils.unit.test.ts +441 -0
- package/src/utils/secureDataAccess.unit.test.ts +334 -0
- package/src/utils/secureErrors.unit.test.ts +390 -0
- package/src/utils/secureStorage.unit.test.ts +289 -0
- package/src/utils/security/auth-utils.ts +38 -27
- package/src/utils/security/secureDataAccess.test.ts +22 -191
- package/src/utils/security/secureDataAccess.ts +241 -281
- 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 +212 -0
- package/src/utils/security/security.ts +15 -18
- package/src/utils/security/securityMonitor.test.ts +90 -0
- package/src/utils/security/securityMonitor.ts +1 -1
- package/src/utils/security.unit.test.ts +155 -0
- package/src/utils/securityMonitor.unit.test.ts +276 -0
- package/src/utils/sessionTracking.unit.test.ts +218 -0
- package/src/utils/storage/config.unit.test.ts +239 -0
- package/src/utils/storage/helpers.test.ts +769 -456
- package/src/utils/storage/helpers.ts +174 -253
- package/src/utils/storage/index.unit.test.ts +68 -0
- package/src/utils/storage/storageUtils.ts +32 -0
- package/src/utils/storage/types.ts +9 -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 +26 -44
- package/src/utils/timezone.test.ts +345 -0
- package/src/utils/validation/common.test.ts +115 -0
- package/src/utils/validation/csrf.test.ts +198 -0
- package/src/utils/validation/csrf.ts +42 -41
- package/src/utils/validation/htmlSanitization.ts +27 -31
- package/src/utils/validation/htmlSanitization.unit.test.ts +618 -0
- package/src/utils/validation/passwordSchema.test.ts +164 -0
- package/src/utils/validation/schema.test.ts +127 -0
- package/src/utils/validation/schema.ts +6 -3
- package/src/utils/validation/sqlInjectionProtection.test.ts +165 -0
- package/src/utils/validation/sqlInjectionProtection.ts +2 -2
- package/src/utils/validation/user.test.ts +173 -0
- package/src/utils/validation/validation.test.ts +197 -0
- package/src/utils/validation/validationUtils.test.ts +294 -0
- package/src/utils/validation.unit.test.ts +307 -0
- package/src/utils/validationUtils.unit.test.ts +558 -0
- package/src/vite-env.d.ts +6 -0
- package/dist/AuthService-DmfO5rGS.d.ts +0 -524
- package/dist/DataTable-DRUIgtUH.d.ts +0 -166
- package/dist/DataTable-SOAFXIWY.js +0 -15
- package/dist/PublicPageProvider-CIGSujI2.d.ts +0 -4147
- package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
- package/dist/UnifiedAuthProvider-CKvHP1MK.d.ts +0 -139
- package/dist/api-7P7DI652.js +0 -4
- package/dist/audit-MYQXYZFU.js +0 -3
- package/dist/auth-BZOJqrdd.d.ts +0 -49
- package/dist/chunk-4DDCYDQ3.js +0 -544
- package/dist/chunk-5HNSDQWH.js +0 -5046
- package/dist/chunk-5W2A3DRC.js +0 -164
- package/dist/chunk-6GLLNA6U.js +0 -31
- package/dist/chunk-7ILTDCL2.js +0 -80
- package/dist/chunk-A3W6LW53.js +0 -70
- package/dist/chunk-AHU7G2R5.js +0 -423
- package/dist/chunk-C7ZQ5O4C.js +0 -481
- package/dist/chunk-EF2UGZWY.js +0 -611
- package/dist/chunk-FEJLJNWA.js +0 -181
- package/dist/chunk-FYHN4DD5.js +0 -415
- package/dist/chunk-GS5672WG.js +0 -2003
- package/dist/chunk-HF6O3O37.js +0 -187
- package/dist/chunk-J2U36LHD.js +0 -8517
- package/dist/chunk-LX6U42O3.js +0 -2177
- package/dist/chunk-MPBLMWVR.js +0 -2161
- package/dist/chunk-OJ4SKRSV.js +0 -105
- package/dist/chunk-S6ZQKDY6.js +0 -62
- package/dist/chunk-S7DKJPLT.js +0 -699
- package/dist/chunk-T5CVK4R3.js +0 -2816
- package/dist/chunk-TTRFSOKR.js +0 -121
- package/dist/chunk-Z2FNRKF3.js +0 -994
- package/dist/database.generated-DT8JTZiP.d.ts +0 -9406
- package/dist/event-CW5YB_2p.d.ts +0 -239
- package/dist/file-reference-BavO2eQj.d.ts +0 -148
- package/dist/functions-lBy5L2ry.d.ts +0 -208
- package/dist/timezone-0AyangqX.d.ts +0 -697
- package/dist/types-BeoeWV5I.d.ts +0 -110
- package/dist/types-DXstZpNI.d.ts +0 -614
- package/dist/types-t9H8qKRw.d.ts +0 -55
- package/dist/usePublicRouteParams-DQLrDqDb.d.ts +0 -876
- package/dist/useToast-AyaT-x7p.d.ts +0 -68
- package/dist/validation-643vUDZW.d.ts +0 -177
- package/scripts/build-docs-incremental.js +0 -179
- package/scripts/eslint-audit.cjs +0 -123
- package/scripts/generate-docs.js +0 -157
- package/scripts/install-cursor-rules.cjs +0 -255
- package/scripts/install-eslint-config.cjs +0 -349
- package/scripts/setup-build-cache.js +0 -73
- package/scripts/validate-pre-publish.js +0 -145
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +0 -260
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +0 -224
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +0 -273
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +0 -99
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -448
- package/src/__tests__/helpers/__tests__/timer-utils.test.ts +0 -371
- package/src/__tests__/hooks/usePermissions.test.ts +0 -268
- package/src/__tests__/integration/UserProfile.test.tsx +0 -124
- package/src/__tests__/public-recipe-view.test.ts +0 -228
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +0 -220
- package/src/__tests__/rls-policies.test.ts +0 -471
- package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +0 -759
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +0 -524
- package/src/components/DataTable/__tests__/DataTable.export.test.tsx +0 -705
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +0 -658
- package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +0 -192
- package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +0 -483
- package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -876
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +0 -220
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +0 -1474
- package/src/components/DataTable/__tests__/README.md +0 -145
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +0 -788
- package/src/components/DataTable/__tests__/keyboard.test.tsx +0 -756
- package/src/components/DataTable/__tests__/mocks/MockRBACProvider.tsx +0 -66
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +0 -730
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -325
- package/src/components/DataTable/__tests__/styles.test.ts +0 -382
- package/src/components/DataTable/__tests__/test-utils/dataFactories.ts +0 -103
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +0 -380
- package/src/components/DataTable/__tests__/test-utils.ts +0 -94
- package/src/components/DataTable/components/AccessDeniedPage.tsx +0 -159
- package/src/components/DataTable/components/ActionButtons.tsx +0 -190
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +0 -160
- package/src/components/DataTable/components/ColumnFilter.tsx +0 -118
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +0 -114
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +0 -225
- package/src/components/DataTable/components/DataTableLayout.tsx +0 -573
- package/src/components/DataTable/components/DataTableModals.tsx +0 -245
- package/src/components/DataTable/components/DataTableToolbar.tsx +0 -271
- package/src/components/DataTable/components/EditFields.tsx +0 -327
- package/src/components/DataTable/components/EditableRow.tsx +0 -462
- package/src/components/DataTable/components/EmptyState.tsx +0 -79
- package/src/components/DataTable/components/FilterRow.tsx +0 -141
- package/src/components/DataTable/components/LoadingState.tsx +0 -17
- package/src/components/DataTable/components/PaginationControls.tsx +0 -289
- package/src/components/DataTable/components/RowComponent.tsx +0 -403
- package/src/components/DataTable/components/SortIndicator.tsx +0 -50
- package/src/components/DataTable/components/UnifiedTableBody.tsx +0 -355
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +0 -657
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +0 -913
- package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +0 -572
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +0 -612
- package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +0 -708
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +0 -479
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +0 -475
- package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +0 -157
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +0 -1061
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +0 -437
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +0 -474
- package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +0 -617
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +0 -1093
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +0 -139
- package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +0 -519
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +0 -1004
- package/src/components/DataTable/components/cellValueUtils.ts +0 -40
- package/src/components/DataTable/components/hooks/useImportModalFocus.ts +0 -53
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -122
- package/src/components/DataTable/components/index.ts +0 -16
- package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +0 -342
- package/src/components/DataTable/core/ActionManager.ts +0 -235
- package/src/components/DataTable/core/ColumnManager.ts +0 -205
- package/src/components/DataTable/core/DataManager.ts +0 -188
- package/src/components/DataTable/core/LocalDataAdapter.ts +0 -274
- package/src/components/DataTable/core/PluginRegistry.ts +0 -229
- package/src/components/DataTable/core/StateManager.ts +0 -312
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -123
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +0 -305
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -84
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -115
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +0 -100
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +0 -120
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -104
- package/src/components/DataTable/core/index.ts +0 -1
- package/src/components/DataTable/core/interfaces.ts +0 -338
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +0 -521
- package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +0 -167
- package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +0 -124
- package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +0 -117
- package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +0 -102
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +0 -596
- package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +0 -53
- package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +0 -214
- package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +0 -448
- package/src/components/DataTable/hooks/index.ts +0 -13
- package/src/components/DataTable/types.ts +0 -761
- package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +0 -612
- package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +0 -94
- package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +0 -266
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +0 -954
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +0 -573
- package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +0 -247
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +0 -570
- package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +0 -470
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +0 -251
- package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +0 -207
- package/src/components/DataTable/utils/index.ts +0 -10
- package/src/components/PublicLayout/index.ts +0 -32
- package/src/components/Select/hooks/useSelectEvents.ts +0 -87
- package/src/components/Select/hooks/useSelectSearch.ts +0 -91
- package/src/components/Select/hooks/useSelectState.ts +0 -104
- package/src/components/Select/utils/text.ts +0 -26
- package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -615
- package/src/hooks/__tests__/hooks.integration.test.tsx +0 -607
- package/src/hooks/__tests__/index.unit.test.ts +0 -220
- package/src/hooks/__tests__/useApiFetch.unit.test.ts +0 -111
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +0 -347
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +0 -144
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +0 -776
- package/src/hooks/__tests__/useDataTableState.test.ts +0 -76
- package/src/hooks/__tests__/useDebounce.unit.test.ts +0 -82
- package/src/hooks/__tests__/useEvents.unit.test.ts +0 -252
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +0 -1112
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +0 -916
- package/src/hooks/__tests__/useFileUrlCache.test.ts +0 -129
- package/src/hooks/__tests__/useFocusManagement.unit.test.ts +0 -230
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +0 -828
- package/src/hooks/__tests__/useFormDialog.test.ts +0 -478
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +0 -446
- package/src/hooks/__tests__/useIsMobile.unit.test.ts +0 -317
- package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +0 -910
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -294
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +0 -961
- package/src/hooks/__tests__/useOrganisations.unit.test.ts +0 -369
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +0 -694
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
- package/src/hooks/__tests__/usePreventTabReload.test.ts +0 -88
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -785
- package/src/hooks/__tests__/usePublicEvent.test.ts +0 -678
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -630
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +0 -951
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +0 -443
- package/src/hooks/__tests__/useQueryCache.test.ts +0 -144
- package/src/hooks/__tests__/useRBAC.unit.test.ts +0 -236
- package/src/hooks/__tests__/useSessionDraft.test.ts +0 -163
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +0 -390
- package/src/hooks/__tests__/useStorage.unit.test.ts +0 -751
- package/src/hooks/__tests__/useToast.unit.test.tsx +0 -481
- package/src/hooks/__tests__/useZodForm.unit.test.tsx +0 -37
- package/src/hooks/public/index.ts +0 -36
- package/src/hooks/public/usePublicFileDisplay.ts +0 -504
- package/src/hooks/useFileDisplay.ts +0 -715
- package/src/providers/OrganisationProvider.tsx +0 -92
- package/src/providers/__tests__/AuthProvider.test.tsx +0 -287
- package/src/providers/__tests__/EventProvider.test.tsx +0 -551
- package/src/providers/__tests__/InactivityProvider.test-helper.tsx +0 -65
- package/src/providers/__tests__/InactivityProvider.test.tsx +0 -572
- package/src/providers/__tests__/OrganisationProvider.test.tsx +0 -617
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +0 -424
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +0 -596
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +0 -263
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +0 -294
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +0 -434
- package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +0 -313
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +0 -486
- package/src/rbac/__tests__/cache-invalidation.test.ts +0 -399
- package/src/rbac/__tests__/engine.comprehensive.test.ts +0 -813
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
- package/src/rbac/__tests__/rbac-core.test.tsx +0 -276
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -392
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +0 -258
- package/src/rbac/__tests__/rbac-functions.test.ts +0 -647
- package/src/rbac/__tests__/rbac-integration.test.ts +0 -524
- package/src/rbac/__tests__/rbac-role-isolation.test.ts +0 -456
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +0 -282
- 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/__tests__/NavigationGuard.test.tsx +0 -975
- package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +0 -248
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +0 -242
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +0 -1107
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +0 -184
- package/src/rbac/components/index.ts +0 -26
- package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -432
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +0 -579
- 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__/contextValidator.test.ts +0 -128
- package/src/rbac/utils/__tests__/deep-equal.test.ts +0 -53
- package/src/rbac/utils/__tests__/eventContext.test.ts +0 -433
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
- package/src/services/__tests__/AuthService.restoreSession.test.ts +0 -39
- package/src/services/__tests__/AuthService.test.ts +0 -1332
- package/src/services/__tests__/BaseService.test.ts +0 -314
- package/src/services/__tests__/EventService.eventColours.test.ts +0 -76
- package/src/services/__tests__/EventService.test.ts +0 -1025
- package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -411
- package/src/services/__tests__/InactivityService.test.ts +0 -654
- package/src/services/__tests__/OrganisationService.pagination.test.ts +0 -409
- package/src/services/__tests__/OrganisationService.test.ts +0 -1176
- package/src/theming/__tests__/parseEventColours.test.ts +0 -321
- package/src/theming/__tests__/runtime.test.ts +0 -569
- package/src/types/__tests__/file-reference.test.ts +0 -447
- package/src/types/__tests__/guards.test.ts +0 -246
- package/src/types/__tests__/organisation.roles.test.ts +0 -55
- package/src/types/__tests__/organisation.test.ts +0 -1133
- package/src/types/__tests__/theme.test.ts +0 -830
- package/src/types/__tests__/type-validation.test.ts +0 -526
- package/src/types/__tests__/validation.test.ts +0 -731
- package/src/utils/__tests__/appConfig.unit.test.ts +0 -55
- package/src/utils/__tests__/audit.unit.test.ts +0 -69
- package/src/utils/__tests__/auth-utils.unit.test.ts +0 -70
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +0 -339
- package/src/utils/__tests__/cn.unit.test.ts +0 -34
- package/src/utils/__tests__/debugLogger.test.ts +0 -417
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +0 -818
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +0 -318
- package/src/utils/__tests__/formatDate.unit.test.ts +0 -109
- package/src/utils/__tests__/formatting.unit.test.ts +0 -99
- package/src/utils/__tests__/index.unit.test.ts +0 -251
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -321
- package/src/utils/__tests__/logger.unit.test.ts +0 -398
- package/src/utils/__tests__/organisationContext.unit.test.ts +0 -191
- package/src/utils/__tests__/performanceBenchmark.test.ts +0 -175
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +0 -253
- package/src/utils/__tests__/permissionTypes.unit.test.ts +0 -250
- package/src/utils/__tests__/permissionUtils.unit.test.ts +0 -362
- package/src/utils/__tests__/sanitization.unit.test.ts +0 -346
- package/src/utils/__tests__/schemaUtils.unit.test.ts +0 -441
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -335
- package/src/utils/__tests__/secureErrors.unit.test.ts +0 -390
- package/src/utils/__tests__/secureStorage.unit.test.ts +0 -289
- package/src/utils/__tests__/security.unit.test.ts +0 -149
- package/src/utils/__tests__/securityMonitor.unit.test.ts +0 -276
- package/src/utils/__tests__/sessionTracking.unit.test.ts +0 -218
- package/src/utils/__tests__/timezone.test.ts +0 -345
- package/src/utils/__tests__/validation.unit.test.ts +0 -308
- package/src/utils/__tests__/validationUtils.unit.test.ts +0 -555
- package/src/utils/app/appNameResolver.simple.test.ts +0 -212
- package/src/utils/file-reference/__tests__/file-reference.test.ts +0 -875
- package/src/utils/google-places/index.ts +0 -26
- package/src/utils/location/index.ts +0 -16
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +0 -135
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +0 -123
- package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
- package/src/utils/storage/__tests__/index.unit.test.ts +0 -16
- package/src/utils/storage/index.ts +0 -67
- package/src/utils/timezone/index.ts +0 -17
- package/src/utils/validation/__tests__/csrf.test.ts +0 -105
- package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +0 -598
- package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +0 -92
- package/src/utils/validation/__tests__/validationUtils.test.ts +0 -72
- package/src/utils/validation/index.ts +0 -73
- /package/src/components/DataTable/{components/__tests__ → ui}/COVERAGE_NOTE.md +0 -0
- /package/src/components/DataTable/utils/{__tests__/COVERAGE_NOTE.md → COVERAGE_NOTE.md} +0 -0
- /package/src/providers/{__tests__/README.md → README.md} +0 -0
- /package/src/types/{__tests__/README.md → README.md} +0 -0
|
@@ -8,17 +8,57 @@ import {
|
|
|
8
8
|
FileReferenceService,
|
|
9
9
|
FileUploadResult,
|
|
10
10
|
FileCategory,
|
|
11
|
-
FileMetadata
|
|
11
|
+
FileMetadata,
|
|
12
|
+
BulkUploadResult
|
|
12
13
|
} from '../../types/file-reference';
|
|
14
|
+
import type { Database } from '../../types/database';
|
|
15
|
+
import { ok, err, type ApiResult, type ApiError } from '../../types/api-result';
|
|
13
16
|
import { uploadFile, getPublicUrl, getSignedUrl, deleteFile, extractFileMetadata } from '../storage/helpers';
|
|
14
17
|
import { setOrganisationContext } from '../context/organisationContext';
|
|
15
|
-
import { invalidateFileDisplayCache } from '../../
|
|
18
|
+
import { invalidateFileDisplayCache } from '../../components/FileDisplay/useFileDisplay';
|
|
16
19
|
import { createLogger } from '../core/logger';
|
|
17
20
|
import { assertAppId } from '../../types/core';
|
|
18
|
-
import { isSuperAdmin } from '../../rbac/api';
|
|
19
21
|
|
|
20
22
|
const log = createLogger('FileReferenceService');
|
|
21
23
|
|
|
24
|
+
function toApiError(error: unknown): ApiError {
|
|
25
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
26
|
+
const code = error instanceof Error && (error as Error & { code?: string }).code
|
|
27
|
+
? (error as Error & { code: string }).code
|
|
28
|
+
: 'FILE_REFERENCE_ERROR';
|
|
29
|
+
return { code, message };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Row type from core_file_references table */
|
|
33
|
+
export type CoreFileReferencesRow = Database['public']['Tables']['core_file_references']['Row'];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Maps a core_file_references DB row to FileReference.
|
|
37
|
+
* Used by useFileDisplay and fetchFileDisplayData when reading from direct queries.
|
|
38
|
+
*/
|
|
39
|
+
export function mapFileReferenceRowToFileReference(f: CoreFileReferencesRow): FileReference {
|
|
40
|
+
const fileName = f.file_path.split('/').pop() || 'unknown';
|
|
41
|
+
const fileType = fileName.split('.').pop() || 'unknown';
|
|
42
|
+
const metadata = f.file_metadata as { fileName?: string; fileType?: string; category?: FileCategory; [key: string]: unknown } | null;
|
|
43
|
+
return {
|
|
44
|
+
id: f.id,
|
|
45
|
+
table_name: f.table_name,
|
|
46
|
+
record_id: f.record_id,
|
|
47
|
+
file_path: f.file_path,
|
|
48
|
+
file_metadata: {
|
|
49
|
+
fileName: metadata?.fileName || fileName,
|
|
50
|
+
fileType: metadata?.fileType || fileType,
|
|
51
|
+
category: metadata?.category || FileCategory.GENERAL_DOCUMENTS,
|
|
52
|
+
...(metadata || {})
|
|
53
|
+
} as FileReference['file_metadata'],
|
|
54
|
+
organisation_id: f.organisation_id,
|
|
55
|
+
app_id: f.app_id as FileReference['app_id'],
|
|
56
|
+
is_public: f.is_public ?? false,
|
|
57
|
+
created_at: f.created_at || new Date().toISOString(),
|
|
58
|
+
updated_at: f.updated_at || new Date().toISOString()
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
22
62
|
export class FileReferenceServiceImpl implements FileReferenceService {
|
|
23
63
|
constructor(private supabase: SupabaseClient) {}
|
|
24
64
|
|
|
@@ -32,17 +72,163 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
32
72
|
.select('name')
|
|
33
73
|
.eq('id', appId)
|
|
34
74
|
.single();
|
|
35
|
-
|
|
75
|
+
|
|
36
76
|
if (error || !data) {
|
|
37
77
|
return 'unknown';
|
|
38
78
|
}
|
|
39
|
-
|
|
79
|
+
|
|
40
80
|
return data.name || 'unknown';
|
|
41
81
|
} catch {
|
|
42
82
|
return 'unknown';
|
|
43
83
|
}
|
|
44
84
|
}
|
|
45
85
|
|
|
86
|
+
private validateCreateOptions(options: FileUploadOptions): ApiResult<{ isUserScoped: boolean }> {
|
|
87
|
+
const isUserScoped = !options.organisation_id && !!options.userId;
|
|
88
|
+
if (!isUserScoped && !options.organisation_id) {
|
|
89
|
+
return err({ code: 'VALIDATION_ERROR', message: 'organisation_id is required for file upload, or userId must be provided for user-scoped files' });
|
|
90
|
+
}
|
|
91
|
+
if (!options.table_name) {
|
|
92
|
+
return err({ code: 'VALIDATION_ERROR', message: 'table_name is required for file upload' });
|
|
93
|
+
}
|
|
94
|
+
if (!options.record_id) {
|
|
95
|
+
return err({ code: 'VALIDATION_ERROR', message: 'record_id is required for file upload' });
|
|
96
|
+
}
|
|
97
|
+
if (!options.folder) {
|
|
98
|
+
return err({ code: 'VALIDATION_ERROR', message: 'folder is required for file upload. The folder prop determines the storage path.' });
|
|
99
|
+
}
|
|
100
|
+
return ok({ isUserScoped });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private async resolveAuthenticatedUserIdForUserScoped(): Promise<ApiResult<string>> {
|
|
104
|
+
const { data: { user: authUser }, error: authError } = await this.supabase.auth.getUser();
|
|
105
|
+
if (authError || !authUser) {
|
|
106
|
+
return err({ code: 'UNAUTHORIZED', message: 'User must be authenticated to upload user-scoped files' });
|
|
107
|
+
}
|
|
108
|
+
log.debug('Using authenticated user ID for user-scoped file upload', { userId: authUser.id });
|
|
109
|
+
return ok(authUser.id);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private async checkSuperAdminUser(userId: string | undefined): Promise<boolean> {
|
|
113
|
+
if (!userId) return false;
|
|
114
|
+
try {
|
|
115
|
+
const { isSuperAdmin } = await import('../../rbac/api');
|
|
116
|
+
const superResult = await isSuperAdmin(userId);
|
|
117
|
+
if (superResult.ok && superResult.data) {
|
|
118
|
+
log.debug('Super admin detected - bypassing permission checks', { userId });
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
} catch (superAdminCheckError) {
|
|
123
|
+
log.warn('Failed to check super-admin status, proceeding with normal permission checks', superAdminCheckError);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private async uploadFileAndExtractMetadata(
|
|
129
|
+
options: FileUploadOptions,
|
|
130
|
+
file: File,
|
|
131
|
+
authenticatedUserId: string | undefined,
|
|
132
|
+
isUserScoped: boolean
|
|
133
|
+
): Promise<ApiResult<{ path: string; metadata: Record<string, unknown> }>> {
|
|
134
|
+
const userId = authenticatedUserId || (isUserScoped ? undefined : options.userId);
|
|
135
|
+
const uploadResult = await uploadFile(this.supabase, file, {
|
|
136
|
+
appName: 'file-reference',
|
|
137
|
+
orgId: options.organisation_id || undefined,
|
|
138
|
+
userId,
|
|
139
|
+
isPublic: options.is_public || false,
|
|
140
|
+
customPath: options.folder
|
|
141
|
+
});
|
|
142
|
+
if (!uploadResult.ok) return err(uploadResult.error);
|
|
143
|
+
const metadataResult = await extractFileMetadata(file, {
|
|
144
|
+
appName: 'file-reference',
|
|
145
|
+
orgId: options.organisation_id || undefined,
|
|
146
|
+
userId,
|
|
147
|
+
isPublic: options.is_public || false
|
|
148
|
+
}, 'system');
|
|
149
|
+
if (!metadataResult.ok) return err(metadataResult.error);
|
|
150
|
+
return ok({ path: uploadResult.data.path, metadata: metadataResult.data as unknown as Record<string, unknown> });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private async setOrgContextIfNeeded(isUserScoped: boolean, organisation_id: string | undefined): Promise<void> {
|
|
154
|
+
if (isUserScoped || !organisation_id) return;
|
|
155
|
+
const ctxResult = await setOrganisationContext(this.supabase, organisation_id);
|
|
156
|
+
if (!ctxResult.ok) {
|
|
157
|
+
log.warn('setOrganisationContext failed (non-fatal):', ctxResult.error.message);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private async resolveRpcUserId(
|
|
162
|
+
authenticatedUserId: string | undefined,
|
|
163
|
+
options: FileUploadOptions
|
|
164
|
+
): Promise<string | null> {
|
|
165
|
+
if (authenticatedUserId) return authenticatedUserId;
|
|
166
|
+
if (options.userId) return options.userId;
|
|
167
|
+
const { data: { user: authUser } } = await this.supabase.auth.getUser();
|
|
168
|
+
return authUser?.id ?? null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private async buildPermissionDeniedMessage(
|
|
172
|
+
options: FileUploadOptions,
|
|
173
|
+
isSuperAdminUser: boolean
|
|
174
|
+
): Promise<string> {
|
|
175
|
+
const appName = await this.getAppName(options.app_id).catch(() => 'unknown');
|
|
176
|
+
const pageContextDisplay = options.pageContext || 'undefined';
|
|
177
|
+
return isSuperAdminUser
|
|
178
|
+
? `File upload failed for super-admin user. Page context: '${pageContextDisplay}', App: '${appName}'.`
|
|
179
|
+
: `File upload denied: insufficient permissions. You need 'create:page.${pageContextDisplay}' or 'update:page.${pageContextDisplay}' for the '${pageContextDisplay}' page.`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private async rollbackUploadAndErr(
|
|
183
|
+
filePath: string,
|
|
184
|
+
isPublic: boolean,
|
|
185
|
+
code: string,
|
|
186
|
+
message: string
|
|
187
|
+
): Promise<ApiResult<FileReference>> {
|
|
188
|
+
await deleteFile(this.supabase, filePath, isPublic);
|
|
189
|
+
return err({ code, message });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private async fetchCreatedFileReference(createdId: string): Promise<ApiResult<FileReference>> {
|
|
193
|
+
const { data: fileRef, error: fetchError } = await this.supabase
|
|
194
|
+
.from('core_file_references')
|
|
195
|
+
.select('id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at')
|
|
196
|
+
.eq('id', createdId)
|
|
197
|
+
.single();
|
|
198
|
+
if (fetchError || !fileRef) {
|
|
199
|
+
return err({ code: 'FETCH_FAILED', message: fetchError?.message ?? 'Failed to fetch created file reference' });
|
|
200
|
+
}
|
|
201
|
+
return ok(fileRef as FileReference);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private buildCreateRpcPayload(
|
|
205
|
+
options: FileUploadOptions,
|
|
206
|
+
filePath: string,
|
|
207
|
+
file: File,
|
|
208
|
+
metadata: Record<string, unknown>,
|
|
209
|
+
rpcUserId: string | null
|
|
210
|
+
): Record<string, unknown> {
|
|
211
|
+
return {
|
|
212
|
+
p_table_name: options.table_name,
|
|
213
|
+
p_record_id: options.record_id,
|
|
214
|
+
p_file_path: filePath,
|
|
215
|
+
p_organisation_id: options.organisation_id ?? null,
|
|
216
|
+
p_app_id: options.app_id,
|
|
217
|
+
p_page_context: options.pageContext,
|
|
218
|
+
p_event_id: options.event_id || null,
|
|
219
|
+
p_file_metadata: {
|
|
220
|
+
fileName: file.name,
|
|
221
|
+
fileType: file.type,
|
|
222
|
+
fileSize: file.size,
|
|
223
|
+
category: options.category,
|
|
224
|
+
...metadata,
|
|
225
|
+
...options.custom_metadata
|
|
226
|
+
},
|
|
227
|
+
p_is_public: options.is_public || false,
|
|
228
|
+
p_user_id: rpcUserId
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
46
232
|
/**
|
|
47
233
|
* Creates a file reference by uploading a file to storage and linking it in the database.
|
|
48
234
|
*
|
|
@@ -57,175 +243,50 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
57
243
|
*
|
|
58
244
|
* This ensures atomicity: either both storage and DB succeed, or both are cleaned up.
|
|
59
245
|
*/
|
|
60
|
-
async createFileReference(options: FileUploadOptions, file: File): Promise<FileReference
|
|
246
|
+
async createFileReference(options: FileUploadOptions, file: File): Promise<ApiResult<FileReference>> {
|
|
61
247
|
try {
|
|
248
|
+
const validation = this.validateCreateOptions(options);
|
|
249
|
+
if (!validation.ok) return validation;
|
|
250
|
+
const { isUserScoped } = validation.data;
|
|
62
251
|
|
|
63
|
-
|
|
64
|
-
const isUserScoped = !options.organisation_id && options.userId;
|
|
65
|
-
if (!isUserScoped && !options.organisation_id) {
|
|
66
|
-
throw new Error('organisation_id is required for file upload, or userId must be provided for user-scoped files');
|
|
67
|
-
}
|
|
68
|
-
if (!options.table_name) {
|
|
69
|
-
throw new Error('table_name is required for file upload');
|
|
70
|
-
}
|
|
71
|
-
if (!options.record_id) {
|
|
72
|
-
throw new Error('record_id is required for file upload');
|
|
73
|
-
}
|
|
74
|
-
if (!options.folder) {
|
|
75
|
-
throw new Error('folder is required for file upload. The folder prop determines the storage path.');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// For user-scoped files, we MUST use auth.uid() for the path to match RLS policies
|
|
79
|
-
// Get the authenticated user ID from the Supabase session
|
|
80
|
-
let authenticatedUserId: string | undefined = undefined;
|
|
252
|
+
let authenticatedUserId: string | undefined;
|
|
81
253
|
if (isUserScoped) {
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
authenticatedUserId = authUser.id;
|
|
87
|
-
log.debug('Using authenticated user ID for user-scoped file upload', { userId: authenticatedUserId });
|
|
254
|
+
const authResult = await this.resolveAuthenticatedUserIdForUserScoped();
|
|
255
|
+
if (!authResult.ok) return authResult;
|
|
256
|
+
authenticatedUserId = authResult.data;
|
|
88
257
|
}
|
|
89
258
|
|
|
90
|
-
|
|
91
|
-
// Super admins bypass all permission checks - this is handled in the application layer,
|
|
92
|
-
// not in RLS policies. The RPC function still validates input and handles the insert,
|
|
93
|
-
// but permission checks are bypassed for super admins.
|
|
94
|
-
let isSuperAdminUser = false;
|
|
95
|
-
const userIdForCheck = authenticatedUserId || options.userId;
|
|
96
|
-
if (userIdForCheck) {
|
|
97
|
-
try {
|
|
98
|
-
// Import isSuperAdmin from rbac/api - this is the standard way to check super admin
|
|
99
|
-
const { isSuperAdmin } = await import('../../rbac/api');
|
|
100
|
-
isSuperAdminUser = await isSuperAdmin(userIdForCheck);
|
|
101
|
-
if (isSuperAdminUser) {
|
|
102
|
-
log.debug('Super admin detected - bypassing permission checks', { userId: userIdForCheck });
|
|
103
|
-
}
|
|
104
|
-
} catch (superAdminCheckError) {
|
|
105
|
-
// If super admin check fails, continue with normal permission flow
|
|
106
|
-
log.warn('Failed to check super-admin status, proceeding with normal permission checks', superAdminCheckError);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
259
|
+
const isSuperAdminUser = await this.checkSuperAdminUser(authenticatedUserId || options.userId);
|
|
109
260
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const uploadResult = await uploadFile(this.supabase, file, {
|
|
114
|
-
appName: 'file-reference',
|
|
115
|
-
orgId: options.organisation_id || undefined,
|
|
116
|
-
userId: authenticatedUserId || (isUserScoped ? undefined : options.userId), // Use auth.uid() for user-scoped files
|
|
117
|
-
isPublic: options.is_public || false,
|
|
118
|
-
customPath: options.folder // Use folder prop as the custom path segment
|
|
119
|
-
});
|
|
120
|
-
if (!uploadResult.success) {
|
|
121
|
-
throw new Error(`Failed to upload file: ${uploadResult.error}`);
|
|
122
|
-
}
|
|
261
|
+
const uploadResult = await this.uploadFileAndExtractMetadata(options, file, authenticatedUserId, isUserScoped);
|
|
262
|
+
if (!uploadResult.ok) return uploadResult;
|
|
263
|
+
const { path: filePath, metadata } = uploadResult.data;
|
|
123
264
|
|
|
124
|
-
|
|
125
|
-
throw new Error('File upload did not return a path');
|
|
126
|
-
}
|
|
265
|
+
await this.setOrgContextIfNeeded(isUserScoped, options.organisation_id ?? undefined);
|
|
127
266
|
|
|
128
|
-
const
|
|
267
|
+
const rpcUserId = await this.resolveRpcUserId(authenticatedUserId, options);
|
|
129
268
|
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
appName: 'file-reference',
|
|
133
|
-
orgId: options.organisation_id || undefined,
|
|
134
|
-
userId: authenticatedUserId || (isUserScoped ? undefined : options.userId), // Use auth.uid() for user-scoped files
|
|
135
|
-
isPublic: options.is_public || false
|
|
136
|
-
}, 'system');
|
|
137
|
-
|
|
138
|
-
// Step 3: Set organisation context in database session before creating file reference
|
|
139
|
-
// Skip for user-scoped files (no org context needed)
|
|
140
|
-
if (!isUserScoped && options.organisation_id) {
|
|
141
|
-
await setOrganisationContext(this.supabase, options.organisation_id);
|
|
142
|
-
}
|
|
269
|
+
const payload = this.buildCreateRpcPayload(options, filePath, file, metadata, rpcUserId);
|
|
270
|
+
const { data, error } = await this.supabase.rpc('data_file_reference_create', payload as Parameters<SupabaseClient['rpc']>[1] & object);
|
|
143
271
|
|
|
144
|
-
// Step 4: Create file reference in database using RPC function
|
|
145
|
-
// This links the storage path to the record in core_file_references table
|
|
146
|
-
// CRITICAL: Always pass the authenticated user ID to SECURITY DEFINER functions
|
|
147
|
-
// In SECURITY DEFINER functions, auth.uid() returns the function owner's ID,
|
|
148
|
-
// not the caller's ID, so we must explicitly pass the user ID
|
|
149
|
-
let rpcUserId: string | null = null;
|
|
150
|
-
if (authenticatedUserId) {
|
|
151
|
-
rpcUserId = authenticatedUserId;
|
|
152
|
-
} else if (options.userId) {
|
|
153
|
-
rpcUserId = options.userId;
|
|
154
|
-
} else {
|
|
155
|
-
// Get authenticated user ID from session as fallback
|
|
156
|
-
const { data: { user: authUser } } = await this.supabase.auth.getUser();
|
|
157
|
-
if (authUser) {
|
|
158
|
-
rpcUserId = authUser.id;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const { data, error } = await this.supabase
|
|
163
|
-
.rpc('data_file_reference_create', {
|
|
164
|
-
p_table_name: options.table_name,
|
|
165
|
-
p_record_id: options.record_id,
|
|
166
|
-
p_file_path: filePath, // Storage path from step 1
|
|
167
|
-
p_organisation_id: options.organisation_id ?? null,
|
|
168
|
-
p_app_id: options.app_id,
|
|
169
|
-
p_page_context: options.pageContext,
|
|
170
|
-
p_event_id: options.event_id || null, // Pass event_id for event-based apps
|
|
171
|
-
p_file_metadata: {
|
|
172
|
-
fileName: file.name,
|
|
173
|
-
fileType: file.type,
|
|
174
|
-
fileSize: file.size,
|
|
175
|
-
category: options.category,
|
|
176
|
-
...metadata,
|
|
177
|
-
...options.custom_metadata
|
|
178
|
-
},
|
|
179
|
-
p_is_public: options.is_public || false,
|
|
180
|
-
p_user_id: rpcUserId // Always pass authenticated user ID for SECURITY DEFINER functions
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// Step 5: Rollback - if database insert fails, clean up uploaded file
|
|
184
272
|
if (error) {
|
|
185
|
-
await
|
|
186
|
-
throw new Error(`Failed to create file reference: ${error.message}`);
|
|
273
|
+
return await this.rollbackUploadAndErr(filePath, options.is_public ?? false, 'CREATE_FAILED', error.message);
|
|
187
274
|
}
|
|
188
|
-
|
|
189
|
-
// Check if RPC returned null (permission denied or other failure)
|
|
190
275
|
if (!data || data === null) {
|
|
191
|
-
|
|
192
|
-
await
|
|
193
|
-
|
|
194
|
-
// Provide more helpful error message
|
|
195
|
-
const appName = await this.getAppName(options.app_id).catch(() => 'unknown');
|
|
196
|
-
const pageContextDisplay = options.pageContext || 'undefined';
|
|
197
|
-
|
|
198
|
-
if (isSuperAdminUser) {
|
|
199
|
-
// Super-admin should have been allowed - this suggests a database or RPC function issue
|
|
200
|
-
// Since we already checked super admin in the application layer, this is unexpected
|
|
201
|
-
throw new Error(
|
|
202
|
-
`File upload failed for super-admin user. This may indicate a database issue. ` +
|
|
203
|
-
`Page context: '${pageContextDisplay}', App: '${appName}'. ` +
|
|
204
|
-
`Please check that the page '${pageContextDisplay}' exists in rbac_app_pages table for app '${appName}'.`
|
|
205
|
-
);
|
|
206
|
-
} else {
|
|
207
|
-
throw new Error(
|
|
208
|
-
`File upload denied: insufficient permissions. You need 'create:page.${pageContextDisplay}' or 'update:page.${pageContextDisplay}' permission for the '${pageContextDisplay}' page. ` +
|
|
209
|
-
`Make sure the page exists in rbac_app_pages table for app '${appName}'.`
|
|
210
|
-
);
|
|
211
|
-
}
|
|
276
|
+
const message = await this.buildPermissionDeniedMessage(options, isSuperAdminUser);
|
|
277
|
+
return await this.rollbackUploadAndErr(filePath, options.is_public ?? false, 'PERMISSION_DENIED', message);
|
|
212
278
|
}
|
|
213
279
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// Clean up uploaded file if we can't fetch the reference
|
|
223
|
-
await deleteFile(this.supabase, filePath, options.is_public || false);
|
|
224
|
-
throw new Error(`Failed to fetch created file reference: ${fetchError?.message}`);
|
|
280
|
+
const fetchResult = await this.fetchCreatedFileReference(data as string);
|
|
281
|
+
if (!fetchResult.ok) {
|
|
282
|
+
return await this.rollbackUploadAndErr(
|
|
283
|
+
filePath,
|
|
284
|
+
options.is_public ?? false,
|
|
285
|
+
'FETCH_FAILED',
|
|
286
|
+
fetchResult.error.message
|
|
287
|
+
);
|
|
225
288
|
}
|
|
226
289
|
|
|
227
|
-
// Invalidate cache for this file display entry so newly uploaded files appear immediately
|
|
228
|
-
// For user-scoped files, pass null for organisation_id
|
|
229
290
|
invalidateFileDisplayCache(
|
|
230
291
|
options.table_name,
|
|
231
292
|
options.record_id,
|
|
@@ -233,53 +294,49 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
233
294
|
options.category
|
|
234
295
|
);
|
|
235
296
|
|
|
236
|
-
return
|
|
297
|
+
return ok(fetchResult.data);
|
|
237
298
|
} catch (error) {
|
|
238
299
|
log.error('Error creating file reference:', error);
|
|
239
|
-
|
|
300
|
+
return err(toApiError(error));
|
|
240
301
|
}
|
|
241
302
|
}
|
|
242
303
|
|
|
243
|
-
async getFileReference(table_name: string, record_id: string, organisation_id?: string): Promise<FileReference | null
|
|
304
|
+
async getFileReference(table_name: string, record_id: string, organisation_id?: string): Promise<ApiResult<FileReference | null>> {
|
|
244
305
|
try {
|
|
245
306
|
let query = this.supabase
|
|
246
307
|
.from('core_file_references')
|
|
247
308
|
.select('id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at')
|
|
248
309
|
.eq('table_name', table_name)
|
|
249
310
|
.eq('record_id', record_id);
|
|
250
|
-
|
|
251
|
-
// Handle NULL organisation_id for user-owned files
|
|
252
311
|
if (organisation_id === null || organisation_id === undefined) {
|
|
253
312
|
query = query.is('organisation_id', null);
|
|
254
313
|
} else {
|
|
255
314
|
query = query.eq('organisation_id', organisation_id);
|
|
256
315
|
}
|
|
257
|
-
|
|
258
316
|
const { data, error } = await query.single();
|
|
259
|
-
|
|
260
317
|
if (error) {
|
|
261
318
|
if (error.code === 'PGRST116') {
|
|
262
|
-
return null;
|
|
319
|
+
return ok(null);
|
|
263
320
|
}
|
|
264
|
-
|
|
321
|
+
return err({ code: 'FETCH_FAILED', message: error.message });
|
|
265
322
|
}
|
|
266
|
-
|
|
267
|
-
return data as FileReference;
|
|
323
|
+
return ok(data as FileReference);
|
|
268
324
|
} catch (error) {
|
|
269
325
|
log.error('Error getting file reference:', error);
|
|
270
|
-
|
|
326
|
+
return err(toApiError(error));
|
|
271
327
|
}
|
|
272
328
|
}
|
|
273
329
|
|
|
274
|
-
async getFileUrl(table_name: string, record_id: string, organisation_id?: string): Promise<string | null
|
|
330
|
+
async getFileUrl(table_name: string, record_id: string, organisation_id?: string): Promise<ApiResult<string | null>> {
|
|
275
331
|
try {
|
|
276
|
-
|
|
277
|
-
|
|
332
|
+
const refResult = await this.getFileReference(table_name, record_id, organisation_id);
|
|
333
|
+
if (!refResult.ok) {
|
|
334
|
+
return refResult;
|
|
335
|
+
}
|
|
336
|
+
const fileRef = refResult.data;
|
|
278
337
|
if (!fileRef) {
|
|
279
|
-
return null;
|
|
338
|
+
return ok(null);
|
|
280
339
|
}
|
|
281
|
-
|
|
282
|
-
// For public files, RPC returns file path - generate public URL client-side
|
|
283
340
|
if (fileRef.is_public) {
|
|
284
341
|
const { data: pathData } = await this.supabase
|
|
285
342
|
.rpc('data_file_reference_url_get', {
|
|
@@ -287,26 +344,20 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
287
344
|
p_record_id: record_id,
|
|
288
345
|
p_organisation_id: organisation_id
|
|
289
346
|
});
|
|
290
|
-
|
|
291
347
|
if (!pathData) {
|
|
292
|
-
return null;
|
|
348
|
+
return ok(null);
|
|
293
349
|
}
|
|
294
|
-
|
|
295
|
-
// Generate public URL using bucket-aware helper
|
|
296
|
-
return getPublicUrl(this.supabase, pathData, true);
|
|
297
|
-
} else {
|
|
298
|
-
// For private files, use signed URL
|
|
299
|
-
return await this.getSignedUrl(table_name, record_id, organisation_id);
|
|
350
|
+
return ok(getPublicUrl(this.supabase, pathData, true));
|
|
300
351
|
}
|
|
352
|
+
return this.getSignedUrl(table_name, record_id, organisation_id);
|
|
301
353
|
} catch (error) {
|
|
302
354
|
log.error('Error getting file URL:', error);
|
|
303
|
-
|
|
355
|
+
return err(toApiError(error));
|
|
304
356
|
}
|
|
305
357
|
}
|
|
306
358
|
|
|
307
|
-
async getSignedUrl(table_name: string, record_id: string, organisation_id?: string, expires_in: number = 3600): Promise<string | null
|
|
359
|
+
async getSignedUrl(table_name: string, record_id: string, organisation_id?: string, expires_in: number = 3600): Promise<ApiResult<string | null>> {
|
|
308
360
|
try {
|
|
309
|
-
// Get file path from RPC function
|
|
310
361
|
const { data: filePath, error } = await this.supabase
|
|
311
362
|
.rpc('data_file_reference_signed_url_get', {
|
|
312
363
|
p_table_name: table_name,
|
|
@@ -314,31 +365,29 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
314
365
|
p_organisation_id: organisation_id ?? null,
|
|
315
366
|
p_expires_in: expires_in
|
|
316
367
|
});
|
|
317
|
-
|
|
318
368
|
if (error) {
|
|
319
|
-
|
|
369
|
+
return err({ code: 'SIGNED_URL_FAILED', message: error.message });
|
|
320
370
|
}
|
|
321
|
-
|
|
322
371
|
if (!filePath) {
|
|
323
|
-
return null;
|
|
372
|
+
return ok(null);
|
|
324
373
|
}
|
|
325
|
-
|
|
326
|
-
// Generate signed URL client-side using bucket-aware helper (files bucket for private files)
|
|
327
374
|
const signedUrlResult = await getSignedUrl(this.supabase, filePath, {
|
|
328
375
|
appName: 'file-reference',
|
|
329
376
|
orgId: organisation_id,
|
|
330
377
|
userId: organisation_id ? undefined : record_id,
|
|
331
378
|
expiresIn: expires_in
|
|
332
379
|
});
|
|
333
|
-
|
|
334
|
-
|
|
380
|
+
if (!signedUrlResult.ok) {
|
|
381
|
+
return err(signedUrlResult.error);
|
|
382
|
+
}
|
|
383
|
+
return ok(signedUrlResult.data.url ?? null);
|
|
335
384
|
} catch (error) {
|
|
336
385
|
log.error('Error getting signed URL:', error);
|
|
337
|
-
|
|
386
|
+
return err(toApiError(error));
|
|
338
387
|
}
|
|
339
388
|
}
|
|
340
389
|
|
|
341
|
-
async updateFileReference(id: string, updates: Partial<FileReference>): Promise<FileReference
|
|
390
|
+
async updateFileReference(id: string, updates: Partial<FileReference>): Promise<ApiResult<FileReference>> {
|
|
342
391
|
try {
|
|
343
392
|
const { data, error } = await this.supabase
|
|
344
393
|
.from('core_file_references')
|
|
@@ -346,23 +395,21 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
346
395
|
.eq('id', id)
|
|
347
396
|
.select()
|
|
348
397
|
.single();
|
|
349
|
-
|
|
350
398
|
if (error) {
|
|
351
|
-
|
|
399
|
+
return err({ code: 'UPDATE_FAILED', message: error.message });
|
|
352
400
|
}
|
|
353
|
-
|
|
354
|
-
return data as FileReference;
|
|
401
|
+
return ok(data as FileReference);
|
|
355
402
|
} catch (error) {
|
|
356
403
|
log.error('Error updating file reference:', error);
|
|
357
|
-
|
|
404
|
+
return err(toApiError(error));
|
|
358
405
|
}
|
|
359
406
|
}
|
|
360
407
|
|
|
361
|
-
async deleteFileReference(table_name: string, record_id: string, organisation_id?: string, delete_file: boolean = false): Promise<boolean
|
|
408
|
+
async deleteFileReference(table_name: string, record_id: string, organisation_id?: string, delete_file: boolean = false): Promise<ApiResult<boolean>> {
|
|
362
409
|
try {
|
|
363
|
-
|
|
364
|
-
const fileRef =
|
|
365
|
-
|
|
410
|
+
const refResult = await this.getFileReference(table_name, record_id, organisation_id);
|
|
411
|
+
const fileRef = refResult.ok ? refResult.data : null;
|
|
412
|
+
|
|
366
413
|
const { error } = await this.supabase
|
|
367
414
|
.rpc('data_file_reference_delete', {
|
|
368
415
|
p_table_name: table_name,
|
|
@@ -370,24 +417,20 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
370
417
|
p_organisation_id: organisation_id ?? null,
|
|
371
418
|
p_delete_file: delete_file
|
|
372
419
|
});
|
|
373
|
-
|
|
374
420
|
if (error) {
|
|
375
|
-
|
|
421
|
+
return err({ code: 'DELETE_FAILED', message: error.message });
|
|
376
422
|
}
|
|
377
|
-
|
|
378
|
-
// If delete_file is true and we have the file reference, delete from storage
|
|
379
423
|
if (delete_file && fileRef) {
|
|
380
424
|
await deleteFile(this.supabase, fileRef.file_path, fileRef.is_public || false);
|
|
381
425
|
}
|
|
382
|
-
|
|
383
|
-
return true;
|
|
426
|
+
return ok(true);
|
|
384
427
|
} catch (error) {
|
|
385
428
|
log.error('Error deleting file reference:', error);
|
|
386
|
-
|
|
429
|
+
return err(toApiError(error));
|
|
387
430
|
}
|
|
388
431
|
}
|
|
389
432
|
|
|
390
|
-
async listFileReferences(table_name: string, record_id: string, organisation_id?: string): Promise<FileReference[]
|
|
433
|
+
async listFileReferences(table_name: string, record_id: string, organisation_id?: string): Promise<ApiResult<FileReference[]>> {
|
|
391
434
|
try {
|
|
392
435
|
const { data, error } = await this.supabase
|
|
393
436
|
.rpc('data_file_reference_list', {
|
|
@@ -395,20 +438,12 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
395
438
|
p_record_id: record_id,
|
|
396
439
|
p_organisation_id: organisation_id ?? null
|
|
397
440
|
});
|
|
398
|
-
|
|
399
441
|
if (error) {
|
|
400
|
-
|
|
442
|
+
return err({ code: 'LIST_FAILED', message: error.message });
|
|
401
443
|
}
|
|
402
|
-
|
|
403
|
-
// RPC returns: id, file_path, file_metadata, is_public, created_at
|
|
404
|
-
// We can construct FileReference objects directly from RPC response + function parameters
|
|
405
|
-
// This avoids a second query and reduces network requests
|
|
406
444
|
if (!data || data.length === 0) {
|
|
407
|
-
return [];
|
|
445
|
+
return ok([]);
|
|
408
446
|
}
|
|
409
|
-
|
|
410
|
-
// Construct FileReference objects from RPC response
|
|
411
|
-
// This avoids RLS issues with direct queries - the RPC already validated permissions
|
|
412
447
|
interface RpcFileItem {
|
|
413
448
|
id: string;
|
|
414
449
|
file_path: string;
|
|
@@ -419,38 +454,29 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
419
454
|
const fileReferences: FileReference[] = data
|
|
420
455
|
.filter((item: RpcFileItem) => item.id && item.file_path && item.file_metadata)
|
|
421
456
|
.map((item: RpcFileItem) => {
|
|
422
|
-
// Extract file name and type from file_path
|
|
423
457
|
const fileName = item.file_path.split('/').pop() || 'unknown';
|
|
424
458
|
const fileType = fileName.split('.').pop() || 'unknown';
|
|
425
|
-
|
|
426
|
-
// Construct complete FileReference from RPC response + function parameters
|
|
427
|
-
const fileRef: FileReference = {
|
|
459
|
+
return {
|
|
428
460
|
id: item.id,
|
|
429
461
|
table_name: table_name,
|
|
430
462
|
record_id: record_id,
|
|
431
463
|
file_path: item.file_path,
|
|
432
|
-
file_metadata: {
|
|
433
|
-
fileName,
|
|
434
|
-
fileType,
|
|
435
|
-
...(item.file_metadata || {}),
|
|
436
|
-
} as FileMetadata,
|
|
464
|
+
file_metadata: { fileName, fileType, ...(item.file_metadata || {}) } as FileMetadata,
|
|
437
465
|
organisation_id: organisation_id ?? null,
|
|
438
|
-
app_id: item.file_metadata?.app_id ? assertAppId(item.file_metadata.app_id) : assertAppId(''),
|
|
466
|
+
app_id: item.file_metadata?.app_id ? assertAppId(item.file_metadata.app_id) : assertAppId(''),
|
|
439
467
|
is_public: item.is_public ?? false,
|
|
440
468
|
created_at: item.created_at || new Date().toISOString(),
|
|
441
|
-
updated_at: item.created_at || new Date().toISOString()
|
|
442
|
-
};
|
|
443
|
-
return fileRef;
|
|
469
|
+
updated_at: item.created_at || new Date().toISOString()
|
|
470
|
+
} as FileReference;
|
|
444
471
|
});
|
|
445
|
-
|
|
446
|
-
return fileReferences;
|
|
472
|
+
return ok(fileReferences);
|
|
447
473
|
} catch (error) {
|
|
448
474
|
log.error('Error listing file references:', error);
|
|
449
|
-
|
|
475
|
+
return err(toApiError(error));
|
|
450
476
|
}
|
|
451
477
|
}
|
|
452
478
|
|
|
453
|
-
async getFileCount(table_name: string, record_id: string, organisation_id?: string): Promise<number
|
|
479
|
+
async getFileCount(table_name: string, record_id: string, organisation_id?: string): Promise<ApiResult<number>> {
|
|
454
480
|
try {
|
|
455
481
|
const { data, error } = await this.supabase
|
|
456
482
|
.rpc('data_file_reference_count_get', {
|
|
@@ -458,51 +484,43 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
458
484
|
p_record_id: record_id,
|
|
459
485
|
p_organisation_id: organisation_id ?? null
|
|
460
486
|
});
|
|
461
|
-
|
|
462
487
|
if (error) {
|
|
463
|
-
|
|
488
|
+
return err({ code: 'COUNT_FAILED', message: error.message });
|
|
464
489
|
}
|
|
465
|
-
|
|
466
|
-
return data || 0;
|
|
490
|
+
return ok(data ?? 0);
|
|
467
491
|
} catch (error) {
|
|
468
492
|
log.error('Error getting file count:', error);
|
|
469
|
-
|
|
493
|
+
return err(toApiError(error));
|
|
470
494
|
}
|
|
471
495
|
}
|
|
472
496
|
|
|
473
|
-
async getFileReferenceById(id: string, organisation_id?: string): Promise<FileReference | null
|
|
497
|
+
async getFileReferenceById(id: string, organisation_id?: string): Promise<ApiResult<FileReference | null>> {
|
|
474
498
|
try {
|
|
475
499
|
const { data, error } = await this.supabase
|
|
476
500
|
.rpc('data_file_reference_get', {
|
|
477
501
|
p_file_reference_id: id,
|
|
478
502
|
p_organisation_id: organisation_id ?? null
|
|
479
503
|
});
|
|
480
|
-
|
|
481
504
|
if (error) {
|
|
482
|
-
|
|
505
|
+
return err({ code: 'FETCH_FAILED', message: error.message });
|
|
483
506
|
}
|
|
484
|
-
|
|
485
507
|
if (!data || data.length === 0) {
|
|
486
|
-
return null;
|
|
508
|
+
return ok(null);
|
|
487
509
|
}
|
|
488
|
-
|
|
489
|
-
return data[0] as FileReference;
|
|
510
|
+
return ok(data[0] as FileReference);
|
|
490
511
|
} catch (error) {
|
|
491
512
|
log.error('Error getting file reference by ID:', error);
|
|
492
|
-
|
|
513
|
+
return err(toApiError(error));
|
|
493
514
|
}
|
|
494
515
|
}
|
|
495
516
|
|
|
496
517
|
async getFilesByCategory(
|
|
497
|
-
table_name: string,
|
|
498
|
-
record_id: string,
|
|
499
|
-
category: FileCategory,
|
|
518
|
+
table_name: string,
|
|
519
|
+
record_id: string,
|
|
520
|
+
category: FileCategory,
|
|
500
521
|
organisation_id?: string
|
|
501
|
-
): Promise<FileReference[]
|
|
522
|
+
): Promise<ApiResult<FileReference[]>> {
|
|
502
523
|
try {
|
|
503
|
-
// CRITICAL: Use RPC function to get files by category - this correctly filters on file_metadata->>'category'
|
|
504
|
-
// NOTE: We MUST use RPC function. Direct queries with .eq('category', ...) will FAIL with HTTP 406
|
|
505
|
-
// because there is NO 'category' column. The category is stored in the JSONB file_metadata field.
|
|
506
524
|
const { data, error } = await this.supabase
|
|
507
525
|
.rpc('data_file_reference_by_category_list', {
|
|
508
526
|
p_table_name: table_name,
|
|
@@ -510,32 +528,13 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
510
528
|
p_category: category,
|
|
511
529
|
p_organisation_id: organisation_id ?? null
|
|
512
530
|
});
|
|
513
|
-
|
|
514
531
|
if (error) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
error,
|
|
518
|
-
errorCode: error.code,
|
|
519
|
-
errorMessage: error.message,
|
|
520
|
-
errorDetails: error.details,
|
|
521
|
-
table_name,
|
|
522
|
-
record_id,
|
|
523
|
-
category,
|
|
524
|
-
organisation_id,
|
|
525
|
-
message: 'CRITICAL: Category filtering MUST use RPC function data_file_reference_by_category_list. Direct queries with .eq(\'category\', ...) will FAIL with HTTP 406 because category is stored in file_metadata JSONB field, not a direct column.'
|
|
526
|
-
});
|
|
527
|
-
throw new Error(`Failed to get files by category: ${error.message}. Category filtering uses file_metadata JSONB field, not a direct column. RPC function required.`);
|
|
532
|
+
log.error('RPC ERROR getting files by category:', { error, table_name, record_id, category });
|
|
533
|
+
return err({ code: 'LIST_FAILED', message: error.message });
|
|
528
534
|
}
|
|
529
|
-
|
|
530
|
-
// RPC returns partial data with: id, file_path, file_metadata, is_public, created_at
|
|
531
|
-
// We have table_name, record_id, organisation_id from function parameters
|
|
532
|
-
// We can construct FileReference objects directly without another query (avoiding RLS issues)
|
|
533
535
|
if (!data || data.length === 0) {
|
|
534
|
-
return [];
|
|
536
|
+
return ok([]);
|
|
535
537
|
}
|
|
536
|
-
|
|
537
|
-
// Construct FileReference objects from RPC response
|
|
538
|
-
// This avoids RLS issues with direct queries - the RPC already validated permissions
|
|
539
538
|
interface RpcFileItem {
|
|
540
539
|
id: string;
|
|
541
540
|
file_path: string;
|
|
@@ -545,64 +544,45 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
545
544
|
}
|
|
546
545
|
const fileReferences: FileReference[] = data
|
|
547
546
|
.filter((item: RpcFileItem) => {
|
|
548
|
-
// Verify category matches (defensive check)
|
|
549
547
|
const fileCategory = item.file_metadata?.category;
|
|
550
|
-
|
|
551
|
-
if (!matches) {
|
|
552
|
-
log.warn('File category mismatch in RPC response:', {
|
|
553
|
-
fileId: item.id,
|
|
554
|
-
expectedCategory: category,
|
|
555
|
-
actualCategory: fileCategory
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
return matches && item.id && item.file_path && item.file_metadata;
|
|
548
|
+
return fileCategory === category && item.id && item.file_path && item.file_metadata;
|
|
559
549
|
})
|
|
560
|
-
.map((item: RpcFileItem) => {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
app_id: item.file_metadata?.app_id ? assertAppId(item.file_metadata.app_id) : assertAppId(''), // May not be in metadata, use empty string
|
|
579
|
-
is_public: item.is_public ?? false,
|
|
580
|
-
created_at: item.created_at || new Date().toISOString(),
|
|
581
|
-
updated_at: item.created_at || new Date().toISOString() // RPC doesn't return updated_at, use created_at
|
|
582
|
-
};
|
|
583
|
-
return fileRef;
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
return fileReferences;
|
|
550
|
+
.map((item: RpcFileItem) => ({
|
|
551
|
+
id: item.id,
|
|
552
|
+
table_name: table_name,
|
|
553
|
+
record_id: record_id,
|
|
554
|
+
file_path: item.file_path,
|
|
555
|
+
file_metadata: {
|
|
556
|
+
fileName: item.file_path.split('/').pop() || 'unknown',
|
|
557
|
+
fileType: (item.file_path.split('.').pop() || 'unknown'),
|
|
558
|
+
category: (item.file_metadata?.category as FileCategory) || FileCategory.GENERAL_DOCUMENTS,
|
|
559
|
+
...(item.file_metadata || {}),
|
|
560
|
+
} as FileMetadata,
|
|
561
|
+
organisation_id: organisation_id ?? null,
|
|
562
|
+
app_id: item.file_metadata?.app_id ? assertAppId(item.file_metadata.app_id) : assertAppId(''),
|
|
563
|
+
is_public: item.is_public ?? false,
|
|
564
|
+
created_at: item.created_at || new Date().toISOString(),
|
|
565
|
+
updated_at: item.created_at || new Date().toISOString(),
|
|
566
|
+
} as FileReference));
|
|
567
|
+
return ok(fileReferences);
|
|
587
568
|
} catch (error) {
|
|
588
569
|
log.error('Error getting files by category:', error);
|
|
589
|
-
|
|
570
|
+
return err(toApiError(error));
|
|
590
571
|
}
|
|
591
572
|
}
|
|
592
573
|
|
|
593
574
|
async uploadMultipleFiles(
|
|
594
575
|
options: FileUploadOptions,
|
|
595
576
|
files: File[]
|
|
596
|
-
): Promise<
|
|
577
|
+
): Promise<ApiResult<BulkUploadResult>> {
|
|
597
578
|
const success: FileReference[] = [];
|
|
598
579
|
const failed: { file: File; error: string }[] = [];
|
|
599
580
|
const results: Array<{ file: File; result: FileUploadResult | null; error?: string }> = [];
|
|
600
581
|
|
|
601
|
-
// Upload files sequentially to avoid overwhelming the server
|
|
602
582
|
for (const file of files) {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
583
|
+
const result = await this.createFileReference(options, file);
|
|
584
|
+
if (result.ok) {
|
|
585
|
+
const fileReference = result.data;
|
|
606
586
|
success.push(fileReference);
|
|
607
587
|
results.push({
|
|
608
588
|
file,
|
|
@@ -613,20 +593,19 @@ export class FileReferenceServiceImpl implements FileReferenceService {
|
|
|
613
593
|
: '',
|
|
614
594
|
},
|
|
615
595
|
});
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
results.push({ file, result: null, error: message });
|
|
596
|
+
} else {
|
|
597
|
+
failed.push({ file, error: result.error.message });
|
|
598
|
+
results.push({ file, result: null, error: result.error.message });
|
|
620
599
|
}
|
|
621
600
|
}
|
|
622
601
|
|
|
623
|
-
return {
|
|
602
|
+
return ok({
|
|
624
603
|
success,
|
|
625
604
|
failed,
|
|
626
605
|
total: results.length,
|
|
627
606
|
successful: success.length,
|
|
628
607
|
results,
|
|
629
|
-
};
|
|
608
|
+
});
|
|
630
609
|
}
|
|
631
610
|
}
|
|
632
611
|
|
|
@@ -640,25 +619,28 @@ export async function uploadFileWithReference(
|
|
|
640
619
|
supabase: SupabaseClient,
|
|
641
620
|
options: FileUploadOptions,
|
|
642
621
|
file: File
|
|
643
|
-
): Promise<FileUploadResult
|
|
644
|
-
|
|
622
|
+
): Promise<ApiResult<FileUploadResult>> {
|
|
645
623
|
const service = createFileReferenceService(supabase);
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
624
|
+
const result = await service.createFileReference(options, file);
|
|
625
|
+
if (!result.ok) {
|
|
626
|
+
return result;
|
|
627
|
+
}
|
|
628
|
+
const fileReference = result.data;
|
|
629
|
+
let urlString: string;
|
|
630
|
+
if (options.is_public) {
|
|
631
|
+
urlString = getPublicUrl(supabase, fileReference.file_path, true);
|
|
632
|
+
} else {
|
|
633
|
+
const signedResult = await getSignedUrl(supabase, fileReference.file_path, {
|
|
634
|
+
appName: 'file-reference',
|
|
635
|
+
orgId: options.organisation_id || undefined,
|
|
636
|
+
userId: options.userId || undefined,
|
|
637
|
+
expiresIn: 3600
|
|
638
|
+
});
|
|
639
|
+
urlString = signedResult.ok ? signedResult.data.url ?? '' : '';
|
|
640
|
+
}
|
|
641
|
+
return ok({
|
|
660
642
|
file_reference: fileReference,
|
|
661
643
|
file_url: urlString,
|
|
662
644
|
signed_url: options.is_public ? undefined : urlString || undefined
|
|
663
|
-
};
|
|
645
|
+
});
|
|
664
646
|
}
|