@pol-studios/db 1.0.30 → 1.0.31

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useDbUpsert
3
- } from "./chunk-EKUDGIQZ.js";
3
+ } from "./chunk-VGEMLNNM.js";
4
4
  import {
5
5
  isUsable,
6
6
  newUuid,
@@ -70,31 +70,12 @@ function useDbQuery(query, config) {
70
70
  }
71
71
 
72
72
  // src/auth/context/setupAuthContext.tsx
73
- import { c as _c } from "react/compiler-runtime";
74
73
  import { createContext } from "react";
75
- import { jsx } from "react/jsx-runtime";
76
74
  var setupAuthContext = createContext({});
77
- function SetupAuthContextProvider(t0) {
78
- const $ = _c(3);
79
- const {
80
- children,
81
- auth
82
- } = t0;
83
- let t1;
84
- if ($[0] !== auth || $[1] !== children) {
85
- t1 = /* @__PURE__ */ jsx(setupAuthContext.Provider, { value: auth, children });
86
- $[0] = auth;
87
- $[1] = children;
88
- $[2] = t1;
89
- } else {
90
- t1 = $[2];
91
- }
92
- return t1;
93
- }
94
75
 
95
76
  // src/auth/context/PermissionContext.tsx
96
77
  import { createContext as createContext2, useCallback, useContext, useEffect, useMemo as useMemo2, useRef as useRef2, useState } from "react";
97
- import { jsx as jsx2 } from "react/jsx-runtime";
78
+ import { jsx } from "react/jsx-runtime";
98
79
  function getCacheKey(userId, entityType, entityId) {
99
80
  return `${userId || "anon"}:${entityType}:${entityId}`;
100
81
  }
@@ -480,7 +461,7 @@ function PermissionProvider({
480
461
  invalidatePermission,
481
462
  isLoading
482
463
  }), [getPermission, checkPermission, prefetchPermissions, invalidatePermission, isLoading]);
483
- return /* @__PURE__ */ jsx2(permissionContext.Provider, { value, children });
464
+ return /* @__PURE__ */ jsx(permissionContext.Provider, { value, children });
484
465
  }
