@jmruthers/pace-core 0.5.181 → 0.5.182
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 +1 -1
- package/README.md +16 -2
- package/dist/{AuthService-DYuQPJj6.d.ts → AuthService-B-cd2MA4.d.ts} +9 -11
- package/dist/{DataTable-CWAZZcXC.d.ts → DataTable-Bz8ffqyA.d.ts} +1 -1
- package/dist/{DataTable-UA6CL4JI.js → DataTable-QAB34V6K.js} +14 -15
- package/dist/UnifiedAuthProvider-7F6T4B6K.js +13 -0
- package/dist/{UnifiedAuthProvider-DJxGTftH.d.ts → UnifiedAuthProvider-F86d7dSi.d.ts} +5 -6
- package/dist/{api-45XYYO2A.js → api-ROMBCNKU.js} +5 -5
- package/dist/{audit-64X3VJXB.js → audit-WRS3KJKI.js} +4 -4
- package/dist/auth-BZOJqrdd.d.ts +49 -0
- package/dist/{chunk-CX5M4ZAG.js → chunk-5DRSZLL2.js} +1 -1
- package/dist/chunk-5DRSZLL2.js.map +1 -0
- package/dist/{chunk-BESYRHQM.js → chunk-6C4YBBJM.js} +10 -7
- package/dist/chunk-6C4YBBJM.js.map +1 -0
- package/dist/{chunk-PLDDJCW6.js → chunk-7D4SUZUM.js} +2 -13
- package/dist/{chunk-HRO5HWN2.js → chunk-CSOFYHAG.js} +55 -162
- package/dist/chunk-CSOFYHAG.js.map +1 -0
- package/dist/{chunk-ANBQRTPX.js → chunk-E66EQZE6.js} +3 -5
- package/dist/{chunk-ANBQRTPX.js.map → chunk-E66EQZE6.js.map} +1 -1
- package/dist/{chunk-Q5QRDWKI.js → chunk-F2IMUDXZ.js} +4 -6
- package/dist/chunk-F2IMUDXZ.js.map +1 -0
- package/dist/{chunk-SBVILCCA.js → chunk-FSFQFJCU.js} +28 -6
- package/dist/chunk-FSFQFJCU.js.map +1 -0
- package/dist/chunk-FUEYYMX5.js +2296 -0
- package/dist/chunk-FUEYYMX5.js.map +1 -0
- package/dist/{chunk-FFKNH6U5.js → chunk-HKIT6O7W.js} +3 -5
- package/dist/{chunk-FFKNH6U5.js.map → chunk-HKIT6O7W.js.map} +1 -1
- package/dist/chunk-KQCRWDSA.js +1 -0
- package/dist/{chunk-S5OFRT4M.js → chunk-KUEN3HFB.js} +6 -6
- package/dist/chunk-KUEN3HFB.js.map +1 -0
- package/dist/chunk-LMC26NLJ.js +84 -0
- package/dist/chunk-LMC26NLJ.js.map +1 -0
- package/dist/{chunk-BVYWGZVV.js → chunk-M7W4CP3M.js} +52 -19
- package/dist/chunk-M7W4CP3M.js.map +1 -0
- package/dist/{chunk-HZLDFOE4.js → chunk-MI7HBHN3.js} +164 -243
- package/dist/chunk-MI7HBHN3.js.map +1 -0
- package/dist/{chunk-PPMP5J6T.js → chunk-PWAHJW4G.js} +180 -29
- package/dist/chunk-PWAHJW4G.js.map +1 -0
- package/dist/chunk-PWLANIRT.js +127 -0
- package/dist/{chunk-XDNLUEXI.js.map → chunk-PWLANIRT.js.map} +1 -1
- package/dist/chunk-QCDXODCA.js +75 -0
- package/dist/chunk-QCDXODCA.js.map +1 -0
- package/dist/{chunk-D7LCGMVS.js → chunk-QETLRQI6.js} +526 -887
- package/dist/chunk-QETLRQI6.js.map +1 -0
- package/dist/{chunk-5MT24GKJ.js → chunk-QUVSNGIP.js} +264 -262
- package/dist/chunk-QUVSNGIP.js.map +1 -0
- package/dist/chunk-QXHPKYJV.js +113 -0
- package/dist/chunk-QXHPKYJV.js.map +1 -0
- package/dist/{chunk-OWAG3GSU.js → chunk-R77UEZ4E.js} +11 -1
- package/dist/chunk-R77UEZ4E.js.map +1 -0
- package/dist/{chunk-ZYTYSTO5.js → chunk-RA3JUFMW.js} +314 -161
- package/dist/chunk-RA3JUFMW.js.map +1 -0
- package/dist/{chunk-ERISIBYU.js → chunk-SQGMNID3.js} +3 -8
- package/dist/chunk-SQGMNID3.js.map +1 -0
- package/dist/{chunk-XJ2HZOBU.js → chunk-UHNYIBXL.js} +1 -1
- package/dist/chunk-UHNYIBXL.js.map +1 -0
- package/{src/utils/secureStorage.ts → dist/chunk-VBXEHIUJ.js} +113 -88
- package/dist/{chunk-7QCC6MCP.js.map → chunk-VBXEHIUJ.js.map} +1 -1
- package/dist/{chunk-VZ4VDGTB.js → chunk-W22JP75J.js} +5 -13
- package/dist/{chunk-VZ4VDGTB.js.map → chunk-W22JP75J.js.map} +1 -1
- package/dist/components.d.ts +12 -93
- package/dist/components.js +23 -106
- package/dist/components.js.map +1 -1
- package/dist/core-CUElvH_C.d.ts +164 -0
- package/dist/database.generated-CBmg2950.d.ts +8284 -0
- package/dist/event-CW5YB_2p.d.ts +239 -0
- package/dist/{file-reference-C6Gkn77H.d.ts → file-reference-D06mEEWW.d.ts} +7 -5
- package/dist/functions-D_kgHktt.d.ts +208 -0
- package/dist/hooks.d.ts +54 -7
- package/dist/hooks.js +204 -17
- package/dist/hooks.js.map +1 -1
- package/dist/{EventLogo-B3V3otev.d.ts → index-Bl--n7-T.d.ts} +387 -397
- package/dist/index.d.ts +94 -261
- package/dist/index.js +314 -126
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +7 -8
- package/dist/providers.js +6 -13
- package/dist/rbac/index.d.ts +171 -101
- package/dist/rbac/index.js +23 -17
- package/dist/styles/index.d.ts +1 -3
- package/dist/styles/index.js +2 -17
- package/dist/theming/runtime.js +3 -3
- package/dist/types-UU913iLA.d.ts +102 -0
- package/dist/{types-Dfz9dmVH.d.ts → types-_x1f4QBF.d.ts} +6 -6
- package/dist/types.d.ts +88 -227
- package/dist/types.js +64 -112
- package/dist/types.js.map +1 -1
- package/dist/{usePublicRouteParams-B7PabvuH.d.ts → usePublicRouteParams-JJczomYq.d.ts} +203 -6
- package/dist/utils.d.ts +299 -13
- package/dist/utils.js +481 -55
- package/dist/utils.js.map +1 -1
- package/dist/validation-643vUDZW.d.ts +177 -0
- package/docs/DOCUMENTATION_REVIEW_TRACKER.md +511 -0
- package/docs/README.md +9 -8
- package/docs/api/README.md +16 -2
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +4 -4
- package/docs/api/classes/RBACAuditManager.md +14 -14
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- package/docs/api/classes/SecureSupabaseClient.md +29 -9
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +17 -17
- package/docs/api/enums/RBACErrorCode.md +228 -0
- package/docs/api/enums/RPCFunction.md +118 -0
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +2 -2
- package/docs/api/interfaces/CalendarProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +29 -3
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +2 -2
- package/docs/api/interfaces/DataTableColumn.md +6 -6
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +2 -2
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +5 -5
- package/docs/api/interfaces/ExportOptions.md +4 -4
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +13 -13
- package/docs/api/interfaces/FileReference.md +12 -12
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +10 -10
- package/docs/api/interfaces/FileUploadProps.md +19 -19
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +166 -0
- package/docs/api/interfaces/FormProps.md +113 -0
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +8 -8
- package/docs/api/interfaces/InputProps.md +2 -2
- package/docs/api/interfaces/LabelProps.md +8 -8
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +17 -73
- package/docs/api/interfaces/NavigationMenuProps.md +38 -53
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +13 -13
- package/docs/api/interfaces/OrganisationContextType.md +21 -21
- package/docs/api/interfaces/OrganisationMembership.md +15 -15
- package/docs/api/interfaces/OrganisationProviderProps.md +59 -2
- package/docs/api/interfaces/OrganisationSecurityError.md +5 -5
- package/docs/api/interfaces/PaceAppLayoutProps.md +26 -39
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +50 -0
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
- package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
- package/docs/api/interfaces/PublicPageLayoutProps.md +15 -15
- package/docs/api/interfaces/RBACAccessValidateParams.md +52 -0
- package/docs/api/interfaces/RBACAccessValidateResult.md +41 -0
- package/docs/api/interfaces/RBACAuditLogParams.md +85 -0
- package/docs/api/interfaces/RBACAuditLogResult.md +52 -0
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +52 -0
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +74 -0
- package/docs/api/interfaces/RBACPermissionCheckParams.md +74 -0
- package/docs/api/interfaces/RBACPermissionCheckResult.md +52 -0
- package/docs/api/interfaces/RBACPermissionsGetParams.md +63 -0
- package/docs/api/interfaces/RBACPermissionsGetResult.md +63 -0
- package/docs/api/interfaces/RBACResult.md +58 -0
- package/docs/api/interfaces/RBACRoleGrantParams.md +63 -0
- package/docs/api/interfaces/RBACRoleGrantResult.md +52 -0
- package/docs/api/interfaces/RBACRoleRevokeParams.md +63 -0
- package/docs/api/interfaces/RBACRoleRevokeResult.md +52 -0
- package/docs/api/interfaces/RBACRoleValidateParams.md +52 -0
- package/docs/api/interfaces/RBACRoleValidateResult.md +63 -0
- package/docs/api/interfaces/RBACRolesListParams.md +52 -0
- package/docs/api/interfaces/RBACRolesListResult.md +74 -0
- package/docs/api/interfaces/RBACSessionTrackParams.md +74 -0
- package/docs/api/interfaces/RBACSessionTrackResult.md +52 -0
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +15 -2
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +43 -2
- package/docs/api/interfaces/TextareaProps.md +2 -2
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +61 -61
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +87 -0
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +81 -0
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +5 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +4 -4
- package/docs/api/interfaces/UserProfile.md +7 -7
- package/docs/api/modules.md +484 -462
- package/docs/api-reference/components.md +186 -15
- package/docs/api-reference/deprecated.md +376 -0
- package/docs/api-reference/hooks.md +149 -19
- package/docs/api-reference/providers.md +61 -6
- package/docs/api-reference/rpc-functions.md +397 -0
- package/docs/api-reference/types.md +135 -78
- package/docs/api-reference/utilities.md +51 -380
- package/docs/architecture/README.md +49 -3
- package/docs/architecture/database-schema-requirements.md +40 -3
- package/docs/architecture/rbac-security-architecture.md +41 -4
- package/docs/architecture/services.md +127 -42
- package/docs/best-practices/README.md +51 -5
- package/docs/best-practices/accessibility.md +32 -3
- package/docs/best-practices/common-patterns.md +50 -3
- package/docs/best-practices/deployment.md +50 -4
- package/docs/best-practices/performance.md +50 -3
- package/docs/best-practices/security.md +94 -41
- package/docs/best-practices/testing.md +33 -4
- package/docs/core-concepts/authentication.md +5 -5
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +3 -3
- package/docs/core-concepts/permissions.md +3 -3
- package/docs/core-concepts/rbac-system.md +5 -5
- package/docs/documentation-index.md +30 -8
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +7 -5
- package/docs/getting-started/examples/basic-auth-app.md +3 -0
- package/docs/getting-started/examples/full-featured-app.md +5 -3
- package/docs/getting-started/faq.md +6 -6
- package/docs/getting-started/installation-guide.md +192 -13
- package/docs/getting-started/local-development.md +303 -0
- package/docs/getting-started/quick-reference.md +3 -3
- package/docs/getting-started/quick-start.md +517 -0
- package/docs/implementation-guides/app-layout.md +45 -3
- package/docs/implementation-guides/authentication.md +66 -7
- package/docs/implementation-guides/component-styling.md +53 -3
- package/docs/implementation-guides/data-tables.md +76 -7
- package/docs/implementation-guides/datatable-filtering.md +1 -2
- package/docs/implementation-guides/datatable-rbac-usage.md +0 -1
- package/docs/implementation-guides/dynamic-colors.md +155 -4
- package/docs/implementation-guides/file-reference-system.md +72 -3
- package/docs/implementation-guides/file-upload-storage.md +72 -3
- package/docs/implementation-guides/forms.md +53 -3
- package/docs/implementation-guides/inactivity-tracking.md +53 -3
- package/docs/implementation-guides/large-datasets.md +1 -1
- package/docs/implementation-guides/navigation.md +55 -5
- package/docs/implementation-guides/organisation-security.md +72 -3
- package/docs/implementation-guides/performance.md +57 -1
- package/docs/implementation-guides/permission-enforcement.md +81 -8
- package/docs/implementation-guides/public-pages.md +560 -14
- package/docs/migration/MIGRATION_GUIDE.md +409 -50
- package/docs/migration/README.md +37 -3
- package/docs/migration/organisation-context-timing-fix.md +39 -4
- package/docs/migration/quick-migration-guide.md +41 -5
- package/docs/migration/rbac-migration.md +59 -3
- package/docs/migration/service-architecture.md +77 -14
- package/docs/rbac/README.md +79 -3
- package/docs/rbac/advanced-patterns.md +47 -3
- package/docs/rbac/api-reference.md +77 -8
- package/docs/rbac/event-based-apps.md +50 -5
- package/docs/rbac/examples/rbac-rls-integration-example.md +3 -3
- package/docs/rbac/examples.md +39 -3
- package/docs/rbac/getting-started.md +63 -4
- package/docs/rbac/quick-start.md +57 -5
- package/docs/rbac/rbac-rls-integration.md +68 -6
- package/docs/rbac/super-admin-guide.md +47 -3
- package/docs/rbac/troubleshooting.md +3 -3
- package/docs/security/README.md +68 -3
- package/docs/security/checklist.md +50 -3
- package/docs/standards/01-architecture-standard.md +39 -0
- package/docs/standards/02-api-and-rpc-standard.md +39 -0
- package/docs/standards/03-component-standard.md +32 -0
- package/docs/standards/04-code-style-standard.md +32 -0
- package/docs/standards/05-security-standard.md +30 -0
- package/docs/standards/06-testing-and-docs-standard.md +29 -0
- package/docs/standards/README.md +35 -0
- package/docs/styles/README.md +89 -8
- package/docs/testing/README.md +175 -24
- package/docs/troubleshooting/README.md +50 -3
- package/docs/troubleshooting/common-issues.md +271 -5
- package/docs/troubleshooting/debugging.md +54 -1
- package/docs/troubleshooting/migration.md +54 -1
- package/docs/troubleshooting/organisation-context-setup.md +29 -3
- package/docs/troubleshooting/styling-issues.md +246 -4
- package/{src/components/DataTable/examples → examples/DataTable}/GroupingAggregationExample.tsx +1 -1
- package/examples/{components 2/DataTable/HierarchicalActionsExample.tsx → DataTable/HierarchicalActionsExample.tsx} +7 -6
- package/{src/components/DataTable/examples → examples/DataTable}/HierarchicalExample.tsx +8 -6
- package/examples/{components 2/DataTable/PerformanceExample.tsx → DataTable/PerformanceExample.tsx} +2 -2
- package/examples/{components 2/DataTable/index.ts → DataTable/index.ts} +1 -0
- package/{src/components/Dialog/examples → examples/Dialog}/HtmlDialogExample.tsx +3 -3
- package/examples/{components 2/Dialog/ScrollableDialogExample.tsx → Dialog/ScrollableDialogExample.tsx} +1 -1
- package/{src/components/Dialog/examples → examples/Dialog}/SmartDialogExample.tsx +1 -1
- package/examples/{components 2/Dialog/index.ts → Dialog/index.ts} +0 -3
- package/examples/{features/public-pages → PublicPages}/CorrectPublicPageImplementation.tsx +52 -17
- package/examples/{features/public-pages → PublicPages}/PublicEventPage.tsx +65 -35
- package/examples/{features/public-pages → PublicPages}/PublicPageApp.tsx +52 -18
- package/examples/{features/public-pages → PublicPages}/PublicPageUsageExample.tsx +28 -15
- package/examples/README.md +81 -33
- package/examples/index.ts +14 -12
- package/examples/{RBAC → rbac}/CompleteRBACExample.tsx +1 -1
- package/examples/{features/rbac → rbac}/EventBasedApp.tsx +4 -4
- package/examples/{features/rbac → rbac}/PermissionExample.tsx +5 -3
- package/package.json +21 -27
- package/src/__tests__/helpers/test-utils.tsx +29 -3
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +7 -5
- package/src/components/Alert/Alert.test.tsx +2 -2
- package/src/components/Alert/Alert.tsx +4 -4
- package/src/components/Avatar/Avatar.test.tsx +17 -6
- package/src/components/Badge/Badge.test.tsx +1 -1
- package/src/components/Badge/Badge.tsx +2 -2
- package/src/components/Button/Button.test.tsx +2 -2
- package/src/components/Button/Button.tsx +11 -7
- package/src/components/Calendar/Calendar.test.tsx +41 -8
- package/src/components/Calendar/Calendar.tsx +39 -36
- package/src/components/Card/Card.tsx +51 -13
- package/src/components/Checkbox/Checkbox.test.tsx +36 -12
- package/src/components/DataTable/DataTable.test.tsx +1 -1
- package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +13 -7
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +14 -42
- package/src/components/DataTable/__tests__/DataTable.export.test.tsx +13 -10
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +14 -11
- package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +4 -2
- package/src/components/DataTable/__tests__/DataTable.test.tsx +13 -7
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +13 -10
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +15 -11
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +12 -6
- package/src/components/DataTable/__tests__/keyboard.test.tsx +12 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +10 -6
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +1 -1
- package/src/components/DataTable/components/DataTableBody.tsx +10 -25
- package/src/components/DataTable/components/DataTableCore.tsx +1 -1
- package/src/components/DataTable/components/FilterRow.tsx +3 -1
- package/src/components/DataTable/components/ImportModal.tsx +1 -1
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +9 -9
- package/src/components/DataTable/core/ColumnFactory.ts +6 -6
- package/src/components/DataTable/core/DataTableContext.tsx +14 -10
- package/src/components/DataTable/core/LocalDataAdapter.ts +2 -1
- package/src/components/DataTable/core/PluginRegistry.ts +3 -3
- package/src/components/DataTable/core/StateManager.ts +12 -11
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +104 -0
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +101 -0
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +84 -0
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +102 -0
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +104 -0
- package/src/components/DataTable/core/interfaces.ts +17 -17
- package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +124 -0
- package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +117 -0
- package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +102 -0
- package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +53 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +0 -2
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +9 -8
- package/src/components/DataTable/types.ts +5 -5
- package/src/components/DataTable/utils/aggregationUtils.ts +4 -4
- package/src/components/DataTable/utils/columnUtils.ts +3 -2
- package/src/components/DataTable/utils/debugTools.ts +1 -1
- package/src/components/DataTable/utils/exportUtils.ts +6 -6
- package/src/components/DataTable/utils/hierarchicalSorting.ts +6 -6
- package/src/components/DataTable/utils/hierarchicalUtils.ts +0 -8
- package/src/components/DataTable/utils/index.ts +0 -1
- package/src/components/DataTable/utils/performanceUtils.ts +9 -4
- package/src/components/Dialog/Dialog.test.tsx +49 -27
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +13 -8
- package/src/components/EventSelector/EventSelector.test.tsx +60 -12
- package/src/components/EventSelector/EventSelector.tsx +38 -15
- package/src/components/EventSelector/index.ts +2 -2
- package/src/components/FileDisplay/FileDisplay.test.tsx +143 -85
- package/src/components/FileDisplay/FileDisplay.tsx +1 -0
- package/src/components/FileUpload/FileUpload.test.tsx +532 -152
- package/src/components/FileUpload/FileUpload.tsx +43 -8
- package/src/components/Footer/Footer.test.tsx +19 -14
- package/src/components/Form/Form.test.tsx +96 -14
- package/src/components/Form/Form.tsx +210 -1
- package/src/components/Form/index.ts +3 -7
- package/src/components/Header/Header.test.tsx +24 -17
- package/src/components/Header/Header.tsx +3 -1
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -4
- package/src/components/Input/Input.test.tsx +61 -36
- package/src/components/Label/{__tests__/Label.test.tsx → Label.test.tsx} +2 -2
- package/src/components/Label/Label.tsx +2 -3
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +6 -5
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +6 -2
- package/src/components/LoginForm/LoginForm.test.tsx +14 -13
- package/src/components/LoginForm/LoginForm.tsx +1 -1
- package/src/components/LoginForm/index.ts +7 -0
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +233 -20
- package/src/components/NavigationMenu/NavigationMenu.tsx +191 -55
- package/src/components/NavigationMenu/index.ts +1 -1
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +20 -11
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
- package/src/components/PaceAppLayout/{__tests__/PaceAppLayout.integration.test.tsx → PaceAppLayout.integration.test.tsx} +272 -79
- package/src/components/PaceAppLayout/{__tests__/PaceAppLayout.performance.test.tsx → PaceAppLayout.performance.test.tsx} +155 -32
- package/src/components/PaceAppLayout/{__tests__/PaceAppLayout.security.test.tsx → PaceAppLayout.security.test.tsx} +211 -65
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +498 -210
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +63 -64
- package/src/components/PaceAppLayout/test-setup.tsx +192 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +193 -39
- package/src/components/{PasswordReset → PasswordChange}/PasswordChangeForm.test.tsx +2 -2
- package/src/components/{PasswordReset → PasswordChange}/PasswordChangeForm.tsx +10 -4
- package/src/components/PasswordChange/index.ts +2 -0
- package/src/components/Progress/Progress.test.tsx +11 -0
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/Progress/index.ts +10 -0
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +2 -1
- package/src/components/PublicLayout/PublicLayout.test.tsx +1210 -0
- package/src/components/PublicLayout/PublicPageLayout.tsx +190 -36
- package/src/components/PublicLayout/PublicPageProvider.tsx +8 -7
- package/src/components/PublicLayout/index.ts +10 -28
- package/src/components/Select/Select.test.tsx +7 -7
- package/src/components/Select/Select.tsx +277 -11
- package/src/components/Select/index.ts +1 -2
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +232 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +40 -19
- package/src/components/Table/{__tests__/Table.test.tsx → Table.test.tsx} +94 -41
- package/src/components/Tabs/Tabs.test.tsx +10 -9
- package/src/components/Tabs/Tabs.tsx +61 -33
- package/src/components/Textarea/Textarea.test.tsx +31 -18
- package/src/components/Toast/Toast.tsx +2 -2
- package/src/components/Tooltip/Tooltip.test.tsx +1 -1
- package/src/components/UserMenu/UserMenu.test.tsx +7 -6
- package/src/components/UserMenu/UserMenu.tsx +2 -2
- package/src/components/index.ts +5 -4
- package/src/constants/performance.ts +19 -8
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +21 -22
- package/src/hooks/__tests__/useEvents.unit.test.ts +5 -4
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +17 -0
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +16 -11
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +1 -3
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +1 -3
- package/src/hooks/__tests__/useRBAC.unit.test.ts +24 -2
- package/src/hooks/index.ts +4 -0
- package/src/hooks/public/index.ts +2 -0
- package/src/hooks/public/usePublicEvent.ts +4 -6
- package/src/hooks/public/usePublicRouteParams.ts +1 -1
- package/src/hooks/services/useAuth.ts +2 -4
- package/src/hooks/services/useCurrentEvent.ts +1 -1
- package/src/hooks/useAppConfig.ts +1 -1
- package/src/hooks/useDataTablePerformance.ts +2 -2
- package/src/hooks/useEventTheme.ts +1 -1
- package/src/hooks/useEvents.ts +51 -10
- package/src/hooks/useOrganisationPermissions.test.ts +3 -3
- package/src/hooks/useOrganisationPermissions.ts +1 -1
- package/src/hooks/useOrganisationSecurity.ts +2 -2
- package/src/hooks/usePermissionCache.test.ts +9 -9
- package/src/hooks/usePermissionCache.ts +2 -2
- package/src/index.ts +19 -12
- package/src/providers/OrganisationProvider.tsx +73 -9
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +113 -13
- package/src/providers/__tests__/AuthProvider.test.tsx +2 -1
- package/src/providers/__tests__/EventProvider.test.tsx +24 -15
- package/src/providers/__tests__/OrganisationProvider.test.tsx +87 -36
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +80 -24
- package/src/providers/index.ts +0 -3
- package/src/providers/services/AuthServiceProvider.tsx +2 -17
- package/src/providers/services/EventServiceProvider.tsx +11 -16
- package/src/providers/services/InactivityServiceProvider.tsx +9 -12
- package/src/providers/services/OrganisationServiceProvider.tsx +9 -12
- package/src/providers/services/UnifiedAuthProvider.tsx +85 -18
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +11 -4
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +105 -21
- package/src/rbac/adapters.tsx +1 -1
- package/src/rbac/api.ts +20 -4
- package/src/rbac/audit-enhanced.ts +47 -2
- package/src/rbac/audit.ts +47 -2
- package/src/rbac/components/NavigationGuard.tsx +1 -1
- package/src/rbac/components/NavigationProvider.test.tsx +7 -6
- package/src/rbac/components/NavigationProvider.tsx +1 -1
- package/src/rbac/components/PagePermissionGuard.tsx +1 -1
- package/src/rbac/components/PagePermissionProvider.test.tsx +7 -6
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +1 -1
- package/src/rbac/components/RoleBasedRouter.tsx +1 -1
- package/src/rbac/components/SecureDataProvider.test.tsx +7 -6
- package/src/rbac/components/SecureDataProvider.tsx +1 -1
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +6 -6
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +11 -10
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +10 -11
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +19 -15
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +13 -12
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +19 -15
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +18 -18
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +11 -10
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +8 -7
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +10 -11
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +48 -19
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +476 -0
- package/src/rbac/hooks/index.ts +3 -0
- package/src/rbac/hooks/usePermissions.ts +31 -85
- package/src/rbac/hooks/useRBAC.test.ts +13 -1
- package/src/rbac/hooks/useRBAC.ts +13 -67
- package/src/rbac/hooks/useResolvedScope.ts +11 -0
- package/src/rbac/hooks/useSecureSupabase.ts +308 -0
- package/src/rbac/index.ts +3 -0
- package/src/rbac/secureClient.ts +53 -6
- package/src/rbac/security.ts +37 -1
- package/src/{types/rbac-functions.ts → rbac/types/functions.ts} +30 -30
- package/src/rbac/types.ts +3 -2
- package/src/services/AuthService.ts +33 -25
- package/src/services/EventService.ts +56 -44
- package/src/services/InactivityService.ts +33 -53
- package/src/services/OrganisationService.ts +36 -40
- package/src/services/__tests__/AuthService.restoreSession.test.ts +6 -2
- package/src/services/__tests__/EventService.test.ts +67 -33
- package/src/services/interfaces/IEventService.ts +1 -1
- package/src/styles/core.css +2 -2
- package/src/styles/index.ts +1 -5
- package/src/types/__tests__/guards.test.ts +1 -1
- package/src/types/__tests__/type-validation.test.ts +0 -1
- package/src/types/auth.ts +42 -2
- package/src/types/core.ts +251 -0
- package/src/types/database.ts +11 -496
- package/src/types/event.ts +102 -0
- package/src/types/file-reference.ts +6 -4
- package/src/types/guards.ts +2 -1
- package/src/types/index.ts +48 -14
- package/src/types/lodash.debounce.d.ts +15 -0
- package/src/types/organisation.ts +14 -10
- package/src/types/supabase.ts +15 -17
- package/src/utils/__tests__/secureErrors.unit.test.ts +1 -1
- package/src/utils/__tests__/validationUtils.unit.test.ts +0 -29
- package/src/utils/app/appNameResolver.ts +1 -1
- package/src/utils/dynamic/dynamicUtils.ts +3 -2
- package/src/utils/file-reference/index.ts +25 -6
- package/src/utils/security/secureErrors.ts +1 -1
- package/src/utils/validation/index.ts +6 -12
- package/src/utils/validation/validationUtils.ts +0 -13
- package/dist/UnifiedAuthProvider-B37ATQHE.js +0 -16
- package/dist/auth-DReDSLq9.d.ts +0 -16
- package/dist/chunk-3JI76CYK.js +0 -2444
- package/dist/chunk-3JI76CYK.js.map +0 -1
- package/dist/chunk-56XJ3TU6.js +0 -11
- package/dist/chunk-56XJ3TU6.js.map +0 -1
- package/dist/chunk-5MT24GKJ.js.map +0 -1
- package/dist/chunk-7QCC6MCP.js +0 -288
- package/dist/chunk-BESYRHQM.js.map +0 -1
- package/dist/chunk-BJPBT3CU.js +0 -21
- package/dist/chunk-BJPBT3CU.js.map +0 -1
- package/dist/chunk-BVYWGZVV.js.map +0 -1
- package/dist/chunk-CX5M4ZAG.js.map +0 -1
- package/dist/chunk-D7LCGMVS.js.map +0 -1
- package/dist/chunk-EGI6MUL6.js +0 -27
- package/dist/chunk-EGI6MUL6.js.map +0 -1
- package/dist/chunk-ERISIBYU.js.map +0 -1
- package/dist/chunk-HRO5HWN2.js.map +0 -1
- package/dist/chunk-HZLDFOE4.js.map +0 -1
- package/dist/chunk-JISYG63F.js +0 -70
- package/dist/chunk-JISYG63F.js.map +0 -1
- package/dist/chunk-LIMSTKYD.js +0 -61
- package/dist/chunk-LIMSTKYD.js.map +0 -1
- package/dist/chunk-OWAG3GSU.js.map +0 -1
- package/dist/chunk-PPMP5J6T.js.map +0 -1
- package/dist/chunk-Q5QRDWKI.js.map +0 -1
- package/dist/chunk-S5OFRT4M.js.map +0 -1
- package/dist/chunk-SBVILCCA.js.map +0 -1
- package/dist/chunk-TUMEWN34.js +0 -15
- package/dist/chunk-TUMEWN34.js.map +0 -1
- package/dist/chunk-XDNLUEXI.js +0 -138
- package/dist/chunk-XJ2HZOBU.js.map +0 -1
- package/dist/chunk-ZYTYSTO5.js.map +0 -1
- package/dist/chunk-ZZ2SS7NI.js +0 -237
- package/dist/chunk-ZZ2SS7NI.js.map +0 -1
- package/dist/database-C6jy7EOu.d.ts +0 -500
- package/dist/organisation-D6qRDtbF.d.ts +0 -93
- package/dist/schema-DTDZQe2u.d.ts +0 -28
- package/dist/unified-DQ4VcT7H.d.ts +0 -198
- package/dist/useInactivityTracker-TO6ZOF35.js +0 -11
- package/dist/validation.d.ts +0 -47
- package/dist/validation.js +0 -24
- package/dist/validation.js.map +0 -1
- package/docs/DOCUMENTATION_AUDIT.md +0 -172
- package/docs/DOCUMENTATION_STANDARD.md +0 -137
- package/docs/api/classes/PublicErrorBoundary.md +0 -132
- package/docs/api/interfaces/EventLogoProps.md +0 -152
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +0 -94
- package/docs/api/interfaces/PublicErrorBoundaryState.md +0 -68
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +0 -86
- package/docs/architecture/rpc-function-standards.md +0 -1106
- package/docs/getting-started/consuming-app-vite-config.md +0 -239
- package/docs/implementation-guides/event-theming-summary.md +0 -226
- package/docs/implementation-guides/public-pages-advanced.md +0 -1038
- package/docs/migration/v0.4.15-tailwind-scanning.md +0 -278
- package/docs/migration/v0.4.16-css-first-approach.md +0 -312
- package/docs/migration/v0.4.17-source-path-fix.md +0 -235
- package/docs/rbac/RBAC_EVENT_CONTEXT_LOADING.md +0 -222
- package/docs/rbac/RBAC_LOGIN_SAFETY_FIX.md +0 -95
- package/docs/rbac/RBAC_V0.5.147_FIX.md +0 -117
- package/docs/rbac/README-rbac-rls-integration.md +0 -374
- package/docs/styles/usage.md +0 -227
- package/docs/testing/visual-testing.md +0 -120
- package/docs/troubleshooting/DEBUG_NETWORK_ERROR.md +0 -152
- package/docs/troubleshooting/FIX_SUPABASE_CORS.md +0 -184
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +0 -193
- package/docs/troubleshooting/database-view-compatibility.md +0 -125
- package/docs/troubleshooting/react-hooks-issue-analysis.md +0 -172
- package/docs/troubleshooting/tailwind-content-scanning.md +0 -219
- package/examples/RBAC/EventBasedApp.tsx +0 -239
- package/examples/RBAC/PermissionExample.tsx +0 -151
- package/examples/STRUCTURE.md +0 -125
- package/examples/components 2/DataTable/HierarchicalExample.tsx +0 -475
- package/examples/components 2/Dialog/BasicHtmlTest.tsx +0 -55
- package/examples/components 2/Dialog/DebugHtmlExample.tsx +0 -68
- package/examples/components 2/Dialog/HtmlDialogExample.tsx +0 -202
- package/examples/components 2/Dialog/SimpleHtmlTest.tsx +0 -61
- package/examples/components 2/Dialog/SmartDialogExample.tsx +0 -322
- package/examples/components 2/index.ts +0 -11
- package/examples/features/index.ts +0 -12
- package/examples/features/rbac/CompleteRBACExample.tsx +0 -324
- package/examples/features/rbac/index.ts +0 -13
- package/examples/public-pages/CorrectPublicPageImplementation.tsx +0 -301
- package/examples/public-pages/PublicEventPage.tsx +0 -274
- package/examples/public-pages/PublicPageApp.tsx +0 -308
- package/examples/public-pages/PublicPageUsageExample.tsx +0 -216
- package/examples/public-pages/index.ts +0 -14
- package/src/__tests__/TEST_STANDARD.md +0 -1008
- package/src/components/Checkbox/__mocks__/Checkbox.tsx +0 -2
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +0 -421
- package/src/components/DataTable/examples/InitialPageSizeExample.tsx +0 -177
- package/src/components/DataTable/examples/PerformanceExample.tsx +0 -506
- package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +0 -316
- package/src/components/DataTable/examples/__tests__/HierarchicalExample.test.tsx +0 -45
- package/src/components/DataTable/examples/__tests__/InitialPageSizeExample.test.tsx +0 -211
- package/src/components/DataTable/examples/__tests__/PerformanceExample.test.tsx +0 -126
- package/src/components/Dialog/README.md +0 -804
- package/src/components/Dialog/examples/BasicHtmlTest.tsx +0 -55
- package/src/components/Dialog/examples/DebugHtmlExample.tsx +0 -68
- package/src/components/Dialog/examples/ScrollableDialogExample.tsx +0 -290
- package/src/components/Dialog/examples/SimpleHtmlTest.tsx +0 -61
- package/src/components/Dialog/examples/__tests__/HtmlDialogExample.test.tsx +0 -71
- package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +0 -122
- package/src/components/Dialog/examples/__tests__/SmartDialogExample.unit.test.tsx +0 -147
- package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +0 -611
- package/src/components/Dialog/utils/safeHtml.ts +0 -185
- package/src/components/EventSelector/types.ts +0 -79
- package/src/components/Form/FormErrorSummary.tsx +0 -113
- package/src/components/Form/FormField.tsx +0 -249
- package/src/components/Form/FormFieldset.tsx +0 -127
- package/src/components/Form/FormLiveRegion.tsx +0 -198
- package/src/components/Input/__mocks__/Input.tsx +0 -2
- package/src/components/NavigationMenu/types.ts +0 -85
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +0 -326
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +0 -1078
- package/src/components/PasswordReset/PasswordResetForm.test.tsx +0 -597
- package/src/components/PasswordReset/PasswordResetForm.tsx +0 -201
- package/src/components/PasswordReset/index.ts +0 -2
- package/src/components/ProtectedRoute/README.md +0 -164
- package/src/components/PublicLayout/EventLogo.tsx +0 -175
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +0 -282
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +0 -216
- package/src/components/PublicLayout/PublicPageContextChecker.tsx +0 -131
- package/src/components/PublicLayout/PublicPageDebugger.tsx +0 -104
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +0 -162
- package/src/components/PublicLayout/PublicPageFooter.tsx +0 -124
- package/src/components/PublicLayout/PublicPageHeader.tsx +0 -209
- package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +0 -449
- package/src/components/PublicLayout/__tests__/PublicLoadingSpinner.test.tsx +0 -393
- package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +0 -192
- package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +0 -351
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +0 -402
- package/src/components/PublicLayout/__tests__/PublicPageLayout.test.tsx +0 -460
- package/src/components/PublicLayout/__tests__/PublicPageProvider.test.tsx +0 -313
- package/src/components/Select/hooks.ts +0 -289
- package/src/hooks/useCounter.test.ts +0 -131
- package/src/hooks/useDebounce.test.ts +0 -375
- package/src/providers/AuthProvider.tsx +0 -15
- package/src/providers/EventProvider.tsx +0 -16
- package/src/providers/InactivityProvider.tsx +0 -15
- package/src/providers/OrganisationProvider.context.test.tsx +0 -169
- package/src/providers/UnifiedAuthProvider.tsx +0 -15
- package/src/types/theme.ts +0 -6
- package/src/types/unified.ts +0 -265
- package/src/utils/appConfig.ts +0 -47
- package/src/utils/appIdResolver.test.ts +0 -499
- package/src/utils/appIdResolver.ts +0 -130
- package/src/utils/appNameResolver.simple.test.ts +0 -212
- package/src/utils/appNameResolver.test.ts +0 -121
- package/src/utils/appNameResolver.ts +0 -191
- package/src/utils/audit.ts +0 -127
- package/src/utils/auth-utils.ts +0 -96
- package/src/utils/bundleAnalysis.ts +0 -129
- package/src/utils/debugLogger.ts +0 -67
- package/src/utils/deviceFingerprint.ts +0 -215
- package/src/utils/dynamicUtils.ts +0 -105
- package/src/utils/file-reference.test.ts +0 -788
- package/src/utils/file-reference.ts +0 -519
- package/src/utils/formatDate.test.ts +0 -237
- package/src/utils/formatting.ts +0 -170
- package/src/utils/lazyLoad.tsx +0 -44
- package/src/utils/logger.ts +0 -179
- package/src/utils/organisationContext.test.ts +0 -322
- package/src/utils/organisationContext.ts +0 -153
- package/src/utils/performanceBenchmark.ts +0 -64
- package/src/utils/performanceBudgets.ts +0 -110
- package/src/utils/permissionTypes.ts +0 -37
- package/src/utils/permissionUtils.test.ts +0 -393
- package/src/utils/permissionUtils.ts +0 -34
- package/src/utils/sanitization.ts +0 -264
- package/src/utils/schemaUtils.ts +0 -37
- package/src/utils/secureDataAccess.test.ts +0 -711
- package/src/utils/secureDataAccess.ts +0 -377
- package/src/utils/secureErrors.ts +0 -79
- package/src/utils/security.ts +0 -156
- package/src/utils/securityMonitor.ts +0 -45
- package/src/utils/sessionTracking.ts +0 -126
- package/src/utils/validation.ts +0 -111
- package/src/utils/validationUtils.ts +0 -120
- package/src/validation/index.ts +0 -12
- /package/dist/{DataTable-UA6CL4JI.js.map → DataTable-QAB34V6K.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-B37ATQHE.js.map → UnifiedAuthProvider-7F6T4B6K.js.map} +0 -0
- /package/dist/{api-45XYYO2A.js.map → api-ROMBCNKU.js.map} +0 -0
- /package/dist/{audit-64X3VJXB.js.map → audit-WRS3KJKI.js.map} +0 -0
- /package/dist/{chunk-PLDDJCW6.js.map → chunk-7D4SUZUM.js.map} +0 -0
- /package/dist/{useInactivityTracker-TO6ZOF35.js.map → chunk-KQCRWDSA.js.map} +0 -0
- /package/examples/{components 2/DataTable → DataTable}/InitialPageSizeExample.tsx +0 -0
- /package/examples/{features/public-pages → PublicPages}/index.ts +0 -0
- /package/examples/{RBAC → rbac}/index.ts +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/OrganisationProvider.tsx","../src/hooks/useEventTheme.ts","../src/components/PublicLayout/PublicErrorBoundary.tsx","../src/components/PublicLayout/PublicPageProvider.tsx","../src/hooks/useAppConfig.ts","../src/utils/storage/config.ts","../src/utils/storage/helpers.ts","../src/hooks/useFileDisplay.ts","../src/utils/organisationContext.ts","../src/utils/file-reference.ts","../src/hooks/public/usePublicFileDisplay.ts"],"sourcesContent":["/**\n * @file Re-export for OrganisationProvider\n * @package @jmruthers/pace-core\n * @module Providers\n * @since 0.1.0\n * \n * Re-exports the service-based OrganisationProvider for backward compatibility.\n */\n\nexport { OrganisationServiceProvider as OrganisationProvider } from './services/OrganisationServiceProvider';\nexport type { OrganisationServiceProviderProps as OrganisationProviderProps } from './services/OrganisationServiceProvider';\n\n// Re-export context and hook\nexport { OrganisationServiceContext, useOrganisationService } from './services/OrganisationServiceProvider';\nexport type { OrganisationServiceContextType } from './services/OrganisationServiceProvider';\n\n// Re-export convenience hook for backward compatibility\nexport { useOrganisations } from '../hooks/useOrganisations';\n\n","/**\n * @file Event Theme Hook\n * @package @jmruthers/pace-core\n * @module Hooks/EventTheme\n * @since 2.0.0\n * \n * Hook that automatically applies event-specific theming when an event is selected.\n * This ensures consistent UX across all apps in the pace suite.\n * \n * Works in two modes:\n * 1. Authenticated mode: Uses EventProvider context via useEvents() hook\n * 2. Public page mode: Accepts event prop directly (no EventProvider required)\n * \n * @example\n * ```tsx\n * // Authenticated pages (with EventProvider)\n * import { useEventTheme } from '@jmruthers/pace-core/hooks';\n * \n * function MyApp() {\n * // Automatically applies event colors when event is selected via EventProvider\n * useEventTheme();\n * \n * return <div>Your app content</div>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Public pages (without EventProvider)\n * import { useEventTheme } from '@jmruthers/pace-core/hooks';\n * \n * function PublicPageLayout({ event }) {\n * // Applies event colors directly from event prop\n * useEventTheme(event);\n * \n * return <div>Public page content</div>;\n * }\n * ```\n */\n\nimport { useEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport { useEvents } from './useEvents';\nimport { applyPalette, clearPalette } from '../theming/runtime';\nimport { parseAndNormalizeEventColours } from '../theming/parseEventColours';\nimport type { Event } from '../types/unified';\nimport { createLogger } from '../utils/core/logger';\n\nconst log = createLogger('useEventTheme');\n\n/**\n * Hook that automatically applies event-specific theming\n * \n * This hook applies event colors using the theming system. It works in two modes:\n * \n * **Authenticated Mode (default):**\n * - Uses EventProvider context via useEvents() hook\n * - Automatically watches selectedEvent changes\n * - Used in authenticated pages with EventProvider\n * \n * **Public Page Mode:**\n * - Accepts event prop directly\n * - No EventProvider required\n * - Used in public pages without authentication context\n * \n * Behavior:\n * - Applies event colors when an event with `event_colours` is provided\n * - Clears theming when no event is provided\n * - Skips theme application when on the login route to prevent login screen styling\n * - Handles cleanup and error cases gracefully\n * \n * @param event - Optional event object. If provided, uses this event directly (public page mode).\n * If not provided, uses EventProvider context via useEvents() (authenticated mode).\n * \n * @returns void - This is an effect hook with no return value\n * \n * @example\n * ```tsx\n * // Authenticated mode - uses EventProvider\n * function MyApp() {\n * useEventTheme(); // Watches selectedEvent from EventProvider\n * return <div>App content</div>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Public page mode - uses event prop\n * function PublicPageLayout({ event }) {\n * useEventTheme(event); // Uses event prop directly\n * return <div>Public content</div>;\n * }\n * ```\n */\nexport function useEventTheme(event?: Event | null): void {\n const location = useLocation();\n \n // Try to get event from EventProvider context (authenticated mode)\n // Only use useEvents() if event prop is not provided\n let selectedEvent: Event | null | undefined;\n try {\n if (event === undefined) {\n // No event prop provided, try to use EventProvider context\n const eventsContext = useEvents();\n selectedEvent = eventsContext.selectedEvent;\n } else {\n // Event prop provided, use it directly (public page mode)\n selectedEvent = event;\n }\n } catch (error) {\n // useEvents() throws if EventProvider is not available\n // This is expected for public pages - use the event prop if provided\n if (event !== undefined) {\n selectedEvent = event;\n } else {\n // No event prop and no EventProvider - can't apply theme\n selectedEvent = null;\n }\n }\n\n useEffect(() => {\n // Skip theme application when on login route\n // This prevents event colors from styling the login screen\n const isOnLoginRoute = location.pathname === '/login' || location.pathname.startsWith('/login');\n \n if (isOnLoginRoute) {\n // Clear any active theme when on login route\n clearPalette();\n return;\n }\n\n // If there's no selected event, clear any dynamic theming\n if (!selectedEvent) {\n clearPalette();\n return;\n }\n\n // Check if the event has theme colors\n const eventColours = selectedEvent.event_colours;\n\n // Parse and normalize event_colours using shared utility\n const normalized = parseAndNormalizeEventColours(eventColours);\n \n if (!normalized) {\n clearPalette();\n return;\n }\n\n // Apply the normalized palette\n try {\n applyPalette(normalized);\n } catch (error) {\n log.error('Failed to apply event palette:', error);\n }\n\n // Cleanup function to clear palette when component unmounts or event changes\n return () => {\n // Don't clear on unmount since we want the theme to persist\n // The next event selection will update it\n };\n }, [selectedEvent, location.pathname]);\n}\n","/**\n * @file Public Error Boundary Component\n * @package @jmruthers/pace-core\n * @module Components/PublicLayout\n * @since 1.0.0\n *\n * An error boundary component specifically designed for public pages.\n * Provides graceful error handling and recovery for public content.\n *\n * Features:\n * - Graceful error handling\n * - User-friendly error messages\n * - Recovery mechanisms\n * - Accessibility compliant\n * - TypeScript support\n *\n * @example\n * ```tsx\n * import { PublicErrorBoundary } from '@jmruthers/pace-core';\n *\n * function PublicEventPage() {\n * return (\n * <PublicErrorBoundary>\n * <PublicPageContent />\n * </PublicErrorBoundary>\n * );\n * }\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Screen reader friendly error messages\n * - Keyboard accessible recovery actions\n * - High contrast support\n *\n * @dependencies\n * - React 18+ - Component framework\n * - Error boundary patterns\n * - Tailwind CSS - Styling\n */\n\nimport React, { Component, ErrorInfo, ReactNode } from 'react';\nimport { cn } from '../../utils/core/cn';\n\nexport interface PublicErrorBoundaryProps {\n /** Child components to wrap */\n children: ReactNode;\n /** Custom error fallback component */\n fallback?: React.ComponentType<PublicErrorBoundaryState>;\n /** Custom CSS classes for error display */\n className?: string;\n /** Whether to show error details in development */\n showErrorDetails?: boolean;\n /** Custom error message */\n customErrorMessage?: string;\n /** Custom recovery action */\n onRecover?: () => void;\n}\n\nexport interface PublicErrorBoundaryState {\n /** Whether an error has occurred */\n hasError: boolean;\n /** The error that occurred */\n error: Error | null;\n /** Error information */\n errorInfo: ErrorInfo | null;\n /** Function to reset the error state */\n resetError: () => void;\n}\n\n/**\n * Error boundary component for public pages\n * \n * This component catches JavaScript errors anywhere in the child component tree,\n * logs those errors, and displays a fallback UI instead of the component tree that crashed.\n * \n * @param props - Error boundary configuration\n * @returns React element with error boundary wrapper\n */\nexport class PublicErrorBoundary extends Component<PublicErrorBoundaryProps, PublicErrorBoundaryState> {\n constructor(props: PublicErrorBoundaryProps) {\n super(props);\n this.state = {\n hasError: false,\n error: null,\n errorInfo: null,\n resetError: this.resetError.bind(this)\n };\n }\n\n static getDerivedStateFromError(error: Error): Partial<PublicErrorBoundaryState> {\n // Update state so the next render will show the fallback UI\n return {\n hasError: true,\n error\n };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo) {\n // Log the error to console in development\n if (import.meta.env.MODE === 'development') {\n console.error('PublicErrorBoundary caught an error:', error, errorInfo);\n }\n\n // Update state with error info\n this.setState({\n error,\n errorInfo\n });\n\n // You can also log the error to an error reporting service here\n // Example: logErrorToService(error, errorInfo);\n }\n\n resetError = () => {\n this.setState({\n hasError: false,\n error: null,\n errorInfo: null\n });\n };\n\n render() {\n if (this.state.hasError) {\n // Custom fallback component\n if (this.props.fallback) {\n return <this.props.fallback {...this.state} />;\n }\n\n // Default error UI\n return (\n <div className={cn(\n \"min-h-screen bg-background flex items-center justify-center\",\n this.props.className\n )}>\n <div className=\"max-w-md mx-auto text-center px-4\">\n <div className=\"mb-6\">\n <div className=\"mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-acc-100 mb-4\">\n <svg\n className=\"h-6 w-6 text-acc-600\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z\"\n />\n </svg>\n </div>\n <h1 className=\"text-2xl font-bold text-sec-900 mb-2\">\n Something went wrong\n </h1>\n <p className=\"text-sec-600 mb-6\">\n {this.props.customErrorMessage || \n 'We encountered an error while loading this page. Please try again.'}\n </p>\n </div>\n\n {/* Error details in development */}\n {this.props.showErrorDetails && this.state.error && (\n <div className=\"mb-6 p-4 bg-acc-50 border border-acc-200 rounded-md text-left\">\n <h3 className=\"text-sm font-medium text-acc-800 mb-2\">\n Error Details (Development Only)\n </h3>\n <pre className=\"text-xs text-acc-700 whitespace-pre-wrap\">\n {this.state.error.toString()}\n {this.state.errorInfo?.componentStack}\n </pre>\n </div>\n )}\n\n {/* Recovery actions */}\n <div className=\"space-y-3\">\n <button\n onClick={this.resetError}\n className=\"w-full px-4 py-2 bg-main-600 text-main-50 rounded-md hover:bg-main-700 transition-colors focus:outline-none focus:ring-2 focus:ring-main-500 focus:ring-offset-2\"\n >\n Try Again\n </button>\n \n <button\n onClick={() => window.location.reload()}\n className=\"w-full px-4 py-2 bg-sec-600 text-main-50 rounded-md hover:bg-sec-700 transition-colors focus:outline-none focus:ring-2 focus:ring-sec-500 focus:ring-offset-2\"\n >\n Reload Page\n </button>\n\n {this.props.onRecover && (\n <button\n onClick={this.props.onRecover}\n className=\"w-full px-4 py-2 bg-main-600 text-main-50 rounded-md hover:bg-main-700 transition-colors focus:outline-none focus:ring-2 focus:ring-main-500 focus:ring-offset-2\"\n >\n Alternative Action\n </button>\n )}\n </div>\n\n {/* Help text */}\n <div className=\"mt-6 text-sm text-sec-500\">\n <p>\n If this problem persists, please contact support or try accessing the page later.\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\n/**\n * Hook for accessing error boundary state\n * Useful for components that need to know if they're inside an error boundary\n */\nexport function useErrorBoundary() {\n const [error, setError] = React.useState<Error | null>(null);\n\n const resetError = React.useCallback(() => {\n setError(null);\n }, []);\n\n const captureError = React.useCallback((error: Error) => {\n setError(error);\n }, []);\n\n // Throw error synchronously during render so error boundary can catch it\n if (error) {\n throw error;\n }\n\n return { captureError, resetError };\n}\n\n/**\n * Default error fallback component\n */\nexport function DefaultPublicErrorFallback({ \n error, \n resetError \n}: PublicErrorBoundaryState) {\n return (\n <div className=\"min-h-screen bg-background flex items-center justify-center\">\n <div className=\"max-w-md mx-auto text-center px-4\">\n <div className=\"mb-6\">\n <div className=\"mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-acc-100 mb-4\">\n <svg\n className=\"h-6 w-6 text-acc-600\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z\"\n />\n </svg>\n </div>\n <h1 className=\"text-2xl font-bold text-sec-900 mb-2\">\n Page Error\n </h1>\n <p className=\"text-sec-600 mb-6\">\n We encountered an error while loading this page.\n </p>\n </div>\n\n <button\n onClick={resetError}\n className=\"w-full px-4 py-2 bg-main-600 text-main-50 rounded-md hover:bg-main-700 transition-colors focus:outline-none focus:ring-2 focus:ring-main-500 focus:ring-offset-2\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n}\n","/**\n * @file Public Page Provider\n * @package @jmruthers/pace-core\n * @module Components/PublicLayout\n * @since 1.0.0\n *\n * A completely isolated provider for public pages that doesn't trigger\n * any authentication context. This ensures public pages work independently\n * of the main application's authentication system.\n *\n * Features:\n * - No authentication required\n * - No organisation context\n * - No event context\n * - Completely isolated from main app context\n * - Environment variable access for public data\n * - Error boundary integration\n *\n * @example\n * ```tsx\n * import { PublicPageProvider } from '@jmruthers/pace-core';\n * \n * const APP_NAME = 'CORE';\n * \n * function PublicApp() {\n * return (\n * <PublicPageProvider appName={APP_NAME}>\n * <Routes>\n * <Route path=\"/events/:eventCode/recipe-grid-report\" element={<PublicRecipePage />} />\n * </Routes>\n * </PublicPageProvider>\n * );\n * }\n * ```\n */\n\nimport React, { createContext, useContext, ReactNode, useMemo } from 'react';\nimport { createClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { PublicErrorBoundary } from './PublicErrorBoundary';\nimport { logger } from '../../utils/core/logger';\n\ninterface PublicPageContextType {\n isPublicPage: true;\n supabase: ReturnType<typeof createClient<Database>> | null;\n appName: string | null;\n environment: {\n supabaseUrl: string | null;\n supabaseKey: string | null;\n };\n}\n\nexport const PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);\n\nexport interface PublicPageProviderProps {\n children: ReactNode;\n /** Application name for logo display and branding. Should match the APP_NAME constant used in App.tsx */\n appName?: string;\n}\n\n/**\n * Provider for public pages that completely isolates them from authentication context\n * \n * This provider:\n * - Does not initialize any authentication providers\n * - Provides environment variables for public data access\n * - Includes error boundary for graceful error handling\n * - Is completely separate from the main app context\n * - Provides appName for consistent logo display\n */\nexport function PublicPageProvider({ children, appName }: PublicPageProviderProps) {\n // Get environment variables for public data access\n // Handle both Vite (import.meta.env) and Node.js (process.env) environments\n const getEnvVar = (key: string): string | undefined => {\n // Check Vite environment first (browser)\n if (typeof import.meta !== 'undefined' && (import.meta as any).env) {\n return (import.meta as any).env[key];\n }\n // Check Node.js environment (server-side)\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n return import.meta.env[key];\n }\n return undefined;\n };\n\n const supabaseUrl = getEnvVar('VITE_SUPABASE_URL') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_URL') || \n null;\n \n const supabaseKey = getEnvVar('VITE_SUPABASE_ANON_KEY') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_ANON_KEY') || \n null;\n\n // Create Supabase client if environment variables are available\n // Note: VITE_SUPABASE_ANON_KEY accepts both legacy anon keys and modern publishable keys (sb_publishable_...)\n const supabase = useMemo(() => {\n if (!supabaseUrl || !supabaseKey) {\n logger.warn('PublicPageProvider', 'Missing Supabase environment variables. Please ensure VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY are set in your environment. Use publishable key if anon key is disabled.');\n return null;\n }\n const client = createClient<Database>(supabaseUrl, supabaseKey);\n logger.info('PublicPageProvider', 'Supabase client created successfully for public pages');\n return client;\n }, [supabaseUrl, supabaseKey]);\n\n const contextValue: PublicPageContextType = {\n isPublicPage: true,\n supabase,\n appName: appName || null,\n environment: {\n supabaseUrl,\n supabaseKey\n }\n };\n\n return (\n <PublicPageContext.Provider value={contextValue}>\n <PublicErrorBoundary>\n {children}\n </PublicErrorBoundary>\n </PublicPageContext.Provider>\n );\n}\n\n/**\n * Hook to access public page context\n * \n * @returns Public page context with environment variables\n */\nexport function usePublicPageContext(): PublicPageContextType {\n const context = useContext(PublicPageContext);\n \n if (!context) {\n throw new Error('usePublicPageContext must be used within a PublicPageProvider');\n }\n \n return context;\n}\n\n/**\n * Hook to check if we're in a public page context\n * \n * @returns True if we're in a public page context\n */\nexport function useIsPublicPage(): boolean {\n const context = useContext(PublicPageContext);\n return context !== undefined;\n}\n","/**\n * @file useAppConfig Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useAppConfig\n * @since 0.4.0\n *\n * Hook for accessing app configuration like direct access support and event requirements.\n * This is a convenience hook that extracts app config from the UnifiedAuthProvider.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { supportsDirectAccess, requiresEvent, isLoading } = useAppConfig();\n * \n * if (isLoading) return <div>Loading...</div>;\n * \n * return (\n * <div>\n * {supportsDirectAccess && (\n * <div>This app supports direct access!</div>\n * )}\n * {requiresEvent && (\n * <EventSelector />\n * )}\n * </div>\n * );\n * }\n * ```\n */\n\nimport { useMemo, useContext } from 'react';\nimport { useUnifiedAuth } from '../providers/UnifiedAuthProvider';\nimport { useIsPublicPage, PublicPageContext } from '../components/PublicLayout/PublicPageProvider';\n\nexport interface UseAppConfigReturn {\n supportsDirectAccess: boolean;\n requiresEvent: boolean;\n isLoading: boolean;\n appName: string;\n}\n\n/**\n * Hook to access app configuration\n * Works in both authenticated and public contexts\n * @returns App configuration and loading state\n */\nexport function useAppConfig(): UseAppConfigReturn {\n // Check if we're in a public page context first\n const isPublicPage = useIsPublicPage();\n \n // Try to get appName from PublicPageContext (access context directly to avoid hook rule violations)\n const publicPageContext = useContext(PublicPageContext);\n const contextAppName = publicPageContext?.appName || null;\n \n if (isPublicPage) {\n const getAppName = (): string => {\n // Priority 1: Use appName from PublicPageContext (passed from App.tsx)\n if (contextAppName) {\n return contextAppName;\n }\n \n // Priority 2: Check environment variables\n if (typeof import.meta !== 'undefined' && (import.meta as any).env) {\n return (import.meta as any).env.VITE_APP_NAME || \n (import.meta as any).env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n return import.meta.env.VITE_APP_NAME || \n import.meta.env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n \n // Priority 3: Default fallback\n return 'PACE';\n };\n \n return useMemo(() => ({\n supportsDirectAccess: false, // Public pages don't support direct access\n requiresEvent: true, // Public pages always require an event\n isLoading: false,\n appName: getAppName()\n }), [contextAppName]);\n }\n \n // For authenticated pages, use UnifiedAuthProvider\n try {\n const { appConfig, appName } = useUnifiedAuth();\n return useMemo(() => ({\n supportsDirectAccess: !(appConfig?.requires_event ?? true),\n requiresEvent: appConfig?.requires_event ?? true,\n isLoading: appConfig === null,\n appName\n }), [appConfig?.requires_event, appName]);\n } catch (error) {\n // Fallback if UnifiedAuthProvider is not available\n return useMemo(() => ({\n supportsDirectAccess: false,\n requiresEvent: true,\n isLoading: false,\n appName: 'PACE'\n }), []);\n }\n} ","/**\n * Storage configuration for pace-core\n */\n\nimport { FileSizeLimits, StorageConfig } from './types';\n\n/**\n * File size limits by MIME type (in bytes)\n */\nexport const FILE_SIZE_LIMITS: FileSizeLimits = {\n // Images\n 'image/jpeg': 5 * 1024 * 1024, // 5MB\n 'image/png': 5 * 1024 * 1024, // 5MB\n 'image/gif': 10 * 1024 * 1024, // 10MB (for animations)\n 'image/webp': 5 * 1024 * 1024, // 5MB\n 'image/svg+xml': 1 * 1024 * 1024, // 1MB (vector graphics)\n \n // Documents\n 'application/pdf': 50 * 1024 * 1024, // 50MB\n 'application/msword': 25 * 1024 * 1024, // 25MB\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 25 * 1024 * 1024, // 25MB\n 'application/vnd.ms-excel': 25 * 1024 * 1024, // 25MB\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 25 * 1024 * 1024, // 25MB\n \n // Archives\n 'application/zip': 100 * 1024 * 1024, // 100MB\n 'application/x-rar-compressed': 100 * 1024 * 1024, // 100MB\n \n // Text files\n 'text/plain': 1 * 1024 * 1024, // 1MB\n 'text/csv': 10 * 1024 * 1024, // 10MB\n 'application/json': 10 * 1024 * 1024, // 10MB\n};\n\n/**\n * Default file size limit for unknown MIME types\n */\nexport const DEFAULT_FILE_SIZE_LIMIT = 10 * 1024 * 1024; // 10MB\n\n/**\n * App-specific path mappings for file organization\n * Maps app names to their primary category in the organization-first structure\n */\nexport const APP_PATH_MAPPING: Record<string, string> = {\n 'PACE': 'event_logos',\n 'TRAC': 'trac_accommodation', // Default category for TRAC files\n 'MEDI': 'documents',\n 'CAKE': 'documents'\n};\n\n/**\n * Storage configuration\n */\nexport const STORAGE_CONFIG: StorageConfig = {\n bucketName: 'files',\n fileSizeLimits: FILE_SIZE_LIMITS,\n defaultFileSizeLimit: DEFAULT_FILE_SIZE_LIMIT,\n};\n\n/**\n * Get the file size limit for a given MIME type\n */\nexport function getFileSizeLimit(mimeType: string): number {\n return STORAGE_CONFIG.fileSizeLimits[mimeType] || STORAGE_CONFIG.defaultFileSizeLimit;\n}\n\n/**\n * Get the bucket name based on whether the file is public or private\n * @param isPublic - Whether the file should be publicly accessible\n * @returns The bucket name: 'public-files' for public files, 'files' for private files\n */\nexport function getBucketName(isPublic: boolean): 'files' | 'public-files' {\n return isPublic ? 'public-files' : 'files';\n}\n\n/**\n * Validate file size against limits\n */\nexport function validateFileSize(file: File): { isValid: boolean; error?: string } {\n const limit = getFileSizeLimit(file.type);\n \n if (file.size > limit) {\n const limitMB = Math.round(limit / (1024 * 1024));\n const fileMB = Math.round(file.size / (1024 * 1024));\n return {\n isValid: false,\n error: `File size (${fileMB}MB) exceeds limit (${limitMB}MB) for ${file.type}`\n };\n }\n \n return { isValid: true };\n}\n\n/**\n * Get human-readable file size\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n if (bytes < 0) return `${bytes} Bytes`; // Handle negative numbers\n \n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n \n // Ensure we don't exceed the available size units\n const sizeIndex = Math.min(Math.max(i, 0), sizes.length - 1);\n \n return parseFloat((bytes / Math.pow(k, sizeIndex)).toFixed(2)) + ' ' + sizes[sizeIndex];\n}\n","/**\n * Storage helper functions for pace-core\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { \n StorageUploadOptions, \n StorageUploadResult, \n StorageFileMetadata,\n StorageUrlOptions,\n StorageListOptions,\n StorageListResult,\n StorageFileInfo\n} from './types';\nimport { validateFileSize, STORAGE_CONFIG, getBucketName } from './config';\nimport { createLogger } from '../core/logger';\n\nconst log = createLogger('StorageHelpers');\n\n/**\n * Generate a file path based on organization-first structure\n */\nexport function generateFilePath(options: StorageUploadOptions, fileName: string): string {\n const { orgId, isPublic = false, customPath } = options;\n \n \n // Validate required orgId\n if (!orgId) {\n throw new Error('orgId is required for file path generation');\n }\n \n if (isPublic) {\n // Public files go to {orgId}/{category}/filename\n if (customPath) {\n return `${orgId}/${customPath}/${fileName}`;\n }\n return `${orgId}/public/${fileName}`;\n }\n \n // Organization-first structure: {orgId}/{category}/filename\n if (customPath) {\n return `${orgId}/${customPath}/${fileName}`;\n }\n \n // Use customPath if available, otherwise default to files\n const pathCategory = customPath || 'files';\n return `${orgId}/${pathCategory}/${fileName}`;\n}\n\n/**\n * Generate a unique filename with timestamp and UUID\n * Sanitizes the original filename to remove spaces and invalid characters for storage compatibility\n */\nexport function generateUniqueFileName(originalName: string): string {\n const timestamp = Date.now();\n const uuid = crypto.randomUUID();\n const extension = originalName.split('.').pop() || '';\n let baseName = originalName.replace(/\\.[^/.]+$/, '');\n \n // Sanitize the base name for storage compatibility\n // Replace spaces with underscores and remove invalid characters\n baseName = baseName\n .trim()\n .replace(/\\s+/g, '_') // Replace spaces with underscores\n .replace(/[<>:\"/\\\\|?*]/g, '') // Remove invalid file name characters\n .replace(/\\.\\./g, '') // Remove directory traversal attempts\n .replace(/^\\.+|\\.+$/g, '') // Remove leading/trailing dots\n .substring(0, 200); // Limit length to leave room for timestamp and UUID\n \n // If there's no extension, don't add one\n if (!extension || extension === originalName) {\n return `${timestamp}-${uuid}-${baseName}`;\n }\n \n return `${timestamp}-${uuid}-${baseName}.${extension}`;\n}\n\n/**\n * Extract file metadata from a File object\n */\nexport async function extractFileMetadata(\n file: File, \n options: StorageUploadOptions,\n uploadedBy: string\n): Promise<StorageFileMetadata> {\n const metadata: StorageFileMetadata = {\n mimeType: file.type,\n size: file.size,\n orgId: options.orgId,\n appName: options.appName || 'pace-core',\n uploadedBy,\n uploadedAt: new Date().toISOString(),\n tags: options.tags || [],\n isPublic: options.isPublic || false,\n customMetadata: options.metadata || {}\n };\n\n // Extract image dimensions if it's an image\n if (file.type.startsWith('image/')) {\n try {\n const dimensions = await getImageDimensions(file);\n metadata.width = dimensions.width;\n metadata.height = dimensions.height;\n } catch (error) {\n // Non-critical error - image dimensions are optional metadata\n // Using Logger would be better, but this is in a utility function\n // For now, silently continue - dimensions are optional\n }\n }\n\n // Generate file hash if possible\n try {\n metadata.hash = await generateFileHash(file);\n } catch (error) {\n // Non-critical error - file hash is optional metadata\n // Using Logger would be better, but this is in a utility function\n // For now, silently continue - hash is optional\n }\n\n return metadata;\n}\n\n/**\n * Get image dimensions from a File object\n */\nasync function getImageDimensions(file: File): Promise<{ width: number; height: number }> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n const url = URL.createObjectURL(file);\n \n img.onload = () => {\n URL.revokeObjectURL(url);\n resolve({ width: img.width, height: img.height });\n };\n \n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('Could not load image'));\n };\n \n img.src = url;\n });\n}\n\n/**\n * Generate a hash for a file\n */\nasync function generateFileHash(file: File): Promise<string> {\n const buffer = await file.arrayBuffer();\n const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n return `sha256:${hashHex}`;\n}\n\n/**\n * Upload a file to Supabase storage with app segregation\n */\nexport async function uploadFile(\n supabase: SupabaseClient,\n file: File,\n options: StorageUploadOptions\n): Promise<StorageUploadResult> {\n try {\n // Validate file size\n const sizeValidation = validateFileSize(file);\n if (!sizeValidation.isValid) {\n return {\n success: false,\n error: sizeValidation.error\n };\n }\n\n // Generate unique filename and path\n const uniqueFileName = generateUniqueFileName(file.name);\n const filePath = generateFilePath(options, uniqueFileName);\n\n // Extract metadata\n const metadata = await extractFileMetadata(file, options, 'current-user'); // TODO: Get actual user ID\n\n // Select bucket based on isPublic flag\n const bucketName = getBucketName(options.isPublic || false);\n\n // Upload file to Supabase\n const { data, error } = await supabase.storage\n .from(bucketName)\n .upload(filePath, file, {\n cacheControl: '3600',\n upsert: false,\n contentType: file.type\n });\n\n if (error) {\n return {\n success: false,\n error: `Upload failed: ${error.message}`\n };\n }\n\n // Generate public URL if file is public\n let publicUrl: string | undefined;\n if (options.isPublic) {\n const { data: urlData } = supabase.storage\n .from(bucketName)\n .getPublicUrl(filePath);\n publicUrl = urlData.publicUrl;\n }\n\n return {\n success: true,\n path: filePath,\n publicUrl,\n metadata\n };\n\n } catch (error) {\n return {\n success: false,\n error: `Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n };\n }\n}\n\n/**\n * Get a public URL for a file\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param isPublic - Whether the file is in the public-files bucket (default: true)\n * @returns Public URL for the file\n */\nexport function getPublicUrl(\n supabase: SupabaseClient,\n path: string,\n isPublic: boolean = true\n): string {\n if (!supabase) {\n throw new Error('Supabase client is required to generate a public URL');\n }\n\n if (!path || typeof path !== 'string') {\n throw new Error('A valid storage path is required to generate a public URL');\n }\n\n // If the path is already an absolute URL, return it directly\n if (/^https?:\\/\\//i.test(path)) {\n return path;\n }\n\n // Normalise path by trimming whitespace and leading slashes\n let normalisedPath = path.trim().replace(/^\\/+/, '');\n\n if (!normalisedPath) {\n throw new Error('Storage path cannot be empty after normalisation');\n }\n\n const { bucketNameFromPath, storagePath } = resolveBucketHint(normalisedPath);\n\n const bucketName = bucketNameFromPath || getBucketName(isPublic);\n\n const { data } = supabase.storage\n .from(bucketName)\n .getPublicUrl(storagePath);\n\n return data.publicUrl;\n}\n\nfunction resolveBucketHint(pathWithPotentialBucket: string): { bucketNameFromPath: string | null; storagePath: string } {\n const BUCKET_NAME_PATTERN = /^[a-z0-9][a-z0-9-_\\.]{1,62}$/i;\n const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n const KNOWN_BUCKET_NAMES = new Set(['files', 'public-files']);\n\n const trimmedPath = pathWithPotentialBucket.trim();\n\n if (!trimmedPath) {\n throw new Error('Storage path cannot be empty after normalisation');\n }\n\n // Support \"bucket::path\" notation to remove ambiguity with colon usage in file names.\n const doubleColonIndex = trimmedPath.indexOf('::');\n if (doubleColonIndex > 0) {\n const potentialBucket = trimmedPath.slice(0, doubleColonIndex).trim();\n const remainingPath = trimmedPath.slice(doubleColonIndex + 2).replace(/^\\/+/, '');\n\n if (potentialBucket && remainingPath && BUCKET_NAME_PATTERN.test(potentialBucket)) {\n return {\n bucketNameFromPath: potentialBucket,\n storagePath: remainingPath\n };\n }\n }\n\n // Support \"bucket/path\" hints, provided the first segment looks like a bucket name and not a directory prefix.\n const firstSlashIndex = trimmedPath.indexOf('/');\n if (firstSlashIndex > 0) {\n const potentialBucket = trimmedPath.slice(0, firstSlashIndex).trim();\n const remainingPath = trimmedPath.slice(firstSlashIndex + 1).replace(/^\\/+/, '');\n\n if (\n potentialBucket &&\n remainingPath &&\n BUCKET_NAME_PATTERN.test(potentialBucket) &&\n !UUID_PATTERN.test(potentialBucket) &&\n (KNOWN_BUCKET_NAMES.has(potentialBucket) || !potentialBucket.includes('-')) &&\n !/^\\d+$/.test(potentialBucket)\n ) {\n return {\n bucketNameFromPath: potentialBucket,\n storagePath: remainingPath\n };\n }\n }\n\n return {\n bucketNameFromPath: null,\n storagePath: trimmedPath\n };\n}\n\n/**\n * Get a signed URL for a protected file\n * Private files are always in the 'files' bucket, so this always uses 'files' bucket\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param options - URL options including expiry time\n * @returns Signed URL with expiration timestamp, or null if failed\n */\nexport async function getSignedUrl(\n supabase: SupabaseClient,\n path: string,\n options: StorageUrlOptions\n): Promise<{ url: string; expiresAt: string } | null> {\n try {\n // Signed URLs are only for private files, which are always in the 'files' bucket\n const bucketName = getBucketName(false);\n \n const { data, error } = await supabase.storage\n .from(bucketName)\n .createSignedUrl(path, options.expiresIn || 3600);\n\n if (error) {\n log.error('Failed to create signed URL:', error);\n return null;\n }\n\n return {\n url: data.signedUrl,\n expiresAt: new Date(Date.now() + (options.expiresIn || 3600) * 1000).toISOString()\n };\n } catch (error) {\n log.error('Failed to create signed URL:', error);\n return null;\n }\n}\n\n/**\n * Delete a file from storage\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param isPublic - Whether the file is in the public-files bucket (default: false)\n * @returns Success status and optional error message\n */\nexport async function deleteFile(\n supabase: SupabaseClient,\n path: string,\n isPublic: boolean = false\n): Promise<{ success: boolean; error?: string }> {\n try {\n const bucketName = getBucketName(isPublic);\n \n const { error } = await supabase.storage\n .from(bucketName)\n .remove([path]);\n\n if (error) {\n return {\n success: false,\n error: `Delete failed: ${error.message}`\n };\n }\n\n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: `Delete failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n };\n }\n}\n\n/**\n * List files in an organization scope\n * @param supabase - Supabase client instance\n * @param options - List options including bucket selection via isPublic\n * @returns List of files with metadata\n */\nexport async function listFiles(\n supabase: SupabaseClient,\n options: StorageListOptions & { isPublic?: boolean }\n): Promise<StorageListResult> {\n try {\n // Select bucket based on isPublic flag (default to private files bucket)\n const bucketName = getBucketName(options.isPublic || false);\n \n // Organization-first structure: {orgId}/{category}/\n const pathPrefix = `${options.orgId}/`;\n const searchPath = options.pathPrefix ? `${pathPrefix}${options.pathPrefix}` : pathPrefix;\n\n const { data, error } = await supabase.storage\n .from(bucketName)\n .list(searchPath, {\n limit: options.limit || 100,\n offset: options.offset || 0,\n sortBy: { column: 'created_at', order: 'desc' }\n });\n\n if (error) {\n log.error('Failed to list files:', error);\n return { files: [], totalCount: 0, hasMore: false };\n }\n\n const files: StorageFileInfo[] = (data || []).map(item => ({\n name: item.name,\n path: `${searchPath}${item.name}`,\n size: item.metadata?.size || 0,\n mimeType: item.metadata?.mimetype || 'application/octet-stream',\n lastModified: item.updated_at || item.created_at || new Date().toISOString(),\n metadata: {\n mimeType: item.metadata?.mimetype || 'application/octet-stream',\n size: item.metadata?.size || 0,\n orgId: options.orgId,\n appName: options.appName,\n uploadedBy: 'unknown',\n uploadedAt: item.created_at || new Date().toISOString(),\n isPublic: options.isPublic || false\n }\n }));\n\n return {\n files,\n totalCount: files.length,\n hasMore: files.length >= (options.limit || 100)\n };\n } catch (error) {\n log.error('Failed to list files:', error);\n return { files: [], totalCount: 0, hasMore: false };\n }\n}\n\n/**\n * Download a file from storage\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param isPublic - Whether the file is in the public-files bucket (default: false)\n * @returns File blob with metadata, or null if failed\n */\nexport async function downloadFile(\n supabase: SupabaseClient,\n path: string,\n isPublic: boolean = false\n): Promise<{ blob: Blob; metadata: { name: string; size: number; type: string } } | null> {\n try {\n const bucketName = getBucketName(isPublic);\n \n const { data, error } = await supabase.storage\n .from(bucketName)\n .download(path);\n\n if (error) {\n log.error('Failed to download file:', error);\n return null;\n }\n\n if (!data) {\n return null;\n }\n\n // Extract file name from path\n const fileName = path.split('/').pop() || 'download';\n \n // Get file metadata\n const { data: fileInfo } = await supabase.storage\n .from(bucketName)\n .list(path.split('/').slice(0, -1).join('/'), {\n search: fileName\n });\n\n const metadata = fileInfo?.[0]?.metadata || {};\n \n return {\n blob: data,\n metadata: {\n name: fileName,\n size: metadata.size || data.size,\n type: metadata.mimetype || 'application/octet-stream'\n }\n };\n } catch (error) {\n log.error('Failed to download file:', error);\n return null;\n }\n}\n\n/**\n * Move a file to archived location (soft delete)\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param options - Archive options including bucket selection via isPublic\n */\nexport async function archiveFile(\n supabase: SupabaseClient,\n path: string,\n options: { appName: string; orgId: string; isPublic?: boolean }\n): Promise<{ success: boolean; error?: string }> {\n try {\n const bucketName = getBucketName(options.isPublic || false);\n \n // Generate archived path for organization-first structure\n const archivedPath = path.replace(`${options.orgId}/`, `archived/${options.orgId}/`);\n \n // Copy file to archived location\n const { error: copyError } = await supabase.storage\n .from(bucketName)\n .copy(path, archivedPath);\n\n if (copyError) {\n return {\n success: false,\n error: `Archive failed: ${copyError.message}`\n };\n }\n\n // Delete original file\n const deleteResult = await deleteFile(supabase, path, options.isPublic || false);\n if (!deleteResult.success) {\n return deleteResult;\n }\n\n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: `Archive failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n };\n }\n}\n","/**\n * @file File Display Hook (Authenticated)\n * @package @jmruthers/pace-core\n * @module Hooks\n *\n * A React hook for accessing file references in authenticated contexts.\n * Can handle both public and private files using the file_references system.\n *\n * Features:\n * - Works in authenticated contexts\n * - Supports both public and private files\n * - Automatic signed URL generation for private files\n * - Caching for performance\n * - Error handling and loading states\n * - Supports both single file (category) and multiple files\n *\n * @example\n * ```tsx\n * import { useFileDisplay } from '@jmruthers/pace-core';\n *\n * function FileView() {\n * const { fileUrl, fileReference, isLoading, error } = useFileDisplay(\n * 'event',\n * eventId,\n * organisationId,\n * FileCategory.EVENT_LOGOS\n * );\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return fileUrl ? <img src={fileUrl} alt=\"File\" /> : null;\n * }\n * ```\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport { FileReference, FileCategory } from '../types/file-reference';\nimport { getPublicUrl, getSignedUrl } from '../utils/storage/helpers';\nimport { createFileReferenceService } from '../utils/file-reference';\nimport { logger } from '../utils/core/logger';\n\n// Simple in-memory cache for authenticated file data\nconst authenticatedFileCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\n// Cache size limit to prevent memory leaks\nconst MAX_CACHE_SIZE = 100;\n\n// Helper function to clean up expired entries and enforce size limit\nfunction cleanupCache() {\n const now = Date.now();\n const entries = Array.from(authenticatedFileCache.entries());\n \n // Remove expired entries\n const expiredKeys: string[] = [];\n entries.forEach(([key, value]) => {\n if (now - value.timestamp >= value.ttl) {\n expiredKeys.push(key);\n }\n });\n expiredKeys.forEach(key => authenticatedFileCache.delete(key));\n \n // If still over limit, remove oldest entries\n if (authenticatedFileCache.size > MAX_CACHE_SIZE) {\n const sorted = entries\n .filter(([key]) => !expiredKeys.includes(key))\n .sort((a, b) => a[1].timestamp - b[1].timestamp);\n const toRemove = sorted.slice(0, authenticatedFileCache.size - MAX_CACHE_SIZE);\n toRemove.forEach(([key]) => authenticatedFileCache.delete(key));\n }\n}\n\nexport interface UseFileDisplayReturn {\n /** Single file URL if category is provided and file found, null otherwise */\n fileUrl: string | null;\n /** Single file reference if category is provided and file found, null otherwise */\n fileReference: FileReference | null;\n /** Array of all file references for the record (when category not provided or for multiple files) */\n fileReferences: FileReference[];\n /** Map of file IDs to URLs for multiple files */\n fileUrls: Map<string, string>;\n /** Total count of files for the record */\n fileCount: number;\n /** Whether the data is currently loading */\n isLoading: boolean;\n /** Any error that occurred during loading */\n error: Error | null;\n /** Function to manually refetch the data */\n refetch: () => Promise<void>;\n}\n\nexport interface UseFileDisplayOptions {\n /** Cache TTL in milliseconds (default: 30 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n /** Supabase client instance (required) */\n supabase: SupabaseClient | null;\n}\n\n/**\n * Hook for accessing file references in authenticated contexts\n * \n * This hook provides access to file references for authenticated users.\n * It supports both public and private files, generating appropriate URLs\n * (public URLs for public files, signed URLs for private files).\n * \n * @param table_name - The table name containing the file reference\n * @param record_id - The record ID that owns the file(s)\n * @param organisation_id - The organisation ID for storage path\n * @param category - Optional file category to filter by (for single file mode)\n * @param options - Configuration options for caching and behavior\n * @returns Object containing file data, loading state, error, and refetch function\n */\nexport function useFileDisplay(\n table_name: string | undefined,\n record_id: string | undefined,\n organisation_id: string | undefined,\n category: FileCategory | undefined,\n options: UseFileDisplayOptions\n): UseFileDisplayReturn {\n const {\n cacheTtl = 30 * 60 * 1000, // 30 minutes\n enableCache = true,\n supabase\n } = options;\n\n const [fileUrl, setFileUrl] = useState<string | null>(null);\n const [fileReference, setFileReference] = useState<FileReference | null>(null);\n const [fileReferences, setFileReferences] = useState<FileReference[]>([]);\n const [fileUrls, setFileUrls] = useState<Map<string, string>>(new Map());\n const [fileCount, setFileCount] = useState<number>(0);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchFiles = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id || !supabase) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n return;\n }\n\n // Validate UUID format for organisationId to prevent database errors\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(organisation_id)) {\n logger.warn('useFileDisplay', 'Invalid organisationId format (not a valid UUID):', organisation_id);\n }\n\n // Check cache first\n const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n if (enableCache) {\n const cached = authenticatedFileCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n const cachedData = cached.data;\n \n // FIX: Regenerate signed URL for private files if missing\n if (cachedData.fileReference && !cachedData.fileUrl && \n cachedData.fileReference.is_public === false && supabase) {\n // Regenerate signed URL without fetching from database\n try {\n const signedUrlResult = await getSignedUrl(supabase, cachedData.fileReference.file_path, {\n appName: 'pace-core',\n orgId: organisation_id,\n expiresIn: 3600\n });\n const regeneratedUrl = signedUrlResult?.url || null;\n setFileUrl(regeneratedUrl);\n setFileReference(cachedData.fileReference);\n setFileReferences(cachedData.fileReferences || []);\n setFileUrls(cachedData.fileUrls || new Map());\n setFileCount(cachedData.fileCount || 0);\n setIsLoading(false);\n setError(null);\n return;\n } catch (err) {\n // If signed URL regeneration fails, fall through to normal fetch\n logger.warn('useFileDisplay', 'Failed to regenerate signed URL from cache, falling back to fetch:', err);\n }\n }\n \n // Normal cache hit for public files or files with URLs\n setFileUrl(cachedData.fileUrl || null);\n setFileReference(cachedData.fileReference || null);\n setFileReferences(cachedData.fileReferences || []);\n setFileUrls(cachedData.fileUrls || new Map());\n setFileCount(cachedData.fileCount || 0);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n const service = createFileReferenceService(supabase);\n let files: FileReference[] = [];\n\n // CRITICAL: When category is provided, MUST use RPC function, not direct queries\n // Category is stored in file_metadata JSONB field, not a direct column\n if (category) {\n // Single file mode - get files by category using RPC\n logger.debug('useFileDisplay', 'Using RPC function for category filtering:', {\n table_name,\n record_id,\n category,\n organisation_id\n });\n files = await service.getFilesByCategory(\n table_name,\n record_id,\n category,\n organisation_id\n );\n } else {\n // Multiple files mode - get all files using RPC\n files = await service.listFileReferences(\n table_name,\n record_id,\n organisation_id\n );\n }\n\n if (files.length === 0) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n \n // Cache empty result\n if (enableCache) {\n authenticatedFileCache.set(cacheKey, {\n data: { fileUrl: null, fileReference: null, fileReferences: [], fileUrls: new Map(), fileCount: 0 },\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n cleanupCache();\n }\n return;\n }\n\n setFileReferences(files);\n setFileCount(files.length);\n\n if (category && files.length > 0) {\n // Single file mode - get first file\n const firstFile = files[0];\n logger.debug('useFileDisplay', 'Processing category files, first file:', {\n id: firstFile.id,\n file_path: firstFile.file_path,\n is_public: firstFile.is_public,\n has_file_metadata: !!firstFile.file_metadata,\n category_in_metadata: firstFile.file_metadata?.category\n });\n \n setFileReference(firstFile);\n \n // Generate URL based on file visibility\n let url: string | null = null;\n if (firstFile.is_public) {\n url = getPublicUrl(supabase, firstFile.file_path, true);\n logger.debug('useFileDisplay', 'Generated public URL:', url);\n } else {\n const signedUrlResult = await getSignedUrl(supabase, firstFile.file_path, {\n appName: 'pace-core',\n orgId: organisation_id,\n expiresIn: 3600\n });\n url = signedUrlResult?.url || null;\n logger.debug('useFileDisplay', 'Generated signed URL:', url ? 'URL generated' : 'URL generation failed');\n }\n logger.debug('useFileDisplay', 'Setting file URL:', url ? 'URL set' : 'URL is null');\n setFileUrl(url);\n } else {\n // Multiple files mode - generate URLs for all files\n const urlMap = new Map<string, string>();\n for (const fileRef of files) {\n let url: string | null = null;\n if (fileRef.is_public) {\n url = getPublicUrl(supabase, fileRef.file_path, true);\n } else {\n const signedUrlResult = await getSignedUrl(supabase, fileRef.file_path, {\n appName: 'pace-core',\n orgId: organisation_id,\n expiresIn: 3600\n });\n url = signedUrlResult?.url || null;\n }\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n setFileUrls(urlMap);\n setFileReference(null);\n setFileUrl(null);\n }\n\n // Cache the result\n if (enableCache) {\n // Prepare cache data\n let cacheData: any = {\n fileReference: category && files.length > 0 ? files[0] : null,\n fileReferences: files,\n fileUrls: new Map(),\n fileCount: files.length\n };\n\n if (category && files.length > 0) {\n const firstFile = files[0];\n let url: string | null = null;\n if (firstFile.is_public) {\n url = getPublicUrl(supabase, firstFile.file_path, true);\n }\n cacheData.fileUrl = url;\n } else {\n const urlMap = new Map<string, string>();\n for (const fileRef of files) {\n if (fileRef.is_public) {\n const url = getPublicUrl(supabase, fileRef.file_path, true);\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n }\n cacheData.fileUrls = urlMap;\n }\n\n authenticatedFileCache.set(cacheKey, {\n data: cacheData,\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n cleanupCache();\n }\n\n } catch (err) {\n logger.error('useFileDisplay', 'Error fetching files:', err);\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n } finally {\n setIsLoading(false);\n }\n }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);\n\n // Fetch files when parameters change\n useEffect(() => {\n if (table_name && record_id && organisation_id && supabase) {\n fetchFiles();\n } else {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n setError(null);\n }\n }, [fetchFiles, table_name, record_id, organisation_id, supabase]);\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id || !supabase) return;\n \n // Clear cache for this file\n if (enableCache) {\n const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n authenticatedFileCache.delete(cacheKey);\n }\n await fetchFiles();\n }, [fetchFiles, table_name, record_id, organisation_id, category, supabase, enableCache]);\n\n return {\n fileUrl,\n fileReference,\n fileReferences,\n fileUrls,\n fileCount,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached authenticated file data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearFileDisplayCache(): void {\n for (const [key] of authenticatedFileCache) {\n if (key.startsWith('file_')) {\n authenticatedFileCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getFileDisplayCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(authenticatedFileCache.keys()).filter(key => key.startsWith('file_'));\n return {\n size: keys.length,\n keys\n };\n}\n\n/**\n * Invalidate cache for a specific file display entry\n * Useful for clearing cache after file uploads or updates\n * \n * @param table_name - The table name containing the file reference\n * @param record_id - The record ID that owns the file(s)\n * @param organisation_id - The organisation ID for storage path\n * @param category - Optional file category to invalidate (if provided, also invalidates 'all' category)\n */\nexport function invalidateFileDisplayCache(\n table_name: string,\n record_id: string,\n organisation_id: string,\n category?: FileCategory\n): void {\n const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n authenticatedFileCache.delete(cacheKey);\n // Also invalidate 'all' category if specific category invalidated\n if (category) {\n const allCategoryKey = `file_${table_name}_${record_id}_${organisation_id}_all`;\n authenticatedFileCache.delete(allCategoryKey);\n }\n}\n\n","/**\n * @file Organisation Context Utility\n * @package @jmruthers/pace-core\n * @module Utils/OrganisationContext\n * @since 0.4.0\n *\n * Utility functions for managing organisation context in database sessions.\n * Provides fallback mechanisms for when database functions are not available.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\n\n/**\n * Set organisation context in the database session\n * \n * This function attempts to set the organisation context using a database function.\n * If the function is not available, it falls back gracefully without throwing errors.\n * \n * @param supabase - Supabase client instance\n * @param organisationId - The organisation ID to set as context\n * @returns Promise that resolves when context is set (or falls back gracefully)\n */\nexport async function setOrganisationContext(\n supabase: SupabaseClient,\n organisationId: string\n): Promise<void> {\n if (!supabase || !organisationId) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n // Add timeout to prevent hanging RPC calls\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => reject(new Error('RPC timeout after 3 seconds')), 3000);\n });\n \n // Call the database function to set organisation context\n const rpcPromise = supabase.rpc('set_organisation_context', {\n org_id: organisationId\n });\n\n const { error } = await Promise.race([rpcPromise, timeoutPromise]) as any;\n\n if (error) {\n // Function might not exist yet - this is expected during migration\n // Silent fail - will fall back to client-side filtering\n console.log('[organisationContext] RPC function not available or failed, continuing without database context');\n } else {\n console.log('[organisationContext] Organisation context set in database successfully');\n }\n } catch (error) {\n // Handle any other errors gracefully\n // Silent fail - will fall back to client-side filtering\n console.log('[organisationContext] Failed to set database context, continuing without it:', error);\n }\n}\n\n/**\n * Clear organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves when context is cleared\n */\nexport async function clearOrganisationContext(\n supabase: SupabaseClient\n): Promise<void> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n const { error } = await supabase.rpc('rbac_audit_log', {\n p_event_type: 'organisation_switched',\n p_metadata: { action: 'clear_context' }\n });\n \n if (error) {\n // Silent fail - function not available\n // TODO: Replace with proper logging service integration\n } else {\n // TODO: Replace with proper logging service integration\n }\n } catch (error) {\n // Silent fail - error occurred\n // TODO: Replace with proper logging service integration\n }\n}\n\n/**\n * Get current organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to the current organisation ID or null\n */\nexport async function getOrganisationContext(\n supabase: SupabaseClient\n): Promise<string | null> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n\n try {\n // For now, return null since we're not using database context\n // This will be replaced with proper context management\n const data = null;\n const error = null;\n \n if (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n \n // Validate that data is a string (allow empty strings)\n if (typeof data === 'string') {\n return data;\n }\n \n // Return null for invalid data formats\n return null;\n } catch (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n}\n\n/**\n * Check if organisation context functions are available in the database\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to true if functions are available\n */\nexport async function isOrganisationContextAvailable(\n supabase: SupabaseClient\n): Promise<boolean> {\n if (!supabase) {\n return false;\n }\n\n try {\n const { error } = await supabase.rpc('get_organisation_context');\n \n if (error) {\n return false;\n }\n \n return true;\n } catch (error) {\n return false;\n }\n} ","// File Reference Service\n// Provides CRUD operations for the centralized file reference system\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { \n FileReference, \n FileUploadOptions, \n FileReferenceService, \n FileUploadResult,\n FileCategory \n} from '../types/file-reference';\nimport { uploadFile, getPublicUrl, getSignedUrl, deleteFile, extractFileMetadata } from './storage/helpers';\nimport { setOrganisationContext } from './organisationContext';\nimport { invalidateFileDisplayCache } from '../hooks/useFileDisplay';\n\nexport class FileReferenceServiceImpl implements FileReferenceService {\n constructor(private supabase: SupabaseClient) {}\n\n /**\n * Creates a file reference by uploading a file to storage and linking it in the database.\n * \n * Storage Flow:\n * 1. Upload file to storage bucket first (files or public-files based on is_public flag)\n * - Path format: {orgId}/{category}/{timestamp-uuid-filename}\n * - Bucket selection: 'files' (private) or 'public-files' (public)\n * 2. Extract file metadata (dimensions, hash, etc.)\n * 3. Set organisation context for RLS policies\n * 4. Create database reference via RPC function\n * 5. If DB insert fails, rollback by deleting uploaded file\n * \n * This ensures atomicity: either both storage and DB succeed, or both are cleaned up.\n */\n async createFileReference(options: FileUploadOptions, file: File): Promise<FileReference> {\n try {\n\n // Validate required options\n if (!options.organisation_id) {\n throw new Error('organisation_id is required for file upload');\n }\n if (!options.table_name) {\n throw new Error('table_name is required for file upload');\n }\n if (!options.record_id) {\n throw new Error('record_id is required for file upload');\n }\n\n // Step 1: Upload file to storage bucket first\n // This generates a unique path: {orgId}/{category}/{timestamp-uuid-filename}\n // Bucket is automatically selected based on is_public flag\n const uploadResult = await uploadFile(this.supabase, file, {\n appName: 'file-reference',\n orgId: options.organisation_id,\n isPublic: options.is_public || false,\n customPath: options.category // Use category as the custom path segment\n });\n if (!uploadResult.success) {\n throw new Error(`Failed to upload file: ${uploadResult.error}`);\n }\n\n if (!uploadResult.path) {\n throw new Error('File upload did not return a path');\n }\n\n const filePath = uploadResult.path;\n\n // Step 2: Extract file metadata (dimensions, hash, etc.)\n const metadata = await extractFileMetadata(file, {\n appName: 'file-reference',\n orgId: options.organisation_id,\n isPublic: options.is_public || false\n }, 'system');\n\n // Step 3: Set organisation context in database session before creating file reference\n // This ensures RLS policies can check the organisation context\n await setOrganisationContext(this.supabase, options.organisation_id);\n\n // Step 4: Create file reference in database using RPC function\n // This links the storage path to the record in file_references table\n const { data, error } = await this.supabase\n .rpc('data_file_reference_create', {\n p_table_name: options.table_name,\n p_record_id: options.record_id,\n p_file_path: filePath, // Storage path from step 1\n p_organisation_id: options.organisation_id,\n p_app_id: options.app_id,\n p_file_metadata: {\n fileName: file.name,\n fileType: file.type,\n fileSize: file.size,\n category: options.category,\n ...metadata,\n ...options.custom_metadata\n },\n p_is_public: options.is_public || false\n });\n\n // Step 5: Rollback - if database insert fails, clean up uploaded file\n if (error) {\n await deleteFile(this.supabase, filePath, options.is_public || false);\n throw new Error(`Failed to create file reference: ${error.message}`);\n }\n\n // Get the created file reference\n const { data: fileRef, error: fetchError } = await this.supabase\n .from('file_references')\n .select('*')\n .eq('id', data)\n .single();\n\n if (fetchError || !fileRef) {\n throw new Error(`Failed to fetch created file reference: ${fetchError?.message}`);\n }\n\n // Invalidate cache for this file display entry so newly uploaded files appear immediately\n invalidateFileDisplayCache(\n options.table_name,\n options.record_id,\n options.organisation_id,\n options.category\n );\n\n return fileRef as FileReference;\n } catch (error) {\n console.error('Error creating file reference:', error);\n throw error;\n }\n }\n\n async getFileReference(table_name: string, record_id: string, organisation_id: string): Promise<FileReference | null> {\n try {\n const { data, error } = await this.supabase\n .from('file_references')\n .select('*')\n .eq('table_name', table_name)\n .eq('record_id', record_id)\n .eq('organisation_id', organisation_id)\n .single();\n\n if (error) {\n if (error.code === 'PGRST116') {\n return null; // No rows found\n }\n throw new Error(`Failed to get file reference: ${error.message}`);\n }\n\n return data as FileReference;\n } catch (error) {\n console.error('Error getting file reference:', error);\n throw error;\n }\n }\n\n async getFileUrl(table_name: string, record_id: string, organisation_id: string): Promise<string | null> {\n try {\n // Get file reference to check if it's public\n const fileRef = await this.getFileReference(table_name, record_id, organisation_id);\n if (!fileRef) {\n return null;\n }\n\n // For public files, RPC returns file path - generate public URL client-side\n if (fileRef.is_public) {\n const { data: pathData } = await this.supabase\n .rpc('data_file_reference_url_get', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (!pathData) {\n return null;\n }\n\n // Generate public URL using bucket-aware helper\n return getPublicUrl(this.supabase, pathData, true);\n } else {\n // For private files, use signed URL\n return await this.getSignedUrl(table_name, record_id, organisation_id);\n }\n } catch (error) {\n console.error('Error getting file URL:', error);\n throw error;\n }\n }\n\n async getSignedUrl(table_name: string, record_id: string, organisation_id: string, expires_in: number = 3600): Promise<string | null> {\n try {\n // Get file path from RPC function\n const { data: filePath, error } = await this.supabase\n .rpc('data_file_reference_signed_url_get', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id,\n p_expires_in: expires_in\n });\n\n if (error) {\n throw new Error(`Failed to get signed URL: ${error.message}`);\n }\n\n if (!filePath) {\n return null;\n }\n\n // Generate signed URL client-side using bucket-aware helper (files bucket for private files)\n const signedUrlResult = await getSignedUrl(this.supabase, filePath, {\n appName: 'file-reference',\n orgId: organisation_id,\n expiresIn: expires_in\n });\n\n return signedUrlResult?.url || null;\n } catch (error) {\n console.error('Error getting signed URL:', error);\n throw error;\n }\n }\n\n async updateFileReference(id: string, updates: Partial<FileReference>): Promise<FileReference> {\n try {\n const { data, error } = await this.supabase\n .from('file_references')\n .update(updates)\n .eq('id', id)\n .select()\n .single();\n\n if (error) {\n throw new Error(`Failed to update file reference: ${error.message}`);\n }\n\n return data as FileReference;\n } catch (error) {\n console.error('Error updating file reference:', error);\n throw error;\n }\n }\n\n async deleteFileReference(table_name: string, record_id: string, organisation_id: string, delete_file: boolean = false): Promise<boolean> {\n try {\n // Get file reference first to determine bucket\n const fileRef = await this.getFileReference(table_name, record_id, organisation_id);\n \n const { error } = await this.supabase\n .rpc('data_file_reference_delete', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id,\n p_delete_file: delete_file\n });\n\n if (error) {\n throw new Error(`Failed to delete file reference: ${error.message}`);\n }\n\n // If delete_file is true and we have the file reference, delete from storage\n if (delete_file && fileRef) {\n await deleteFile(this.supabase, fileRef.file_path, fileRef.is_public || false);\n }\n\n return true;\n } catch (error) {\n console.error('Error deleting file reference:', error);\n throw error;\n }\n }\n\n async listFileReferences(table_name: string, record_id: string, organisation_id: string): Promise<FileReference[]> {\n try {\n const { data, error } = await this.supabase\n .rpc('data_file_reference_list', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n throw new Error(`Failed to list file references: ${error.message}`);\n }\n\n // RPC returns partial data, need to fetch full file references\n if (!data || data.length === 0) {\n return [];\n }\n\n // Fetch full file reference data for each ID\n const ids = data.map((item: any) => item.id);\n const { data: fullData, error: fetchError } = await this.supabase\n .from('file_references')\n .select('*')\n .in('id', ids);\n\n if (fetchError) {\n throw new Error(`Failed to fetch file references: ${fetchError.message}`);\n }\n\n return (fullData || []) as FileReference[];\n } catch (error) {\n console.error('Error listing file references:', error);\n throw error;\n }\n }\n\n async getFileCount(table_name: string, record_id: string, organisation_id: string): Promise<number> {\n try {\n const { data, error } = await this.supabase\n .rpc('data_file_reference_count_get', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n throw new Error(`Failed to get file count: ${error.message}`);\n }\n\n return data || 0;\n } catch (error) {\n console.error('Error getting file count:', error);\n throw error;\n }\n }\n\n async getFileReferenceById(id: string, organisation_id: string): Promise<FileReference | null> {\n try {\n const { data, error } = await this.supabase\n .rpc('data_file_reference_get', {\n p_file_reference_id: id,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n throw new Error(`Failed to get file reference by ID: ${error.message}`);\n }\n\n if (!data || data.length === 0) {\n return null;\n }\n\n return data[0] as FileReference;\n } catch (error) {\n console.error('Error getting file reference by ID:', error);\n throw error;\n }\n }\n\n async getFilesByCategory(\n table_name: string, \n record_id: string, \n category: FileCategory, \n organisation_id: string\n ): Promise<FileReference[]> {\n try {\n // CRITICAL: Use RPC function to get files by category - this correctly filters on file_metadata->>'category'\n // NOTE: We MUST use RPC function. Direct queries with .eq('category', ...) will FAIL with HTTP 406\n // because there is NO 'category' column. The category is stored in the JSONB file_metadata field.\n console.log('[FileReferenceService.getFilesByCategory] Calling RPC function:', {\n table_name,\n record_id,\n category,\n organisation_id\n });\n const { data, error } = await this.supabase\n .rpc('data_file_reference_by_category_list', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_category: category,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n // Provide clear error message about category filtering\n console.error('[FileReferenceService.getFilesByCategory] RPC ERROR:', {\n error,\n errorCode: error.code,\n errorMessage: error.message,\n errorDetails: error.details,\n table_name,\n record_id,\n category,\n organisation_id,\n 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.'\n });\n 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.`);\n }\n\n // RPC returns partial data with: id, file_path, file_metadata, is_public, created_at\n // We have table_name, record_id, organisation_id from function parameters\n // We can construct FileReference objects directly without another query (avoiding RLS issues)\n console.log('[FileReferenceService.getFilesByCategory] RPC response received:', {\n dataLength: data?.length || 0,\n data: data ? data.slice(0, 2) : null, // Log first 2 items for debugging\n fullDataAvailable: data?.every((item: any) => item.id && item.file_path && item.file_metadata)\n });\n\n if (!data || data.length === 0) {\n console.log('[FileReferenceService.getFilesByCategory] No data from RPC, returning empty array');\n return [];\n }\n\n // Construct FileReference objects from RPC response\n // This avoids RLS issues with direct queries - the RPC already validated permissions\n const fileReferences: FileReference[] = data\n .filter((item: any) => {\n // Verify category matches (defensive check)\n const fileCategory = item.file_metadata?.category;\n const matches = fileCategory === category;\n if (!matches) {\n console.warn('[FileReferenceService.getFilesByCategory] File category mismatch in RPC response:', {\n fileId: item.id,\n expectedCategory: category,\n actualCategory: fileCategory\n });\n }\n return matches && item.id && item.file_path && item.file_metadata;\n })\n .map((item: any) => {\n // Construct complete FileReference from RPC response + function parameters\n const fileRef: FileReference = {\n id: item.id,\n table_name: table_name,\n record_id: record_id,\n file_path: item.file_path,\n file_metadata: item.file_metadata || {},\n organisation_id: organisation_id,\n app_id: item.file_metadata?.app_id || '', // May not be in metadata, use empty string\n is_public: item.is_public ?? false,\n created_at: item.created_at || new Date().toISOString(),\n updated_at: item.created_at || new Date().toISOString() // RPC doesn't return updated_at, use created_at\n };\n return fileRef;\n });\n\n console.log('[FileReferenceService.getFilesByCategory] Constructed file references from RPC response:', {\n count: fileReferences.length,\n firstFileId: fileReferences[0]?.id,\n firstFilePath: fileReferences[0]?.file_path,\n firstFileIsPublic: fileReferences[0]?.is_public,\n firstFileHasAllRequiredFields: !!(fileReferences[0]?.id && fileReferences[0]?.file_path && fileReferences[0]?.file_metadata && fileReferences[0]?.table_name && fileReferences[0]?.record_id && fileReferences[0]?.organisation_id)\n });\n\n return fileReferences;\n } catch (error) {\n console.error('Error getting files by category:', error);\n throw error;\n }\n }\n\n async uploadMultipleFiles(\n options: FileUploadOptions,\n files: File[]\n ): Promise<import('../types/file-reference').BulkUploadResult> {\n const success: FileReference[] = [];\n const failed: { file: File; error: string }[] = [];\n const results: Array<{ file: File; result: FileUploadResult | null; error?: string }> = [];\n\n // Upload files sequentially to avoid overwhelming the server\n for (const file of files) {\n try {\n const fileReference = await this.createFileReference(options, file);\n\n success.push(fileReference);\n results.push({\n file,\n result: {\n file_reference: fileReference,\n file_url: fileReference.is_public\n ? getPublicUrl(this.supabase, fileReference.file_path, true)\n : '',\n },\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n failed.push({ file, error: message });\n results.push({ file, result: null, error: message });\n }\n }\n\n return {\n success,\n failed,\n total: results.length,\n successful: success.length,\n results,\n };\n }\n}\n\n// Factory function to create file reference service\nexport function createFileReferenceService(supabase: SupabaseClient): FileReferenceService {\n return new FileReferenceServiceImpl(supabase);\n}\n\n// Helper function to upload file and create reference in one operation\nexport async function uploadFileWithReference(\n supabase: SupabaseClient,\n options: FileUploadOptions,\n file: File\n): Promise<FileUploadResult> {\n \n const service = createFileReferenceService(supabase);\n const fileReference = await service.createFileReference(options, file);\n \n const fileUrl = options.is_public \n ? getPublicUrl(supabase, fileReference.file_path, true)\n : await getSignedUrl(supabase, fileReference.file_path, { \n appName: 'file-reference',\n orgId: options.organisation_id,\n expiresIn: 3600 \n });\n\n const urlString = typeof fileUrl === 'string' ? fileUrl : fileUrl?.url || '';\n\n return {\n file_reference: fileReference,\n file_url: urlString,\n signed_url: options.is_public ? undefined : urlString || undefined\n };\n}\n","/**\n * @file Public File Display Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for accessing file references in public contexts without authentication.\n * Provides file URLs and metadata for public pages.\n *\n * Features:\n * - No authentication required\n * - Only returns public files (is_public = true)\n * - Caching for performance\n * - Error handling and loading states\n * - TypeScript support\n * - Supports both single file (category) and multiple files\n *\n * @example\n * ```tsx\n * import { usePublicFileDisplay } from '@jmruthers/pace-core';\n *\n * function PublicFileView() {\n * const { fileUrl, fileReference, isLoading, error } = usePublicFileDisplay(\n * 'event',\n * eventId,\n * organisationId,\n * FileCategory.EVENT_LOGOS,\n * { supabase }\n * );\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return fileUrl ? <img src={fileUrl} alt=\"File\" /> : null;\n * }\n * ```\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { FileReference, FileCategory } from '../../types/file-reference';\nimport { getPublicUrl } from '../../utils/storage/helpers';\nimport { logger } from '../../utils/core/logger';\n\n// Simple in-memory cache for public file data\nconst publicFileCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\nexport interface UsePublicFileDisplayReturn {\n /** Single file URL if category is provided and file found, null otherwise */\n fileUrl: string | null;\n /** Single file reference if category is provided and file found, null otherwise */\n fileReference: FileReference | null;\n /** Array of all file references for the record (when category not provided or for multiple files) */\n fileReferences: FileReference[];\n /** Map of file IDs to URLs for multiple files */\n fileUrls: Map<string, string>;\n /** Total count of files for the record */\n fileCount: number;\n /** Whether the data is currently loading */\n isLoading: boolean;\n /** Any error that occurred during loading */\n error: Error | null;\n /** Function to manually refetch the data */\n refetch: () => Promise<void>;\n}\n\nexport interface UsePublicFileDisplayOptions {\n /** Cache TTL in milliseconds (default: 30 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n /** Supabase client instance (required) */\n supabase: SupabaseClient<Database>;\n}\n\n/**\n * Hook for accessing public file references\n * \n * This hook provides access to file references without requiring\n * authentication. It only returns public files and generates public URLs.\n * \n * @param table_name - The table name containing the file reference\n * @param record_id - The record ID that owns the file(s)\n * @param organisation_id - The organisation ID for storage path\n * @param category - Optional file category to filter by (for single file mode)\n * @param options - Configuration options for caching and behavior\n * @returns Object containing file data, loading state, error, and refetch function\n */\nexport function usePublicFileDisplay(\n table_name: string | undefined,\n record_id: string | undefined,\n organisation_id: string | undefined,\n category: FileCategory | undefined,\n options: UsePublicFileDisplayOptions\n): UsePublicFileDisplayReturn {\n const {\n cacheTtl = 30 * 60 * 1000, // 30 minutes\n enableCache = true,\n supabase\n } = options;\n\n const [fileUrl, setFileUrl] = useState<string | null>(null);\n const [fileReference, setFileReference] = useState<FileReference | null>(null);\n const [fileReferences, setFileReferences] = useState<FileReference[]>([]);\n const [fileUrls, setFileUrls] = useState<Map<string, string>>(new Map());\n const [fileCount, setFileCount] = useState<number>(0);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchFiles = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id || !supabase) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n return;\n }\n\n // Validate UUID format for organisationId to prevent database errors\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(organisation_id)) {\n logger.warn('usePublicFileDisplay', 'Invalid organisationId format (not a valid UUID)', { organisation_id });\n }\n\n // Check cache first\n const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n if (enableCache) {\n const cached = publicFileCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n const cachedData = cached.data;\n setFileUrl(cachedData.fileUrl || null);\n setFileReference(cachedData.fileReference || null);\n setFileReferences(cachedData.fileReferences || []);\n setFileUrls(cachedData.fileUrls || new Map());\n setFileCount(cachedData.fileCount || 0);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n let files: any[] = [];\n\n // CRITICAL: When category is provided, MUST use RPC function, not direct queries\n // Category is stored in file_metadata JSONB field, not a direct column\n if (category) {\n // Single file mode - use RPC to get files by category\n const rpcParams = {\n p_table_name: table_name,\n p_record_id: record_id,\n p_category: category,\n p_organisation_id: organisation_id\n };\n \n const { data, error: rpcError } = await (supabase as any)\n .rpc('data_file_reference_by_category_list', rpcParams);\n\n if (rpcError) {\n logger.error('usePublicFileDisplay', 'RPC function error', {\n function: 'data_file_reference_by_category_list',\n table_name,\n record_id,\n category,\n organisation_id,\n error: rpcError.message,\n errorCode: rpcError.code,\n errorDetails: rpcError.details,\n errorHint: rpcError.hint\n });\n throw new Error(rpcError.message || 'Failed to fetch file reference');\n }\n\n // RPC returns partial data with: id, file_path, file_metadata, is_public, created_at\n // We have table_name, record_id, organisation_id from function parameters\n // We can construct FileReference objects directly without another query (avoiding RLS issues)\n if (!data || data.length === 0) {\n files = [];\n } else {\n // Construct file reference objects from RPC response\n // This avoids RLS issues - the RPC already validated permissions and filtered public files\n files = data\n .filter((item: any) => {\n // RPC should only return public files for public context, but verify\n return item.is_public === true && item.id && item.file_path && item.file_metadata;\n })\n .map((item: any) => {\n // Construct complete file reference from RPC response + function parameters\n return {\n id: item.id,\n table_name: table_name,\n record_id: record_id,\n file_path: item.file_path,\n file_metadata: item.file_metadata || {},\n organisation_id: organisation_id,\n app_id: item.file_metadata?.app_id || null,\n is_public: true, // RPC already filtered for public files\n created_at: item.created_at || new Date().toISOString(),\n updated_at: item.created_at || new Date().toISOString()\n };\n });\n }\n } else {\n // Multiple files mode - use RPC to get all files\n const { data: fileIds, error: rpcError } = await (supabase as any)\n .rpc('data_file_reference_list', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (rpcError) {\n logger.error('usePublicFileDisplay', 'RPC function error', {\n function: 'data_file_reference_list',\n table_name,\n record_id,\n organisation_id,\n error: rpcError.message,\n errorCode: rpcError.code,\n errorDetails: rpcError.details,\n errorHint: rpcError.hint\n });\n throw new Error(rpcError.message || 'Failed to fetch file references');\n }\n\n if (!fileIds || fileIds.length === 0) {\n files = [];\n } else {\n // Fetch full file reference data for each ID, but only public files\n const ids = fileIds.map((item: any) => item.id);\n const { data: fullData, error: fetchError } = await supabase\n .from('file_references')\n .select('*')\n .in('id', ids)\n .eq('is_public', true); // Only public files in public context\n\n if (fetchError) {\n throw new Error(fetchError.message || 'Failed to fetch file references');\n }\n\n files = fullData || [];\n }\n }\n\n // Ensure all files are public (category RPC might return both)\n const publicFiles = files.filter((f: any) => f.is_public === true);\n\n if (publicFiles.length === 0) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n \n // Cache empty result\n if (enableCache) {\n publicFileCache.set(cacheKey, {\n data: { fileUrl: null, fileReference: null, fileReferences: [], fileUrls: new Map(), fileCount: 0 },\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n return;\n }\n\n // Convert to FileReference format\n const fileRefs: FileReference[] = publicFiles.map((f: any) => ({\n id: f.id,\n table_name: f.table_name,\n record_id: f.record_id,\n file_path: f.file_path,\n file_metadata: f.file_metadata || {},\n organisation_id: f.organisation_id,\n app_id: f.app_id,\n is_public: f.is_public ?? true,\n created_at: f.created_at,\n updated_at: f.updated_at\n }));\n\n setFileReferences(fileRefs);\n setFileCount(fileRefs.length);\n\n if (category && fileRefs.length > 0) {\n // Single file mode - get first file\n const firstFile = fileRefs[0];\n setFileReference(firstFile);\n \n // Generate public URL\n const url = getPublicUrl(supabase, firstFile.file_path, true);\n setFileUrl(url);\n } else {\n // Multiple files mode - generate URLs for all files\n const urlMap = new Map<string, string>();\n for (const fileRef of fileRefs) {\n const url = getPublicUrl(supabase, fileRef.file_path, true);\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n setFileUrls(urlMap);\n setFileReference(null);\n setFileUrl(null);\n }\n\n // Cache the result\n if (enableCache) {\n publicFileCache.set(cacheKey, {\n data: {\n fileUrl: category ? (fileRefs.length > 0 ? getPublicUrl(supabase, fileRefs[0].file_path, true) : null) : null,\n fileReference: category && fileRefs.length > 0 ? fileRefs[0] : null,\n fileReferences: fileRefs,\n fileUrls: category ? new Map() : (() => {\n const urlMap = new Map<string, string>();\n for (const fileRef of fileRefs) {\n const url = getPublicUrl(supabase, fileRef.file_path, true);\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n return urlMap;\n })(),\n fileCount: fileRefs.length\n },\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n\n } catch (err) {\n logger.error('usePublicFileDisplay', 'Error fetching files', {\n table_name,\n record_id,\n organisation_id,\n category,\n error: err instanceof Error ? err.message : 'Unknown error',\n errorDetails: err instanceof Error ? err.stack : String(err)\n });\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n } finally {\n setIsLoading(false);\n }\n }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);\n\n // Fetch files when parameters change\n useEffect(() => {\n if (table_name && record_id && organisation_id) {\n fetchFiles();\n } else {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n setError(null);\n }\n }, [fetchFiles, table_name, record_id, organisation_id]);\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id) return;\n \n // Clear cache for this file\n if (enableCache) {\n const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n publicFileCache.delete(cacheKey);\n }\n await fetchFiles();\n }, [fetchFiles, table_name, record_id, organisation_id, category, enableCache]);\n\n return {\n fileUrl,\n fileReference,\n fileReferences,\n fileUrls,\n fileCount,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached public file data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearPublicFileDisplayCache(): void {\n for (const [key] of publicFileCache) {\n if (key.startsWith('public_file_')) {\n publicFileCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getPublicFileDisplayCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(publicFileCache.keys()).filter(key => key.startsWith('public_file_'));\n return {\n size: keys.length,\n keys\n };\n}\n\n"],"mappings":"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","names":["error","jsx","useMemo","useContext","useContext","useMemo","log","useEffect","error","useEffect","useState","useEffect","useCallback","useState","useCallback","error","useEffect"]}
|
package/dist/chunk-JISYG63F.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// src/utils/validation.ts
|
|
2
|
-
function isValidEmail(email) {
|
|
3
|
-
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4
|
-
return emailPattern.test(email);
|
|
5
|
-
}
|
|
6
|
-
function isEmpty(value) {
|
|
7
|
-
return value === null || value === void 0 || value.trim() === "";
|
|
8
|
-
}
|
|
9
|
-
function isStrongPassword(password) {
|
|
10
|
-
const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
|
|
11
|
-
return passwordPattern.test(password);
|
|
12
|
-
}
|
|
13
|
-
function isValidUrl(url) {
|
|
14
|
-
try {
|
|
15
|
-
new URL(url);
|
|
16
|
-
return true;
|
|
17
|
-
} catch {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function isValidDate(dateStr) {
|
|
22
|
-
const date = new Date(dateStr);
|
|
23
|
-
return !isNaN(date.getTime());
|
|
24
|
-
}
|
|
25
|
-
function isWithinRange(value, min, max) {
|
|
26
|
-
return value >= min && value <= max;
|
|
27
|
-
}
|
|
28
|
-
function matchesPattern(value, pattern) {
|
|
29
|
-
return pattern.test(value);
|
|
30
|
-
}
|
|
31
|
-
function deepMerge(target, source) {
|
|
32
|
-
const output = { ...target };
|
|
33
|
-
if (isObject(target) && isObject(source)) {
|
|
34
|
-
Object.keys(source).forEach((key) => {
|
|
35
|
-
if (isObject(source[key])) {
|
|
36
|
-
if (!(key in target)) {
|
|
37
|
-
Object.assign(output, { [key]: source[key] });
|
|
38
|
-
} else {
|
|
39
|
-
const targetKey = key;
|
|
40
|
-
const targetValue = target[targetKey];
|
|
41
|
-
if (isObject(targetValue)) {
|
|
42
|
-
output[targetKey] = deepMerge(
|
|
43
|
-
targetValue,
|
|
44
|
-
source[key]
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
} else {
|
|
49
|
-
Object.assign(output, { [key]: source[key] });
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
return output;
|
|
54
|
-
}
|
|
55
|
-
function isObject(item) {
|
|
56
|
-
return item !== null && typeof item === "object" && !Array.isArray(item);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export {
|
|
60
|
-
isValidEmail,
|
|
61
|
-
isEmpty,
|
|
62
|
-
isStrongPassword,
|
|
63
|
-
isValidUrl,
|
|
64
|
-
isValidDate,
|
|
65
|
-
isWithinRange,
|
|
66
|
-
matchesPattern,
|
|
67
|
-
deepMerge,
|
|
68
|
-
isObject
|
|
69
|
-
};
|
|
70
|
-
//# sourceMappingURL=chunk-JISYG63F.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/validation.ts"],"sourcesContent":["/**\n * @file Internal utilities for validation module\n * @internal This file contains implementation details that should not be used directly\n */\n\n/**\n * Utility functions for validating data in the application\n */\n\n/**\n * Check if a string is a valid email\n */\nexport function isValidEmail(email: string): boolean {\n const emailPattern = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailPattern.test(email);\n}\n\n/**\n * Check if a string is empty (either null, undefined, or just whitespace)\n */\nexport function isEmpty(value: string | null | undefined): boolean {\n return value === null || value === undefined || value.trim() === '';\n}\n\n/**\n * Check if a password meets minimum requirements\n */\nexport function isStrongPassword(password: string): boolean {\n // Minimum 8 characters, at least one uppercase, one lowercase, one number\n const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$/;\n return passwordPattern.test(password);\n}\n\n/**\n * Check if a URL is valid\n */\nexport function isValidUrl(url: string): boolean {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a date string is valid\n */\nexport function isValidDate(dateStr: string): boolean {\n const date = new Date(dateStr);\n return !isNaN(date.getTime());\n}\n\n/**\n * Check if a value is within a range\n */\nexport function isWithinRange(value: number, min: number, max: number): boolean {\n return value >= min && value <= max;\n}\n\n/**\n * Check if a value matches a specific pattern\n */\nexport function matchesPattern(value: string, pattern: RegExp): boolean {\n return pattern.test(value);\n}\n\n/**\n * Utility function to deep merge objects for schema combination\n * @internal\n */\nexport function deepMerge<T extends Record<string, unknown>>(\n target: T, \n source: Record<string, unknown>\n): T {\n const output = { ...target };\n \n if (isObject(target) && isObject(source)) {\n Object.keys(source).forEach(key => {\n if (isObject(source[key])) {\n if (!(key in target)) {\n Object.assign(output, { [key]: source[key] });\n } else {\n // Use a type assertion to safely handle the indexing\n const targetKey = key as keyof typeof target;\n const targetValue = target[targetKey];\n \n if (isObject(targetValue)) {\n // Safe cast using type assertion\n output[targetKey] = deepMerge(\n targetValue as Record<string, unknown>,\n source[key] as Record<string, unknown>\n ) as unknown as T[keyof T];\n }\n }\n } else {\n Object.assign(output, { [key]: source[key] });\n }\n });\n }\n \n return output as T;\n}\n\n/**\n * Type guard to check if a value is a plain object\n * @internal\n */\nexport function isObject(item: unknown): item is Record<string, unknown> {\n return item !== null && typeof item === 'object' && !Array.isArray(item);\n}\n"],"mappings":";AAYO,SAAS,aAAa,OAAwB;AACnD,QAAM,eAAe;AACrB,SAAO,aAAa,KAAK,KAAK;AAChC;AAKO,SAAS,QAAQ,OAA2C;AACjE,SAAO,UAAU,QAAQ,UAAU,UAAa,MAAM,KAAK,MAAM;AACnE;AAKO,SAAS,iBAAiB,UAA2B;AAE1D,QAAM,kBAAkB;AACxB,SAAO,gBAAgB,KAAK,QAAQ;AACtC;AAKO,SAAS,WAAW,KAAsB;AAC/C,MAAI;AACF,QAAI,IAAI,GAAG;AACX,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,YAAY,SAA0B;AACpD,QAAM,OAAO,IAAI,KAAK,OAAO;AAC7B,SAAO,CAAC,MAAM,KAAK,QAAQ,CAAC;AAC9B;AAKO,SAAS,cAAc,OAAe,KAAa,KAAsB;AAC9E,SAAO,SAAS,OAAO,SAAS;AAClC;AAKO,SAAS,eAAe,OAAe,SAA0B;AACtE,SAAO,QAAQ,KAAK,KAAK;AAC3B;AAMO,SAAS,UACd,QACA,QACG;AACH,QAAM,SAAS,EAAE,GAAG,OAAO;AAE3B,MAAI,SAAS,MAAM,KAAK,SAAS,MAAM,GAAG;AACxC,WAAO,KAAK,MAAM,EAAE,QAAQ,SAAO;AACjC,UAAI,SAAS,OAAO,GAAG,CAAC,GAAG;AACzB,YAAI,EAAE,OAAO,SAAS;AACpB,iBAAO,OAAO,QAAQ,EAAE,CAAC,GAAG,GAAG,OAAO,GAAG,EAAE,CAAC;AAAA,QAC9C,OAAO;AAEL,gBAAM,YAAY;AAClB,gBAAM,cAAc,OAAO,SAAS;AAEpC,cAAI,SAAS,WAAW,GAAG;AAEzB,mBAAO,SAAS,IAAI;AAAA,cAClB;AAAA,cACA,OAAO,GAAG;AAAA,YACZ;AAAA,UACF;AAAA,QACF;AAAA,MACF,OAAO;AACL,eAAO,OAAO,QAAQ,EAAE,CAAC,GAAG,GAAG,OAAO,GAAG,EAAE,CAAC;AAAA,MAC9C;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAMO,SAAS,SAAS,MAAgD;AACvE,SAAO,SAAS,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI;AACzE;","names":[]}
|
package/dist/chunk-LIMSTKYD.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
init_useEventService,
|
|
3
|
-
init_useOrganisationService,
|
|
4
|
-
useEventService2 as useEventService,
|
|
5
|
-
useOrganisationService2 as useOrganisationService
|
|
6
|
-
} from "./chunk-3JI76CYK.js";
|
|
7
|
-
import {
|
|
8
|
-
__esm
|
|
9
|
-
} from "./chunk-PLDDJCW6.js";
|
|
10
|
-
|
|
11
|
-
// src/hooks/useOrganisations.ts
|
|
12
|
-
function useOrganisations() {
|
|
13
|
-
const organisationService = useOrganisationService();
|
|
14
|
-
const selectedOrg = organisationService.getSelectedOrganisation();
|
|
15
|
-
return {
|
|
16
|
-
selectedOrganisation: selectedOrg,
|
|
17
|
-
organisations: organisationService.getOrganisations(),
|
|
18
|
-
userMemberships: organisationService.getUserMemberships(),
|
|
19
|
-
isLoading: organisationService.isLoading(),
|
|
20
|
-
error: organisationService.getError(),
|
|
21
|
-
hasValidOrganisationContext: organisationService.hasValidOrganisationContext(),
|
|
22
|
-
isContextReady: organisationService.isContextReady(),
|
|
23
|
-
setSelectedOrganisation: (org) => organisationService.setSelectedOrganisation(org),
|
|
24
|
-
switchOrganisation: (orgId) => organisationService.switchOrganisation(orgId),
|
|
25
|
-
getUserRole: (orgId) => organisationService.getUserRole(orgId),
|
|
26
|
-
validateOrganisationAccess: (orgId) => organisationService.validateOrganisationAccess(orgId),
|
|
27
|
-
refreshOrganisations: () => organisationService.refreshOrganisations(),
|
|
28
|
-
ensureOrganisationContext: () => organisationService.ensureOrganisationContext(),
|
|
29
|
-
isOrganisationSecure: () => organisationService.isOrganisationSecure(),
|
|
30
|
-
getPrimaryOrganisation: () => organisationService.getPrimaryOrganisation()
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
var init_useOrganisations = __esm({
|
|
34
|
-
"src/hooks/useOrganisations.ts"() {
|
|
35
|
-
"use strict";
|
|
36
|
-
init_useOrganisationService();
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// src/hooks/useEvents.ts
|
|
41
|
-
init_useEventService();
|
|
42
|
-
function useEvents() {
|
|
43
|
-
const eventService = useEventService();
|
|
44
|
-
const events = eventService.getEvents();
|
|
45
|
-
return {
|
|
46
|
-
events,
|
|
47
|
-
selectedEvent: eventService.getSelectedEvent(),
|
|
48
|
-
isLoading: eventService.isLoading(),
|
|
49
|
-
error: eventService.getError(),
|
|
50
|
-
setSelectedEvent: (event) => eventService.setSelectedEvent(event),
|
|
51
|
-
refreshEvents: () => eventService.refreshEvents(),
|
|
52
|
-
clearEventSelection: () => eventService.clearEventSelection()
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export {
|
|
57
|
-
useOrganisations,
|
|
58
|
-
init_useOrganisations,
|
|
59
|
-
useEvents
|
|
60
|
-
};
|
|
61
|
-
//# sourceMappingURL=chunk-LIMSTKYD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useOrganisations.ts","../src/hooks/useEvents.ts"],"sourcesContent":["/**\n * @file Organisation Hook\n * @package @jmruthers/pace-core\n * @module Hooks\n * @since 0.1.0\n *\n * Convenience hook for accessing organisation context.\n * This hook provides a simple interface for working with organisations.\n * \n * Note: This is a convenience wrapper around the OrganisationService.\n * For better performance, consider using useOrganisationService directly.\n */\n\nimport { useOrganisationService } from './services/useOrganisationService';\nimport { Organisation, OrganisationMembership, OrganisationContextType } from '../types/organisation';\n\n/**\n * Hook to access organisation context\n * \n * @returns Organisation context with selected organisation, organisations list, and helper methods\n * @throws {Error} If used outside OrganisationServiceProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { selectedOrganisation, organisations, switchOrganisation } = useOrganisations();\n * \n * return (\n * <div>\n * <h1>Current Organisation: {selectedOrganisation.display_name}</h1>\n * {organisations.map(org => (\n * <button key={org.id} onClick={() => switchOrganisation(org.id)}>\n * {org.display_name}\n * </button>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useOrganisations(): OrganisationContextType {\n const organisationService = useOrganisationService();\n\n const selectedOrg = organisationService.getSelectedOrganisation();\n \n return {\n selectedOrganisation: selectedOrg as Organisation | null,\n organisations: organisationService.getOrganisations(),\n userMemberships: organisationService.getUserMemberships(),\n isLoading: organisationService.isLoading(),\n error: organisationService.getError(),\n hasValidOrganisationContext: organisationService.hasValidOrganisationContext(),\n isContextReady: organisationService.isContextReady(),\n setSelectedOrganisation: (org: Organisation | null) => organisationService.setSelectedOrganisation(org),\n switchOrganisation: (orgId: string) => organisationService.switchOrganisation(orgId),\n getUserRole: (orgId?: string) => organisationService.getUserRole(orgId),\n validateOrganisationAccess: (orgId: string) => organisationService.validateOrganisationAccess(orgId),\n refreshOrganisations: () => organisationService.refreshOrganisations(),\n ensureOrganisationContext: () => organisationService.ensureOrganisationContext(),\n isOrganisationSecure: () => organisationService.isOrganisationSecure(),\n getPrimaryOrganisation: () => organisationService.getPrimaryOrganisation(),\n };\n}\n\n","/**\n * @file Event Hook\n * @package @jmruthers/pace-core\n * @module Hooks\n * @since 0.1.0\n *\n * Convenience hook for accessing event context.\n * This hook provides a simple interface for working with events.\n * \n * Note: This is a convenience wrapper around the EventService.\n * For better performance, consider using useEventService directly.\n */\n\nimport { useEventService } from './services/useEventService';\nimport { Event } from '../types/unified';\n\nexport interface EventContextType {\n events: Event[];\n selectedEvent: Event | null;\n isLoading: boolean;\n error: Error | null;\n setSelectedEvent: (event: Event | null) => void;\n refreshEvents: () => Promise<void>;\n clearEventSelection: () => Promise<void>;\n}\n\n/**\n * Hook to access event context\n * \n * @returns Event context with events, selected event, and helper methods\n * @throws {Error} If used outside EventServiceProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { events, selectedEvent, setSelectedEvent } = useEvents();\n * \n * return (\n * <div>\n * {events.map(event => (\n * <button key={event.id} onClick={() => setSelectedEvent(event)}>\n * {event.event_name}\n * </button>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useEvents(): EventContextType {\n const eventService = useEventService();\n\n const events = eventService.getEvents();\n\n return {\n events,\n selectedEvent: eventService.getSelectedEvent(),\n isLoading: eventService.isLoading(),\n error: eventService.getError(),\n setSelectedEvent: (event: Event | null) => eventService.setSelectedEvent(event),\n refreshEvents: () => eventService.refreshEvents(),\n clearEventSelection: () => eventService.clearEventSelection(),\n };\n}\n\n"],"mappings":";;;;;;;;;;;AAwCO,SAAS,mBAA4C;AAC1D,QAAM,sBAAsB,uBAAuB;AAEnD,QAAM,cAAc,oBAAoB,wBAAwB;AAEhE,SAAO;AAAA,IACL,sBAAsB;AAAA,IACtB,eAAe,oBAAoB,iBAAiB;AAAA,IACpD,iBAAiB,oBAAoB,mBAAmB;AAAA,IACxD,WAAW,oBAAoB,UAAU;AAAA,IACzC,OAAO,oBAAoB,SAAS;AAAA,IACpC,6BAA6B,oBAAoB,4BAA4B;AAAA,IAC7E,gBAAgB,oBAAoB,eAAe;AAAA,IACnD,yBAAyB,CAAC,QAA6B,oBAAoB,wBAAwB,GAAG;AAAA,IACtG,oBAAoB,CAAC,UAAkB,oBAAoB,mBAAmB,KAAK;AAAA,IACnF,aAAa,CAAC,UAAmB,oBAAoB,YAAY,KAAK;AAAA,IACtE,4BAA4B,CAAC,UAAkB,oBAAoB,2BAA2B,KAAK;AAAA,IACnG,sBAAsB,MAAM,oBAAoB,qBAAqB;AAAA,IACrE,2BAA2B,MAAM,oBAAoB,0BAA0B;AAAA,IAC/E,sBAAsB,MAAM,oBAAoB,qBAAqB;AAAA,IACrE,wBAAwB,MAAM,oBAAoB,uBAAuB;AAAA,EAC3E;AACF;AA9DA;AAAA;AAAA;AAaA;AAAA;AAAA;;;ACAA;AAoCO,SAAS,YAA8B;AAC5C,QAAM,eAAe,gBAAgB;AAErC,QAAM,SAAS,aAAa,UAAU;AAEtC,SAAO;AAAA,IACL;AAAA,IACA,eAAe,aAAa,iBAAiB;AAAA,IAC7C,WAAW,aAAa,UAAU;AAAA,IAClC,OAAO,aAAa,SAAS;AAAA,IAC7B,kBAAkB,CAAC,UAAwB,aAAa,iBAAiB,KAAK;AAAA,IAC9E,eAAe,MAAM,aAAa,cAAc;AAAA,IAChD,qBAAqB,MAAM,aAAa,oBAAoB;AAAA,EAC9D;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/validation/htmlSanitization.ts"],"sourcesContent":["/**\n * @file HTML Sanitization Utilities\n * @package @jmruthers/pace-core\n * @module Utils/Validation/HTMLSanitization\n * @since 0.4.36\n * \n * Utilities for safely rendering HTML content.\n * Provides sanitization and validation for basic HTML elements.\n */\n\n/**\n * Allowed HTML tags for safe rendering\n */\nconst ALLOWED_TAGS = [\n 'p', 'div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'strong', 'em', 'b', 'i', 'u', 's', 'mark', 'small', 'sub', 'sup',\n 'ul', 'ol', 'li', 'dl', 'dt', 'dd',\n 'blockquote', 'pre', 'code', 'kbd', 'samp',\n 'a', 'br', 'hr',\n 'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td',\n 'img', 'figure', 'figcaption',\n 'section', 'article', 'aside', 'header', 'footer', 'main', 'nav',\n 'details', 'summary'\n] as const;\n\n/**\n * Allowed HTML attributes for safe rendering\n */\nconst ALLOWED_ATTRIBUTES = [\n 'id', 'class', 'style', 'title', 'lang', 'dir',\n 'href', 'target', 'rel', 'download',\n 'src', 'alt', 'width', 'height', 'loading',\n 'colspan', 'rowspan', 'scope', 'headers',\n 'open', 'datetime', 'cite'\n] as const;\n\n/**\n * Basic HTML sanitization function using regex-based approach\n * Removes potentially dangerous elements and attributes while preserving basic formatting\n * This approach is more reliable in SSR environments and doesn't require DOM manipulation\n * \n * @param html - The HTML string to sanitize\n * @returns Sanitized HTML string safe for rendering\n * \n * @example\n * ```tsx\n * const safeHtml = sanitizeHtml('<p>Hello <strong>world</strong>!</p>');\n * // Returns: '<p>Hello <strong>world</strong>!</p>'\n * \n * const dangerousHtml = sanitizeHtml('<script>alert(\"xss\")</script><p>Safe content</p>');\n * // Returns: '<p>Safe content</p>'\n * ```\n */\nexport function sanitizeHtml(html: string): string {\n if (!html || typeof html !== 'string') {\n return '';\n }\n\n // Basic safety: just remove script tags and dangerous attributes\n let sanitized = html\n // Remove script tags (including self-closing and malformed)\n .replace(/<script\\b[^>]*>.*?<\\/script>/gi, '')\n .replace(/<script\\b[^>]*\\/>/gi, '')\n // Remove iframe tags (including self-closing and malformed)\n .replace(/<iframe\\b[^>]*>.*?<\\/iframe>/gi, '')\n .replace(/<iframe\\b[^>]*\\/>/gi, '')\n // Remove object tags (including self-closing and malformed)\n .replace(/<object\\b[^>]*>.*?<\\/object>/gi, '')\n .replace(/<object\\b[^>]*\\/>/gi, '')\n // Remove embed tags (including self-closing)\n .replace(/<embed\\b[^>]*\\/?>/gi, '')\n // Remove form tags (including self-closing and malformed)\n .replace(/<form\\b[^>]*>.*?<\\/form>/gi, '')\n .replace(/<form\\b[^>]*\\/>/gi, '')\n // Remove input tags (including self-closing)\n .replace(/<input\\b[^>]*\\/?>/gi, '')\n // Remove button tags (including self-closing and malformed)\n .replace(/<button\\b[^>]*>.*?<\\/button>/gi, '')\n .replace(/<button\\b[^>]*\\/>/gi, '')\n // Remove event handlers from any remaining tags - correct pattern\n .replace(/\\s*on\\w+\\s*=\\s*[\"'][^\"']*[\"']/gi, '')\n // Remove javascript: protocols - correct pattern\n .replace(/javascript:[^\"'\\s>]*/gi, '')\n // Remove data: protocols - correct pattern\n .replace(/data:[^\"'\\s>]*/gi, '');\n\n return sanitized;\n}\n\n/**\n * Validates if HTML content is safe for rendering\n * \n * @param html - The HTML string to validate\n * @returns Object with validation result and any warnings\n * \n * @example\n * ```tsx\n * const validation = validateHtml('<p>Safe content</p>');\n * console.log(validation.isValid); // true\n * console.log(validation.warnings); // []\n * ```\n */\nexport function validateHtml(html: string): { isValid: boolean; warnings: string[] } {\n const warnings: string[] = [];\n \n if (!html || typeof html !== 'string') {\n return { isValid: false, warnings: ['HTML content must be a non-empty string'] };\n }\n\n // Check for potentially dangerous patterns\n const dangerousPatterns = [\n { pattern: /<script\\b[^>]*>.*?<\\/script>/gi, message: 'Script tags are not allowed' },\n { pattern: /<script\\b[^>]*\\/>/gi, message: 'Script tags are not allowed' },\n { pattern: /<iframe\\b[^>]*>.*?<\\/iframe>/gi, message: 'Iframe tags are not allowed' },\n { pattern: /<iframe\\b[^>]*\\/>/gi, message: 'Iframe tags are not allowed' },\n { pattern: /<object\\b[^>]*>.*?<\\/object>/gi, message: 'Object tags are not allowed' },\n { pattern: /<object\\b[^>]*\\/>/gi, message: 'Object tags are not allowed' },\n { pattern: /<embed\\b[^>]*\\/?>/gi, message: 'Embed tags are not allowed' },\n { pattern: /<form\\b[^>]*>.*?<\\/form>/gi, message: 'Form tags are not allowed' },\n { pattern: /<form\\b[^>]*\\/>/gi, message: 'Form tags are not allowed' },\n { pattern: /<input\\b[^>]*\\/?>/gi, message: 'Input tags are not allowed' },\n { pattern: /<button\\b[^>]*>.*?<\\/button>/gi, message: 'Button tags are not allowed' },\n { pattern: /<button\\b[^>]*\\/>/gi, message: 'Button tags are not allowed' },\n { pattern: /on\\w+\\s*=/gi, message: 'Event handlers are not allowed' },\n { pattern: /javascript:/gi, message: 'JavaScript protocols are not allowed' },\n { pattern: /data:/gi, message: 'Data protocols are not allowed' }\n ];\n\n dangerousPatterns.forEach(({ pattern, message }) => {\n if (pattern.test(html)) {\n warnings.push(message);\n }\n });\n\n return {\n isValid: warnings.length === 0,\n warnings\n };\n}\n\n/**\n * Safely renders HTML content with sanitization\n * \n * @param html - The HTML string to render\n * @param options - Rendering options\n * @returns Object with sanitized HTML and validation info\n * \n * @example\n * ```tsx\n * const result = renderSafeHtml('<p>Hello <strong>world</strong>!</p>');\n * console.log(result.html); // Sanitized HTML\n * console.log(result.isValid); // true\n * ```\n */\nexport function renderSafeHtml(\n html: string, \n options: { \n strict?: boolean; \n logWarnings?: boolean;\n } = {}\n): { \n html: string; \n isValid: boolean; \n warnings: string[] \n} {\n const { strict = true, logWarnings = false } = options;\n \n const validation = validateHtml(html);\n const sanitizedHtml = sanitizeHtml(html);\n \n if (logWarnings && validation.warnings.length > 0) {\n // Use logger if needed, but this is controlled by logWarnings option\n // For now, keep console.warn as it's only called when explicitly requested\n // via logWarnings option, which is typically for debugging\n console.warn('HTML content warnings:', validation.warnings);\n }\n \n return {\n html: sanitizedHtml,\n isValid: validation.isValid,\n warnings: validation.warnings\n };\n}\n\n"],"mappings":";AAqDO,SAAS,aAAa,MAAsB;AACjD,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO;AAAA,EACT;AAGA,MAAI,YAAY,KAEb,QAAQ,kCAAkC,EAAE,EAC5C,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,kCAAkC,EAAE,EAC5C,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,kCAAkC,EAAE,EAC5C,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,8BAA8B,EAAE,EACxC,QAAQ,qBAAqB,EAAE,EAE/B,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,kCAAkC,EAAE,EAC5C,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,mCAAmC,EAAE,EAE7C,QAAQ,0BAA0B,EAAE,EAEpC,QAAQ,oBAAoB,EAAE;AAEjC,SAAO;AACT;AAeO,SAAS,aAAa,MAAwD;AACnF,QAAM,WAAqB,CAAC;AAE5B,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO,EAAE,SAAS,OAAO,UAAU,CAAC,yCAAyC,EAAE;AAAA,EACjF;AAGA,QAAM,oBAAoB;AAAA,IACxB,EAAE,SAAS,kCAAkC,SAAS,8BAA8B;AAAA,IACpF,EAAE,SAAS,uBAAuB,SAAS,8BAA8B;AAAA,IACzE,EAAE,SAAS,kCAAkC,SAAS,8BAA8B;AAAA,IACpF,EAAE,SAAS,uBAAuB,SAAS,8BAA8B;AAAA,IACzE,EAAE,SAAS,kCAAkC,SAAS,8BAA8B;AAAA,IACpF,EAAE,SAAS,uBAAuB,SAAS,8BAA8B;AAAA,IACzE,EAAE,SAAS,uBAAuB,SAAS,6BAA6B;AAAA,IACxE,EAAE,SAAS,8BAA8B,SAAS,4BAA4B;AAAA,IAC9E,EAAE,SAAS,qBAAqB,SAAS,4BAA4B;AAAA,IACrE,EAAE,SAAS,uBAAuB,SAAS,6BAA6B;AAAA,IACxE,EAAE,SAAS,kCAAkC,SAAS,8BAA8B;AAAA,IACpF,EAAE,SAAS,uBAAuB,SAAS,8BAA8B;AAAA,IACzE,EAAE,SAAS,eAAe,SAAS,iCAAiC;AAAA,IACpE,EAAE,SAAS,iBAAiB,SAAS,uCAAuC;AAAA,IAC5E,EAAE,SAAS,WAAW,SAAS,iCAAiC;AAAA,EAClE;AAEA,oBAAkB,QAAQ,CAAC,EAAE,SAAS,QAAQ,MAAM;AAClD,QAAI,QAAQ,KAAK,IAAI,GAAG;AACtB,eAAS,KAAK,OAAO;AAAA,IACvB;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,SAAS,SAAS,WAAW;AAAA,IAC7B;AAAA,EACF;AACF;AAgBO,SAAS,eACd,MACA,UAGI,CAAC,GAKL;AACA,QAAM,EAAE,SAAS,MAAM,cAAc,MAAM,IAAI;AAE/C,QAAM,aAAa,aAAa,IAAI;AACpC,QAAM,gBAAgB,aAAa,IAAI;AAEvC,MAAI,eAAe,WAAW,SAAS,SAAS,GAAG;AAIjD,YAAQ,KAAK,0BAA0B,WAAW,QAAQ;AAAA,EAC5D;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,WAAW;AAAA,IACpB,UAAU,WAAW;AAAA,EACvB;AACF;","names":[]}
|