@jmruthers/pace-core 0.5.53 → 0.5.55
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/README.md +0 -4
- package/dist/{DataTable-7FMFXA7A.js → DataTable-4T627QFJ.js} +11 -11
- package/dist/{PublicLoadingSpinner-Bq_-BeK-.d.ts → PublicLoadingSpinner-SL8WaQN7.d.ts} +2 -21
- package/dist/{api-H5A3H4IR.js → api-LUNF5O6M.js} +3 -3
- package/dist/{appConfig-BVGyuvI7.d.ts → appConfig-DjpeG6P-.d.ts} +9 -1
- package/dist/{appNameResolver-7GHF5ED2.js → appNameResolver-UURKN7NF.js} +2 -2
- package/dist/{audit-BUW3LMJB.js → audit-6TOCAMKO.js} +2 -2
- package/dist/{chunk-MZBUOP4P.js → chunk-5BSLGBYI.js} +4 -3
- package/dist/chunk-5BSLGBYI.js.map +1 -0
- package/dist/{chunk-I5Z3QH5X.js → chunk-66C4BSAY.js} +2 -2
- package/dist/{chunk-I5Z3QH5X.js.map → chunk-66C4BSAY.js.map} +1 -1
- package/dist/{chunk-MYP2EGHX.js → chunk-AJ2KMES7.js} +21 -14
- package/dist/chunk-AJ2KMES7.js.map +1 -0
- package/dist/{chunk-EL2O4IUX.js → chunk-AQFRLC7K.js} +16 -24
- package/dist/{chunk-EL2O4IUX.js.map → chunk-AQFRLC7K.js.map} +1 -1
- package/dist/{chunk-7BNPOCLL.js → chunk-B2WTCLCV.js} +6 -2
- package/dist/chunk-B2WTCLCV.js.map +1 -0
- package/dist/{chunk-WJARTBCT.js → chunk-D7ARGIA3.js} +16 -7
- package/dist/chunk-D7ARGIA3.js.map +1 -0
- package/dist/{chunk-NRK4AIHQ.js → chunk-KBRACSJI.js} +3 -3
- package/dist/{chunk-NYUJ4FJR.js → chunk-KJDPSM64.js} +7 -7
- package/dist/chunk-KJDPSM64.js.map +1 -0
- package/dist/{chunk-GWSBHC4J.js → chunk-KLPVOPRI.js} +261 -38
- package/dist/chunk-KLPVOPRI.js.map +1 -0
- package/dist/{chunk-TRIZ7IB7.js → chunk-MPQDF75X.js} +148 -288
- package/dist/chunk-MPQDF75X.js.map +1 -0
- package/dist/{chunk-MSFACPQQ.js → chunk-PAEM3OWN.js} +11 -11
- package/dist/{chunk-MSFACPQQ.js.map → chunk-PAEM3OWN.js.map} +1 -1
- package/dist/{chunk-GIO7BFE7.js → chunk-RQD3D2CO.js} +66 -169
- package/dist/{chunk-GIO7BFE7.js.map → chunk-RQD3D2CO.js.map} +1 -1
- package/dist/{chunk-YDJW5XTN.js → chunk-STT7INZR.js} +25 -1
- package/dist/chunk-STT7INZR.js.map +1 -0
- package/dist/{chunk-6MTY77WU.js → chunk-TNMXZLDR.js} +3 -3
- package/dist/{chunk-BC3S53OZ.js → chunk-UQE2Y64H.js} +30 -14
- package/dist/chunk-UQE2Y64H.js.map +1 -0
- package/dist/{chunk-22KLBHPS.js → chunk-W66AZIOH.js} +2 -2
- package/dist/chunk-W66AZIOH.js.map +1 -0
- package/dist/{chunk-SS3E6QLB.js → chunk-YNUBMSMV.js} +2 -2
- package/dist/chunk-YNUBMSMV.js.map +1 -0
- package/dist/{chunk-NZ655MWE.js → chunk-ZOD2ZY6X.js} +5 -4
- package/dist/chunk-ZOD2ZY6X.js.map +1 -0
- package/dist/{chunk-74C6SNEC.js → chunk-ZPK5656W.js} +3 -3
- package/dist/{chunk-74C6SNEC.js.map → chunk-ZPK5656W.js.map} +1 -1
- package/dist/components.d.ts +22 -899
- package/dist/components.js +436 -3118
- package/dist/components.js.map +1 -1
- package/dist/file-reference-9xUOnwyt.d.ts +70 -0
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +10 -10
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +49 -9
- package/dist/index.js +190 -25
- package/dist/index.js.map +1 -1
- package/dist/{organisation-CO3Sh3_D.d.ts → organisation-t-vvQC3g.d.ts} +1 -8
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +5 -5
- package/dist/rbac/index.d.ts +65 -46
- package/dist/rbac/index.js +10 -12
- package/dist/styles/core.css +0 -125
- package/dist/types.d.ts +2 -1
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/dist/{usePublicRouteParams-B2OcAsur.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +1 -1
- package/dist/utils.d.ts +3 -4
- package/dist/utils.js +44 -13
- package/dist/utils.js.map +1 -1
- package/docs/FILE_REFERENCE_SYSTEM.md +440 -0
- package/docs/INDEX.md +7 -5
- package/docs/README.md +0 -1
- package/docs/api/README.md +0 -4
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +2 -2
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +12 -12
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +6 -6
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +281 -0
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- 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/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- 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 +4 -4
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- 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/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +204 -200
- package/docs/api-reference/components.md +141 -163
- package/docs/api-reference/hooks.md +347 -0
- package/docs/core-concepts/rbac-system.md +69 -16
- package/docs/getting-started/examples/basic-auth-app.md +0 -1
- package/docs/implementation-guides/datatable-rbac-usage.md +12 -11
- package/docs/implementation-guides/file-upload-storage.md +733 -0
- package/docs/implementation-guides/inactivity-tracking.md +779 -0
- package/docs/implementation-guides/organisation-security.md +748 -0
- package/docs/implementation-guides/public-pages-advanced.md +1022 -0
- package/docs/migration/MIGRATION_GUIDE.md +684 -0
- package/docs/migration/README.md +13 -2
- package/docs/migration/rbac-migration.md +73 -0
- package/docs/rbac/examples/rbac-rls-integration-example.md +11 -13
- package/docs/style-guide.md +269 -1
- package/package.json +1 -1
- package/src/__tests__/TESTING_GUIDELINES.md +331 -18
- package/src/__tests__/helpers/supabaseMock.ts +99 -0
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -7
- package/src/__tests__/shared.ts +6 -0
- package/src/components/DataTable/components/ActionButtons.tsx +2 -2
- package/src/components/DataTable/components/DataTableCore.tsx +2 -2
- package/src/components/DataTable/components/UnifiedTableBody.tsx +1 -1
- package/src/components/DataTable/utils/debugTools.ts +2 -2
- package/src/components/Dialog/Dialog.test.tsx +12 -2
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +6 -6
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +2 -2
- package/src/components/FileDisplay.tsx +233 -0
- package/src/components/FileUpload.tsx +176 -0
- package/src/components/Footer/Footer.test.tsx +7 -7
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +13 -6
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +30 -3
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +558 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
- package/src/components/PublicLayout/PublicPageDebugger.tsx +2 -2
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +2 -2
- package/src/components/PublicLayout/PublicPageProvider.tsx +2 -2
- package/src/components/Select/Select.test.tsx +50 -15
- package/src/components/SuperAdminGuard.tsx +2 -2
- package/src/components/__tests__/SuperAdminGuard.test.tsx +559 -0
- package/src/components/index.ts +0 -183
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +1 -1
- package/src/hooks/__tests__/useRBAC.unit.test.ts +191 -138
- package/src/hooks/public/usePublicEvent.ts +2 -2
- package/src/hooks/useAppConfig.ts +3 -3
- package/src/hooks/useComponentPerformance.ts +1 -1
- package/src/hooks/useDataTablePerformance.ts +1 -1
- package/src/hooks/useFileReference.ts +232 -0
- package/src/hooks/useOrganisationPermissions.test.ts +254 -344
- package/src/hooks/useOrganisationPermissions.ts +15 -7
- package/src/hooks/useOrganisationSecurity.test.ts +390 -402
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePermissionCache.test.ts +264 -395
- package/src/hooks/usePermissionCache.ts +34 -4
- package/src/hooks/useSecureDataAccess.test.ts +486 -0
- package/src/hooks/useSecureDataAccess.ts +4 -1
- package/src/providers/InactivityProvider.tsx +2 -2
- package/src/providers/OrganisationProvider.test.simple.tsx +168 -0
- package/src/providers/OrganisationProvider.test.tsx +168 -0
- package/src/providers/OrganisationProvider.tsx +18 -31
- package/src/providers/UnifiedAuthProvider.test.simple.tsx +205 -0
- package/src/providers/UnifiedAuthProvider.test.tsx +128 -0
- package/src/providers/__tests__/InactivityProvider.test.tsx +3 -4
- package/src/providers/__tests__/OrganisationProvider.test.tsx +19 -14
- package/src/rbac/__tests__/integration.authflow.test.tsx +123 -0
- package/src/rbac/__tests__/integration.navigation.test.tsx +72 -0
- package/src/rbac/__tests__/integration.securedata.test.tsx +92 -0
- package/src/rbac/__tests__/integration.smoke.test.tsx +73 -0
- package/src/rbac/__tests__/rbac-core.test.tsx +26 -22
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +411 -0
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +285 -0
- package/src/rbac/__tests__/rbac-functions.test.ts +655 -0
- package/src/rbac/__tests__/rbac-integration.test.ts +532 -0
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +196 -0
- package/src/rbac/api.test.ts +6 -6
- package/src/rbac/api.ts +2 -2
- package/src/rbac/audit.test.ts +485 -0
- package/src/rbac/audit.ts +7 -1
- package/src/rbac/cache-invalidation.ts +318 -0
- package/src/rbac/cache.test.ts +286 -0
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +559 -0
- package/src/rbac/components/EnhancedNavigationMenu.tsx +29 -23
- package/src/rbac/components/NavigationProvider.test.tsx +449 -0
- package/src/rbac/components/PagePermissionGuard.tsx +4 -4
- package/src/rbac/components/PagePermissionProvider.test.tsx +479 -0
- package/src/rbac/components/SecureDataProvider.test.tsx +511 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +159 -430
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +4 -5
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +112 -118
- package/src/rbac/config.test.ts +410 -0
- package/src/rbac/engine.test.simple.ts +237 -0
- package/src/rbac/engine.test.ts +233 -0
- package/src/rbac/engine.ts +37 -41
- package/src/rbac/examples/CompleteRBACExample.tsx +3 -3
- package/src/rbac/examples/EventBasedApp.tsx +4 -4
- package/src/rbac/hooks/useRBAC.simple.test.ts +16 -0
- package/src/rbac/hooks/useRBAC.test.ts +207 -455
- package/src/rbac/hooks/useRBAC.ts +30 -22
- package/src/rbac/permissions.test.ts +128 -0
- package/src/rbac/permissions.ts +56 -141
- package/src/rbac/providers/RBACProvider.tsx +1 -1
- package/src/rbac/secureClient.test.ts +444 -0
- package/src/rbac/security.test.ts +390 -0
- package/src/rbac/security.ts +1 -1
- package/src/rbac/types.test.ts +382 -0
- package/src/rbac/types.ts +2 -2
- package/src/styles/base.css +208 -0
- package/src/styles/core.css +0 -125
- package/src/styles/semantic.css +24 -0
- package/src/types/file-reference.ts +77 -0
- package/src/types/rbac-functions.ts +290 -0
- package/src/types/supabase.ts +10 -28
- package/src/types/unified.ts +4 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +81 -55
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +21 -12
- package/src/utils/__tests__/organisationContext.unit.test.ts +13 -7
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +3 -3
- package/src/utils/__tests__/sessionTracking.unit.test.ts +32 -12
- package/src/utils/appConfig.ts +1 -1
- package/src/utils/appIdResolver.test.ts +503 -0
- package/src/utils/appIdResolver.ts +1 -1
- package/src/utils/appNameResolver.test.ts +494 -0
- package/src/utils/appNameResolver.ts +3 -2
- package/src/utils/bundleAnalysis.ts +3 -3
- package/src/utils/debugLogger.ts +1 -1
- package/src/utils/file-reference.ts +263 -0
- package/src/utils/formatDate.test.ts +2 -2
- package/src/utils/organisationContext.test.ts +340 -0
- package/src/utils/organisationContext.ts +19 -6
- package/src/utils/performanceBudgets.ts +2 -2
- package/src/utils/permissionUtils.test.ts +393 -0
- package/src/utils/permissionUtils.ts +5 -2
- package/src/utils/secureDataAccess.test.ts +715 -0
- package/src/utils/secureDataAccess.ts +21 -5
- package/src/utils/sessionTracking.ts +34 -4
- package/src/utils/storage/__tests__/helpers.unit.test.ts +328 -0
- package/src/utils/storage/__tests__/index.unit.test.ts +16 -0
- package/src/utils/storage/helpers.ts +20 -25
- package/src/utils/storage/index.ts +29 -1
- package/src/vite-env.d.ts +17 -0
- package/dist/chunk-22KLBHPS.js.map +0 -1
- package/dist/chunk-7BNPOCLL.js.map +0 -1
- package/dist/chunk-BC3S53OZ.js.map +0 -1
- package/dist/chunk-GWSBHC4J.js.map +0 -1
- package/dist/chunk-MYP2EGHX.js.map +0 -1
- package/dist/chunk-MZBUOP4P.js.map +0 -1
- package/dist/chunk-NYUJ4FJR.js.map +0 -1
- package/dist/chunk-NZ655MWE.js.map +0 -1
- package/dist/chunk-SS3E6QLB.js.map +0 -1
- package/dist/chunk-TRIZ7IB7.js.map +0 -1
- package/dist/chunk-WJARTBCT.js.map +0 -1
- package/dist/chunk-YDJW5XTN.js.map +0 -1
- package/docs/print-components/README.md +0 -258
- package/docs/print-components/api-reference.md +0 -636
- package/docs/print-components/examples/README.md +0 -204
- package/docs/print-components/examples/basic-report.tsx +0 -92
- package/docs/print-components/examples/card-catalog.tsx +0 -149
- package/docs/print-components/examples/cover-page-report.tsx +0 -163
- package/docs/print-components/quick-start.md +0 -363
- package/src/components/PrintButton/PrintButton.tsx +0 -321
- package/src/components/PrintButton/PrintButtonGroup.tsx +0 -84
- package/src/components/PrintButton/PrintToolbar.tsx +0 -94
- package/src/components/PrintButton/__tests__/PrintButton.test.tsx +0 -271
- package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +0 -438
- package/src/components/PrintButton/index.ts +0 -33
- package/src/components/PrintButton/types.ts +0 -173
- package/src/components/PrintCard/PrintCard.tsx +0 -154
- package/src/components/PrintCard/PrintCardContent.tsx +0 -57
- package/src/components/PrintCard/PrintCardFooter.tsx +0 -60
- package/src/components/PrintCard/PrintCardGrid.tsx +0 -91
- package/src/components/PrintCard/PrintCardHeader.tsx +0 -78
- package/src/components/PrintCard/PrintCardImage.tsx +0 -81
- package/src/components/PrintCard/examples/PrintCardShowcase.tsx +0 -239
- package/src/components/PrintCard/index.ts +0 -34
- package/src/components/PrintCard/types.ts +0 -171
- package/src/components/PrintDataTable/PrintDataTable.tsx +0 -215
- package/src/components/PrintDataTable/PrintTableGroup.tsx +0 -90
- package/src/components/PrintDataTable/PrintTableRow.tsx +0 -76
- package/src/components/PrintDataTable/index.ts +0 -25
- package/src/components/PrintDataTable/types.ts +0 -67
- package/src/components/PrintFooter/PrintFooter.tsx +0 -183
- package/src/components/PrintFooter/PrintFooterContent.tsx +0 -71
- package/src/components/PrintFooter/PrintFooterInfo.tsx +0 -86
- package/src/components/PrintFooter/PrintPageNumber.tsx +0 -90
- package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +0 -390
- package/src/components/PrintFooter/index.ts +0 -30
- package/src/components/PrintFooter/types.ts +0 -149
- package/src/components/PrintGrid/PrintGrid.tsx +0 -180
- package/src/components/PrintGrid/PrintGridBreakpoint.tsx +0 -109
- package/src/components/PrintGrid/PrintGridContainer.tsx +0 -128
- package/src/components/PrintGrid/PrintGridItem.tsx +0 -220
- package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +0 -359
- package/src/components/PrintGrid/index.ts +0 -31
- package/src/components/PrintGrid/types.ts +0 -159
- package/src/components/PrintHeader/PrintCoverHeader.tsx +0 -230
- package/src/components/PrintHeader/PrintHeader.tsx +0 -150
- package/src/components/PrintHeader/index.ts +0 -17
- package/src/components/PrintHeader/types.ts +0 -42
- package/src/components/PrintLayout/PrintLayout.tsx +0 -122
- package/src/components/PrintLayout/PrintLayoutContext.tsx +0 -66
- package/src/components/PrintLayout/PrintPageBreak.tsx +0 -52
- package/src/components/PrintLayout/examples/PrintShowcase.tsx +0 -230
- package/src/components/PrintLayout/index.ts +0 -19
- package/src/components/PrintLayout/types.ts +0 -37
- package/src/components/PrintPageBreak/PrintPageBreak.tsx +0 -120
- package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +0 -90
- package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +0 -112
- package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +0 -279
- package/src/components/PrintPageBreak/index.ts +0 -23
- package/src/components/PrintPageBreak/types.ts +0 -94
- package/src/components/PrintSection/PrintColumn.tsx +0 -104
- package/src/components/PrintSection/PrintDivider.tsx +0 -101
- package/src/components/PrintSection/PrintSection.tsx +0 -129
- package/src/components/PrintSection/PrintSectionContent.tsx +0 -75
- package/src/components/PrintSection/PrintSectionHeader.tsx +0 -97
- package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +0 -258
- package/src/components/PrintSection/index.ts +0 -33
- package/src/components/PrintSection/types.ts +0 -155
- package/src/components/PrintText/PrintText.tsx +0 -116
- package/src/components/PrintText/index.ts +0 -16
- package/src/components/PrintText/types.ts +0 -24
- package/src/rbac/__tests__/integration.test.tsx +0 -218
- package/src/utils/print/PrintDataProcessor.ts +0 -390
- package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +0 -397
- package/src/utils/print/index.ts +0 -29
- package/src/utils/print/types.ts +0 -196
- package/src/utils/print/usePrintOptimization.ts +0 -272
- /package/dist/{DataTable-7FMFXA7A.js.map → DataTable-4T627QFJ.js.map} +0 -0
- /package/dist/{api-H5A3H4IR.js.map → api-LUNF5O6M.js.map} +0 -0
- /package/dist/{appNameResolver-7GHF5ED2.js.map → appNameResolver-UURKN7NF.js.map} +0 -0
- /package/dist/{audit-BUW3LMJB.js.map → audit-6TOCAMKO.js.map} +0 -0
- /package/dist/{chunk-NRK4AIHQ.js.map → chunk-KBRACSJI.js.map} +0 -0
- /package/dist/{chunk-6MTY77WU.js.map → chunk-TNMXZLDR.js.map} +0 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file App ID Resolver Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Utils/AppIdResolver
|
|
5
|
+
* @since 1.0.0
|
|
6
|
+
*
|
|
7
|
+
* Comprehensive tests for app ID resolution utility functions covering all critical functionality.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
11
|
+
import { getAppId, getAppIds, CachedAppIdResolver } from './appIdResolver';
|
|
12
|
+
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
13
|
+
import type { Database } from '../types/database';
|
|
14
|
+
|
|
15
|
+
// Mock Supabase client
|
|
16
|
+
const createMockSupabaseClient = () => {
|
|
17
|
+
const queryBuilder = {
|
|
18
|
+
select: vi.fn().mockReturnThis(),
|
|
19
|
+
ilike: vi.fn().mockReturnThis(),
|
|
20
|
+
eq: vi.fn().mockReturnThis(),
|
|
21
|
+
or: vi.fn().mockReturnThis(),
|
|
22
|
+
single: vi.fn().mockResolvedValue({ data: null, error: null }),
|
|
23
|
+
mockResolvedValue: vi.fn(),
|
|
24
|
+
mockRejectedValue: vi.fn()
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Make the chained methods return the query builder with mock methods
|
|
28
|
+
queryBuilder.select.mockReturnValue(queryBuilder);
|
|
29
|
+
queryBuilder.ilike.mockReturnValue(queryBuilder);
|
|
30
|
+
queryBuilder.eq.mockReturnValue(queryBuilder);
|
|
31
|
+
queryBuilder.or.mockReturnValue(queryBuilder);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
from: vi.fn(() => queryBuilder),
|
|
35
|
+
queryBuilder // Export the query builder for direct access
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
describe('App ID Resolver', () => {
|
|
40
|
+
let mockSupabase: any;
|
|
41
|
+
let mockQueryBuilder: any;
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
vi.clearAllMocks();
|
|
45
|
+
mockSupabase = createMockSupabaseClient();
|
|
46
|
+
mockQueryBuilder = mockSupabase.queryBuilder;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
vi.restoreAllMocks();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('getAppId', () => {
|
|
54
|
+
it('resolves app ID for valid app name', async () => {
|
|
55
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
56
|
+
data: { id: 'app-123' },
|
|
57
|
+
error: null
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const result = await getAppId(mockSupabase as any, 'MY_APP');
|
|
61
|
+
|
|
62
|
+
expect(result).toBe('app-123');
|
|
63
|
+
expect(mockSupabase.from).toHaveBeenCalledWith('rbac_apps');
|
|
64
|
+
expect(mockQueryBuilder.select).toHaveBeenCalledWith('id');
|
|
65
|
+
expect(mockQueryBuilder.ilike).toHaveBeenCalledWith('name', 'MY_APP');
|
|
66
|
+
expect(mockQueryBuilder.eq).toHaveBeenCalledWith('is_active', true);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('returns null when app not found', async () => {
|
|
70
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
71
|
+
data: null,
|
|
72
|
+
error: { message: 'No rows found' }
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const result = await getAppId(mockSupabase as any, 'NONEXISTENT_APP');
|
|
76
|
+
|
|
77
|
+
expect(result).toBeNull();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('handles database errors gracefully', async () => {
|
|
81
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
82
|
+
data: null,
|
|
83
|
+
error: { message: 'Database error' }
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
87
|
+
|
|
88
|
+
const result = await getAppId(mockSupabase as any, 'MY_APP');
|
|
89
|
+
|
|
90
|
+
expect(result).toBeNull();
|
|
91
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
92
|
+
'Failed to resolve app ID for app name:',
|
|
93
|
+
'MY_APP',
|
|
94
|
+
{ message: 'Database error' }
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
consoleSpy.mockRestore();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('handles exceptions gracefully', async () => {
|
|
101
|
+
mockQueryBuilder.single.mockRejectedValue(new Error('Network error'));
|
|
102
|
+
|
|
103
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
104
|
+
|
|
105
|
+
const result = await getAppId(mockSupabase as any, 'MY_APP');
|
|
106
|
+
|
|
107
|
+
expect(result).toBeNull();
|
|
108
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
109
|
+
'Error resolving app ID for app name:',
|
|
110
|
+
'MY_APP',
|
|
111
|
+
expect.any(Error)
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
consoleSpy.mockRestore();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('handles null/undefined app name', async () => {
|
|
118
|
+
const result1 = await getAppId(mockSupabase as any, null as any);
|
|
119
|
+
const result2 = await getAppId(mockSupabase as any, undefined as any);
|
|
120
|
+
|
|
121
|
+
expect(result1).toBeNull();
|
|
122
|
+
expect(result2).toBeNull();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('handles empty app name', async () => {
|
|
126
|
+
const result = await getAppId(mockSupabase as any, '');
|
|
127
|
+
|
|
128
|
+
expect(result).toBeNull();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('getAppIds', () => {
|
|
133
|
+
it('resolves multiple app IDs', async () => {
|
|
134
|
+
// Mock the chained call sequence for getAppIds
|
|
135
|
+
const mockChain = {
|
|
136
|
+
select: vi.fn().mockReturnThis(),
|
|
137
|
+
or: vi.fn().mockReturnThis(),
|
|
138
|
+
eq: vi.fn().mockResolvedValue({
|
|
139
|
+
data: [
|
|
140
|
+
{ id: 'app-123', name: 'MY_APP' },
|
|
141
|
+
{ id: 'app-456', name: 'ANOTHER_APP' }
|
|
142
|
+
],
|
|
143
|
+
error: null
|
|
144
|
+
})
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
mockQueryBuilder.select.mockReturnValue(mockChain);
|
|
148
|
+
mockQueryBuilder.or.mockReturnValue(mockChain);
|
|
149
|
+
mockQueryBuilder.eq.mockReturnValue(mockChain);
|
|
150
|
+
|
|
151
|
+
const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP', 'MISSING_APP']);
|
|
152
|
+
|
|
153
|
+
expect(result).toEqual({
|
|
154
|
+
'MY_APP': 'app-123',
|
|
155
|
+
'ANOTHER_APP': 'app-456',
|
|
156
|
+
'MISSING_APP': null
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('handles case-insensitive matching', async () => {
|
|
161
|
+
// Mock the chained call sequence for getAppIds
|
|
162
|
+
const mockChain = {
|
|
163
|
+
select: vi.fn().mockReturnThis(),
|
|
164
|
+
or: vi.fn().mockReturnThis(),
|
|
165
|
+
eq: vi.fn().mockResolvedValue({
|
|
166
|
+
data: [
|
|
167
|
+
{ id: 'app-123', name: 'my_app' },
|
|
168
|
+
{ id: 'app-456', name: 'another_app' }
|
|
169
|
+
],
|
|
170
|
+
error: null
|
|
171
|
+
})
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
mockQueryBuilder.select.mockReturnValue(mockChain);
|
|
175
|
+
mockQueryBuilder.or.mockReturnValue(mockChain);
|
|
176
|
+
mockQueryBuilder.eq.mockReturnValue(mockChain);
|
|
177
|
+
|
|
178
|
+
const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP']);
|
|
179
|
+
|
|
180
|
+
expect(result).toEqual({
|
|
181
|
+
'MY_APP': 'app-123',
|
|
182
|
+
'ANOTHER_APP': 'app-456'
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('returns empty object for empty app names array', async () => {
|
|
187
|
+
const result = await getAppIds(mockSupabase as any, []);
|
|
188
|
+
|
|
189
|
+
expect(result).toEqual({});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('handles database errors gracefully', async () => {
|
|
193
|
+
const mockChain = {
|
|
194
|
+
select: vi.fn().mockReturnThis(),
|
|
195
|
+
or: vi.fn().mockReturnThis(),
|
|
196
|
+
eq: vi.fn().mockResolvedValue({
|
|
197
|
+
data: null,
|
|
198
|
+
error: { message: 'Database error' }
|
|
199
|
+
})
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
mockQueryBuilder.select.mockReturnValue(mockChain);
|
|
203
|
+
mockQueryBuilder.or.mockReturnValue(mockChain);
|
|
204
|
+
mockQueryBuilder.eq.mockReturnValue(mockChain);
|
|
205
|
+
|
|
206
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
207
|
+
|
|
208
|
+
const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP']);
|
|
209
|
+
|
|
210
|
+
expect(result).toEqual({});
|
|
211
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
212
|
+
'Failed to resolve app IDs for app names:',
|
|
213
|
+
['MY_APP', 'ANOTHER_APP'],
|
|
214
|
+
{ message: 'Database error' }
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
consoleSpy.mockRestore();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('handles exceptions gracefully', async () => {
|
|
221
|
+
const mockChain = {
|
|
222
|
+
select: vi.fn().mockReturnThis(),
|
|
223
|
+
or: vi.fn().mockReturnThis(),
|
|
224
|
+
eq: vi.fn().mockRejectedValue(new Error('Network error'))
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
mockQueryBuilder.select.mockReturnValue(mockChain);
|
|
228
|
+
mockQueryBuilder.or.mockReturnValue(mockChain);
|
|
229
|
+
mockQueryBuilder.eq.mockReturnValue(mockChain);
|
|
230
|
+
|
|
231
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
232
|
+
|
|
233
|
+
const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP']);
|
|
234
|
+
|
|
235
|
+
expect(result).toEqual({});
|
|
236
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
237
|
+
'Error resolving app IDs for app names:',
|
|
238
|
+
['MY_APP', 'ANOTHER_APP'],
|
|
239
|
+
expect.any(Error)
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
consoleSpy.mockRestore();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('handles null/undefined app names array', async () => {
|
|
246
|
+
const result1 = await getAppIds(mockSupabase as any, null as any);
|
|
247
|
+
const result2 = await getAppIds(mockSupabase as any, undefined as any);
|
|
248
|
+
|
|
249
|
+
expect(result1).toEqual({});
|
|
250
|
+
expect(result2).toEqual({});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('CachedAppIdResolver', () => {
|
|
255
|
+
let resolver: CachedAppIdResolver;
|
|
256
|
+
|
|
257
|
+
beforeEach(() => {
|
|
258
|
+
resolver = new CachedAppIdResolver();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('resolves app ID and caches result', async () => {
|
|
262
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
263
|
+
data: { id: 'app-123' },
|
|
264
|
+
error: null
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const result1 = await resolver.getAppId(mockSupabase as any, 'MY_APP');
|
|
268
|
+
const result2 = await resolver.getAppId(mockSupabase as any, 'MY_APP');
|
|
269
|
+
|
|
270
|
+
expect(result1).toBe('app-123');
|
|
271
|
+
expect(result2).toBe('app-123');
|
|
272
|
+
expect(mockQueryBuilder.single).toHaveBeenCalledTimes(1); // Should only call once due to caching
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it.skip('expires cached results after TTL', async () => {
|
|
276
|
+
resolver = new CachedAppIdResolver(100); // 100ms TTL
|
|
277
|
+
|
|
278
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
279
|
+
data: { id: 'app-123' },
|
|
280
|
+
error: null
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const result1 = await resolver.getAppId(mockSupabase as any, 'MY_APP');
|
|
284
|
+
expect(result1).toBe('app-123');
|
|
285
|
+
|
|
286
|
+
// Wait for TTL to expire
|
|
287
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
288
|
+
|
|
289
|
+
const result2 = await resolver.getAppId(mockSupabase as any, 'MY_APP');
|
|
290
|
+
expect(result2).toBe('app-123');
|
|
291
|
+
expect(mockQueryBuilder.single).toHaveBeenCalledTimes(2); // Should call again after TTL
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('handles cache misses gracefully', async () => {
|
|
295
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
296
|
+
data: null,
|
|
297
|
+
error: { message: 'No rows found' }
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const result = await resolver.getAppId(mockSupabase as any, 'NONEXISTENT_APP');
|
|
301
|
+
|
|
302
|
+
expect(result).toBeNull();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('handles database errors gracefully', async () => {
|
|
306
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
307
|
+
data: null,
|
|
308
|
+
error: { message: 'Database error' }
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
312
|
+
|
|
313
|
+
const result = await resolver.getAppId(mockSupabase as any, 'MY_APP');
|
|
314
|
+
|
|
315
|
+
expect(result).toBeNull();
|
|
316
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
317
|
+
'Failed to resolve app ID for app name:',
|
|
318
|
+
'MY_APP',
|
|
319
|
+
{ message: 'Database error' }
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
consoleSpy.mockRestore();
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('clears cache correctly', async () => {
|
|
326
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
327
|
+
data: { id: 'app-123' },
|
|
328
|
+
error: null
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
await resolver.getAppId(mockSupabase as any, 'MY_APP');
|
|
332
|
+
resolver.clearCache();
|
|
333
|
+
|
|
334
|
+
const result = await resolver.getAppId(mockSupabase as any, 'MY_APP');
|
|
335
|
+
|
|
336
|
+
expect(result).toBe('app-123');
|
|
337
|
+
expect(mockQueryBuilder.single).toHaveBeenCalledTimes(2); // Should call again after cache clear
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('respects max cache size', async () => {
|
|
341
|
+
resolver = new CachedAppIdResolver(60000, 2); // 2 max cache entries
|
|
342
|
+
|
|
343
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
344
|
+
data: { id: 'app-123' },
|
|
345
|
+
error: null
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Fill cache beyond max size
|
|
349
|
+
await resolver.getAppId(mockSupabase as any, 'APP1');
|
|
350
|
+
await resolver.getAppId(mockSupabase as any, 'APP2');
|
|
351
|
+
await resolver.getAppId(mockSupabase as any, 'APP3');
|
|
352
|
+
|
|
353
|
+
// Should have called database for all three apps
|
|
354
|
+
expect(mockQueryBuilder.single).toHaveBeenCalledTimes(3);
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
describe('Integration Tests', () => {
|
|
359
|
+
it('integrates with real Supabase client structure', async () => {
|
|
360
|
+
const mockClient = {
|
|
361
|
+
from: vi.fn(() => ({
|
|
362
|
+
select: vi.fn().mockReturnThis(),
|
|
363
|
+
ilike: vi.fn().mockReturnThis(),
|
|
364
|
+
eq: vi.fn().mockReturnThis(),
|
|
365
|
+
single: vi.fn().mockResolvedValue({
|
|
366
|
+
data: { id: 'app-123' },
|
|
367
|
+
error: null
|
|
368
|
+
})
|
|
369
|
+
}))
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const result = await getAppId(mockClient as any, 'MY_APP');
|
|
373
|
+
|
|
374
|
+
expect(result).toBe('app-123');
|
|
375
|
+
expect(mockClient.from).toHaveBeenCalledWith('rbac_apps');
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('handles complex app name patterns', async () => {
|
|
379
|
+
const appNames = [
|
|
380
|
+
'MY_APP',
|
|
381
|
+
'my-app',
|
|
382
|
+
'my_app',
|
|
383
|
+
'MyApp',
|
|
384
|
+
'myapp'
|
|
385
|
+
];
|
|
386
|
+
|
|
387
|
+
const mockChain = {
|
|
388
|
+
select: vi.fn().mockReturnThis(),
|
|
389
|
+
or: vi.fn().mockReturnThis(),
|
|
390
|
+
eq: vi.fn().mockResolvedValue({
|
|
391
|
+
data: [
|
|
392
|
+
{ id: 'app-123', name: 'my_app' },
|
|
393
|
+
{ id: 'app-456', name: 'my-app' }
|
|
394
|
+
],
|
|
395
|
+
error: null
|
|
396
|
+
})
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
mockQueryBuilder.select.mockReturnValue(mockChain);
|
|
400
|
+
mockQueryBuilder.or.mockReturnValue(mockChain);
|
|
401
|
+
mockQueryBuilder.eq.mockReturnValue(mockChain);
|
|
402
|
+
|
|
403
|
+
const result = await getAppIds(mockSupabase as any, appNames);
|
|
404
|
+
|
|
405
|
+
expect(result).toEqual({
|
|
406
|
+
'MY_APP': 'app-123',
|
|
407
|
+
'my-app': 'app-456',
|
|
408
|
+
'my_app': null,
|
|
409
|
+
'MyApp': null,
|
|
410
|
+
'myapp': null
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
describe('Error Handling', () => {
|
|
416
|
+
it('handles network timeouts', async () => {
|
|
417
|
+
mockQueryBuilder.single.mockRejectedValue(new Error('Request timeout'));
|
|
418
|
+
|
|
419
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
420
|
+
|
|
421
|
+
const result = await getAppId(mockSupabase as any, 'MY_APP');
|
|
422
|
+
|
|
423
|
+
expect(result).toBeNull();
|
|
424
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
425
|
+
'Error resolving app ID for app name:',
|
|
426
|
+
'MY_APP',
|
|
427
|
+
expect.any(Error)
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
consoleSpy.mockRestore();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('handles malformed responses', async () => {
|
|
434
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
435
|
+
data: { invalid: 'structure' },
|
|
436
|
+
error: null
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
const result = await getAppId(mockSupabase as any, 'MY_APP');
|
|
440
|
+
|
|
441
|
+
expect(result).toBeNull();
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('handles null data responses', async () => {
|
|
445
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
446
|
+
data: null,
|
|
447
|
+
error: null
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const result = await getAppId(mockSupabase as any, 'MY_APP');
|
|
451
|
+
|
|
452
|
+
expect(result).toBeNull();
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
describe('Performance', () => {
|
|
457
|
+
it('handles large numbers of app names efficiently', async () => {
|
|
458
|
+
const appNames = Array.from({ length: 100 }, (_, i) => `APP_${i}`);
|
|
459
|
+
|
|
460
|
+
const mockChain = {
|
|
461
|
+
select: vi.fn().mockReturnThis(),
|
|
462
|
+
or: vi.fn().mockReturnThis(),
|
|
463
|
+
eq: vi.fn().mockResolvedValue({
|
|
464
|
+
data: appNames.map((name, i) => ({ id: `app-${i}`, name: name.toLowerCase() })),
|
|
465
|
+
error: null
|
|
466
|
+
})
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
mockQueryBuilder.select.mockReturnValue(mockChain);
|
|
470
|
+
mockQueryBuilder.or.mockReturnValue(mockChain);
|
|
471
|
+
mockQueryBuilder.eq.mockReturnValue(mockChain);
|
|
472
|
+
|
|
473
|
+
const startTime = Date.now();
|
|
474
|
+
const result = await getAppIds(mockSupabase as any, appNames);
|
|
475
|
+
const endTime = Date.now();
|
|
476
|
+
|
|
477
|
+
expect(Object.keys(result)).toHaveLength(100);
|
|
478
|
+
expect(endTime - startTime).toBeLessThan(1000); // Should complete within 1 second
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('caches results efficiently', async () => {
|
|
482
|
+
const resolver = new CachedAppIdResolver();
|
|
483
|
+
|
|
484
|
+
mockQueryBuilder.single.mockResolvedValue({
|
|
485
|
+
data: { id: 'app-123' },
|
|
486
|
+
error: null
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
const startTime = Date.now();
|
|
490
|
+
|
|
491
|
+
// First call - should hit database
|
|
492
|
+
await resolver.getAppId(mockSupabase as any, 'MY_APP');
|
|
493
|
+
|
|
494
|
+
// Second call - should hit cache
|
|
495
|
+
await resolver.getAppId(mockSupabase as any, 'MY_APP');
|
|
496
|
+
|
|
497
|
+
const endTime = Date.now();
|
|
498
|
+
|
|
499
|
+
expect(mockQueryBuilder.single).toHaveBeenCalledTimes(1);
|
|
500
|
+
expect(endTime - startTime).toBeLessThan(100); // Should be very fast due to caching
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
});
|
|
@@ -96,7 +96,7 @@ export async function getAppIds(
|
|
|
96
96
|
/**
|
|
97
97
|
* Cached app ID resolver with TTL
|
|
98
98
|
*/
|
|
99
|
-
class CachedAppIdResolver {
|
|
99
|
+
export class CachedAppIdResolver {
|
|
100
100
|
private cache = new Map<string, { id: string | null; expires: number }>();
|
|
101
101
|
private ttl = 5 * 60 * 1000; // 5 minutes
|
|
102
102
|
|