485
466
  function usePermissions() {
486
467
  const context = useContext(permissionContext);
@@ -492,7 +473,23 @@ function usePermissions() {
492
473
 
493
474
  // src/auth/context/AuthProvider.tsx
494
475
  import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
495
- import { jsx as jsx3 } from "react/jsx-runtime";
476
+ import { jsx as jsx2 } from "react/jsx-runtime";
477
+ function getPermissionLevel(action) {
478
+ switch (action.toLowerCase()) {
479
+ case "view":
480
+ case "read":
481
+ return 1;
482
+ case "edit":
483
+ case "write":
484
+ return 2;
485
+ case "admin":
486
+ case "delete":
487
+ case "share":
488
+ return 3;
489
+ default:
490
+ return 0;
491
+ }
492
+ }
496
493
  var profileQuery = typedSupabase?.schema("core").from("Profile").select("*, UserAccess(accessKey), status").single();
497
494
  function AuthProvider({
498
495
  children,
@@ -590,9 +587,18 @@ function AuthProvider({
590
587
  enabled: isUsable(currentUser),
591
588
  crossOrganization: true
592
589
  });
590
+ const effectivePermissionsRequest = useDbQuery(supabase.schema("core").rpc("get_user_effective_permissions", {
591
+ user_id: currentUser?.id
592
+ }), {
593
+ enabled: isUsable(currentUser),
594
+ crossOrganization: true
595
+ });
593
596
  const refetchAccessKeys = useCallback2(() => {
594
597
  accessKeysRequest.refetch();
595
598
  }, [accessKeysRequest.refetch]);
599
+ const refetchEffectivePermissions = useCallback2(() => {
600
+ effectivePermissionsRequest.refetch();
601
+ }, [effectivePermissionsRequest.refetch]);
596
602
  const userGroupIdsRef = useRef3(/* @__PURE__ */ new Set());
597
603
  useEffect2(() => {
598
604
  if (accessKeysRequest.data) {
@@ -614,6 +620,7 @@ function AuthProvider({
614
620
  filter: `userId=eq.${currentUser.id}`
615
621
  }, () => {
616
622
  refetchAccessKeys();
623
+ refetchEffectivePermissions();
617
624
  }).on("postgres_changes", {
618
625
  event: "*",
619
626
  schema: "core",
@@ -621,6 +628,7 @@ function AuthProvider({
621
628
  filter: `userId=eq.${currentUser.id}`
622
629
  }, () => {
623
630
  refetchAccessKeys();
631
+ refetchEffectivePermissions();
624
632
  }).on("postgres_changes", {
625
633
  event: "*",
626
634
  schema: "core",
@@ -629,6 +637,7 @@ function AuthProvider({
629
637
  const groupId = payload.new?.groupId || payload.old?.groupId;
630
638
  if (groupId && userGroupIdsRef.current.has(groupId)) {
631
639
  refetchAccessKeys();
640
+ refetchEffectivePermissions();
632
641
  }
633
642
  }).on("postgres_changes", {
634
643
  event: "UPDATE",
@@ -640,13 +649,14 @@ function AuthProvider({
640
649
  const groupId_0 = payload_0.new?.id;
641
650
  if (oldActive !== newActive && groupId_0 && userGroupIdsRef.current.has(groupId_0)) {
642
651
  refetchAccessKeys();
652
+ refetchEffectivePermissions();
643
653
  }
644
654
  }).subscribe();
645
655
  return () => {
646
656
  channel.unsubscribe();
647
657
  supabase.removeChannel(channel);
648
658
  };
649
- }, [supabase, currentUser?.id, refetchAccessKeys]);
659
+ }, [supabase, currentUser?.id, refetchAccessKeys, refetchEffectivePermissions]);
650
660
  useEffect2(() => {
651
661
  if (!currentUser?.id) return;
652
662
  const profileChannel = supabase.channel(`profile-status-${currentUser.id}`).on("postgres_changes", {
@@ -679,6 +689,20 @@ function AuthProvider({
679
689
  }
680
690
  return profileRequest.data?.UserAccess?.map((x_3) => x_3.accessKey) || [];
681
691
  }, [accessKeysRequest.data, profileRequest.data?.UserAccess]);
692
+ const effectivePermissions = useMemo3(() => {
693
+ if (!effectivePermissionsRequest.data) {
694
+ return [];
695
+ }
696
+ return effectivePermissionsRequest.data.map((item_1) => ({
697
+ resourceType: item_1.resource_type,
698
+ resourceId: item_1.resource_id,
699
+ permission: item_1.permission,
700
+ permissionLevel: item_1.permission_level,
701
+ source: item_1.source,
702
+ inheritedFrom: item_1.inherited_from,
703
+ expiresAt: item_1.expires_at
704
+ }));
705
+ }, [effectivePermissionsRequest.data]);
682
706
  const profileStatus = profileRequest.data?.status;
683
707
  const isArchived = profileStatus === "archived";
684
708
  const isSuspended = profileStatus === "suspended";
@@ -691,32 +715,42 @@ function AuthProvider({
691
715
  if (accessGiven.includes("owner")) return true;
692
716
  if (accessGiven.includes(key)) return true;
693
717
  if (isUsable(key) === false) return true;
718
+ const parts = key.split(":");
719
+ if (parts.length === 3) {
720
+ const [type, id_1, action_0] = parts;
721
+ const requiredLevel = getPermissionLevel(action_0);
722
+ const hasPermission = effectivePermissions.some((p) => p.resourceType === type && p.resourceId === id_1 && p.permissionLevel >= requiredLevel);
723
+ if (hasPermission) {
724
+ return true;
725
+ }
726
+ }
694
727
  return false;
695
- }, [combinedAccess, isArchived, isSuspended]);
728
+ }, [combinedAccess, effectivePermissions, isArchived, isSuspended]);
696
729
  const authStateWithLoading = useMemo3(() => ({
697
730
  hasAccess,
698
731
  user: currentUser,
699
732
  profile: profileRequest.data,
700
733
  access: combinedAccess,
734
+ effectivePermissions,
701
735
  profileStatus,
702
736
  isArchived,
703
737
  isSuspended,
704
- isLoading: currentUser === null ? false : profileRequest.isLoading || accessKeysRequest.isLoading || currentUser === void 0,
738
+ isLoading: currentUser === null ? false : profileRequest.isLoading || accessKeysRequest.isLoading || effectivePermissionsRequest.isLoading || currentUser === void 0,
705
739
  signInAsync,
706
740
  signOutAsync,
707
741
  onSignOut,
708
742
  removeOnSignOut,
709
743
  registerAsync,
710
744
  refreshAsync
711
- }), [profileRequest.data, profileRequest.isLoading, accessKeysRequest.data, accessKeysRequest.isLoading, currentUser, combinedAccess, profileStatus, isArchived, isSuspended, hasAccess]);
712
- const content = enableEntityPermissions ? /* @__PURE__ */ jsx3(PermissionProvider, { children }) : children;
713
- return /* @__PURE__ */ jsx3(setupAuthContext.Provider, { value: authStateWithLoading, children: content });
745
+ }), [profileRequest.data, profileRequest.isLoading, accessKeysRequest.data, accessKeysRequest.isLoading, effectivePermissionsRequest.isLoading, currentUser, combinedAccess, effectivePermissions, profileStatus, isArchived, isSuspended, hasAccess]);
746
+ const content = enableEntityPermissions ? /* @__PURE__ */ jsx2(PermissionProvider, { children }) : children;
747
+ return /* @__PURE__ */ jsx2(setupAuthContext.Provider, { value: authStateWithLoading, children: content });
714
748
  }
715
749
 
716
750
  // src/auth/context/UserMetadataContext.tsx
717
- import { c as _c2 } from "react/compiler-runtime";
751
+ import { c as _c } from "react/compiler-runtime";
718
752
  import { createContext as createContext3, useContext as useContext2, useEffect as useEffect3, useMemo as useMemo4, useState as useState3, useCallback as useCallback3, useRef as useRef4 } from "react";
719
- import { jsx as jsx4 } from "react/jsx-runtime";
753
+ import { jsx as jsx3 } from "react/jsx-runtime";
720
754
  var UserMetadataQuery = {
721
755
  schema: "core",
722
756
  table: "UserMetadata",
@@ -809,7 +843,7 @@ function UserMetadataProvider({
809
843
  removeMetadata,
810
844
  refreshMetadata
811
845
  }), [metadata, isLoading, error, setMetadata, getMetadata, removeMetadata, refreshMetadata]);
812
- return /* @__PURE__ */ jsx4(userMetadataContext.Provider, { value: contextValue, children });
846
+ return /* @__PURE__ */ jsx3(userMetadataContext.Provider, { value: contextValue, children });
813
847
  }
814
848
  function useUserMetadata() {
815
849
  const context = useContext2(userMetadataContext);
@@ -819,7 +853,7 @@ function useUserMetadata() {
819
853
  return context;
820
854
  }
821
855
  function useUserMetadataValue(key) {
822
- const $ = _c2(3);
856
+ const $ = _c(3);
823
857
  const {
824
858
  getMetadata
825
859
  } = useUserMetadata();
@@ -835,7 +869,7 @@ function useUserMetadataValue(key) {
835
869
  return t0;
836
870
  }
837
871
  function useSetUserMetadata() {
838
- const $ = _c2(3);
872
+ const $ = _c(3);
839
873
  const {
840
874
  setMetadata,
841
875
  removeMetadata
@@ -855,7 +889,7 @@ function useSetUserMetadata() {
855
889
  return t0;
856
890
  }
857
891
  function useUserMetadataState(key, defaultValue, options) {
858
- const $ = _c2(11);
892
+ const $ = _c(11);
859
893
  const {
860
894
  metadata,
861
895
  setMetadata,
@@ -928,7 +962,6 @@ export {
928
962
  isTimeoutError,
929
963
  useDbQuery,
930
964
  setupAuthContext,
931
- SetupAuthContextProvider,
932
965
  permissionContext,
933
966
  entityPermissionContext,
934
967
  PermissionProvider,
@@ -941,4 +974,4 @@ export {
941
974
  useSetUserMetadata,
942
975
  useUserMetadataState
943
976
  };
944
- //# sourceMappingURL=chunk-A7C2FMUF.js.map
977
+ //# sourceMappingURL=chunk-WX4ABYIF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors/TimeoutError.ts","../src/useDbQuery.ts","../src/auth/context/setupAuthContext.tsx","../src/auth/context/PermissionContext.tsx","../src/auth/context/AuthProvider.tsx","../src/auth/context/UserMetadataContext.tsx"],"sourcesContent":["export const TIMEOUT_ERROR_MESSAGE = \"Request timed out\";\nexport const DEFAULT_QUERY_TIMEOUT = 15_000; // 15 seconds\n\nexport function isTimeoutError(error: Error | null | undefined): boolean {\n if (!error) return false;\n return error.name === \"AbortError\" || error.message === TIMEOUT_ERROR_MESSAGE || error.message.toLowerCase().includes(\"timed out\");\n}","import { PostgrestError, PostgrestSingleResponse } from \"@supabase/supabase-js\";\nimport { useMemo, useRef } from \"react\";\nimport { ItemType } from \"@pol-studios/utils\";\nimport { encode, UseQuerySingleReturn } from \"@supabase-cache-helpers/postgrest-react-query\";\nimport { omit } from \"@pol-studios/utils\";\nimport { useQuery, UseQueryOptions } from \"@tanstack/react-query\";\nimport { useDelayedValue } from \"@pol-studios/hooks/state\";\nimport { DEFAULT_QUERY_TIMEOUT, TIMEOUT_ERROR_MESSAGE } from \"./errors/TimeoutError\";\ntype ConfigurationOptions<T> = {\n crossOrganization?: boolean;\n filter?: (item: ItemType<T>) => boolean;\n timeout?: number;\n};\nexport type UseDbQuerySingleReturn<T> = UseQuerySingleReturn<T> & {\n data: T | null | undefined;\n count?: number | null;\n};\nexport function useDbQuery<Result>(query: PromiseLike<PostgrestSingleResponse<Result>> & {\n abortSignal?: (signal: AbortSignal) => PromiseLike<PostgrestSingleResponse<Result>>;\n}, config?: Omit<UseQueryOptions<Result, PostgrestError>, \"queryKey\" | \"queryFn\"> & ConfigurationOptions<Result>) {\n // Debounce query key to prevent rapid query churn during fast navigation\n const queryKey = encode(query, false);\n const queryKeyString = queryKey.join(\"-\");\n const debouncedKeyString = useDelayedValue(queryKeyString, 50);\n\n // Only enable query when key has stabilized (debounced matches current)\n const isKeyStable = queryKeyString === debouncedKeyString;\n const effectiveEnabled = config?.enabled !== false && isKeyStable;\n const timeoutMs = config?.timeout ?? DEFAULT_QUERY_TIMEOUT;\n\n // Track count from Supabase response (for queries with count: \"exact\")\n const countRef = useRef<number | null>(null);\n const request = useQuery(useMemo(() => ({\n queryKey,\n queryFn: async ({\n signal\n }) => {\n const controller = new AbortController();\n signal.addEventListener(\"abort\", () => controller.abort());\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n try {\n // Execute query with abort signal\n const queryWithSignal = query.abortSignal?.(controller.signal) ?? query;\n const result = await queryWithSignal;\n if (result.error) throw result.error;\n // Store count if available (from queries with count: \"exact\")\n countRef.current = result.count ?? null;\n return result.data;\n } catch (err: any) {\n if (err.name === \"AbortError\") {\n throw new Error(TIMEOUT_ERROR_MESSAGE);\n }\n throw err;\n } finally {\n clearTimeout(timeoutId);\n }\n },\n ...omit({\n retry: 1,\n ...config,\n enabled: effectiveEnabled\n }, [\"queryKey\", \"timeout\"])\n }), [queryKey, config, effectiveEnabled, timeoutMs, query]));\n\n // Return request with count property added\n return useMemo(() => ({\n ...request,\n count: countRef.current\n }), [request, countRef.current]) as UseDbQuerySingleReturn<Result>;\n}","/**\n * SetupAuthContext - Shared context for auth state\n *\n * This file is separate to avoid circular dependencies between\n * AuthProvider and PermissionContext.\n */\n\nimport { createContext, ReactNode } from \"react\";\nimport { AuthTokenResponsePassword, SignUpWithPasswordCredentials, User } from \"@supabase/supabase-js\";\nexport type ProfileStatus = \"active\" | \"archived\" | \"suspended\";\n\n/**\n * Represents an effective permission for a user on a specific resource.\n * These are eagerly loaded at login for fast permission checks.\n */\nexport interface EffectivePermission {\n /** The type of resource (e.g., 'project', 'client', 'database') */\n resourceType: string;\n /** The ID of the specific resource */\n resourceId: string;\n /** The permission action (e.g., 'view', 'edit', 'admin') */\n permission: string;\n /** Numeric level for comparison: view=1, edit=2, admin=3 */\n permissionLevel: number;\n /** How the permission was granted */\n source: \"direct\" | \"group\" | \"inherited\";\n /** If inherited, from which resource */\n inheritedFrom: string | null;\n /** When the permission expires, if applicable */\n expiresAt: string | null;\n}\nexport interface SetupAuthContext {\n user?: User | null | undefined;\n isLoading: boolean;\n profile: Profile | null | undefined;\n access: string[];\n /** Eagerly loaded effective permissions for the user */\n effectivePermissions: EffectivePermission[];\n /** Whether the profile is archived */\n isArchived: boolean;\n /** Whether the profile is suspended */\n isSuspended: boolean;\n /** The profile status (active, archived, suspended) */\n profileStatus: ProfileStatus | undefined;\n registerAsync: (register: SignUpWithPasswordCredentials) => Promise<any>;\n signInAsync: (username: string, password: string) => Promise<AuthTokenResponsePassword>;\n signOutAsync: () => Promise<any>;\n refreshAsync: () => Promise<void>;\n onSignOut: (action: () => any) => string;\n removeOnSignOut: (id: string) => any;\n hasAccess?: (key: string) => boolean;\n}\n\n// Profile type - simplified version, the full type is in AuthProvider\nexport interface Profile {\n id: string;\n email?: string;\n fullName?: string;\n status?: ProfileStatus;\n UserAccess?: Array<{\n accessKey: string;\n }>;\n [key: string]: any;\n}\nexport const setupAuthContext = createContext({} as SetupAuthContext);\n\n/**\n * Props for SetupAuthContextProvider\n * A simpler provider that takes pre-computed auth state\n */\nexport interface SetupAuthContextProviderProps {\n children: ReactNode;\n auth: SetupAuthContext;\n}","import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport useSupabase from \"../../useSupabase\";\nimport { setupAuthContext } from \"./setupAuthContext\";\nimport { EntityType, EntityPermissionLevel, EntityAction, EntityPermissionCheck } from \"../types/EntityPermissions\";\n\n// Cache entry with TTL\ninterface CacheEntry {\n permission: EntityPermissionCheck;\n expiresAt: number;\n}\n\n// Entity identifier for batch lookups\ninterface EntityIdentifier {\n entityType: EntityType;\n entityId: number;\n}\n\n// Cache key helper\nfunction getCacheKey(userId: string | undefined, entityType: EntityType, entityId: number): string {\n return `${userId || \"anon\"}:${entityType}:${entityId}`;\n}\n\n// Default loading state\nconst loadingPermission: EntityPermissionCheck = {\n canView: false,\n canAdminView: false,\n canEdit: false,\n canCreate: false,\n canDelete: false,\n canShare: false,\n permissionLevel: null,\n isLoading: true\n};\n\n// No permission state\nconst noPermission: EntityPermissionCheck = {\n canView: false,\n canAdminView: false,\n canEdit: false,\n canCreate: false,\n canDelete: false,\n canShare: false,\n permissionLevel: null,\n isLoading: false,\n isDenied: false\n};\n\n// Denied permission state - explicitly blocked access\nconst deniedPermission: EntityPermissionCheck = {\n canView: false,\n canAdminView: false,\n canEdit: false,\n canCreate: false,\n canDelete: false,\n canShare: false,\n permissionLevel: null,\n isLoading: false,\n isDenied: true\n};\n\n// Map permission level to permission check\n// If permission is 'denied', it means access was explicitly blocked\n// Handles both legacy formats (ReadOnly, AdminReadOnly, ReadWrite, Admin)\n// and new formats (view, edit, admin, denied)\nfunction mapPermissionLevel(level: EntityPermissionLevel | \"denied\" | string | null): EntityPermissionCheck {\n if (!level) {\n return noPermission;\n }\n\n // Normalize to lowercase for comparison to handle both legacy and new formats\n const normalizedLevel = level.toLowerCase();\n switch (normalizedLevel) {\n // Legacy format: ReadOnly, New format: view\n case \"readonly\":\n case \"view\":\n return {\n canView: true,\n canAdminView: false,\n canEdit: false,\n canCreate: false,\n canDelete: false,\n canShare: false,\n permissionLevel: \"ReadOnly\",\n isLoading: false,\n isDenied: false\n };\n // Legacy format: AdminReadOnly (no new equivalent, keep for backwards compatibility)\n case \"adminreadonly\":\n return {\n canView: true,\n canAdminView: true,\n canEdit: false,\n canCreate: false,\n canDelete: false,\n canShare: false,\n permissionLevel: \"AdminReadOnly\",\n isLoading: false,\n isDenied: false\n };\n // Legacy format: ReadWrite, New format: edit\n case \"readwrite\":\n case \"edit\":\n return {\n canView: true,\n canAdminView: false,\n canEdit: true,\n canCreate: true,\n canDelete: false,\n canShare: false,\n permissionLevel: \"ReadWrite\",\n isLoading: false,\n isDenied: false\n };\n // Legacy format: Admin, New format: admin\n case \"admin\":\n return {\n canView: true,\n canAdminView: true,\n canEdit: true,\n canCreate: true,\n canDelete: true,\n canShare: true,\n permissionLevel: \"Admin\",\n isLoading: false,\n isDenied: false\n };\n // New format: denied - explicit access denial\n case \"denied\":\n return deniedPermission;\n default:\n console.warn(`Unknown permission level: ${level}`);\n return noPermission;\n }\n}\n\n// Context interface\nexport interface PermissionContextValue {\n getPermission: (entityType: EntityType, entityId: number) => EntityPermissionCheck;\n checkPermission: (entityType: EntityType, entityId: number, action: EntityAction) => boolean;\n prefetchPermissions: (entities: EntityIdentifier[]) => Promise<void>;\n invalidatePermission: (entityType: EntityType, entityId: number) => void;\n isLoading: boolean;\n}\n\n/**\n * @deprecated Use permissionContext instead\n */\nexport interface EntityPermissionContextValue extends PermissionContextValue {}\nexport const permissionContext = createContext<PermissionContextValue>({} as PermissionContextValue);\n\n/**\n * @deprecated Use permissionContext instead\n */\nexport const entityPermissionContext = permissionContext;\n\n// TTL for cache entries (5 minutes)\nconst CACHE_TTL_MS = 5 * 60 * 1000;\n\n// TTL for error cache entries (30 seconds) - shorter to allow quick retry\nconst ERROR_CACHE_TTL_MS = 30 * 1000;\n\n// Batch collection delay (50ms)\nconst BATCH_DELAY_MS = 50;\nexport function PermissionProvider({\n children\n}: {\n children: ReactNode;\n}) {\n const supabase = useSupabase();\n const setupAuth = useContext(setupAuthContext);\n const user = setupAuth?.user;\n\n // Permission cache\n const cacheRef = useRef<Map<string, CacheEntry>>(new Map());\n\n // Pending lookups for batching\n const pendingLookupsRef = useRef<Set<string>>(new Set());\n\n // In-flight lookups to prevent duplicate requests during async RPC calls\n const inFlightRef = useRef<Set<string>>(new Set());\n\n // Batch timer ref\n const batchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Loading state\n const [isLoading, setIsLoading] = useState(false);\n\n // Force re-render trigger\n const [, forceUpdate] = useState(0);\n\n // Clean up expired cache entries\n const cleanupExpiredEntries = useCallback(() => {\n const now = Date.now();\n const cache = cacheRef.current;\n let hasExpired = false;\n for (const [key, entry] of cache.entries()) {\n if (entry.expiresAt < now) {\n cache.delete(key);\n hasExpired = true;\n }\n }\n if (hasExpired) {\n forceUpdate(prev => prev + 1);\n }\n }, []);\n\n // Periodic cleanup of expired entries\n useEffect(() => {\n const cleanupInterval = setInterval(cleanupExpiredEntries, 60 * 1000);\n return () => clearInterval(cleanupInterval);\n }, [cleanupExpiredEntries]);\n\n // Execute batch lookup\n const executeBatchLookup = useCallback(async () => {\n const pending = Array.from(pendingLookupsRef.current);\n pendingLookupsRef.current.clear();\n if (pending.length === 0 || !user?.id) {\n return;\n }\n\n // Move pending keys to in-flight to prevent duplicate requests\n pending.forEach(k => inFlightRef.current.add(k));\n setIsLoading(true);\n try {\n // Parse pending keys back to entities (format: userId:entityType:entityId)\n const entities = pending.map(key_0 => {\n const parts = key_0.split(\":\");\n // Skip the userId part (index 0), use entityType (index 1) and entityId (index 2)\n const entityType = parts[1];\n const entityIdStr = parts[2];\n return {\n entity_type: entityType as EntityType,\n entity_id: parseInt(entityIdStr, 10)\n };\n });\n\n // Call RPC for batch lookup\n // Using 'as any' because the RPC function type isn't in the generated database types\n const {\n data,\n error\n } = await (supabase.rpc as any)(\"get_user_entity_permissions\", {\n p_user_id: user.id,\n p_entities: entities\n });\n if (error) {\n console.error(\"Failed to fetch entity permissions:\", error);\n // Mark all pending as no permission with shorter TTL to allow quick retry\n const cache_0 = cacheRef.current;\n const now_0 = Date.now();\n for (const key_1 of pending) {\n cache_0.set(key_1, {\n permission: noPermission,\n expiresAt: now_0 + ERROR_CACHE_TTL_MS\n });\n }\n } else if (data) {\n // Update cache with results\n const cache_1 = cacheRef.current;\n const now_1 = Date.now();\n\n // Create a map of results for quick lookup\n // Permission can be a standard level or 'denied' for explicit deny\n const resultsMap = new Map<string, EntityPermissionLevel | \"denied\">();\n // Type the data as an array of permission results\n const results = data as Array<{\n entity_type: EntityType;\n entity_id: number;\n permission: EntityPermissionLevel | \"denied\";\n }>;\n for (const result of results) {\n const key_2 = getCacheKey(user?.id, result.entity_type, result.entity_id);\n resultsMap.set(key_2, result.permission);\n }\n\n // Update cache for all pending keys\n for (const key_3 of pending) {\n const permissionLevel = resultsMap.get(key_3) || null;\n cache_1.set(key_3, {\n permission: mapPermissionLevel(permissionLevel),\n expiresAt: now_1 + CACHE_TTL_MS\n });\n }\n }\n forceUpdate(prev_0 => prev_0 + 1);\n } catch (err) {\n console.error(\"Unexpected error fetching entity permissions:\", err);\n } finally {\n // Remove from in-flight after completion (success or error)\n pending.forEach(k => inFlightRef.current.delete(k));\n setIsLoading(false);\n }\n }, [supabase, user?.id]);\n\n // Schedule batch lookup\n const scheduleBatchLookup = useCallback(() => {\n if (batchTimerRef.current) {\n clearTimeout(batchTimerRef.current);\n }\n batchTimerRef.current = setTimeout(() => {\n batchTimerRef.current = null;\n executeBatchLookup();\n }, BATCH_DELAY_MS);\n }, [executeBatchLookup]);\n\n // Get permission for an entity\n const getPermission = useCallback((entityType_0: EntityType, entityId: number): EntityPermissionCheck => {\n const key_4 = getCacheKey(user?.id, entityType_0, entityId);\n const cache_2 = cacheRef.current;\n const cached = cache_2.get(key_4);\n const now_2 = Date.now();\n\n // Return cached if valid\n if (cached && cached.expiresAt > now_2) {\n return cached.permission;\n }\n\n // Don't add if already pending or in-flight to prevent duplicate requests\n if (!pendingLookupsRef.current.has(key_4) && !inFlightRef.current.has(key_4)) {\n pendingLookupsRef.current.add(key_4);\n scheduleBatchLookup();\n }\n return loadingPermission;\n }, [scheduleBatchLookup, user?.id]);\n\n // Check specific permission action\n const checkPermission = useCallback((entityType_1: EntityType, entityId_0: number, action: EntityAction): boolean => {\n const permission = getPermission(entityType_1, entityId_0);\n if (permission.isLoading) {\n return false;\n }\n switch (action) {\n case \"view\":\n return permission.canView;\n case \"adminView\":\n return permission.canAdminView;\n case \"edit\":\n return permission.canEdit;\n case \"create\":\n return permission.canCreate;\n case \"delete\":\n return permission.canDelete;\n case \"share\":\n return permission.canShare;\n default:\n return false;\n }\n }, [getPermission]);\n\n // Prefetch permissions for multiple entities\n const prefetchPermissions = useCallback(async (entities_0: EntityIdentifier[]): Promise<void> => {\n if (!user?.id || entities_0.length === 0) {\n return;\n }\n const cache_3 = cacheRef.current;\n const now_3 = Date.now();\n\n // Filter out already cached entries, items already pending, and items in-flight\n const toFetch = entities_0.filter(entity => {\n const key_5 = getCacheKey(user?.id, entity.entityType, entity.entityId);\n const cached_0 = cache_3.get(key_5);\n const isPending = pendingLookupsRef.current.has(key_5);\n const isInFlight = inFlightRef.current.has(key_5);\n return !isPending && !isInFlight && (!cached_0 || cached_0.expiresAt <= now_3);\n });\n if (toFetch.length === 0) {\n return;\n }\n setIsLoading(true);\n try {\n const entitiesParam = toFetch.map(e => ({\n entity_type: e.entityType,\n entity_id: e.entityId\n }));\n\n // Using 'as any' because the RPC function type isn't in the generated database types\n const {\n data: data_0,\n error: error_0\n } = await (supabase.rpc as any)(\"get_user_entity_permissions\", {\n p_user_id: user.id,\n p_entities: entitiesParam\n });\n if (error_0) {\n console.error(\"Failed to prefetch entity permissions:\", error_0);\n return;\n }\n if (data_0) {\n // Capture timestamp AFTER the RPC call completes\n const cacheTimestamp = Date.now();\n\n // Permission can be a standard level or 'denied' for explicit deny\n const resultsMap_0 = new Map<string, EntityPermissionLevel | \"denied\">();\n // Type the data as an array of permission results\n const results_0 = data_0 as Array<{\n entity_type: EntityType;\n entity_id: number;\n permission: EntityPermissionLevel | \"denied\";\n }>;\n for (const result_0 of results_0) {\n const key_6 = getCacheKey(user?.id, result_0.entity_type, result_0.entity_id);\n resultsMap_0.set(key_6, result_0.permission);\n }\n for (const entity_0 of toFetch) {\n const key_7 = getCacheKey(user?.id, entity_0.entityType, entity_0.entityId);\n const permissionLevel_0 = resultsMap_0.get(key_7) || null;\n cache_3.set(key_7, {\n permission: mapPermissionLevel(permissionLevel_0),\n expiresAt: cacheTimestamp + CACHE_TTL_MS\n });\n }\n forceUpdate(prev_1 => prev_1 + 1);\n }\n } catch (err_0) {\n console.error(\"Unexpected error prefetching entity permissions:\", err_0);\n } finally {\n setIsLoading(false);\n }\n }, [supabase, user?.id]);\n\n // Invalidate a specific permission\n const invalidatePermission = useCallback((entityType_2: EntityType, entityId_1: number): void => {\n const key_8 = getCacheKey(user?.id, entityType_2, entityId_1);\n cacheRef.current.delete(key_8);\n forceUpdate(prev_2 => prev_2 + 1);\n }, [user?.id]);\n\n // Parse scoped access key format: <entity_type>:<entity_id>:<permission_level>\n // Returns null if the key format is invalid\n const parseScopedAccessKey = useCallback((key_9: string): {\n entityType: EntityType;\n entityId: number;\n } | null => {\n if (!key_9 || typeof key_9 !== \"string\") {\n return null;\n }\n const parts_0 = key_9.split(\":\");\n if (parts_0.length < 2) {\n return null;\n }\n const entityType_3 = parts_0[0];\n const entityId_2 = parseInt(parts_0[1], 10);\n if (isNaN(entityId_2)) {\n return null;\n }\n // Use explicit mapping instead of string manipulation\n // This correctly handles \"database\" -> \"ProjectDatabase\" case\n const entityTypeMap: Record<string, EntityType> = {\n client: \"Client\",\n project: \"Project\",\n database: \"ProjectDatabase\",\n projectdatabase: \"ProjectDatabase\"\n };\n const normalizedEntityType = entityTypeMap[entityType_3.toLowerCase()];\n if (!normalizedEntityType) {\n return null;\n }\n return {\n entityType: normalizedEntityType,\n entityId: entityId_2\n };\n }, []);\n\n // Real-time subscription for permission changes\n useEffect(() => {\n if (!user?.id) {\n return;\n }\n\n // Subscribe to changes on UserAccess table for the current user\n // Use unique channel name per user to avoid collisions\n const channel = supabase.channel(`entity-permissions-${user.id}`).on(\"postgres_changes\", {\n event: \"*\",\n schema: \"core\",\n table: \"UserAccess\",\n filter: `userId=eq.${user.id}`\n }, payload => {\n // Parse the scoped access key from the new record\n if (payload.new && typeof payload.new === \"object\" && \"scopedAccessKey\" in payload.new && typeof payload.new.scopedAccessKey === \"string\") {\n const parsed = parseScopedAccessKey(payload.new.scopedAccessKey);\n if (parsed) {\n invalidatePermission(parsed.entityType, parsed.entityId);\n }\n }\n // Parse the scoped access key from the old record (for deletes/updates)\n if (payload.old && typeof payload.old === \"object\" && \"scopedAccessKey\" in payload.old && typeof payload.old.scopedAccessKey === \"string\") {\n const parsed_0 = parseScopedAccessKey(payload.old.scopedAccessKey);\n if (parsed_0) {\n invalidatePermission(parsed_0.entityType, parsed_0.entityId);\n }\n }\n }).subscribe();\n return () => {\n channel.unsubscribe();\n supabase.removeChannel(channel);\n };\n }, [supabase, user?.id, invalidatePermission, parseScopedAccessKey]);\n\n // Clear cache on user change\n useEffect(() => {\n cacheRef.current.clear();\n pendingLookupsRef.current.clear();\n inFlightRef.current.clear();\n if (batchTimerRef.current) {\n clearTimeout(batchTimerRef.current);\n batchTimerRef.current = null;\n }\n forceUpdate(prev_3 => prev_3 + 1);\n }, [user?.id]);\n\n // Cleanup batch timer on unmount\n useEffect(() => {\n return () => {\n if (batchTimerRef.current) {\n clearTimeout(batchTimerRef.current);\n }\n };\n }, []);\n const value = useMemo(() => ({\n getPermission,\n checkPermission,\n prefetchPermissions,\n invalidatePermission,\n isLoading\n }), [getPermission, checkPermission, prefetchPermissions, invalidatePermission, isLoading]);\n return <permissionContext.Provider value={value}>\n {children}\n </permissionContext.Provider>;\n}\nexport function usePermissions() {\n const context = useContext(permissionContext);\n if (!context || Object.keys(context).length === 0) {\n throw new Error(\"usePermissions must be used within a PermissionProvider\");\n }\n return context;\n}","import { useDbQuery as useQuery } from \"../../useDbQuery\";\nimport useSupabase, { typedSupabase } from \"../../useSupabase\";\nimport { isUsable, newUuid } from \"@pol-studios/utils\";\nimport { QueryData, SignUpWithPasswordCredentials, User } from \"@supabase/supabase-js\";\nimport { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { PermissionProvider } from \"./PermissionContext\";\n// Import from shared context to use internally\nimport { setupAuthContext, type ProfileStatus, type EffectivePermission } from \"./setupAuthContext\";\n// Re-export from shared context to maintain API compatibility\nexport { setupAuthContext, type SetupAuthContext, type SetupAuthContextProviderProps, type ProfileStatus, type EffectivePermission } from \"./setupAuthContext\";\n\n/**\n * Maps permission action strings to numeric levels for comparison.\n * Higher numbers grant more access.\n */\nfunction getPermissionLevel(action: string): number {\n switch (action.toLowerCase()) {\n case \"view\":\n case \"read\":\n return 1;\n case \"edit\":\n case \"write\":\n return 2;\n case \"admin\":\n case \"delete\":\n case \"share\":\n return 3;\n default:\n return 0;\n }\n}\nconst profileQuery = typedSupabase?.schema(\"core\").from(\"Profile\").select(\"*, UserAccess(accessKey), status\").single();\nexport type Profile = QueryData<typeof profileQuery>;\nexport interface AuthProviderProps {\n children: ReactNode;\n /**\n * Enable entity-level permissions system (Project, Client, ProjectDatabase access control).\n * When enabled, wraps children with PermissionProvider.\n * @default false\n */\n enableEntityPermissions?: boolean;\n}\nexport function AuthProvider({\n children,\n enableEntityPermissions = false\n}: AuthProviderProps) {\n const supabase = useSupabase();\n const [currentUser, setCurrentUser] = useState<User | null | undefined>(undefined);\n const [userNeedsChange, setUserNeedsChange] = useState(true);\n // Prevents premature isLoading=false during async gap between auth event and session fetch\n const [onSignOutCallbacks, setOnSignOutCallbacks] = useState(new Map<string, () => any>());\n async function registerAsync(register: SignUpWithPasswordCredentials) {\n const response = await supabase.auth.signUp(register);\n setCurrentUser(prev => {\n const newUser = response.data.user;\n if (prev?.id === newUser?.id) {\n return prev;\n }\n return newUser;\n });\n return response;\n }\n async function signInAsync(username: string, password: string) {\n const response_0 = await supabase.auth.signInWithPassword({\n email: username,\n password\n });\n if (response_0.data) {\n setCurrentUser(prev_0 => {\n const newUser_0 = response_0.data.user;\n if (prev_0?.id === newUser_0?.id) {\n return prev_0;\n }\n return newUser_0;\n });\n }\n return response_0;\n }\n async function signOutAsync() {\n const response_1 = await supabase.auth.signOut();\n if (isUsable(response_1.error) === false) {\n Array.from(onSignOutCallbacks.values()).forEach(x => {\n x();\n });\n }\n return response_1;\n }\n function onSignOut(action: () => any) {\n const id = newUuid();\n setOnSignOutCallbacks(x_0 => new Map(x_0).set(id, action));\n return id;\n }\n function removeOnSignOut(id_0: string) {\n setOnSignOutCallbacks(x_1 => {\n const map = new Map(x_1);\n map.delete(id_0);\n return map;\n });\n }\n async function refreshAsync() {}\n useEffect(() => {\n const request = supabase.auth.onAuthStateChange(event => {\n if (event === \"SIGNED_IN\" || event === \"SIGNED_OUT\") {\n setUserNeedsChange(true);\n }\n });\n return () => {\n request.data.subscription.unsubscribe();\n };\n }, [supabase.auth]);\n useEffect(() => {\n if (userNeedsChange === false) return;\n supabase.auth.getSession().then(x_2 => {\n setCurrentUser(prev_1 => {\n const newUser_1 = x_2?.data?.session?.user ?? null;\n if (newUser_1 === null) return null;\n // Only update reference if user actually changed\n if (prev_1?.id === newUser_1?.id) {\n return prev_1;\n }\n return newUser_1;\n });\n setUserNeedsChange(false);\n }).catch(error => {\n console.error(\"Failed to get session:\", error);\n setCurrentUser(prev_2 => prev_2 === null ? prev_2 : null);\n setUserNeedsChange(false);\n });\n }, [userNeedsChange]);\n const profileRequest = useQuery(supabase.schema(\"core\").from(\"Profile\").select(\"*, UserAccess(accessKey), status\").eq(\"id\", currentUser?.id!).limit(1).maybeSingle(), {\n enabled: isUsable(currentUser),\n crossOrganization: true\n });\n\n // Fetch all access keys (direct + group-based) using the database function\n const accessKeysRequest = useQuery(supabase.schema(\"core\").rpc(\"get_user_access_keys\", {\n user_id: currentUser?.id!\n }), {\n enabled: isUsable(currentUser),\n crossOrganization: true\n });\n\n // Fetch effective permissions (parsed resource-level permissions) eagerly at login\n const effectivePermissionsRequest = useQuery(supabase.schema(\"core\").rpc(\"get_user_effective_permissions\", {\n user_id: currentUser?.id!\n }), {\n enabled: isUsable(currentUser),\n crossOrganization: true\n });\n\n // Stable refetch callback for real-time subscriptions\n const refetchAccessKeys = useCallback(() => {\n accessKeysRequest.refetch();\n }, [accessKeysRequest.refetch]);\n\n // Stable refetch callback for effective permissions\n const refetchEffectivePermissions = useCallback(() => {\n effectivePermissionsRequest.refetch();\n }, [effectivePermissionsRequest.refetch]);\n\n // Track user's group IDs for filtering GroupAccessKey changes\n const userGroupIdsRef = useRef<Set<number>>(new Set());\n\n // Update group IDs when access keys change\n useEffect(() => {\n if (accessKeysRequest.data) {\n const groupIds = new Set<number>();\n for (const item of accessKeysRequest.data) {\n if (item.source === \"group\" && item.source_id) {\n groupIds.add(item.source_id);\n }\n }\n userGroupIdsRef.current = groupIds;\n }\n }, [accessKeysRequest.data]);\n\n // Real-time subscription for access key changes (direct + group-based)\n useEffect(() => {\n if (!currentUser?.id) return;\n const channel = supabase.channel(`user-access-keys-${currentUser.id}`)\n // Direct access changes\n .on(\"postgres_changes\", {\n event: \"*\",\n schema: \"core\",\n table: \"UserAccess\",\n filter: `userId=eq.${currentUser.id}`\n }, () => {\n refetchAccessKeys();\n refetchEffectivePermissions();\n })\n // Group membership changes for this user\n .on(\"postgres_changes\", {\n event: \"*\",\n schema: \"core\",\n table: \"UserGroup\",\n filter: `userId=eq.${currentUser.id}`\n }, () => {\n refetchAccessKeys();\n refetchEffectivePermissions();\n })\n // Group access key changes (check if user is in affected group)\n .on(\"postgres_changes\", {\n event: \"*\",\n schema: \"core\",\n table: \"GroupAccessKey\"\n }, payload => {\n const groupId = (payload.new as {\n groupId?: number;\n })?.groupId || (payload.old as {\n groupId?: number;\n })?.groupId;\n if (groupId && userGroupIdsRef.current.has(groupId)) {\n refetchAccessKeys();\n refetchEffectivePermissions();\n }\n })\n // Group activation/deactivation\n .on(\"postgres_changes\", {\n event: \"UPDATE\",\n schema: \"core\",\n table: \"Group\"\n }, payload_0 => {\n const oldActive = (payload_0.old as {\n isActive?: boolean;\n })?.isActive;\n const newActive = (payload_0.new as {\n isActive?: boolean;\n })?.isActive;\n const groupId_0 = (payload_0.new as {\n id?: number;\n })?.id;\n // Refetch if activation status changed and user is in this group\n if (oldActive !== newActive && groupId_0 && userGroupIdsRef.current.has(groupId_0)) {\n refetchAccessKeys();\n refetchEffectivePermissions();\n }\n }).subscribe();\n return () => {\n channel.unsubscribe();\n supabase.removeChannel(channel);\n };\n }, [supabase, currentUser?.id, refetchAccessKeys, refetchEffectivePermissions]);\n\n // Real-time subscription for profile status changes (force signout if archived/suspended)\n useEffect(() => {\n if (!currentUser?.id) return;\n const profileChannel = supabase.channel(`profile-status-${currentUser.id}`).on(\"postgres_changes\", {\n event: \"UPDATE\",\n schema: \"core\",\n table: \"Profile\",\n filter: `id=eq.${currentUser.id}`\n }, payload_1 => {\n const newStatus = (payload_1.new as {\n status?: string;\n })?.status;\n const oldStatus = (payload_1.old as {\n status?: string;\n })?.status;\n\n // If status changed to archived/suspended, force sign out\n if (oldStatus === \"active\" && (newStatus === \"archived\" || newStatus === \"suspended\")) {\n signOutAsync();\n }\n\n // Refetch profile data to update UI\n profileRequest.refetch();\n }).subscribe();\n return () => {\n profileChannel.unsubscribe();\n supabase.removeChannel(profileChannel);\n };\n }, [supabase, currentUser?.id, profileRequest.refetch]);\n\n // Combine access keys from both sources, preferring the RPC result\n const combinedAccess: string[] = useMemo(() => {\n // If we have the RPC result, use it (includes both direct and group-based)\n if (accessKeysRequest.data) {\n const uniqueKeys = new Set<string>();\n for (const item_0 of accessKeysRequest.data) {\n if (item_0.access_key) {\n uniqueKeys.add(item_0.access_key);\n }\n }\n return Array.from(uniqueKeys);\n }\n // Fallback to direct access only (for backwards compatibility during loading)\n return profileRequest.data?.UserAccess?.map(x_3 => x_3.accessKey) || [];\n }, [accessKeysRequest.data, profileRequest.data?.UserAccess]);\n\n // Parse effective permissions from the RPC response\n const effectivePermissions: EffectivePermission[] = useMemo(() => {\n if (!effectivePermissionsRequest.data) {\n return [];\n }\n // The RPC returns an array of permission objects\n return (effectivePermissionsRequest.data as any[]).map(item_1 => ({\n resourceType: item_1.resource_type,\n resourceId: item_1.resource_id,\n permission: item_1.permission,\n permissionLevel: item_1.permission_level,\n source: item_1.source as \"direct\" | \"group\" | \"inherited\",\n inheritedFrom: item_1.inherited_from,\n expiresAt: item_1.expires_at\n }));\n }, [effectivePermissionsRequest.data]);\n\n // Compute profile status values\n const profileStatus = profileRequest.data?.status as ProfileStatus | undefined;\n const isArchived = profileStatus === \"archived\";\n const isSuspended = profileStatus === \"suspended\";\n const hasAccess = useCallback((key: string) => {\n // Archived/suspended users have no access\n if (isArchived || isSuspended) {\n return false;\n }\n const accessGiven = combinedAccess;\n if (isUsable(accessGiven) === false) return false;\n if (accessGiven.includes(\"owner\")) return true;\n if (accessGiven.includes(key)) return true;\n if (isUsable(key) === false) return true;\n\n // Check if this is an entity-level permission key (format: type:id:action)\n const parts = key.split(\":\");\n if (parts.length === 3) {\n const [type, id_1, action_0] = parts;\n const requiredLevel = getPermissionLevel(action_0);\n\n // Check against effective permissions\n const hasPermission = effectivePermissions.some(p => p.resourceType === type && p.resourceId === id_1 && p.permissionLevel >= requiredLevel);\n if (hasPermission) {\n return true;\n }\n }\n return false;\n }, [combinedAccess, effectivePermissions, isArchived, isSuspended]);\n const authStateWithLoading = useMemo(() => ({\n hasAccess,\n user: currentUser,\n profile: profileRequest.data,\n access: combinedAccess,\n effectivePermissions,\n profileStatus,\n isArchived,\n isSuspended,\n isLoading: currentUser === null ? false : profileRequest.isLoading || accessKeysRequest.isLoading || effectivePermissionsRequest.isLoading || currentUser === undefined,\n signInAsync,\n signOutAsync,\n onSignOut,\n removeOnSignOut,\n registerAsync,\n refreshAsync\n }), [profileRequest.data, profileRequest.isLoading, accessKeysRequest.data, accessKeysRequest.isLoading, effectivePermissionsRequest.isLoading, currentUser, combinedAccess, effectivePermissions, profileStatus, isArchived, isSuspended, hasAccess]);\n const content = enableEntityPermissions ? <PermissionProvider>{children}</PermissionProvider> : children;\n return <setupAuthContext.Provider value={authStateWithLoading}>\n {content}\n </setupAuthContext.Provider>;\n}","import { c as _c } from \"react/compiler-runtime\";\nimport { ReactNode, createContext, useContext, useEffect, useMemo, useState, useCallback, useRef } from \"react\";\nimport { useDbQuery as useQuery } from \"../../useDbQuery\";\nimport { useDbUpsert as useUpsert } from \"../../hooks\";\nimport useSupabase, { Database } from \"../../useSupabase\";\nimport { isUsable } from \"@pol-studios/utils\";\nimport { setupAuthContext } from \"./AuthProvider\";\n\n// UserMetadata query constant\nconst UserMetadataQuery = {\n schema: \"core\",\n table: \"UserMetadata\",\n defaultQuery: \"key, userId, value\"\n} as const;\n\n// Type definitions for UserMetadata\nexport type UserMetadataRow = Database[\"core\"][\"Tables\"][\"UserMetadata\"][\"Row\"];\nexport type UserMetadataInsert = Database[\"core\"][\"Tables\"][\"UserMetadata\"][\"Insert\"];\nexport type UserMetadataUpdate = Database[\"core\"][\"Tables\"][\"UserMetadata\"][\"Update\"];\n\n// Context interface\nexport interface UserMetadataContextType {\n metadata: Record<string, string>;\n isLoading: boolean;\n error: Error | null;\n setMetadata: (key: string, value: string) => Promise<void>;\n getMetadata: (key: string) => string | undefined;\n removeMetadata: (key: string) => Promise<void>;\n refreshMetadata: () => Promise<void>;\n}\n\n// Create context\nexport const userMetadataContext = createContext<UserMetadataContextType | null>(null);\n\n// Provider component\nexport function UserMetadataProvider({\n children\n}: {\n children: ReactNode;\n}) {\n const supabase = useSupabase();\n const [metadata, setMetadataState] = useState<Record<string, string>>({});\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Get current user ID from auth context\n const setupAuth = useContext(setupAuthContext);\n const userId = setupAuth?.user?.id;\n\n // Query to fetch all user metadata\n const metadataQuery = useQuery(supabase.schema(\"core\").from(\"UserMetadata\").select(UserMetadataQuery.defaultQuery).eq(\"userId\", userId!).order(\"key\"), {\n enabled: isUsable(userId),\n crossOrganization: true\n });\n\n // Upsert mutation for updating metadata\n const upsertMutation = useUpsert(\"UserMetadata\", {\n invalidateTables: [\"UserMetadata\"]\n });\n\n // Ref to hold mutation to avoid infinite loops in useCallback dependencies\n const upsertMutationRef = useRef(upsertMutation);\n upsertMutationRef.current = upsertMutation;\n\n // Update local state when query data changes\n useEffect(() => {\n if (metadataQuery.data) {\n const metadataMap: Record<string, string> = {};\n metadataQuery.data.forEach((item: any) => {\n metadataMap[item.key] = item.value;\n });\n setMetadataState(metadataMap);\n setIsLoading(false);\n setError(null);\n } else if (metadataQuery.error) {\n setError(metadataQuery.error);\n setIsLoading(false);\n } else if (metadataQuery.isLoading) {\n setIsLoading(true);\n }\n }, [metadataQuery.data, metadataQuery.error, metadataQuery.isLoading]);\n\n // Set metadata function\n const setMetadata = useCallback(async (key: string, value: string) => {\n if (!userId) {\n throw new Error(\"User not authenticated\");\n }\n try {\n await upsertMutationRef.current.mutateAsync({\n userId,\n key,\n value\n });\n\n // Update local state optimistically\n setMetadataState(prev => ({\n ...prev,\n [key]: value\n }));\n } catch (err) {\n setError(err as Error);\n throw err;\n }\n }, [userId]);\n\n // Get metadata function\n const getMetadata = useCallback((key_0: string): string | undefined => {\n return metadata[key_0];\n }, [metadata]);\n\n // Remove metadata function\n const removeMetadata = useCallback(async (key_1: string) => {\n if (!userId) {\n throw new Error(\"User not authenticated\");\n }\n try {\n await supabase.schema(\"core\").from(\"UserMetadata\").delete().eq(\"userId\", userId).eq(\"key\", key_1);\n\n // Update local state\n setMetadataState(prev_0 => {\n const newState = {\n ...prev_0\n };\n delete newState[key_1];\n return newState;\n });\n } catch (err_0) {\n setError(err_0 as Error);\n throw err_0;\n }\n }, [userId, supabase]);\n\n // Refresh metadata function\n const refreshMetadata = useCallback(async () => {\n await metadataQuery.refetch();\n }, [metadataQuery]);\n\n // Context value\n const contextValue = useMemo(() => ({\n metadata,\n isLoading,\n error,\n setMetadata,\n getMetadata,\n removeMetadata,\n refreshMetadata\n }), [metadata, isLoading, error, setMetadata, getMetadata, removeMetadata, refreshMetadata]);\n return <userMetadataContext.Provider value={contextValue}>\n {children}\n </userMetadataContext.Provider>;\n}\n\n// Hook to use the context\nexport function useUserMetadata() {\n const context = useContext(userMetadataContext);\n if (!context) {\n throw new Error(\"useUserMetadata must be used within a UserMetadataProvider\");\n }\n return context;\n}\n\n// Convenience hook for getting a specific metadata value\nexport function useUserMetadataValue(key) {\n const $ = _c(3);\n const {\n getMetadata\n } = useUserMetadata();\n let t0;\n if ($[0] !== getMetadata || $[1] !== key) {\n t0 = getMetadata(key);\n $[0] = getMetadata;\n $[1] = key;\n $[2] = t0;\n } else {\n t0 = $[2];\n }\n return t0;\n}\n\n// Convenience hook for setting a specific metadata value\nexport function useSetUserMetadata() {\n const $ = _c(3);\n const {\n setMetadata,\n removeMetadata\n } = useUserMetadata();\n let t0;\n if ($[0] !== removeMetadata || $[1] !== setMetadata) {\n t0 = {\n setMetadata,\n removeMetadata\n };\n $[0] = removeMetadata;\n $[1] = setMetadata;\n $[2] = t0;\n } else {\n t0 = $[2];\n }\n return t0;\n}\n\n// Advanced state-like hook with JSON serialization support\nexport function useUserMetadataState(key, defaultValue, options) {\n const $ = _c(11);\n const {\n metadata,\n setMetadata,\n isLoading\n } = useUserMetadata();\n const serialize = options?.serialize ?? _temp;\n const deserialize = options?.deserialize ?? _temp2;\n let t0;\n bb0: {\n const rawValue = metadata[key];\n if (!rawValue) {\n t0 = defaultValue;\n break bb0;\n }\n ;\n try {\n let t2;\n if ($[0] !== deserialize || $[1] !== rawValue) {\n t2 = deserialize(rawValue);\n $[0] = deserialize;\n $[1] = rawValue;\n $[2] = t2;\n } else {\n t2 = $[2];\n }\n t0 = t2;\n } catch (t1) {\n const error = t1;\n console.warn(`Failed to deserialize metadata for key \"${key}\":`, error);\n t0 = defaultValue;\n }\n }\n const currentValue = t0;\n let t1;\n if ($[3] !== key || $[4] !== serialize || $[5] !== setMetadata) {\n t1 = async value_1 => {\n const serializedValue = serialize(value_1);\n await setMetadata(key, serializedValue);\n };\n $[3] = key;\n $[4] = serialize;\n $[5] = setMetadata;\n $[6] = t1;\n } else {\n t1 = $[6];\n }\n const setValue = t1;\n let t2;\n if ($[7] !== currentValue || $[8] !== isLoading || $[9] !== setValue) {\n t2 = [currentValue, setValue, isLoading];\n $[7] = currentValue;\n $[8] = isLoading;\n $[9] = setValue;\n $[10] = t2;\n } else {\n t2 = $[10];\n }\n return t2;\n}\nfunction _temp2(value_0) {\n return JSON.parse(value_0);\n}\nfunction _temp(value) {\n return JSON.stringify(value);\n}"],"mappings":"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","names":["createContext","useMemo","useRef","createContext","useRef","useMemo","useCallback","useEffect","useMemo","useRef","useState","jsx","useState","useEffect","useCallback","useRef","useMemo","createContext","useContext","useEffect","useMemo","useState","useCallback","useRef","jsx","createContext","useState","useContext","useRef","useEffect","useCallback","useMemo","t2","t1"]}
@@ -15,7 +15,7 @@ import {
15
15
  useOnlineStatus,
16
16
  useSyncControl,
17
17
  useSyncStatus
18
- } from "../chunk-EKUDGIQZ.js";
18
+ } from "../chunk-VGEMLNNM.js";
19
19
  import "../chunk-GC3TBUWE.js";
20
20
  import "../chunk-J4ZVCXZ4.js";
21
21
  import "../chunk-OQ7U6EQ3.js";
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  useHasConflicts,
25
25
  usePendingConflicts,
26
26
  useSupabaseUpload
27
- } from "./chunk-KQNOEADH.js";
27
+ } from "./chunk-2NVSXZKQ.js";
28
28
  import {
29
29
  ADAPTER_STRATEGIES,
30
30
  AdapterAutoDetector,
@@ -69,7 +69,7 @@ import {
69
69
  useUpsertChangelog,
70
70
  useUpsertChangelogEntry,
71
71
  useUpsertChangelogMedia
72
- } from "./chunk-IXBDCSM3.js";
72
+ } from "./chunk-O7SETNGD.js";
73
73
  import {
74
74
  LiveChangeContext,
75
75
  LiveChangeContextProvider,
@@ -80,7 +80,7 @@ import {
80
80
  useUserMetadataState,
81
81
  useUserMetadataValue,
82
82
  userMetadataContext
83
- } from "./chunk-D522CKBU.js";
83
+ } from "./chunk-ADD5MIMK.js";
84
84
  import {
85
85
  PostgrestFilter,
86
86
  binarySearch,
@@ -301,12 +301,12 @@ import {
301
301
  UserMetadata
302
302
  } from "./chunk-SM73S2DY.js";
303
303
  import "./chunk-NSIAAYW3.js";
304
- import "./chunk-7DJELYBA.js";
304
+ import "./chunk-5BLKZUKM.js";
305
305
  import {
306
306
  DEFAULT_QUERY_TIMEOUT,
307
307
  TIMEOUT_ERROR_MESSAGE,
308
308
  isTimeoutError
309
- } from "./chunk-A7C2FMUF.js";
309
+ } from "./chunk-WX4ABYIF.js";
310
310
  import {
311
311
  DataLayerContext,
312
312
  DataLayerCoreContext,
@@ -326,7 +326,7 @@ import {
326
326
  useOnlineStatus,
327
327
  useSyncControl,
328
328
  useSyncStatus
329
- } from "./chunk-EKUDGIQZ.js";
329
+ } from "./chunk-VGEMLNNM.js";
330
330
  import "./chunk-HAWJTZCK.js";
331
331
  import "./chunk-CNIGRBRE.js";
332
332
  import {
@@ -13,7 +13,7 @@ import {
13
13
  useHasConflicts,
14
14
  usePendingConflicts,
15
15
  useSupabaseUpload
16
- } from "./chunk-KQNOEADH.js";
16
+ } from "./chunk-2NVSXZKQ.js";
17
17
  import {
18
18
  ADAPTER_STRATEGIES,
19
19
  AdapterAutoDetector,
@@ -58,7 +58,7 @@ import {
58
58
  useUpsertChangelog,
59
59
  useUpsertChangelogEntry,
60
60
  useUpsertChangelogMedia
61
- } from "./chunk-IXBDCSM3.js";
61
+ } from "./chunk-O7SETNGD.js";
62
62
  import {
63
63
  LiveChangeContext,
64
64
  LiveChangeContextProvider,
@@ -69,7 +69,7 @@ import {
69
69
  useUserMetadataState,
70
70
  useUserMetadataValue,
71
71
  userMetadataContext
72
- } from "./chunk-D522CKBU.js";
72
+ } from "./chunk-ADD5MIMK.js";
73
73
  import {
74
74
  PostgrestFilter,
75
75
  binarySearch,
@@ -290,12 +290,12 @@ import {
290
290
  UserMetadata
291
291
  } from "./chunk-SM73S2DY.js";
292
292
  import "./chunk-NSIAAYW3.js";
293
- import "./chunk-7DJELYBA.js";
293
+ import "./chunk-5BLKZUKM.js";
294
294
  import {
295
295
  DEFAULT_QUERY_TIMEOUT,
296
296
  TIMEOUT_ERROR_MESSAGE,
297
297
  isTimeoutError
298
- } from "./chunk-A7C2FMUF.js";
298
+ } from "./chunk-WX4ABYIF.js";
299
299
  import {
300
300
  DataLayerContext,
301
301
  DataLayerCoreContext,
@@ -313,7 +313,7 @@ import {
313
313
  useOnlineStatus,
314
314
  useSyncControl,
315
315
  useSyncStatus
316
- } from "./chunk-EKUDGIQZ.js";
316
+ } from "./chunk-VGEMLNNM.js";
317
317
  import "./chunk-HAWJTZCK.js";
318
318
  import "./chunk-CNIGRBRE.js";
319
319
  import {
package/dist/index.web.js CHANGED
@@ -52,7 +52,7 @@ import {
52
52
  useUpsertChangelog,
53
53
  useUpsertChangelogEntry,
54
54
  useUpsertChangelogMedia
55
- } from "./chunk-IXBDCSM3.js";
55
+ } from "./chunk-O7SETNGD.js";
56
56
  import {
57
57
  LiveChangeContext,
58
58
  LiveChangeContextProvider,
@@ -63,7 +63,7 @@ import {
63
63
  useUserMetadataState,
64
64
  useUserMetadataValue,
65
65
  userMetadataContext
66
- } from "./chunk-D522CKBU.js";
66
+ } from "./chunk-ADD5MIMK.js";
67
67
  import {
68
68
  PostgrestFilter,
69
69
  binarySearch,
@@ -286,13 +286,13 @@ import {
286
286
  UserMetadata
287
287
  } from "./chunk-SM73S2DY.js";
288
288
  import "./chunk-NSIAAYW3.js";
289
- import "./chunk-7DJELYBA.js";
289
+ import "./chunk-5BLKZUKM.js";
290
290
  import {
291
291
  DEFAULT_QUERY_TIMEOUT,
292
292
  TIMEOUT_ERROR_MESSAGE,
293
293
  isTimeoutError,
294
294
  useDbQuery as useDbQuery2
295
- } from "./chunk-A7C2FMUF.js";
295
+ } from "./chunk-WX4ABYIF.js";
296
296
  import {
297
297
  DataLayerContext,
298
298
  useAdvanceQuery,
@@ -306,7 +306,7 @@ import {
306
306
  useDbQueryById,
307
307
  useDbUpdate,
308
308
  useDbUpsert
309
- } from "./chunk-EKUDGIQZ.js";
309
+ } from "./chunk-VGEMLNNM.js";
310
310
  import "./chunk-HAWJTZCK.js";
311
311
  import {
312
312
  useAdvancedFilterQuery,
@@ -1,14 +1,35 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
1
  import * as react from 'react';
3
2
  import { ReactNode } from 'react';
4
3
  import { User, SignUpWithPasswordCredentials, AuthTokenResponsePassword } from '@supabase/supabase-js';
5
4
 
6
5
  type ProfileStatus = "active" | "archived" | "suspended";
6
+ /**
7
+ * Represents an effective permission for a user on a specific resource.
8
+ * These are eagerly loaded at login for fast permission checks.
9
+ */
10
+ interface EffectivePermission {
11
+ /** The type of resource (e.g., 'project', 'client', 'database') */
12
+ resourceType: string;
13
+ /** The ID of the specific resource */
14
+ resourceId: string;
15
+ /** The permission action (e.g., 'view', 'edit', 'admin') */
16
+ permission: string;
17
+ /** Numeric level for comparison: view=1, edit=2, admin=3 */
18
+ permissionLevel: number;
19
+ /** How the permission was granted */
20
+ source: "direct" | "group" | "inherited";
21
+ /** If inherited, from which resource */
22
+ inheritedFrom: string | null;
23
+ /** When the permission expires, if applicable */
24
+ expiresAt: string | null;
25
+ }
7
26
  interface SetupAuthContext {
8
27
  user?: User | null | undefined;
9
28
  isLoading: boolean;
10
29
  profile: Profile | null | undefined;
11
30
  access: string[];
31
+ /** Eagerly loaded effective permissions for the user */
32
+ effectivePermissions: EffectivePermission[];
12
33
  /** Whether the profile is archived */
13
34
  isArchived: boolean;
14
35
  /** Whether the profile is suspended */
@@ -42,20 +63,5 @@ interface SetupAuthContextProviderProps {
42
63
  children: ReactNode;
43
64
  auth: SetupAuthContext;
44
65
  }
45
- /**
46
- * Simple provider that takes auth state and provides it via context.
47
- * Use this when you already have the auth state and just need to provide it to children.
48
- *
49
- * @example
50
- * ```tsx
51
- * const auth = useSetupAuth();
52
- * return (
53
- * <SetupAuthContextProvider auth={auth}>
54
- * <MyComponent />
55
- * </SetupAuthContextProvider>
56
- * );
57
- * ```
58
- */
59
- declare function SetupAuthContextProvider({ children, auth, }: SetupAuthContextProviderProps): react_jsx_runtime.JSX.Element;
60
66
 
61
- export { type ProfileStatus as P, SetupAuthContextProvider as S, type SetupAuthContext as a, type SetupAuthContextProviderProps as b, type Profile as c, setupAuthContext as s };
67
+ export { type EffectivePermission as E, type Profile as P, type SetupAuthContext as S, type ProfileStatus as a, type SetupAuthContextProviderProps as b, setupAuthContext as s };
@@ -1,5 +1,5 @@
1
1
  import { u as useSupabase } from '../useSupabase-DvWVuHHE.js';
2
- import { P as ProfileStatus } from '../setupAuthContext-Kv-THH-h.js';
2
+ import { a as ProfileStatus } from '../setupAuthContext-B76nbIP6.js';
3
3
  import * as _tanstack_react_query from '@tanstack/react-query';
4
4
  import { useMutation, useQuery } from '@tanstack/react-query';
5
5
  export { e as UserMetadataContextProvider, c as UserMetadataContextType, a as UserMetadataInsert, U as UserMetadataRow, b as UserMetadataUpdate, u as useLiveChangesIndicator, h as useSetUserMetadata, i as useUserMetadataState, g as useUserMetadataValue, d as userMetadataContext } from '../UserMetadataContext-B8gVWGMl.js';
@@ -5,19 +5,19 @@ import {
5
5
  useUserMetadataState,
6
6
  useUserMetadataValue,
7
7
  userMetadataContext
8
- } from "../chunk-D522CKBU.js";
8
+ } from "../chunk-ADD5MIMK.js";
9
9
  import "../chunk-SM73S2DY.js";
10
10
  import "../chunk-NSIAAYW3.js";
11
11
  import {
12
12
  useAuth,
13
13
  useSetupAuth
14
- } from "../chunk-7DJELYBA.js";
14
+ } from "../chunk-5BLKZUKM.js";
15
15
  import {
16
16
  useDbQuery
17
- } from "../chunk-A7C2FMUF.js";
17
+ } from "../chunk-WX4ABYIF.js";
18
18
  import {
19
19
  useDbUpsert
20
- } from "../chunk-EKUDGIQZ.js";
20
+ } from "../chunk-VGEMLNNM.js";
21
21
  import "../chunk-HAWJTZCK.js";
22
22
  import {
23
23
  getSupabaseUrl