@jmruthers/pace-core 0.5.190 → 0.5.191
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/dist/{DataTable-IVYljGJ6.d.ts → DataTable-Be6dH_dR.d.ts} +1 -1
- package/dist/{DataTable-ON3IXISJ.js → DataTable-WKRZD47S.js} +6 -6
- package/dist/{PublicPageProvider-C4uxosp6.d.ts → PublicPageProvider-ULXC_u6U.d.ts} +1 -1
- package/dist/{UnifiedAuthProvider-X5NXANVI.js → UnifiedAuthProvider-FTSG5XH7.js} +3 -3
- package/dist/{api-I6UCQ5S6.js → api-IHKALJZD.js} +2 -2
- package/dist/{chunk-J2XXC7R5.js → chunk-6LTQQAT6.js} +77 -111
- package/dist/chunk-6LTQQAT6.js.map +1 -0
- package/dist/{chunk-STYK4OH2.js → chunk-6TQDD426.js} +10 -10
- package/dist/chunk-6TQDD426.js.map +1 -0
- package/dist/{chunk-DZWK57KZ.js → chunk-G37KK66H.js} +1 -1
- package/dist/{chunk-DZWK57KZ.js.map → chunk-G37KK66H.js.map} +1 -1
- package/dist/{chunk-73HSNNOQ.js → chunk-LOMZXPSN.js} +13 -13
- package/dist/{chunk-Y4BUBBHD.js → chunk-OETXORNB.js} +3 -3
- package/dist/{chunk-RUYZKXOD.js → chunk-ROXMHMY2.js} +5 -3
- package/dist/chunk-ROXMHMY2.js.map +1 -0
- package/dist/{chunk-SDMHPX3X.js → chunk-ULHIJK66.js} +56 -21
- package/dist/{chunk-SDMHPX3X.js.map → chunk-ULHIJK66.js.map} +1 -1
- package/dist/{chunk-VVBAW5A5.js → chunk-VKB2CO4Z.js} +46 -35
- package/dist/chunk-VKB2CO4Z.js.map +1 -0
- package/dist/{chunk-HQVPB5MZ.js → chunk-VRGWKHDB.js} +6 -6
- package/dist/{chunk-NIU6J6OX.js → chunk-XNYQOL3Z.js} +16 -16
- package/dist/chunk-XNYQOL3Z.js.map +1 -0
- package/dist/{chunk-4QYC5L4K.js → chunk-XYXSXPUK.js} +22 -27
- package/dist/chunk-XYXSXPUK.js.map +1 -0
- package/dist/components.d.ts +3 -3
- package/dist/components.js +8 -8
- package/dist/{database.generated-DI89OQeI.d.ts → database.generated-CzIvgcPu.d.ts} +165 -201
- package/dist/hooks.d.ts +12 -12
- package/dist/hooks.js +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +18 -23
- package/dist/index.js.map +1 -1
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +1 -1
- package/dist/rbac/index.js +6 -6
- package/dist/{types-Bwgl--Xo.d.ts → types-CEpcvwwF.d.ts} +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/{usePublicRouteParams-DxIDS4bC.d.ts → usePublicRouteParams-TZe0gy-4.d.ts} +1 -1
- package/dist/utils.d.ts +8 -8
- package/dist/utils.js +2 -2
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/Logger.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 +1 -1
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +5 -5
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.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/ComplianceResult.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 +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/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- 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 +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.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/LoggerConfig.md +1 -1
- 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 +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/ParsedAddress.md +2 -2
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.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/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- 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/RuntimeComplianceResult.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 +1 -1
- package/docs/api/interfaces/SetupIssue.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/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 +1 -1
- package/docs/api/interfaces/TextareaProps.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/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.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 +2 -2
- 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/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 +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +16 -16
- package/docs/migration/README.md +18 -0
- package/docs/migration/database-changes-december-2025.md +767 -0
- package/docs/migration/person-scoped-profiles-migration-guide.md +472 -0
- package/package.json +1 -1
- package/src/__tests__/public-recipe-view.test.ts +10 -10
- package/src/__tests__/rls-policies.test.ts +13 -13
- package/src/components/AddressField/README.md +6 -6
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +35 -15
- package/src/components/Select/Select.test.tsx +4 -1
- package/src/components/Select/Select.tsx +60 -15
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +192 -0
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +741 -0
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +703 -0
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +581 -0
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +9 -8
- package/src/hooks/public/usePublicEvent.ts +8 -8
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/useFileDisplay.ts +8 -9
- package/src/hooks/useQueryCache.ts +6 -6
- package/src/hooks/useSecureDataAccess.test.ts +8 -8
- package/src/hooks/useSecureDataAccess.ts +15 -11
- package/src/providers/__tests__/OrganisationProvider.test.tsx +27 -21
- package/src/rbac/hooks/useRBAC.simple.test.ts +95 -0
- package/src/rbac/utils/__tests__/eventContext.test.ts +2 -2
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +490 -0
- package/src/rbac/utils/eventContext.ts +5 -2
- package/src/services/AuthService.ts +37 -8
- package/src/services/OrganisationService.ts +92 -139
- package/src/services/__tests__/OrganisationService.pagination.test.ts +34 -8
- package/src/services/__tests__/OrganisationService.test.ts +218 -86
- package/src/types/database.generated.ts +166 -201
- package/src/types/supabase.ts +2 -2
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +3 -2
- package/src/utils/file-reference/index.ts +4 -4
- package/src/utils/google-places/googlePlacesUtils.ts +1 -1
- package/src/utils/google-places/types.ts +1 -1
- package/src/utils/request-deduplication.ts +4 -4
- package/src/utils/security/secureDataAccess.test.ts +1 -1
- package/src/utils/security/secureDataAccess.ts +7 -4
- package/src/utils/storage/README.md +1 -1
- package/dist/chunk-4QYC5L4K.js.map +0 -1
- package/dist/chunk-J2XXC7R5.js.map +0 -1
- package/dist/chunk-NIU6J6OX.js.map +0 -1
- package/dist/chunk-RUYZKXOD.js.map +0 -1
- package/dist/chunk-STYK4OH2.js.map +0 -1
- package/dist/chunk-VVBAW5A5.js.map +0 -1
- /package/dist/{DataTable-ON3IXISJ.js.map → DataTable-WKRZD47S.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-X5NXANVI.js.map → UnifiedAuthProvider-FTSG5XH7.js.map} +0 -0
- /package/dist/{api-I6UCQ5S6.js.map → api-IHKALJZD.js.map} +0 -0
- /package/dist/{chunk-73HSNNOQ.js.map → chunk-LOMZXPSN.js.map} +0 -0
- /package/dist/{chunk-Y4BUBBHD.js.map → chunk-OETXORNB.js.map} +0 -0
- /package/dist/{chunk-HQVPB5MZ.js.map → chunk-VRGWKHDB.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useZodForm.ts","../src/hooks/useFormDialog.ts","../src/hooks/useOrganisationSecurity.ts","../src/hooks/useOrganisationPermissions.ts","../src/utils/storage/index.ts","../src/hooks/public/usePublicEvent.ts","../src/hooks/public/usePublicEventLogo.ts","../src/hooks/public/usePublicRouteParams.ts"],"sourcesContent":["\n/**\n * Zod Form Hook\n * \n * Enhanced form hook with Zod validation\n */\n\nimport { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { z } from 'zod';\n\ninterface UseZodFormProps<T extends z.ZodTypeAny> {\n schema: T;\n defaultValues?: Partial<z.infer<T>>;\n mode?: 'onSubmit' | 'onBlur' | 'onChange' | 'onTouched' | 'all';\n}\n\nexport function useZodForm<T extends z.ZodTypeAny>({\n schema,\n defaultValues,\n mode = 'onSubmit'\n}: UseZodFormProps<T>) {\n return useForm<z.infer<T>>({\n resolver: zodResolver(schema),\n defaultValues: defaultValues as any,\n mode\n });\n}\n","/**\n * @file useFormDialog Hook\n * @package @jmruthers/pace-core\n * @module Hooks\n * @since 0.1.0\n * \n * Generic React hook for managing form dialog state (open/close, form data, reset behavior).\n * \n * @example\n * ```ts\n * interface ContactFormData {\n * name: string;\n * email: string;\n * }\n * \n * function ContactDialog() {\n * const { isOpen, formData, openDialog, closeDialog, handleOpenChange } = \n * useFormDialog<ContactFormData>();\n * \n * // Use in Dialog component\n * // <Dialog open={isOpen} onOpenChange={handleOpenChange}>...</Dialog>\n * }\n * ```\n */\n\nimport { useState, useCallback } from 'react';\n\n/**\n * Options for configuring the useFormDialog hook behavior\n */\nexport interface UseFormDialogOptions<T = unknown> {\n /**\n * Callback invoked when the dialog open state changes\n * @param open - The new open state\n */\n onOpenChange?: (open: boolean) => void;\n \n /**\n * Whether to reset form data when the dialog closes\n * @default true\n */\n resetOnClose?: boolean;\n}\n\n/**\n * Return type for the useFormDialog hook\n */\nexport interface UseFormDialogReturn<T = unknown> {\n /**\n * Current open state of the dialog\n */\n isOpen: boolean;\n \n /**\n * Current form data (null when dialog is closed or no data provided)\n */\n formData: T | null;\n \n /**\n * Open the dialog with optional form data\n * @param data - Optional form data to populate the dialog\n */\n openDialog: (data?: T | null) => void;\n \n /**\n * Close the dialog\n */\n closeDialog: () => void;\n \n /**\n * Handler for controlled dialog components (e.g., Radix UI Dialog)\n * @param open - The new open state\n */\n handleOpenChange: (open: boolean) => void;\n}\n\n/**\n * Generic React hook for managing form dialog state.\n * \n * Provides state management for form dialogs including:\n * - Open/close state\n * - Form data management\n * - Automatic reset on close (configurable)\n * - Controlled and uncontrolled usage patterns\n * \n * @param options - Configuration options for the hook\n * @returns Object containing dialog state and control functions\n * \n * @example\n * ```ts\n * // Basic usage\n * const { isOpen, openDialog, closeDialog } = useFormDialog();\n * \n * // With form data\n * interface UserData {\n * id: string;\n * name: string;\n * }\n * const { isOpen, formData, openDialog } = useFormDialog<UserData>();\n * \n * // Open with data\n * openDialog({ id: '1', name: 'John' });\n * \n * // Controlled usage with callback\n * const { handleOpenChange, isOpen } = useFormDialog({\n * onOpenChange: (open) => console.log('Dialog state:', open),\n * resetOnClose: false\n * });\n * ```\n */\nexport function useFormDialog<T = unknown>({\n onOpenChange,\n resetOnClose = true,\n}: UseFormDialogOptions<T> = {}): UseFormDialogReturn<T> {\n const [isOpen, setIsOpen] = useState(false);\n const [formData, setFormData] = useState<T | null>(null);\n\n const openDialog = useCallback((data: T | null = null) => {\n setFormData(data);\n setIsOpen(true);\n onOpenChange?.(true);\n }, [onOpenChange]);\n\n const closeDialog = useCallback(() => {\n setIsOpen(false);\n if (resetOnClose) {\n setFormData(null);\n }\n onOpenChange?.(false);\n }, [resetOnClose, onOpenChange]);\n\n const handleOpenChange = useCallback((open: boolean) => {\n setIsOpen(open);\n if (!open && resetOnClose) {\n setFormData(null);\n }\n onOpenChange?.(open);\n }, [resetOnClose, onOpenChange]);\n\n return {\n isOpen,\n formData,\n openDialog,\n closeDialog,\n handleOpenChange,\n };\n}\n","/**\n * @file Organisation Security Hook\n * @package @jmruthers/pace-core\n * @module Hooks/OrganisationSecurity\n * @since 0.4.0\n *\n * Security-focused hook for organisation access validation and super admin functionality.\n * Provides utilities for validating user access to organisations and checking permissions.\n */\n\nimport { useCallback, useMemo, useEffect, useState } from 'react';\nimport { useUnifiedAuth } from '../providers';\nimport { useOrganisations } from './useOrganisations';\n// Legacy useRBAC hook removed - use new RBAC system instead\nimport type { OrganisationSecurityError, SuperAdminContext } from '../types/organisation';\nimport type { Permission } from '../rbac/types';\nimport { logger } from '../utils/core/logger';\n\nexport interface OrganisationSecurityHook {\n // Super admin context\n superAdminContext: SuperAdminContext;\n \n // Access validation\n validateOrganisationAccess: (orgId: string) => Promise<boolean>;\n hasMinimumRole: (minRole: string, orgId?: string) => boolean;\n canAccessChildOrganisations: (orgId?: string) => boolean;\n \n // Permission checks\n hasPermission: (permission: string, orgId?: string) => Promise<boolean>;\n getUserPermissions: (orgId?: string) => Promise<string[]>;\n \n // Audit logging\n logOrganisationAccess: (action: string, details?: Record<string, unknown>) => Promise<void>;\n \n // Security utilities\n ensureOrganisationAccess: (orgId: string) => Promise<void>;\n validateUserAccess: (userId: string, orgId: string) => Promise<boolean>;\n}\n\nexport const useOrganisationSecurity = (): OrganisationSecurityHook => {\n const { user, session, supabase } = useUnifiedAuth();\n const { selectedOrganisation, getUserRole, validateOrganisationAccess: validateAccess } = useOrganisations();\n \n // Super admin status - query database for security (user_metadata can be spoofed)\n const [isSuperAdmin, setIsSuperAdmin] = useState<boolean>(false);\n const [isCheckingSuperAdmin, setIsCheckingSuperAdmin] = useState(false);\n\n // Check super admin status from database\n useEffect(() => {\n if (!user || !session || !supabase) {\n setIsSuperAdmin(false);\n return;\n }\n\n const checkSuperAdmin = async () => {\n setIsCheckingSuperAdmin(true);\n try {\n const now = new Date().toISOString();\n const { data, error } = await supabase\n .from('rbac_global_roles')\n .select('role')\n .eq('user_id', user.id)\n .eq('role', 'super_admin')\n .lte('valid_from', now)\n .or(`valid_to.is.null,valid_to.gte.${now}`)\n .limit(1);\n\n setIsSuperAdmin(!error && data && data.length > 0);\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Error checking super admin status:', error);\n setIsSuperAdmin(false);\n } finally {\n setIsCheckingSuperAdmin(false);\n }\n };\n\n checkSuperAdmin();\n }, [user, session, supabase]);\n\n // Super admin context\n const superAdminContext = useMemo((): SuperAdminContext => {\n return {\n isSuperAdmin,\n hasGlobalAccess: isSuperAdmin,\n canManageAllOrganisations: isSuperAdmin\n };\n }, [isSuperAdmin]);\n\n // Validate organisation access with database check\n const validateOrganisationAccess = useCallback(async (orgId: string): Promise<boolean> => {\n if (!user || !session || !supabase) return false;\n \n try {\n // Super admin has access to all organisations\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n // Check organisation membership using consolidated rbac_organisation_roles table\n const { data, error } = await supabase\n .from('rbac_organisation_roles')\n .select('id')\n .eq('user_id', user.id)\n .eq('organisation_id', orgId)\n .eq('status', 'active')\n .is('revoked_at', null)\n .in('role', ['org_admin', 'leader', 'member']) // Only actual members, not supporters\n .single();\n\n if (error) {\n logger.error('useOrganisationSecurity', 'Error validating organisation access:', error);\n return false;\n }\n\n return !!data;\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception validating organisation access:', error);\n return false;\n }\n }, [user, session, supabase, superAdminContext.isSuperAdmin]);\n\n // Check if user has minimum role\n const hasMinimumRole = useCallback((minRole: string, orgId?: string): boolean => {\n // Super admin has all roles\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId) return false;\n\n const userRole = getUserRole(targetOrgId);\n const roleHierarchy = ['supporter', 'member', 'leader', 'org_admin'];\n \n const userRoleIndex = roleHierarchy.indexOf(userRole);\n const minRoleIndex = roleHierarchy.indexOf(minRole);\n \n return userRoleIndex >= minRoleIndex;\n }, [selectedOrganisation, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Check if user can access child organisations\n const canAccessChildOrganisations = useCallback((orgId?: string): boolean => {\n // Super admin can access all organisations\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId) return false;\n\n const userRole = getUserRole(targetOrgId);\n return userRole === 'org_admin';\n }, [selectedOrganisation, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Check specific permission using the new RBAC system\n const hasPermission = useCallback(async (permission: string, orgId?: string): Promise<boolean> => {\n // Super admin has all permissions\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId || !user) return false;\n\n try {\n // Use the new RBAC system with caching\n const { isPermittedCached } = await import('../rbac/api');\n \n const scope = {\n organisationId: targetOrgId,\n eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,\n appId: user.user_metadata?.appId || user.app_metadata?.appId,\n };\n\n return await isPermittedCached({\n userId: user.id,\n scope,\n permission: permission as Permission\n });\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception checking permission:', error);\n return false;\n }\n }, [selectedOrganisation, user, superAdminContext.isSuperAdmin]);\n\n // Get user's permissions for organisation using the new RBAC system\n const getUserPermissions = useCallback(async (orgId?: string): Promise<string[]> => {\n // Super admin has all permissions\n if (superAdminContext.isSuperAdmin) {\n return ['*']; // All permissions\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId || !user) return [];\n\n try {\n // Use the new RBAC system\n const { getPermissionMap } = await import('../rbac/api');\n \n const scope = {\n organisationId: targetOrgId,\n eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,\n appId: user.user_metadata?.appId || user.app_metadata?.appId,\n };\n\n const permissionMap = await getPermissionMap({\n userId: user.id,\n scope\n });\n \n // Flatten all permissions from all pages\n const allPermissions = Object.entries(permissionMap)\n .filter(([, allowed]) => allowed)\n .map(([permission]) => permission);\n return [...new Set(allPermissions)]; // Remove duplicates\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception getting user permissions:', error);\n return [];\n }\n }, [selectedOrganisation, user, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Log organisation access for audit using the new RBAC audit system\n const logOrganisationAccess = useCallback(async (action: string, details?: Record<string, unknown>): Promise<void> => {\n if (!user || !selectedOrganisation) return;\n\n try {\n // Use the new RBAC audit system - only if we have a valid organisation ID\n if (selectedOrganisation.id) {\n const { emitAuditEvent } = await import('../rbac/audit');\n \n await emitAuditEvent({\n type: 'permission_check',\n userId: user.id,\n organisationId: selectedOrganisation.id,\n permission: action,\n decision: true, // Assume access was granted if we're logging it\n source: 'api',\n duration_ms: 0, // No actual permission check performed here\n metadata: details || {}\n });\n }\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Error logging organisation access:', error);\n }\n }, [user, selectedOrganisation]);\n\n // Ensure organisation access (throws if no access)\n const ensureOrganisationAccess = useCallback(async (orgId: string): Promise<void> => {\n const hasAccess = await validateOrganisationAccess(orgId);\n \n if (!hasAccess) {\n const error = new Error(`User does not have access to organisation ${orgId}`) as OrganisationSecurityError;\n error.name = 'OrganisationSecurityError';\n error.code = 'ACCESS_DENIED';\n error.organisationId = orgId;\n error.userId = user?.id;\n throw error;\n }\n }, [validateOrganisationAccess, user]);\n\n // Validate user access (for admin functions)\n const validateUserAccess = useCallback(async (userId: string, orgId: string): Promise<boolean> => {\n if (!supabase) return false;\n\n try {\n // Super admin can validate any user\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n // Regular users can only validate their own access\n if (userId !== user?.id) {\n return false;\n }\n\n return await validateOrganisationAccess(orgId);\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception validating user access:', error);\n return false;\n }\n }, [supabase, superAdminContext.isSuperAdmin, user, validateOrganisationAccess]);\n\n return {\n superAdminContext,\n validateOrganisationAccess,\n hasMinimumRole,\n canAccessChildOrganisations,\n hasPermission,\n getUserPermissions,\n logOrganisationAccess,\n ensureOrganisationAccess,\n validateUserAccess\n };\n}; ","/**\n * @file useOrganisationPermissions Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useOrganisationPermissions\n * @since 0.4.0\n *\n * Hook for managing organisation-specific permissions and role validation.\n * Provides secure access to user's role and permissions within organisations.\n *\n * @example\n * ```tsx\n * function OrganisationComponent() {\n * const { \n * isOrgAdmin, \n * canManageMembers,\n * userRole,\n * hasOrganisationAccess\n * } = useOrganisationPermissions();\n * \n * return (\n * <div>\n * {isOrgAdmin && <AdminPanel />}\n * {canManageMembers && <MemberManagement />}\n * <p>Your role: {userRole}</p>\n * </div>\n * );\n * }\n * \n * // For specific organisation\n * function MultiOrgComponent() {\n * const permissions = useOrganisationPermissions('org-123');\n * \n * if (!permissions.hasOrganisationAccess) {\n * return <div>No access to this organisation</div>;\n * }\n * \n * return <div>Role in org-123: {permissions.userRole}</div>;\n * }\n * ```\n *\n * @security\n * - Validates user membership in organisation\n * - Provides role-based permission checks\n * - Ensures secure access to organisation data\n * - Real-time permission validation\n */\n\nimport { useMemo } from 'react';\nimport { useOrganisations } from './useOrganisations';\nimport { useOrganisationSecurity } from './useOrganisationSecurity';\nimport type { OrganisationRole, OrganisationPermission } from '../types/organisation';\n\nexport interface UseOrganisationPermissionsReturn {\n /** User's role in the organisation */\n userRole: OrganisationRole | 'no_access';\n \n /** Whether user has organisation admin role */\n isOrgAdmin: boolean;\n \n /** Whether user is a super admin */\n isSuperAdmin: boolean;\n \n /** Whether user can moderate content */\n canModerate: boolean;\n \n /** Whether user can manage members */\n canManageMembers: boolean;\n \n /** Whether user can manage organisation settings */\n canManageSettings: boolean;\n \n /** Whether user can manage events */\n canManageEvents: boolean;\n \n /** Whether user has any admin privileges */\n hasAdminPrivileges: boolean;\n \n /** Whether user has access to the organisation */\n hasOrganisationAccess: boolean;\n \n /** Check if user has specific permission */\n hasPermission: (permission: OrganisationPermission) => boolean;\n \n /** Get all permissions for the user */\n getAllPermissions: () => OrganisationPermission[];\n \n /** Organisation ID being checked */\n organisationId: string;\n}\n\n/**\n * Hook to access organisation-specific permissions and roles\n * \n * @param orgId - Optional organisation ID. Defaults to currently selected organisation\n * @returns Organisation permissions and role information\n */\nexport function useOrganisationPermissions(orgId?: string): UseOrganisationPermissionsReturn {\n const { \n selectedOrganisation, \n getUserRole, \n validateOrganisationAccess,\n ensureOrganisationContext\n } = useOrganisations();\n \n // Get super admin context if available (may not be available in all contexts)\n let superAdminContext: { isSuperAdmin: boolean } = { isSuperAdmin: false };\n try {\n superAdminContext = useOrganisationSecurity().superAdminContext;\n } catch {\n // Not available in this context, default to false\n }\n\n const organisationId = useMemo(() => {\n if (orgId) {\n return orgId;\n }\n try {\n const currentOrg = ensureOrganisationContext();\n return currentOrg.id;\n } catch {\n return '';\n }\n }, [orgId, ensureOrganisationContext]);\n\n const userRole = useMemo(() => {\n if (!organisationId) return 'no_access';\n const role = getUserRole(organisationId);\n // Map to valid OrganisationRole or 'no_access'\n if (role === 'org_admin' || role === 'leader' || role === 'member' || role === 'supporter') {\n return role as OrganisationRole;\n }\n return 'no_access';\n }, [organisationId, getUserRole]);\n\n const hasOrganisationAccess = useMemo(() => {\n if (!organisationId) return false;\n return validateOrganisationAccess(organisationId);\n }, [organisationId, validateOrganisationAccess]);\n\n const permissions = useMemo(() => {\n if (!hasOrganisationAccess || userRole === 'no_access') {\n return {\n isOrgAdmin: false,\n isSuperAdmin: false,\n canModerate: false,\n canManageMembers: false,\n canManageSettings: false,\n canManageEvents: false,\n hasAdminPrivileges: false\n };\n }\n\n const isOrgAdmin = userRole === 'org_admin';\n const isLeader = userRole === 'leader';\n const isMember = userRole === 'member';\n const isSupporter = userRole === 'supporter';\n\n // Super admin status - database backed (user_metadata can be spoofed)\n // Get super admin status from the security hook\n const isSuperAdmin = superAdminContext.isSuperAdmin;\n\n return {\n isOrgAdmin,\n isSuperAdmin,\n canModerate: isSuperAdmin || isOrgAdmin || isLeader,\n canManageMembers: isSuperAdmin || isOrgAdmin || isLeader, // Leaders can manage members\n canManageSettings: isSuperAdmin || isOrgAdmin,\n canManageEvents: isSuperAdmin || isOrgAdmin || isLeader,\n hasAdminPrivileges: isSuperAdmin || isOrgAdmin || isLeader // Leaders have admin privileges\n };\n }, [hasOrganisationAccess, userRole]);\n\n const hasPermission = useMemo(() => {\n return (permission: OrganisationPermission): boolean => {\n if (!hasOrganisationAccess || userRole === 'no_access') {\n return false;\n }\n\n // Super admin has all permissions (org_admin acts as super admin within org)\n if (userRole === 'org_admin' || permission === '*') {\n return true;\n }\n\n // Map permissions to roles using the defined permissions from organisation types\n const rolePermissions: Record<OrganisationRole, OrganisationPermission[]> = {\n supporter: ['view_basic'],\n member: ['view_basic', 'view_details'],\n leader: ['view_basic', 'view_details', 'moderate_content', 'manage_events'],\n org_admin: ['view_basic', 'view_details', 'moderate_content', 'manage_events', 'manage_members', 'manage_settings']\n };\n\n const userPermissions = rolePermissions[userRole as OrganisationRole] || [];\n return userPermissions.includes(permission) || userPermissions.includes('*');\n };\n }, [hasOrganisationAccess, userRole]);\n\n const getAllPermissions = useMemo(() => {\n return (): OrganisationPermission[] => {\n if (!hasOrganisationAccess || userRole === 'no_access') {\n return [];\n }\n\n const rolePermissions: Record<OrganisationRole, OrganisationPermission[]> = {\n supporter: ['view_basic'],\n member: ['view_basic', 'view_details'],\n leader: ['view_basic', 'view_details', 'moderate_content', 'manage_events'],\n org_admin: ['view_basic', 'view_details', 'moderate_content', 'manage_events', 'manage_members', 'manage_settings']\n };\n\n return rolePermissions[userRole as OrganisationRole] || [];\n };\n }, [hasOrganisationAccess, userRole]);\n\n return useMemo(() => ({\n userRole,\n organisationId,\n hasOrganisationAccess,\n hasPermission,\n getAllPermissions,\n ...permissions\n }), [userRole, organisationId, hasOrganisationAccess, hasPermission, getAllPermissions, permissions]);\n} ","/**\n * Storage utilities for pace-core\n * \n * Provides app-segregated file storage with organisation-scoped access\n */\n\nexport * from './types';\nexport * from './config';\nexport * from './helpers';\n\n// Import functions for StorageUtils class\nimport {\n uploadFile,\n getPublicUrl,\n getSignedUrl,\n deleteFile,\n downloadFile,\n listFiles,\n archiveFile,\n generateFilePath,\n generateUniqueFileName,\n extractFileMetadata\n} from './helpers';\n\n// Re-export commonly used functions for convenience\nexport {\n uploadFile,\n getPublicUrl,\n getSignedUrl,\n deleteFile,\n downloadFile,\n listFiles,\n archiveFile,\n generateFilePath,\n generateUniqueFileName,\n extractFileMetadata\n};\n\nexport {\n validateFileSize,\n getFileSizeLimit,\n formatFileSize\n} from './config';\n\n\nexport {\n FILE_SIZE_LIMITS,\n DEFAULT_FILE_SIZE_LIMIT,\n STORAGE_CONFIG,\n APP_PATH_MAPPING\n} from './config';\n\n/**\n * StorageUtils class for convenient access to storage functions\n */\nexport class StorageUtils {\n static generateFilePath = generateFilePath;\n static generateUniqueFileName = generateUniqueFileName;\n static extractFileMetadata = extractFileMetadata;\n static uploadFile = uploadFile;\n static getPublicUrl = getPublicUrl;\n static getSignedUrl = getSignedUrl;\n static deleteFile = deleteFile;\n static downloadFile = downloadFile;\n static listFiles = listFiles;\n static archiveFile = archiveFile;\n}\n","/**\n * @file Public Event Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for accessing public event data without authentication.\n * Provides event information by event_code for public pages.\n *\n * Features:\n * - No authentication required\n * - Caching for performance\n * - Error handling and loading states\n * - TypeScript support\n * - Automatic refetch capabilities\n *\n * @example\n * ```tsx\n * import { usePublicEvent } from '@jmruthers/pace-core';\n *\n * function PublicEventPage() {\n * const { eventCode } = usePublicRouteParams();\n * const { event, isLoading, error, refetch } = usePublicEvent(eventCode);\n *\n * if (isLoading) return <div>Loading event...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * if (!event) return <div>Event not found</div>;\n *\n * return (\n * <div>\n * <h1>{event.event_name}</h1>\n * <p>Date: {event.event_date}</p>\n * <p>Venue: {event.event_venue}</p>\n * </div>\n * );\n * }\n * ```\n *\n * @accessibility\n * - No direct accessibility concerns (hook)\n * - Enables accessible public event display\n * - Supports screen reader friendly loading states\n *\n * @security\n * - Only returns public-safe event data\n * - Validates event_code before querying\n * - No sensitive information exposed\n * - Rate limiting applied at database level\n *\n * @performance\n * - Built-in caching with TTL\n * - Minimal re-renders with stable references\n * - Lazy loading support\n * - Error boundary integration\n *\n * @dependencies\n * - React 18+ - Hooks and effects\n * - @supabase/supabase-js - Database integration\n * - Event types - Type definitions\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport { createClient } from '@supabase/supabase-js';\nimport type { Event } from '../../types/event';\nimport type { Database } from '../../types/database';\nimport { assertOrganisationId } from '../../types/core';\nimport { usePublicPageContext } from '../../components/PublicLayout/PublicPageProvider';\nimport { logger } from '../../utils/core/logger';\n\n// Simple in-memory cache for public data\nconst publicDataCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\nexport interface UsePublicEventReturn {\n /** The event data, null if not loaded or not found */\n event: Event | null;\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 UsePublicEventOptions {\n /** Cache TTL in milliseconds (default: 5 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n}\n\n/**\n * Hook for accessing public event data by event_code\n * \n * This hook provides access to public event information without requiring\n * authentication. It includes caching, error handling, and loading states.\n * \n * @param eventCode - The event code to look up\n * @param options - Configuration options for caching and behavior\n * @returns Object containing event data, loading state, error, and refetch function\n */\nexport function usePublicEvent(\n eventCode: string,\n options: UsePublicEventOptions = {}\n): UsePublicEventReturn {\n const {\n cacheTtl = 5 * 60 * 1000, // 5 minutes\n enableCache = true\n } = options;\n\n const [event, setEvent] = useState<Event | null>(null);\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Get environment variables from public page context or fallback to direct access\n let environment: { supabaseUrl: string | null; supabaseKey: string | null };\n \n try {\n environment = usePublicPageContext().environment;\n } catch {\n // Fallback to direct environment variable access if not in PublicPageProvider\n environment = {\n supabaseUrl: (import.meta as any).env?.VITE_SUPABASE_URL || (import.meta as any).env?.NEXT_PUBLIC_SUPABASE_URL || null,\n supabaseKey: (import.meta as any).env?.VITE_SUPABASE_PUBLISHABLE_KEY || (import.meta as any).env?.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || null\n };\n }\n \n // Create a simple Supabase client for public access\n const supabase = useMemo(() => {\n if (typeof window === 'undefined') return null;\n \n if (!environment.supabaseUrl || !environment.supabaseKey) {\n logger.warn('usePublicEvent', 'Missing Supabase environment variables. Please ensure VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY are set in your environment.');\n return null;\n }\n\n return createClient<Database>(environment.supabaseUrl, environment.supabaseKey);\n }, [environment.supabaseUrl, environment.supabaseKey]);\n\n // Helper function to try refreshing schema cache\n const refreshSchemaCache = useCallback(async () => {\n try {\n // Try to trigger a schema refresh by querying a system table\n await (supabase as any).from('information_schema.routines').select('routine_name').limit(1);\n } catch (error) {\n // Ignore errors, this is just an attempt to refresh cache\n logger.debug('usePublicEvent', 'Schema cache refresh attempt failed:', error);\n }\n }, [supabase]);\n\n const fetchEvent = useCallback(async (): Promise<void> => {\n if (!eventCode || !supabase) {\n setError(new Error('Invalid event code or Supabase client not available'));\n setIsLoading(false);\n return;\n }\n\n // Check cache first\n const cacheKey = `public_event_${eventCode}`;\n if (enableCache) {\n const cached = publicDataCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n setEvent(cached.data);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n let eventData: any = null;\n\n try {\n // Try to call the public event RPC function first\n const response = await (supabase as any).rpc('get_public_event_by_code', {\n event_code_param: eventCode\n });\n \n const data = response?.data;\n const rpcError = response?.error;\n\n if (rpcError) {\n // If RPC function doesn't exist or schema cache issue, try refresh first, then fallback\n if (rpcError.message?.includes('Could not find the function') || \n rpcError.message?.includes('does not exist') ||\n rpcError.message?.includes('schema cache')) {\n logger.warn('usePublicEvent', 'RPC function not found or schema cache issue, attempting refresh:', rpcError.message);\n \n // Try to refresh schema cache first\n await refreshSchemaCache();\n \n // Try RPC call one more time after refresh\n try {\n const retryResponse = await (supabase as any).rpc('get_public_event_by_code', {\n event_code_param: eventCode\n });\n \n const retryData = retryResponse?.data;\n const retryError = retryResponse?.error;\n \n if (!retryError && retryData && retryData.length > 0) {\n eventData = retryData[0];\n } else {\n throw new Error('RPC still failing after cache refresh');\n }\n } catch (retryError) {\n logger.warn('usePublicEvent', 'RPC still failing after cache refresh, falling back to direct table access');\n \n // Fallback: Direct table access with public RLS policy\n const tableResponse2 = await (supabase as any)\n .from('core_events')\n .select(`\n event_id,\n event_name,\n event_date,\n event_venue,\n event_participants,\n event_colours,\n organisation_id,\n event_days,\n event_typicalunit,\n event_rounddown,\n event_youthmultiplier,\n event_catering_email,\n event_news,\n event_billing,\n event_email\n `)\n .eq('event_code', eventCode)\n .eq('is_visible', true)\n .not('organisation_id', 'is', null)\n .limit(1)\n .single();\n\n const tableData = tableResponse2?.data;\n const tableError = tableResponse2?.error;\n\n if (tableError) {\n throw new Error(tableError?.message || 'Failed to fetch event from table');\n }\n\n if (!tableData) {\n setEvent(null);\n setError(new Error('Event not found'));\n return;\n }\n\n // Get event logo from core_file_references\n const logoResponse = await (supabase as any)\n .from('core_file_references')\n .select('file_path')\n .eq('table_name', 'core_events')\n .eq('record_id', tableData.event_id)\n .eq('is_public', true)\n .eq('file_metadata->>category', 'event_logos')\n .limit(1)\n .single();\n \n const logoData = logoResponse?.data;\n\n eventData = {\n ...tableData,\n event_logo: logoData?.file_path || null\n };\n }\n } else {\n // For RPC errors that aren't schema cache issues, throw immediately without fallback\n const errorMessage = rpcError?.message || rpcError?.toString() || 'Failed to fetch event';\n setEvent(null);\n setError(new Error(errorMessage));\n setIsLoading(false);\n return;\n }\n } else {\n if (!data || data.length === 0 || !data[0]) {\n setEvent(null);\n setError(new Error('Event not found'));\n return;\n }\n eventData = data[0];\n }\n } catch (rpcError) {\n // If RPC call fails for any reason (including schema cache issues), try direct table access\n logger.warn('usePublicEvent', 'RPC call failed, falling back to direct table access:', rpcError);\n \n const tableResponse = await (supabase as any)\n .from('core_events')\n .select(`\n event_id,\n event_name,\n event_date,\n event_venue,\n event_participants,\n event_colours,\n organisation_id,\n event_days,\n event_typicalunit,\n event_rounddown,\n event_youthmultiplier,\n event_catering_email,\n event_news,\n event_billing,\n event_email\n `)\n .eq('event_code', eventCode)\n .eq('is_visible', true)\n .not('organisation_id', 'is', null)\n .limit(1)\n .single();\n\n const tableData = tableResponse?.data;\n const tableError = tableResponse?.error;\n\n if (tableError) {\n throw new Error(tableError?.message || 'Failed to fetch event from table');\n }\n\n if (!tableData) {\n setEvent(null);\n setError(new Error('Event not found'));\n return;\n }\n\n // Get event logo from core_file_references\n const logoResponse = await (supabase as any)\n .from('core_file_references')\n .select('file_path')\n .eq('table_name', 'core_events')\n .eq('record_id', tableData.event_id)\n .eq('is_public', true)\n .eq('file_metadata->>category', 'event_logos')\n .limit(1)\n .single();\n \n const logoData = logoResponse?.data;\n\n eventData = {\n ...tableData,\n event_logo: logoData?.file_path || null\n };\n }\n \n // Transform to Event type\n const transformedEvent: Event = {\n id: eventData.event_id,\n event_id: eventData.event_id,\n event_name: eventData.event_name,\n event_code: eventCode,\n event_date: eventData.event_date,\n event_venue: eventData.event_venue,\n event_participants: eventData.event_participants,\n event_logo: eventData.event_logo,\n event_colours: eventData.event_colours,\n organisation_id: assertOrganisationId(eventData.organisation_id),\n is_visible: true,\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString()\n };\n\n setEvent(transformedEvent);\n\n // Cache the result\n if (enableCache) {\n publicDataCache.set(cacheKey, {\n data: transformedEvent,\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n\n } catch (err) {\n logger.error('usePublicEvent', 'Error fetching event:', err);\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setEvent(null);\n } finally {\n setIsLoading(false);\n }\n }, [eventCode, supabase, cacheTtl, enableCache]);\n\n // Fetch event when eventCode changes\n useEffect(() => {\n fetchEvent();\n }, [fetchEvent]);\n\n const refetch = useCallback(async (): Promise<void> => {\n // Clear cache for this event\n if (enableCache) {\n const cacheKey = `public_event_${eventCode}`;\n publicDataCache.delete(cacheKey);\n }\n await fetchEvent();\n }, [fetchEvent, eventCode, enableCache]);\n\n return {\n event,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached public event data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearPublicEventCache(): void {\n for (const [key] of publicDataCache) {\n if (key.startsWith('public_event_')) {\n publicDataCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getPublicEventCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(publicDataCache.keys()).filter(key => key.startsWith('public_event_'));\n return {\n size: keys.length,\n keys\n };\n}\n","/**\n * @file Public Event Logo Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for accessing public event logo URLs without authentication.\n * Provides logo URLs with fallback handling for public pages.\n *\n * Features:\n * - No authentication required\n * - Automatic fallback to event initials\n * - Caching for performance\n * - Error handling and loading states\n * - TypeScript support\n * - Image validation\n *\n * @example\n * ```tsx\n * import { usePublicEventLogo } from '@jmruthers/pace-core';\n *\n * function EventHeader() {\n * const { logoUrl, fallbackText, isLoading, error } = usePublicEventLogo(\n * eventId, \n * eventName, \n * organisationId\n * );\n *\n * if (isLoading) return <div>Loading logo...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return (\n * <div>\n * {logoUrl ? (\n * <img src={logoUrl} alt={`${eventName} logo`} />\n * ) : (\n * <div className=\"logo-fallback\">{fallbackText}</div>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @accessibility\n * - No direct accessibility concerns (hook)\n * - Enables accessible logo display with proper alt text\n * - Supports screen reader friendly fallbacks\n *\n * @security\n * - Only returns public-safe logo URLs\n * - Validates image existence before returning URL\n * - No sensitive information exposed\n * - Rate limiting applied at storage level\n *\n * @performance\n * - Built-in caching with TTL\n * - Image validation and optimization\n * - Minimal re-renders with stable references\n * - Lazy loading support\n *\n * @dependencies\n * - React 18+ - Hooks and effects\n * - @supabase/supabase-js - Storage integration\n * - Event types - Type definitions\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { createLogger } from '../../utils/core/logger';\n\nconst log = createLogger('usePublicEventLogo');\n\n// Simple in-memory cache for public data\nconst publicDataCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\nexport interface UsePublicEventLogoReturn {\n /** The logo URL if available, null if not found or error */\n logoUrl: string | null;\n /** Fallback text (event initials) if no logo is available */\n fallbackText: string;\n /** Whether the logo is currently loading */\n isLoading: boolean;\n /** Any error that occurred during loading */\n error: Error | null;\n /** Function to manually refetch the logo */\n refetch: () => Promise<void>;\n}\n\nexport interface UsePublicEventLogoOptions {\n /** Cache TTL in milliseconds (default: 30 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n /** Whether to validate image existence (default: true) */\n validateImage?: boolean;\n /** Custom fallback text generator */\n generateFallbackText?: (eventName: string) => string;\n /** Supabase client instance (required) */\n supabase: SupabaseClient<Database>;\n}\n\n/**\n * Generate fallback text from event name (first letter of each word)\n */\nfunction defaultGenerateFallbackText(eventName: string): string {\n if (!eventName) return 'EV';\n \n return eventName\n .split(' ')\n .map(word => word.charAt(0).toUpperCase())\n .join('')\n .substring(0, 3); // Max 3 characters\n}\n\n/**\n * Hook for accessing public event logo URLs\n * \n * This hook provides access to event logo URLs without requiring\n * authentication. It includes fallback handling and image validation.\n * \n * @param eventId - The event ID to fetch logo for\n * @param eventName - The event name for fallback text generation\n * @param organisationId - The organisation ID for storage path\n * @param options - Configuration options for caching and behavior\n * @returns Object containing logo URL, fallback text, loading state, error, and refetch function\n */\nexport function usePublicEventLogo(\n eventId: string | undefined,\n eventName: string | undefined,\n organisationId: string | undefined,\n options: UsePublicEventLogoOptions\n): UsePublicEventLogoReturn {\n const {\n cacheTtl = 30 * 60 * 1000, // 30 minutes\n enableCache = true,\n validateImage = true,\n generateFallbackText = defaultGenerateFallbackText,\n supabase\n } = options;\n\n const [logoUrl, setLogoUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Generate fallback text\n const fallbackText = useMemo(() => {\n return eventName ? generateFallbackText(eventName) : 'EV';\n }, [eventName, generateFallbackText]);\n\n const fetchLogo = useCallback(async (): Promise<void> => {\n if (!eventId || !organisationId || !supabase) {\n setLogoUrl(null);\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(organisationId)) {\n log.warn('Invalid organisationId format (not a valid UUID):', organisationId);\n // Don't return early - let the database handle the validation\n // This allows for more graceful error handling\n }\n\n // Check cache first\n const cacheKey = `public_logo_${eventId}_${organisationId}`;\n if (enableCache) {\n const cached = publicDataCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n setLogoUrl(cached.data);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n // Call the public logo RPC function\n const { data, error: rpcError } = await (supabase as any).rpc('get_public_event_logo', {\n event_id_param: eventId,\n organisation_id_param: organisationId\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to fetch logo');\n }\n\n if (!data || data.length === 0 || !data[0] || !data[0].logo_url) {\n setLogoUrl(null);\n return;\n }\n\n const logoUrl = data[0].logo_url;\n\n // Validate image existence if requested\n if (validateImage) {\n try {\n const response = await fetch(logoUrl, { method: 'HEAD' });\n if (!response.ok) {\n log.warn('Logo URL not accessible:', logoUrl);\n setLogoUrl(null);\n return;\n }\n } catch (fetchError) {\n log.warn('Error validating logo URL:', fetchError);\n setLogoUrl(null);\n return;\n }\n }\n\n setLogoUrl(logoUrl);\n\n // Cache the result\n if (enableCache) {\n publicDataCache.set(cacheKey, {\n data: logoUrl,\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n\n } catch (err) {\n log.error('Error fetching logo:', err);\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setLogoUrl(null);\n } finally {\n setIsLoading(false);\n }\n }, [eventId, organisationId, supabase, cacheTtl, enableCache, validateImage]);\n\n // Fetch logo when parameters change\n useEffect(() => {\n if (eventId && organisationId) {\n fetchLogo();\n } else {\n setLogoUrl(null);\n setIsLoading(false);\n setError(null);\n }\n }, [fetchLogo, eventId, organisationId]);\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!eventId || !organisationId) return;\n \n // Clear cache for this logo\n if (enableCache) {\n const cacheKey = `public_logo_${eventId}_${organisationId}`;\n publicDataCache.delete(cacheKey);\n }\n await fetchLogo();\n }, [fetchLogo, eventId, organisationId, enableCache]);\n\n return {\n logoUrl,\n fallbackText,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached public logo data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearPublicLogoCache(): void {\n for (const [key] of publicDataCache) {\n if (key.startsWith('public_logo_')) {\n publicDataCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getPublicLogoCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(publicDataCache.keys()).filter(key => key.startsWith('public_logo_'));\n return {\n size: keys.length,\n keys\n };\n}\n","/**\n * @file Public Route Params Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for extracting and validating public route parameters.\n * Provides event code extraction and validation for public pages.\n *\n * Features:\n * - URL parameter extraction\n * - Event code validation\n * - TypeScript support\n * - Error handling\n * - Route pattern support\n *\n * @example\n * ```tsx\n * import { usePublicRouteParams } from '@jmruthers/pace-core';\n *\n * function PublicEventPage() {\n * const { eventCode, eventId, event, error, isLoading } = usePublicRouteParams();\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * if (!event) return <div>Event not found</div>;\n *\n * return (\n * <div>\n * <h1>{event.event_name}</h1>\n * <p>Event Code: {eventCode}</p>\n * </div>\n * );\n * }\n * ```\n *\n * @accessibility\n * - No direct accessibility concerns (hook)\n * - Enables accessible route parameter handling\n * - Supports screen reader friendly error states\n *\n * @security\n * - Validates event codes before processing\n * - Sanitizes URL parameters\n * - No sensitive information exposed\n * - Rate limiting applied at route level\n *\n * @performance\n * - Minimal re-renders with stable references\n * - Efficient parameter extraction\n * - Caching integration\n *\n * @dependencies\n * - React 18+ - Hooks and effects\n * - React Router - URL parameter extraction\n * - Event types - Type definitions\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useParams, useLocation } from 'react-router-dom';\nimport type { Event } from '../../types/event';\nimport { usePublicEvent } from './usePublicEvent';\n\nexport interface UsePublicRouteParamsReturn {\n /** The event code from the URL */\n eventCode: string | null;\n /** The event ID (resolved from event code) */\n eventId: string | null;\n /** The full event object */\n event: Event | null;\n /** Whether the route parameters are being processed */\n isLoading: boolean;\n /** Any error that occurred during processing */\n error: Error | null;\n /** Function to manually refetch the event data */\n refetch: () => Promise<void>;\n}\n\ninterface UsePublicRouteParamsOptions {\n /** Whether to automatically fetch event data (default: true) */\n fetchEventData?: boolean;\n /** Custom event code parameter name (default: 'eventCode') */\n eventCodeParam?: string;\n /** Whether to validate event code format (default: true) */\n validateEventCode?: boolean;\n}\n\n/**\n * Validate event code format\n * Event codes should be alphanumeric with optional hyphens/underscores in the middle\n * Supports 2-50 characters\n */\nfunction validateEventCodeFormat(eventCode: string): boolean {\n if (!eventCode || typeof eventCode !== 'string') return false;\n \n // Length check: 2-50 characters\n if (eventCode.length < 2 || eventCode.length > 50) return false;\n \n // For 2-character codes: both must be alphanumeric\n if (eventCode.length === 2) {\n return /^[a-zA-Z0-9]{2}$/.test(eventCode);\n }\n \n // For 3+ character codes: alphanumeric at start and end, hyphens/underscores allowed in middle\n // Must not start or end with hyphen/underscore\n const eventCodeRegex = /^[a-zA-Z0-9][a-zA-Z0-9_-]{1,48}[a-zA-Z0-9]$/;\n const matchesFormat = eventCodeRegex.test(eventCode);\n \n if (!matchesFormat) return false;\n \n // Additional check: no consecutive hyphens or underscores\n if (eventCode.includes('--') || eventCode.includes('__') || eventCode.includes('-_') || eventCode.includes('_-')) {\n return false;\n }\n \n return true;\n}\n\n/**\n * Hook for extracting and validating public route parameters\n * \n * This hook extracts event codes from URL parameters and optionally\n * fetches the corresponding event data. It provides validation and\n * error handling for public routes.\n * \n * @param options - Configuration options for behavior\n * @returns Object containing route parameters, event data, loading state, error, and refetch function\n */\nexport function usePublicRouteParams(\n options: UsePublicRouteParamsOptions = {}\n): UsePublicRouteParamsReturn {\n const {\n fetchEventData = true,\n eventCodeParam = 'eventCode',\n validateEventCode = true\n } = options;\n\n const params = useParams();\n const location = useLocation();\n \n const [error, setError] = useState<Error | null>(null);\n\n // Extract event code from URL parameters\n const eventCode = useMemo(() => {\n const code = params[eventCodeParam] as string;\n \n if (!code) {\n // Don't set error immediately - let the component handle missing eventCode gracefully\n return null;\n }\n\n // Validate event code format if requested\n if (validateEventCode && !validateEventCodeFormat(code)) {\n setError(new Error(`Invalid event code format: ${code}`));\n return null;\n }\n\n setError(null);\n return code;\n }, [params, eventCodeParam, validateEventCode]);\n\n // Use the public event hook to fetch event data\n const {\n event,\n isLoading: eventLoading,\n error: eventError,\n refetch: refetchEvent\n } = usePublicEvent(eventCode || '', {\n enableCache: true,\n cacheTtl: 5 * 60 * 1000 // 5 minutes\n });\n\n // Determine if we should show loading state\n const isLoading = useMemo(() => {\n if (!fetchEventData) return false;\n return eventLoading;\n }, [fetchEventData, eventLoading]);\n\n // Determine the final error state\n const finalError = useMemo(() => {\n if (error) return error;\n if (eventError) return eventError;\n return null;\n }, [error, eventError]);\n\n // Extract event ID from event data\n const eventId = useMemo(() => {\n if (!event) return null;\n return event.event_id || event.id;\n }, [event]);\n\n // Refetch function\n const refetch = useCallback(async (): Promise<void> => {\n if (!fetchEventData) return;\n await refetchEvent();\n }, [fetchEventData, refetchEvent]);\n\n return {\n eventCode,\n eventId,\n event: fetchEventData ? event : null,\n isLoading,\n error: finalError,\n refetch\n };\n}\n\n/**\n * Hook for extracting just the event code without fetching event data\n * Useful when you only need the event code and will fetch data separately\n */\nexport function usePublicEventCode(\n eventCodeParam: string = 'eventCode'\n): { eventCode: string | null; error: Error | null } {\n const params = useParams();\n \n const eventCode = useMemo(() => {\n const code = params[eventCodeParam] as string;\n \n if (!code) {\n return null;\n }\n\n // Validate event code format\n if (!validateEventCodeFormat(code)) {\n return null;\n }\n\n return code;\n }, [params, eventCodeParam]);\n\n const error = useMemo(() => {\n if (!eventCode) {\n return new Error(`Event code parameter '${eventCodeParam}' not found or invalid`);\n }\n return null;\n }, [eventCode, eventCodeParam]);\n\n return {\n eventCode,\n error\n };\n}\n\n/**\n * Utility function to generate public route paths\n */\nexport function generatePublicRoutePath(\n eventCode: string,\n pageName: string = 'index'\n): string {\n if (!eventCode || !validateEventCodeFormat(eventCode)) {\n throw new Error('Invalid event code for route generation');\n }\n \n return `/public/event/${eventCode}/${pageName}`;\n}\n\n/**\n * Utility function to extract event code from a public route path\n * Supports 2-50 character event codes\n */\nexport function extractEventCodeFromPath(path: string): string | null {\n const match = path.match(/^\\/public\\/event\\/([a-zA-Z0-9_-]{2,50})(?:\\/.*)?$/);\n if (!match) return null;\n \n const eventCode = match[1];\n // Validate the extracted code using the same validation function\n if (!validateEventCodeFormat(eventCode)) {\n return null;\n }\n \n return eventCode;\n}\n"],"mappings":"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","names":["useCallback","useState","useState","useCallback","useMemo","useMemo","useState","useEffect","useCallback","useMemo","useState","useMemo","useCallback","error","useEffect","useState","useEffect","useCallback","useMemo","publicDataCache","useState","useMemo","useCallback","logoUrl","error","useEffect","useState","useCallback","useMemo","useState","useMemo","useCallback"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/request-deduplication.ts","../src/utils/google-places/loadGoogleMapsScript.ts","../src/utils/google-places/googlePlacesUtils.ts"],"sourcesContent":["/**\n * Request Deduplication Utility\n * @package @jmruthers/pace-core\n * @module Utils/RequestDeduplication\n * @since 2.0.0\n * \n * Provides request deduplication to prevent duplicate in-flight requests.\n * When multiple components request the same data simultaneously, only one\n * request is made and all callers share the same promise.\n */\n\nimport { createLogger } from './core/logger';\n\nconst log = createLogger('request-deduplication');\n\n/**\n * In-flight request cache\n * Key: request identifier (e.g., \"GET:table:filter:value\")\n * Value: Promise that resolves to the request result\n */\nconst inFlightRequests = new Map<string, Promise<any>>();\n\n/**\n * Generate a request key from request parameters\n * \n * @param method - HTTP method (GET, POST, etc.)\n * @param table - Table name\n * @param filters - Filter object\n * @param select - Select columns\n * @returns Request key string\n */\nexport function generateRequestKey(\n method: string,\n table: string,\n filters?: Record<string, any>,\n select?: string\n): string {\n const filterStr = filters ? JSON.stringify(filters) : '';\n const selectStr = select || '*';\n return `${method}:${table}:${filterStr}:${selectStr}`;\n}\n\n/**\n * Get or create a request\n * \n * If a request with the same key is already in-flight, returns the existing promise.\n * Otherwise, creates a new request and stores it for deduplication.\n * \n * @param key - Request key\n * @param requestFn - Function that performs the actual request\n * @returns Promise that resolves to the request result\n * \n * @example\n * ```ts\n * const data = await getOrCreateRequest(\n * 'GET:pace_person:{\"user_id\":\"123\"}',\n * async () => {\n * const { data } = await supabase\n * .from('pace_person')\n * .select('id, first_name')\n * .eq('user_id', '123')\n * .single();\n * return data;\n * }\n * );\n * ```\n */\nexport async function getOrCreateRequest<T>(\n key: string,\n requestFn: () => Promise<T>\n): Promise<T> {\n // Check if request is already in-flight\n const existingRequest = inFlightRequests.get(key);\n if (existingRequest) {\n log.debug(`Request deduplication: reusing in-flight request for ${key}`);\n return existingRequest as Promise<T>;\n }\n \n // Create new request\n log.debug(`Creating new request for ${key}`);\n const requestPromise = requestFn()\n .then((result) => {\n // Remove from in-flight cache after completion\n inFlightRequests.delete(key);\n return result;\n })\n .catch((error) => {\n // Remove from in-flight cache on error\n inFlightRequests.delete(key);\n throw error;\n });\n \n // Store in-flight request\n inFlightRequests.set(key, requestPromise);\n \n return requestPromise;\n}\n\n/**\n * Clear all in-flight requests\n * \n * Useful for cleanup or testing.\n */\nexport function clearInFlightRequests(): void {\n const count = inFlightRequests.size;\n inFlightRequests.clear();\n log.debug(`Cleared ${count} in-flight requests`);\n}\n\n/**\n * Get statistics about in-flight requests\n * \n * @returns Statistics object\n */\nexport function getInFlightRequestStats(): {\n count: number;\n keys: string[];\n} {\n return {\n count: inFlightRequests.size,\n keys: Array.from(inFlightRequests.keys()),\n };\n}\n\n/**\n * Supabase query wrapper with automatic deduplication\n * \n * Wraps a Supabase query to automatically deduplicate identical requests.\n * \n * @param supabase - Supabase client\n * @param table - Table name\n * @param filters - Filter object (e.g., { user_id: '123' })\n * @param select - Select columns (default: '*')\n * @param requestFn - Function that performs the query\n * @returns Promise that resolves to query result\n * \n * @example\n * ```ts\n * const person = await deduplicatedQuery(\n * supabase,\n * 'pace_person',\n * { user_id: userId },\n * 'id, first_name, last_name',\n * async () => {\n * const { data } = await supabase\n * .from('pace_person')\n * .select('id, first_name, last_name')\n * .eq('user_id', userId)\n * .single();\n * return data;\n * }\n * );\n * ```\n */\nexport async function deduplicatedQuery<T>(\n supabase: any,\n table: string,\n filters: Record<string, any>,\n select: string,\n requestFn: () => Promise<T>\n): Promise<T> {\n const key = generateRequestKey('GET', table, filters, select);\n return getOrCreateRequest(key, requestFn);\n}\n\n","/**\n * @file Google Maps Script Loader\n * @package @jmruthers/pace-core\n * @module Utils/GooglePlaces\n * @since 0.1.0\n *\n * Utility to dynamically load the Google Maps JavaScript API.\n * This is required because the REST API doesn't support CORS from browsers.\n */\n\n// Type definitions for Google Maps (minimal, just what we need)\ndeclare global {\n interface Window {\n google?: {\n maps: {\n places: {\n // New AutocompleteSuggestion API (recommended)\n AutocompleteSuggestion: {\n fetchAutocompleteSuggestions: (\n request: {\n input: string;\n includedRegionCodes?: string[];\n locationBias?: {\n circle?: {\n center: { latitude: number; longitude: number };\n radius: number;\n };\n };\n includedPrimaryTypes?: string[];\n languageCode?: string;\n }\n ) => Promise<{\n suggestions: Array<{\n placePrediction?: {\n placeId: string;\n text: {\n text: string;\n matches: Array<{\n endOffset: number;\n startOffset: number;\n }>;\n };\n structuredFormat?: {\n mainText: { text: string };\n secondaryText: { text: string };\n };\n };\n }>;\n }>;\n };\n // Legacy AutocompleteService (deprecated but still works)\n AutocompleteService: new () => {\n getPlacePredictions: (\n request: {\n input: string;\n componentRestrictions?: { country: string | string[] };\n location?: { lat: () => number; lng: () => number };\n radius?: number;\n types?: string[];\n language?: string;\n },\n callback: (\n predictions: Array<{\n description: string;\n place_id: string;\n structured_formatting: {\n main_text: string;\n secondary_text: string;\n };\n }> | null,\n status: string\n ) => void\n ) => void;\n };\n PlacesService: new (element: HTMLElement) => {\n getDetails: (\n request: { placeId: string; fields?: string[] },\n callback: (\n place: {\n place_id: string;\n formatted_address?: string;\n address_components?: Array<{\n long_name: string;\n short_name: string;\n types: string[];\n }>;\n geometry?: {\n location?: {\n lat: () => number;\n lng: () => number;\n };\n };\n } | null,\n status: string\n ) => void\n ) => void;\n };\n PlacesServiceStatus: {\n OK: string;\n ZERO_RESULTS: string;\n NOT_FOUND: string;\n REQUEST_DENIED: string;\n INVALID_REQUEST: string;\n OVER_QUERY_LIMIT: string;\n };\n };\n LatLng: new (lat: number, lng: number) => { lat: () => number; lng: () => number };\n };\n };\n }\n}\n\n/**\n * Load Google Maps JavaScript API script\n * @param apiKey - Google Places API key\n * @param libraries - Comma-separated list of libraries to load (default: 'places')\n * @returns Promise that resolves when the script is loaded\n */\nexport function loadGoogleMapsScript(\n apiKey: string,\n libraries: string = 'places'\n): Promise<void> {\n return new Promise((resolve, reject) => {\n // Check if script is already loaded\n if (window.google && window.google.maps && window.google.maps.places) {\n resolve();\n return;\n }\n\n // Check if script is already being loaded\n const existingScript = document.querySelector(\n `script[src*=\"maps.googleapis.com/maps/api/js\"]`\n );\n if (existingScript) {\n // Wait for existing script to load\n existingScript.addEventListener('load', () => {\n // Wait for the library to initialize with multiple retries\n let attempts = 0;\n const maxAttempts = 20; // 2 seconds total\n \n const checkPlaces = () => {\n if (window.google?.maps?.places) {\n resolve();\n } else if (attempts < maxAttempts) {\n attempts++;\n setTimeout(checkPlaces, 100);\n } else {\n reject(new Error('Google Maps script loaded but Places library not available. Make sure the Places API is enabled in your Google Cloud Console.'));\n }\n };\n \n checkPlaces();\n });\n existingScript.addEventListener('error', () => {\n reject(new Error('Failed to load Google Maps script'));\n });\n return;\n }\n\n // Create and load script\n const script = document.createElement('script');\n script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=${libraries}&loading=async`;\n script.async = true;\n script.defer = true;\n\n script.onload = () => {\n // Wait for the library to initialize with multiple retries\n let attempts = 0;\n const maxAttempts = 20; // 2 seconds total (20 * 100ms)\n \n const checkPlaces = () => {\n if (window.google?.maps?.places) {\n resolve();\n } else if (attempts < maxAttempts) {\n attempts++;\n setTimeout(checkPlaces, 100);\n } else {\n // Check if google.maps exists but places doesn't\n if (window.google?.maps && !window.google.maps.places) {\n reject(new Error('Google Maps loaded but Places library not available. Make sure the Places API is enabled in your Google Cloud Console and the \"places\" library is included in the script URL.'));\n } else if (!window.google) {\n reject(new Error('Google Maps script loaded but google object not available. Check your API key and network connection.'));\n } else {\n reject(new Error('Google Maps script loaded but Places library not available after multiple attempts. Make sure the Places API is enabled in your Google Cloud Console.'));\n }\n }\n };\n \n // Start checking immediately, then retry if needed\n checkPlaces();\n };\n\n script.onerror = () => {\n reject(new Error('Failed to load Google Maps script'));\n };\n\n document.head.appendChild(script);\n });\n}\n\n/**\n * Check if Google Maps is already loaded\n */\nexport function isGoogleMapsLoaded(): boolean {\n return !!(window.google && window.google.maps && window.google.maps.places);\n}\n\n","/**\n * @file Google Places API Utilities\n * @package @jmruthers/pace-core\n * @module Utils/GooglePlaces\n * @since 0.1.0\n *\n * Utility functions for interacting with Google Places API.\n * Uses the Google Maps JavaScript API to avoid CORS issues.\n *\n * Features:\n * - Places Autocomplete Service integration\n * - Places Service integration\n * - Address component parsing\n * - Error handling\n * - Request deduplication\n */\n\nimport { getOrCreateRequest } from '../request-deduplication';\nimport { createLogger } from '../core/logger';\nimport { loadGoogleMapsScript, isGoogleMapsLoaded } from './loadGoogleMapsScript';\nimport type {\n GooglePlaceAutocompletePrediction,\n ParsedAddress,\n AutocompleteOptions,\n} from './types';\n\nconst log = createLogger('google-places');\n\n// Google Maps types are defined in loadGoogleMapsScript.ts\n\n/**\n * Fetch place autocomplete predictions using Google Maps JavaScript API\n *\n * @param query - Search query string\n * @param apiKey - Google Places API key\n * @param options - Optional autocomplete options\n * @returns Promise resolving to array of predictions\n *\n * @example\n * ```ts\n * const predictions = await fetchPlaceAutocomplete(\n * '123 Main St',\n * 'your-api-key',\n * { components: 'country:au' }\n * );\n * ```\n */\nexport async function fetchPlaceAutocomplete(\n query: string,\n apiKey: string,\n options?: AutocompleteOptions\n): Promise<GooglePlaceAutocompletePrediction[]> {\n if (!query.trim()) {\n return [];\n }\n\n if (!apiKey) {\n throw new Error('Google Places API key is required');\n }\n\n // Ensure Google Maps script is loaded\n if (!isGoogleMapsLoaded()) {\n await loadGoogleMapsScript(apiKey, 'places');\n }\n\n const requestKey = `google-places-autocomplete:${query}:${JSON.stringify(options || {})}`;\n\n return getOrCreateRequest(requestKey, async () => {\n try {\n log.debug(`Fetching autocomplete for query: ${query}`);\n\n if (!window.google?.maps?.places) {\n throw new Error('Google Maps Places library not available');\n }\n\n // Try to use the new AutocompleteSuggestion API first, fallback to legacy AutocompleteService\n const useNewAPI = window.google.maps.places.AutocompleteSuggestion !== undefined;\n \n if (useNewAPI) {\n // Use new AutocompleteSuggestion API\n const request: {\n input: string;\n includedRegionCodes?: string[];\n locationBias?: {\n circle?: {\n center: { latitude: number; longitude: number };\n radius: number;\n };\n };\n includedPrimaryTypes?: string[];\n languageCode?: string;\n } = {\n input: query.trim(),\n };\n\n // Parse components option (e.g., \"country:au\" -> [\"au\"])\n if (options?.components) {\n const componentParts = options.components.split('|');\n const countries: string[] = [];\n\n componentParts.forEach((part) => {\n const [key, value] = part.split(':');\n if (key === 'country') {\n countries.push(...value.split(',').map(c => c.toUpperCase()));\n }\n });\n\n if (countries.length > 0) {\n request.includedRegionCodes = countries;\n }\n }\n\n if (options?.location && options?.radius) {\n const [lat, lng] = options.location.split(',').map(Number);\n request.locationBias = {\n circle: {\n center: { latitude: lat, longitude: lng },\n radius: options.radius,\n },\n };\n }\n\n if (options?.language) {\n request.languageCode = options.language;\n }\n\n // Call new API\n return new Promise<GooglePlaceAutocompletePrediction[]>((resolve, reject) => {\n window.google!.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(request)\n .then((response) => {\n if (response.suggestions && response.suggestions.length > 0) {\n const result: GooglePlaceAutocompletePrediction[] = response.suggestions\n .filter(s => s.placePrediction)\n .map((s) => ({\n description: s.placePrediction!.text.text,\n place_id: s.placePrediction!.placeId,\n structured_formatting: {\n main_text: s.placePrediction!.structuredFormat?.mainText?.text || s.placePrediction!.text.text,\n secondary_text: s.placePrediction!.structuredFormat?.secondaryText?.text || '',\n },\n }));\n log.debug(`Received ${result.length} predictions (new API)`);\n resolve(result);\n } else {\n log.debug('No results found (new API)');\n resolve([]);\n }\n })\n .catch((error) => {\n log.error('Autocomplete fetch failed (new API):', error);\n reject(new Error(`Failed to fetch autocomplete predictions: ${error.message || 'Unknown error'}`));\n });\n });\n }\n\n // Fallback to legacy AutocompleteService\n const autocompleteService = new window.google.maps.places.AutocompleteService();\n\n // Build request\n const request: {\n input: string;\n componentRestrictions?: { country: string | string[] };\n location?: { lat: () => number; lng: () => number };\n radius?: number;\n types?: string[];\n language?: string;\n } = {\n input: query.trim(),\n };\n\n // Parse components option (e.g., \"country:au\" -> { country: \"AU\" })\n if (options?.components) {\n const componentParts = options.components.split('|');\n const restrictions: { country?: string[] } = {};\n const countries: string[] = [];\n\n componentParts.forEach((part) => {\n const [key, value] = part.split(':');\n if (key === 'country') {\n // Convert to uppercase for consistency with Google Maps API\n countries.push(...value.split(',').map(c => c.toUpperCase()));\n }\n });\n\n if (countries.length > 0) {\n request.componentRestrictions = { country: countries.length === 1 ? countries[0] : countries };\n }\n }\n\n if (options?.location) {\n const [lat, lng] = options.location.split(',').map(Number);\n if (window.google?.maps?.LatLng) {\n request.location = new window.google.maps.LatLng(lat, lng);\n }\n }\n\n if (options?.radius) {\n request.radius = options.radius;\n }\n\n if (options?.types) {\n request.types = [options.types];\n }\n\n if (options?.language) {\n request.language = options.language;\n }\n\n // Call AutocompleteService\n return new Promise<GooglePlaceAutocompletePrediction[]>((resolve, reject) => {\n autocompleteService.getPlacePredictions(request, (predictions, status) => {\n if (status === 'OK' && predictions) {\n log.debug(`Received ${predictions.length} predictions`);\n // Convert to our format\n const result: GooglePlaceAutocompletePrediction[] = predictions.map((pred) => ({\n description: pred.description,\n place_id: pred.place_id,\n structured_formatting: {\n main_text: pred.structured_formatting.main_text,\n secondary_text: pred.structured_formatting.secondary_text,\n },\n }));\n resolve(result);\n } else if (status === 'ZERO_RESULTS') {\n log.debug('No results found');\n resolve([]);\n } else {\n const errorMsg = `Google Places API error: ${status}`;\n log.error('Autocomplete fetch failed:', errorMsg);\n reject(new Error(errorMsg));\n }\n });\n });\n } catch (error) {\n if (error instanceof Error) {\n log.error('Autocomplete fetch failed:', error.message);\n throw error;\n }\n log.error('Autocomplete fetch failed: Unknown error');\n throw new Error('Failed to fetch autocomplete predictions');\n }\n });\n}\n\n/**\n * Fetch place details from Google Places API\n *\n * @param placeId - Google Place ID\n * @param apiKey - Google Places API key\n * @returns Promise resolving to place details\n *\n * @example\n * ```ts\n * const place = await fetchPlaceDetails('ChIJ...', 'your-api-key');\n * ```\n */\nexport async function fetchPlaceDetails(\n placeId: string,\n apiKey: string\n): Promise<{\n place_id: string;\n formatted_address?: string;\n address_components?: Array<{\n long_name: string;\n short_name: string;\n types: string[];\n }>;\n geometry?: {\n location?: {\n lat: () => number;\n lng: () => number;\n };\n };\n}> {\n if (!placeId) {\n throw new Error('Place ID is required');\n }\n\n if (!apiKey) {\n throw new Error('Google Places API key is required');\n }\n\n // Ensure Google Maps script is loaded\n if (!isGoogleMapsLoaded()) {\n await loadGoogleMapsScript(apiKey, 'places');\n }\n\n const requestKey = `google-places-details:${placeId}`;\n\n return getOrCreateRequest(requestKey, async () => {\n try {\n log.debug(`Fetching place details for place_id: ${placeId}`);\n\n if (!window.google?.maps?.places) {\n throw new Error('Google Maps Places library not available');\n }\n\n // Create a dummy element for PlacesService (it needs an element but we don't use it)\n const dummyElement = document.createElement('div');\n const placesService = new window.google.maps.places.PlacesService(dummyElement);\n\n // Call getDetails\n return new Promise<{\n place_id: string;\n formatted_address?: string;\n address_components?: Array<{\n long_name: string;\n short_name: string;\n types: string[];\n }>;\n geometry?: {\n location?: {\n lat: () => number;\n lng: () => number;\n };\n };\n }>((resolve, reject) => {\n placesService.getDetails(\n {\n placeId: placeId,\n fields: ['place_id', 'formatted_address', 'address_components', 'geometry'],\n },\n (place, status) => {\n if (status === 'OK' && place) {\n log.debug('Place details fetched successfully');\n resolve(place as any);\n } else if (status === 'NOT_FOUND') {\n log.error('Place not found:', placeId);\n reject(new Error('Place not found'));\n } else {\n const errorMsg = `Failed to fetch place details: ${status}`;\n log.error('Place details fetch failed:', errorMsg);\n reject(new Error(errorMsg));\n }\n }\n );\n });\n } catch (error) {\n if (error instanceof Error) {\n log.error('Place details fetch failed:', error.message);\n throw error;\n }\n log.error('Place details fetch failed: Unknown error');\n throw new Error('Failed to fetch place details');\n }\n });\n}\n\n/**\n * Parse address components from Google Places API response\n *\n * @param components - Array of address components from Google API\n * @returns Parsed address components\n */\nexport function parseAddressComponents(\n components: Array<{\n long_name: string;\n short_name: string;\n types: string[];\n }> | undefined\n): {\n street_number: string | null;\n route: string | null;\n suburb: string | null;\n state: string | null;\n postcode: string | null;\n country: string | null;\n} {\n if (!components || components.length === 0) {\n return {\n street_number: null,\n route: null,\n suburb: null,\n state: null,\n postcode: null,\n country: null,\n };\n }\n\n const result = {\n street_number: null as string | null,\n route: null as string | null,\n suburb: null as string | null,\n state: null as string | null,\n postcode: null as string | null,\n country: null as string | null,\n };\n\n for (const component of components) {\n const types = component.types || [];\n\n if (types.includes('street_number')) {\n result.street_number = component.long_name;\n } else if (types.includes('route')) {\n result.route = component.long_name;\n } else if (types.includes('locality') || types.includes('sublocality')) {\n result.suburb = component.long_name;\n } else if (types.includes('administrative_area_level_1')) {\n result.state = component.short_name;\n } else if (types.includes('postal_code')) {\n result.postcode = component.long_name;\n } else if (types.includes('country')) {\n result.country = component.short_name;\n }\n }\n\n return result;\n}\n\n/**\n * Create parsed address from Google Places API place result\n *\n * @param place - Place result from Google Places Details API\n * @returns Parsed address matching pace_address table structure\n */\nexport function createAddressFromPlaceResult(\n place: {\n place_id: string;\n formatted_address?: string;\n address_components?: Array<{\n long_name: string;\n short_name: string;\n types: string[];\n }>;\n geometry?: {\n location?: {\n lat: () => number;\n lng: () => number;\n };\n };\n }\n): ParsedAddress {\n const components = parseAddressComponents(place.address_components || []);\n\n return {\n place_id: place.place_id,\n full_address: place.formatted_address || null,\n street_number: components.street_number,\n route: components.route,\n suburb: components.suburb,\n state: components.state,\n postcode: components.postcode,\n country: components.country,\n lat: place.geometry?.location?.lat() ?? null,\n lng: place.geometry?.location?.lng() ?? null,\n };\n}\n\n/**\n * Get full address details by place_id\n * Useful for retrieving address from stored place_id without autocomplete search\n *\n * @param placeId - Google Place ID\n * @param apiKey - Google Places API key\n * @returns Promise resolving to parsed address\n *\n * @example\n * ```ts\n * const address = await getAddressByPlaceId('ChIJ...', 'your-api-key');\n * // Returns: { place_id: 'ChIJ...', full_address: '...', ... }\n * ```\n */\nexport async function getAddressByPlaceId(\n placeId: string,\n apiKey: string\n): Promise<ParsedAddress | null> {\n try {\n const place = await fetchPlaceDetails(placeId, apiKey);\n return createAddressFromPlaceResult(place);\n } catch (error) {\n log.error('Failed to get address by place_id:', error);\n return null;\n }\n}\n\n"],"mappings":"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","names":["log","request"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/request-deduplication.ts","../src/utils/google-places/loadGoogleMapsScript.ts","../src/utils/google-places/googlePlacesUtils.ts"],"sourcesContent":["/**\n * Request Deduplication Utility\n * @package @jmruthers/pace-core\n * @module Utils/RequestDeduplication\n * @since 2.0.0\n * \n * Provides request deduplication to prevent duplicate in-flight requests.\n * When multiple components request the same data simultaneously, only one\n * request is made and all callers share the same promise.\n */\n\nimport { createLogger } from './core/logger';\n\nconst log = createLogger('request-deduplication');\n\n/**\n * In-flight request cache\n * Key: request identifier (e.g., \"GET:table:filter:value\")\n * Value: Promise that resolves to the request result\n */\nconst inFlightRequests = new Map<string, Promise<any>>();\n\n/**\n * Generate a request key from request parameters\n * \n * @param method - HTTP method (GET, POST, etc.)\n * @param table - Table name\n * @param filters - Filter object\n * @param select - Select columns\n * @returns Request key string\n */\nexport function generateRequestKey(\n method: string,\n table: string,\n filters?: Record<string, any>,\n select?: string\n): string {\n const filterStr = filters ? JSON.stringify(filters) : '';\n const selectStr = select || '*';\n return `${method}:${table}:${filterStr}:${selectStr}`;\n}\n\n/**\n * Get or create a request\n * \n * If a request with the same key is already in-flight, returns the existing promise.\n * Otherwise, creates a new request and stores it for deduplication.\n * \n * @param key - Request key\n * @param requestFn - Function that performs the actual request\n * @returns Promise that resolves to the request result\n * \n * @example\n * ```ts\n * const data = await getOrCreateRequest(\n * 'GET:core_person:{\"user_id\":\"123\"}',\n * async () => {\n * const { data } = await supabase\n * .from('core_person')\n * .select('id, first_name')\n * .eq('user_id', '123')\n * .single();\n * return data;\n * }\n * );\n * ```\n */\nexport async function getOrCreateRequest<T>(\n key: string,\n requestFn: () => Promise<T>\n): Promise<T> {\n // Check if request is already in-flight\n const existingRequest = inFlightRequests.get(key);\n if (existingRequest) {\n log.debug(`Request deduplication: reusing in-flight request for ${key}`);\n return existingRequest as Promise<T>;\n }\n \n // Create new request\n log.debug(`Creating new request for ${key}`);\n const requestPromise = requestFn()\n .then((result) => {\n // Remove from in-flight cache after completion\n inFlightRequests.delete(key);\n return result;\n })\n .catch((error) => {\n // Remove from in-flight cache on error\n inFlightRequests.delete(key);\n throw error;\n });\n \n // Store in-flight request\n inFlightRequests.set(key, requestPromise);\n \n return requestPromise;\n}\n\n/**\n * Clear all in-flight requests\n * \n * Useful for cleanup or testing.\n */\nexport function clearInFlightRequests(): void {\n const count = inFlightRequests.size;\n inFlightRequests.clear();\n log.debug(`Cleared ${count} in-flight requests`);\n}\n\n/**\n * Get statistics about in-flight requests\n * \n * @returns Statistics object\n */\nexport function getInFlightRequestStats(): {\n count: number;\n keys: string[];\n} {\n return {\n count: inFlightRequests.size,\n keys: Array.from(inFlightRequests.keys()),\n };\n}\n\n/**\n * Supabase query wrapper with automatic deduplication\n * \n * Wraps a Supabase query to automatically deduplicate identical requests.\n * \n * @param supabase - Supabase client\n * @param table - Table name\n * @param filters - Filter object (e.g., { user_id: '123' })\n * @param select - Select columns (default: '*')\n * @param requestFn - Function that performs the query\n * @returns Promise that resolves to query result\n * \n * @example\n * ```ts\n * const person = await deduplicatedQuery(\n * supabase,\n * 'core_person',\n * { user_id: userId },\n * 'id, first_name, last_name',\n * async () => {\n * const { data } = await supabase\n * .from('core_person')\n * .select('id, first_name, last_name')\n * .eq('user_id', userId)\n * .single();\n * return data;\n * }\n * );\n * ```\n */\nexport async function deduplicatedQuery<T>(\n supabase: any,\n table: string,\n filters: Record<string, any>,\n select: string,\n requestFn: () => Promise<T>\n): Promise<T> {\n const key = generateRequestKey('GET', table, filters, select);\n return getOrCreateRequest(key, requestFn);\n}\n\n","/**\n * @file Google Maps Script Loader\n * @package @jmruthers/pace-core\n * @module Utils/GooglePlaces\n * @since 0.1.0\n *\n * Utility to dynamically load the Google Maps JavaScript API.\n * This is required because the REST API doesn't support CORS from browsers.\n */\n\n// Type definitions for Google Maps (minimal, just what we need)\ndeclare global {\n interface Window {\n google?: {\n maps: {\n places: {\n // New AutocompleteSuggestion API (recommended)\n AutocompleteSuggestion: {\n fetchAutocompleteSuggestions: (\n request: {\n input: string;\n includedRegionCodes?: string[];\n locationBias?: {\n circle?: {\n center: { latitude: number; longitude: number };\n radius: number;\n };\n };\n includedPrimaryTypes?: string[];\n languageCode?: string;\n }\n ) => Promise<{\n suggestions: Array<{\n placePrediction?: {\n placeId: string;\n text: {\n text: string;\n matches: Array<{\n endOffset: number;\n startOffset: number;\n }>;\n };\n structuredFormat?: {\n mainText: { text: string };\n secondaryText: { text: string };\n };\n };\n }>;\n }>;\n };\n // Legacy AutocompleteService (deprecated but still works)\n AutocompleteService: new () => {\n getPlacePredictions: (\n request: {\n input: string;\n componentRestrictions?: { country: string | string[] };\n location?: { lat: () => number; lng: () => number };\n radius?: number;\n types?: string[];\n language?: string;\n },\n callback: (\n predictions: Array<{\n description: string;\n place_id: string;\n structured_formatting: {\n main_text: string;\n secondary_text: string;\n };\n }> | null,\n status: string\n ) => void\n ) => void;\n };\n PlacesService: new (element: HTMLElement) => {\n getDetails: (\n request: { placeId: string; fields?: string[] },\n callback: (\n place: {\n place_id: string;\n formatted_address?: string;\n address_components?: Array<{\n long_name: string;\n short_name: string;\n types: string[];\n }>;\n geometry?: {\n location?: {\n lat: () => number;\n lng: () => number;\n };\n };\n } | null,\n status: string\n ) => void\n ) => void;\n };\n PlacesServiceStatus: {\n OK: string;\n ZERO_RESULTS: string;\n NOT_FOUND: string;\n REQUEST_DENIED: string;\n INVALID_REQUEST: string;\n OVER_QUERY_LIMIT: string;\n };\n };\n LatLng: new (lat: number, lng: number) => { lat: () => number; lng: () => number };\n };\n };\n }\n}\n\n/**\n * Load Google Maps JavaScript API script\n * @param apiKey - Google Places API key\n * @param libraries - Comma-separated list of libraries to load (default: 'places')\n * @returns Promise that resolves when the script is loaded\n */\nexport function loadGoogleMapsScript(\n apiKey: string,\n libraries: string = 'places'\n): Promise<void> {\n return new Promise((resolve, reject) => {\n // Check if script is already loaded\n if (window.google && window.google.maps && window.google.maps.places) {\n resolve();\n return;\n }\n\n // Check if script is already being loaded\n const existingScript = document.querySelector(\n `script[src*=\"maps.googleapis.com/maps/api/js\"]`\n );\n if (existingScript) {\n // Wait for existing script to load\n existingScript.addEventListener('load', () => {\n // Wait for the library to initialize with multiple retries\n let attempts = 0;\n const maxAttempts = 20; // 2 seconds total\n \n const checkPlaces = () => {\n if (window.google?.maps?.places) {\n resolve();\n } else if (attempts < maxAttempts) {\n attempts++;\n setTimeout(checkPlaces, 100);\n } else {\n reject(new Error('Google Maps script loaded but Places library not available. Make sure the Places API is enabled in your Google Cloud Console.'));\n }\n };\n \n checkPlaces();\n });\n existingScript.addEventListener('error', () => {\n reject(new Error('Failed to load Google Maps script'));\n });\n return;\n }\n\n // Create and load script\n const script = document.createElement('script');\n script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=${libraries}&loading=async`;\n script.async = true;\n script.defer = true;\n\n script.onload = () => {\n // Wait for the library to initialize with multiple retries\n let attempts = 0;\n const maxAttempts = 20; // 2 seconds total (20 * 100ms)\n \n const checkPlaces = () => {\n if (window.google?.maps?.places) {\n resolve();\n } else if (attempts < maxAttempts) {\n attempts++;\n setTimeout(checkPlaces, 100);\n } else {\n // Check if google.maps exists but places doesn't\n if (window.google?.maps && !window.google.maps.places) {\n reject(new Error('Google Maps loaded but Places library not available. Make sure the Places API is enabled in your Google Cloud Console and the \"places\" library is included in the script URL.'));\n } else if (!window.google) {\n reject(new Error('Google Maps script loaded but google object not available. Check your API key and network connection.'));\n } else {\n reject(new Error('Google Maps script loaded but Places library not available after multiple attempts. Make sure the Places API is enabled in your Google Cloud Console.'));\n }\n }\n };\n \n // Start checking immediately, then retry if needed\n checkPlaces();\n };\n\n script.onerror = () => {\n reject(new Error('Failed to load Google Maps script'));\n };\n\n document.head.appendChild(script);\n });\n}\n\n/**\n * Check if Google Maps is already loaded\n */\nexport function isGoogleMapsLoaded(): boolean {\n return !!(window.google && window.google.maps && window.google.maps.places);\n}\n\n","/**\n * @file Google Places API Utilities\n * @package @jmruthers/pace-core\n * @module Utils/GooglePlaces\n * @since 0.1.0\n *\n * Utility functions for interacting with Google Places API.\n * Uses the Google Maps JavaScript API to avoid CORS issues.\n *\n * Features:\n * - Places Autocomplete Service integration\n * - Places Service integration\n * - Address component parsing\n * - Error handling\n * - Request deduplication\n */\n\nimport { getOrCreateRequest } from '../request-deduplication';\nimport { createLogger } from '../core/logger';\nimport { loadGoogleMapsScript, isGoogleMapsLoaded } from './loadGoogleMapsScript';\nimport type {\n GooglePlaceAutocompletePrediction,\n ParsedAddress,\n AutocompleteOptions,\n} from './types';\n\nconst log = createLogger('google-places');\n\n// Google Maps types are defined in loadGoogleMapsScript.ts\n\n/**\n * Fetch place autocomplete predictions using Google Maps JavaScript API\n *\n * @param query - Search query string\n * @param apiKey - Google Places API key\n * @param options - Optional autocomplete options\n * @returns Promise resolving to array of predictions\n *\n * @example\n * ```ts\n * const predictions = await fetchPlaceAutocomplete(\n * '123 Main St',\n * 'your-api-key',\n * { components: 'country:au' }\n * );\n * ```\n */\nexport async function fetchPlaceAutocomplete(\n query: string,\n apiKey: string,\n options?: AutocompleteOptions\n): Promise<GooglePlaceAutocompletePrediction[]> {\n if (!query.trim()) {\n return [];\n }\n\n if (!apiKey) {\n throw new Error('Google Places API key is required');\n }\n\n // Ensure Google Maps script is loaded\n if (!isGoogleMapsLoaded()) {\n await loadGoogleMapsScript(apiKey, 'places');\n }\n\n const requestKey = `google-places-autocomplete:${query}:${JSON.stringify(options || {})}`;\n\n return getOrCreateRequest(requestKey, async () => {\n try {\n log.debug(`Fetching autocomplete for query: ${query}`);\n\n if (!window.google?.maps?.places) {\n throw new Error('Google Maps Places library not available');\n }\n\n // Try to use the new AutocompleteSuggestion API first, fallback to legacy AutocompleteService\n const useNewAPI = window.google.maps.places.AutocompleteSuggestion !== undefined;\n \n if (useNewAPI) {\n // Use new AutocompleteSuggestion API\n const request: {\n input: string;\n includedRegionCodes?: string[];\n locationBias?: {\n circle?: {\n center: { latitude: number; longitude: number };\n radius: number;\n };\n };\n includedPrimaryTypes?: string[];\n languageCode?: string;\n } = {\n input: query.trim(),\n };\n\n // Parse components option (e.g., \"country:au\" -> [\"au\"])\n if (options?.components) {\n const componentParts = options.components.split('|');\n const countries: string[] = [];\n\n componentParts.forEach((part) => {\n const [key, value] = part.split(':');\n if (key === 'country') {\n countries.push(...value.split(',').map(c => c.toUpperCase()));\n }\n });\n\n if (countries.length > 0) {\n request.includedRegionCodes = countries;\n }\n }\n\n if (options?.location && options?.radius) {\n const [lat, lng] = options.location.split(',').map(Number);\n request.locationBias = {\n circle: {\n center: { latitude: lat, longitude: lng },\n radius: options.radius,\n },\n };\n }\n\n if (options?.language) {\n request.languageCode = options.language;\n }\n\n // Call new API\n return new Promise<GooglePlaceAutocompletePrediction[]>((resolve, reject) => {\n window.google!.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(request)\n .then((response) => {\n if (response.suggestions && response.suggestions.length > 0) {\n const result: GooglePlaceAutocompletePrediction[] = response.suggestions\n .filter(s => s.placePrediction)\n .map((s) => ({\n description: s.placePrediction!.text.text,\n place_id: s.placePrediction!.placeId,\n structured_formatting: {\n main_text: s.placePrediction!.structuredFormat?.mainText?.text || s.placePrediction!.text.text,\n secondary_text: s.placePrediction!.structuredFormat?.secondaryText?.text || '',\n },\n }));\n log.debug(`Received ${result.length} predictions (new API)`);\n resolve(result);\n } else {\n log.debug('No results found (new API)');\n resolve([]);\n }\n })\n .catch((error) => {\n log.error('Autocomplete fetch failed (new API):', error);\n reject(new Error(`Failed to fetch autocomplete predictions: ${error.message || 'Unknown error'}`));\n });\n });\n }\n\n // Fallback to legacy AutocompleteService\n const autocompleteService = new window.google.maps.places.AutocompleteService();\n\n // Build request\n const request: {\n input: string;\n componentRestrictions?: { country: string | string[] };\n location?: { lat: () => number; lng: () => number };\n radius?: number;\n types?: string[];\n language?: string;\n } = {\n input: query.trim(),\n };\n\n // Parse components option (e.g., \"country:au\" -> { country: \"AU\" })\n if (options?.components) {\n const componentParts = options.components.split('|');\n const restrictions: { country?: string[] } = {};\n const countries: string[] = [];\n\n componentParts.forEach((part) => {\n const [key, value] = part.split(':');\n if (key === 'country') {\n // Convert to uppercase for consistency with Google Maps API\n countries.push(...value.split(',').map(c => c.toUpperCase()));\n }\n });\n\n if (countries.length > 0) {\n request.componentRestrictions = { country: countries.length === 1 ? countries[0] : countries };\n }\n }\n\n if (options?.location) {\n const [lat, lng] = options.location.split(',').map(Number);\n if (window.google?.maps?.LatLng) {\n request.location = new window.google.maps.LatLng(lat, lng);\n }\n }\n\n if (options?.radius) {\n request.radius = options.radius;\n }\n\n if (options?.types) {\n request.types = [options.types];\n }\n\n if (options?.language) {\n request.language = options.language;\n }\n\n // Call AutocompleteService\n return new Promise<GooglePlaceAutocompletePrediction[]>((resolve, reject) => {\n autocompleteService.getPlacePredictions(request, (predictions, status) => {\n if (status === 'OK' && predictions) {\n log.debug(`Received ${predictions.length} predictions`);\n // Convert to our format\n const result: GooglePlaceAutocompletePrediction[] = predictions.map((pred) => ({\n description: pred.description,\n place_id: pred.place_id,\n structured_formatting: {\n main_text: pred.structured_formatting.main_text,\n secondary_text: pred.structured_formatting.secondary_text,\n },\n }));\n resolve(result);\n } else if (status === 'ZERO_RESULTS') {\n log.debug('No results found');\n resolve([]);\n } else {\n const errorMsg = `Google Places API error: ${status}`;\n log.error('Autocomplete fetch failed:', errorMsg);\n reject(new Error(errorMsg));\n }\n });\n });\n } catch (error) {\n if (error instanceof Error) {\n log.error('Autocomplete fetch failed:', error.message);\n throw error;\n }\n log.error('Autocomplete fetch failed: Unknown error');\n throw new Error('Failed to fetch autocomplete predictions');\n }\n });\n}\n\n/**\n * Fetch place details from Google Places API\n *\n * @param placeId - Google Place ID\n * @param apiKey - Google Places API key\n * @returns Promise resolving to place details\n *\n * @example\n * ```ts\n * const place = await fetchPlaceDetails('ChIJ...', 'your-api-key');\n * ```\n */\nexport async function fetchPlaceDetails(\n placeId: string,\n apiKey: string\n): Promise<{\n place_id: string;\n formatted_address?: string;\n address_components?: Array<{\n long_name: string;\n short_name: string;\n types: string[];\n }>;\n geometry?: {\n location?: {\n lat: () => number;\n lng: () => number;\n };\n };\n}> {\n if (!placeId) {\n throw new Error('Place ID is required');\n }\n\n if (!apiKey) {\n throw new Error('Google Places API key is required');\n }\n\n // Ensure Google Maps script is loaded\n if (!isGoogleMapsLoaded()) {\n await loadGoogleMapsScript(apiKey, 'places');\n }\n\n const requestKey = `google-places-details:${placeId}`;\n\n return getOrCreateRequest(requestKey, async () => {\n try {\n log.debug(`Fetching place details for place_id: ${placeId}`);\n\n if (!window.google?.maps?.places) {\n throw new Error('Google Maps Places library not available');\n }\n\n // Create a dummy element for PlacesService (it needs an element but we don't use it)\n const dummyElement = document.createElement('div');\n const placesService = new window.google.maps.places.PlacesService(dummyElement);\n\n // Call getDetails\n return new Promise<{\n place_id: string;\n formatted_address?: string;\n address_components?: Array<{\n long_name: string;\n short_name: string;\n types: string[];\n }>;\n geometry?: {\n location?: {\n lat: () => number;\n lng: () => number;\n };\n };\n }>((resolve, reject) => {\n placesService.getDetails(\n {\n placeId: placeId,\n fields: ['place_id', 'formatted_address', 'address_components', 'geometry'],\n },\n (place, status) => {\n if (status === 'OK' && place) {\n log.debug('Place details fetched successfully');\n resolve(place as any);\n } else if (status === 'NOT_FOUND') {\n log.error('Place not found:', placeId);\n reject(new Error('Place not found'));\n } else {\n const errorMsg = `Failed to fetch place details: ${status}`;\n log.error('Place details fetch failed:', errorMsg);\n reject(new Error(errorMsg));\n }\n }\n );\n });\n } catch (error) {\n if (error instanceof Error) {\n log.error('Place details fetch failed:', error.message);\n throw error;\n }\n log.error('Place details fetch failed: Unknown error');\n throw new Error('Failed to fetch place details');\n }\n });\n}\n\n/**\n * Parse address components from Google Places API response\n *\n * @param components - Array of address components from Google API\n * @returns Parsed address components\n */\nexport function parseAddressComponents(\n components: Array<{\n long_name: string;\n short_name: string;\n types: string[];\n }> | undefined\n): {\n street_number: string | null;\n route: string | null;\n suburb: string | null;\n state: string | null;\n postcode: string | null;\n country: string | null;\n} {\n if (!components || components.length === 0) {\n return {\n street_number: null,\n route: null,\n suburb: null,\n state: null,\n postcode: null,\n country: null,\n };\n }\n\n const result = {\n street_number: null as string | null,\n route: null as string | null,\n suburb: null as string | null,\n state: null as string | null,\n postcode: null as string | null,\n country: null as string | null,\n };\n\n for (const component of components) {\n const types = component.types || [];\n\n if (types.includes('street_number')) {\n result.street_number = component.long_name;\n } else if (types.includes('route')) {\n result.route = component.long_name;\n } else if (types.includes('locality') || types.includes('sublocality')) {\n result.suburb = component.long_name;\n } else if (types.includes('administrative_area_level_1')) {\n result.state = component.short_name;\n } else if (types.includes('postal_code')) {\n result.postcode = component.long_name;\n } else if (types.includes('country')) {\n result.country = component.short_name;\n }\n }\n\n return result;\n}\n\n/**\n * Create parsed address from Google Places API place result\n *\n * @param place - Place result from Google Places Details API\n * @returns Parsed address matching core_address table structure\n */\nexport function createAddressFromPlaceResult(\n place: {\n place_id: string;\n formatted_address?: string;\n address_components?: Array<{\n long_name: string;\n short_name: string;\n types: string[];\n }>;\n geometry?: {\n location?: {\n lat: () => number;\n lng: () => number;\n };\n };\n }\n): ParsedAddress {\n const components = parseAddressComponents(place.address_components || []);\n\n return {\n place_id: place.place_id,\n full_address: place.formatted_address || null,\n street_number: components.street_number,\n route: components.route,\n suburb: components.suburb,\n state: components.state,\n postcode: components.postcode,\n country: components.country,\n lat: place.geometry?.location?.lat() ?? null,\n lng: place.geometry?.location?.lng() ?? null,\n };\n}\n\n/**\n * Get full address details by place_id\n * Useful for retrieving address from stored place_id without autocomplete search\n *\n * @param placeId - Google Place ID\n * @param apiKey - Google Places API key\n * @returns Promise resolving to parsed address\n *\n * @example\n * ```ts\n * const address = await getAddressByPlaceId('ChIJ...', 'your-api-key');\n * // Returns: { place_id: 'ChIJ...', full_address: '...', ... }\n * ```\n */\nexport async function getAddressByPlaceId(\n placeId: string,\n apiKey: string\n): Promise<ParsedAddress | null> {\n try {\n const place = await fetchPlaceDetails(placeId, apiKey);\n return createAddressFromPlaceResult(place);\n } catch (error) {\n log.error('Failed to get address by place_id:', error);\n return null;\n }\n}\n\n"],"mappings":"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","names":["log","request"]}
|
|
@@ -3,23 +3,23 @@ import {
|
|
|
3
3
|
useAccessLevel,
|
|
4
4
|
useCan,
|
|
5
5
|
useMultiplePermissions
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-VRGWKHDB.js";
|
|
7
7
|
import {
|
|
8
8
|
useSecureDataAccess
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XYXSXPUK.js";
|
|
10
10
|
import {
|
|
11
11
|
useResolvedScope
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OETXORNB.js";
|
|
13
13
|
import {
|
|
14
14
|
useUnifiedAuth
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-6LTQQAT6.js";
|
|
16
16
|
import {
|
|
17
17
|
RBACCache,
|
|
18
18
|
RBACNotInitializedError,
|
|
19
19
|
getRBACConfig,
|
|
20
20
|
getRBACLogger,
|
|
21
21
|
rbacCache
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-ROXMHMY2.js";
|
|
23
23
|
import {
|
|
24
24
|
createLogger,
|
|
25
25
|
logger
|
|
@@ -1247,7 +1247,7 @@ function withPermissionGuard(config, handler) {
|
|
|
1247
1247
|
if (!userId || !organisationId) {
|
|
1248
1248
|
throw new Error("User context required for permission check");
|
|
1249
1249
|
}
|
|
1250
|
-
const { isPermitted: isPermitted2 } = await import("./api-
|
|
1250
|
+
const { isPermitted: isPermitted2 } = await import("./api-IHKALJZD.js");
|
|
1251
1251
|
const hasPermission2 = await isPermitted2({
|
|
1252
1252
|
userId,
|
|
1253
1253
|
scope: { organisationId, eventId, appId },
|
|
@@ -1270,7 +1270,7 @@ function withAccessLevelGuard(minLevel, handler) {
|
|
|
1270
1270
|
if (!userId || !organisationId) {
|
|
1271
1271
|
throw new Error("User context required for access level check");
|
|
1272
1272
|
}
|
|
1273
|
-
const { getAccessLevel: getAccessLevel2 } = await import("./api-
|
|
1273
|
+
const { getAccessLevel: getAccessLevel2 } = await import("./api-IHKALJZD.js");
|
|
1274
1274
|
const accessLevel = await getAccessLevel2({
|
|
1275
1275
|
userId,
|
|
1276
1276
|
scope: { organisationId, eventId, appId }
|
|
@@ -1295,7 +1295,7 @@ function withRoleGuard(config, handler) {
|
|
|
1295
1295
|
throw new Error("User context required for role check");
|
|
1296
1296
|
}
|
|
1297
1297
|
if (config.globalRoles && config.globalRoles.length > 0) {
|
|
1298
|
-
const { isSuperAdmin } = await import("./api-
|
|
1298
|
+
const { isSuperAdmin } = await import("./api-IHKALJZD.js");
|
|
1299
1299
|
const isSuper = await isSuperAdmin(userId);
|
|
1300
1300
|
if (isSuper) {
|
|
1301
1301
|
if (organisationId) {
|
|
@@ -1321,14 +1321,14 @@ function withRoleGuard(config, handler) {
|
|
|
1321
1321
|
}
|
|
1322
1322
|
}
|
|
1323
1323
|
if (config.organisationRoles && config.organisationRoles.length > 0) {
|
|
1324
|
-
const { isOrganisationAdmin } = await import("./api-
|
|
1324
|
+
const { isOrganisationAdmin } = await import("./api-IHKALJZD.js");
|
|
1325
1325
|
const isOrgAdmin = await isOrganisationAdmin(userId, organisationId);
|
|
1326
1326
|
if (!isOrgAdmin && config.requireAll !== false) {
|
|
1327
1327
|
throw new Error(`Organisation admin role required`);
|
|
1328
1328
|
}
|
|
1329
1329
|
}
|
|
1330
1330
|
if (config.eventAppRoles && config.eventAppRoles.length > 0 && eventId && appId) {
|
|
1331
|
-
const { isEventAdmin } = await import("./api-
|
|
1331
|
+
const { isEventAdmin } = await import("./api-IHKALJZD.js");
|
|
1332
1332
|
const isEventAdminUser = await isEventAdmin(userId, { organisationId, eventId, appId });
|
|
1333
1333
|
if (!isEventAdminUser && config.requireAll !== false) {
|
|
1334
1334
|
throw new Error(`Event admin role required`);
|
|
@@ -1368,7 +1368,7 @@ function createRBACMiddleware(config) {
|
|
|
1368
1368
|
);
|
|
1369
1369
|
if (protectedRoute) {
|
|
1370
1370
|
try {
|
|
1371
|
-
const { isPermitted: isPermitted2 } = await import("./api-
|
|
1371
|
+
const { isPermitted: isPermitted2 } = await import("./api-IHKALJZD.js");
|
|
1372
1372
|
const hasPermission2 = await isPermitted2({
|
|
1373
1373
|
userId,
|
|
1374
1374
|
scope: { organisationId },
|
|
@@ -1395,7 +1395,7 @@ function createRBACExpressMiddleware(config) {
|
|
|
1395
1395
|
return res.status(401).json({ error: "User context required" });
|
|
1396
1396
|
}
|
|
1397
1397
|
try {
|
|
1398
|
-
const { isPermitted: isPermitted2 } = await import("./api-
|
|
1398
|
+
const { isPermitted: isPermitted2 } = await import("./api-IHKALJZD.js");
|
|
1399
1399
|
const hasPermission2 = await isPermitted2({
|
|
1400
1400
|
userId,
|
|
1401
1401
|
scope: { organisationId, eventId, appId },
|
|
@@ -1924,4 +1924,4 @@ export {
|
|
|
1924
1924
|
getDirectSupabaseAuthFixes,
|
|
1925
1925
|
getQuickFixes
|
|
1926
1926
|
};
|
|
1927
|
-
//# sourceMappingURL=chunk-
|
|
1927
|
+
//# sourceMappingURL=chunk-LOMZXPSN.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useEventService,
|
|
3
3
|
useUnifiedAuth
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-6LTQQAT6.js";
|
|
5
5
|
import {
|
|
6
6
|
ContextValidator,
|
|
7
7
|
isSuperAdmin
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ROXMHMY2.js";
|
|
9
9
|
import {
|
|
10
10
|
getCurrentAppName,
|
|
11
11
|
performanceBudgetMonitor
|
|
@@ -611,4 +611,4 @@ export {
|
|
|
611
611
|
useAppConfig,
|
|
612
612
|
useSuperAdminBypass
|
|
613
613
|
};
|
|
614
|
-
//# sourceMappingURL=chunk-
|
|
614
|
+
//# sourceMappingURL=chunk-OETXORNB.js.map
|
|
@@ -701,12 +701,14 @@ async function getOrganisationFromEvent(supabase, eventId) {
|
|
|
701
701
|
if (orgDerivationCache.has(eventId)) {
|
|
702
702
|
return orgDerivationCache.get(eventId) ?? null;
|
|
703
703
|
}
|
|
704
|
-
const { data, error } = await supabase.from("
|
|
704
|
+
const { data, error } = await supabase.from("core_events").select("organisation_id").eq("event_id", eventId).single();
|
|
705
705
|
let organisationId = null;
|
|
706
706
|
if (error || !data) {
|
|
707
707
|
organisationId = null;
|
|
708
|
-
} else {
|
|
708
|
+
} else if (data.organisation_id) {
|
|
709
709
|
organisationId = data.organisation_id;
|
|
710
|
+
} else {
|
|
711
|
+
organisationId = null;
|
|
710
712
|
}
|
|
711
713
|
if (orgDerivationCache.size >= MAX_CACHE_SIZE) {
|
|
712
714
|
const firstKey = orgDerivationCache.keys().next().value;
|
|
@@ -2200,4 +2202,4 @@ export {
|
|
|
2200
2202
|
invalidateAppCache,
|
|
2201
2203
|
clearCache
|
|
2202
2204
|
};
|
|
2203
|
-
//# sourceMappingURL=chunk-
|
|
2205
|
+
//# sourceMappingURL=chunk-ROXMHMY2.js.map
|