@pol-studios/db 1.0.37 → 1.0.39

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.
Files changed (39) hide show
  1. package/dist/UserMetadataContext-yLZQu24J.d.ts +33 -0
  2. package/dist/auth/context.d.ts +5 -59
  3. package/dist/auth/context.js +2 -26
  4. package/dist/auth/hooks.d.ts +3 -107
  5. package/dist/auth/hooks.js +3 -9
  6. package/dist/auth/index.d.ts +5 -5
  7. package/dist/auth/index.js +10 -40
  8. package/dist/{chunk-7P2LGZYQ.js → chunk-3UW5K5PL.js} +3 -3
  9. package/dist/{chunk-UBHORKBS.js → chunk-5SJ5O2NQ.js} +75 -2
  10. package/dist/chunk-5SJ5O2NQ.js.map +1 -0
  11. package/dist/{chunk-X747EEWD.js → chunk-AQ5JJKIN.js} +4 -4
  12. package/dist/{chunk-NP34C3O3.js → chunk-D2F3APBF.js} +30 -103
  13. package/dist/chunk-D2F3APBF.js.map +1 -0
  14. package/dist/{chunk-7NFMEDJW.js → chunk-D7UZEYKO.js} +9 -42
  15. package/dist/chunk-D7UZEYKO.js.map +1 -0
  16. package/dist/{chunk-YQUNORJD.js → chunk-MZLEDWJF.js} +310 -392
  17. package/dist/chunk-MZLEDWJF.js.map +1 -0
  18. package/dist/chunk-NSIAAYW3.js +1 -0
  19. package/dist/{chunk-U4BZKCBH.js → chunk-Z6ZHXO2R.js} +4 -4
  20. package/dist/hooks/index.js +4 -4
  21. package/dist/index.js +10 -11
  22. package/dist/index.native.js +10 -11
  23. package/dist/index.web.js +9 -10
  24. package/dist/index.web.js.map +1 -1
  25. package/dist/with-auth/index.js +6 -7
  26. package/dist/with-auth/index.js.map +1 -1
  27. package/package.json +1 -1
  28. package/dist/UserMetadataContext-QLIv-mfF.d.ts +0 -171
  29. package/dist/chunk-5HJLTYRA.js +0 -355
  30. package/dist/chunk-5HJLTYRA.js.map +0 -1
  31. package/dist/chunk-6KN7KLEG.js +0 -1
  32. package/dist/chunk-7NFMEDJW.js.map +0 -1
  33. package/dist/chunk-NP34C3O3.js.map +0 -1
  34. package/dist/chunk-UBHORKBS.js.map +0 -1
  35. package/dist/chunk-YQUNORJD.js.map +0 -1
  36. /package/dist/{chunk-7P2LGZYQ.js.map → chunk-3UW5K5PL.js.map} +0 -0
  37. /package/dist/{chunk-X747EEWD.js.map → chunk-AQ5JJKIN.js.map} +0 -0
  38. /package/dist/{chunk-6KN7KLEG.js.map → chunk-NSIAAYW3.js.map} +0 -0
  39. /package/dist/{chunk-U4BZKCBH.js.map → chunk-Z6ZHXO2R.js.map} +0 -0
@@ -1,6 +1,7 @@
1
1
  import {
2
- useDbQuery
3
- } from "./chunk-UBHORKBS.js";
2
+ useDbQuery,
3
+ useDbQueryById
4
+ } from "./chunk-5SJ5O2NQ.js";
4
5
  import {
5
6
  buildNormalizedQuery,
6
7
  encode,
@@ -8,7 +9,6 @@ import {
8
9
  useUpsertItem
9
10
  } from "./chunk-YUX6RGLZ.js";
10
11
  import {
11
- typedSupabase,
12
12
  useSupabase
13
13
  } from "./chunk-DMVUEJG2.js";
14
14
 
@@ -72,9 +72,292 @@ function useDbQuery2(query, config) {
72
72
  import { createContext } from "react";
73
73
  var setupAuthContext = createContext({});
74
74
 
75
- // src/auth/context/PermissionContext.tsx
76
- import { createContext as createContext2, useCallback, useContext, useEffect, useMemo as useMemo2, useRef as useRef2, useState } from "react";
75
+ // src/auth/context/AuthProvider.tsx
76
+ import { useCallback, useEffect, useMemo as useMemo2, useRef as useRef2, useState } from "react";
77
+ import { isUsable, newUuid } from "@pol-studios/utils";
77
78
  import { jsx } from "react/jsx-runtime";
79
+ var SESSION_FETCH_TIMEOUT_MS = 3e3;
80
+ function getPermissionLevel(action) {
81
+ switch (action.toLowerCase()) {
82
+ case "view":
83
+ case "read":
84
+ return 1;
85
+ case "edit":
86
+ case "write":
87
+ return 2;
88
+ case "admin":
89
+ case "delete":
90
+ case "share":
91
+ return 3;
92
+ default:
93
+ return 0;
94
+ }
95
+ }
96
+ function isNotExpired(expiresAt) {
97
+ return !expiresAt || new Date(expiresAt) > /* @__PURE__ */ new Date();
98
+ }
99
+ function matchesAccessPattern(pattern, requestedKey) {
100
+ if (pattern === requestedKey) return true;
101
+ if (pattern === "*:*:*") return true;
102
+ const patternParts = pattern.split(":");
103
+ const keyParts = requestedKey.split(":");
104
+ if (patternParts.length !== keyParts.length) return false;
105
+ for (let i = 0; i < patternParts.length; i++) {
106
+ if (patternParts[i] === "*") continue;
107
+ if (patternParts[i] !== keyParts[i]) return false;
108
+ }
109
+ return true;
110
+ }
111
+ function AuthProvider({
112
+ children
113
+ }) {
114
+ const supabase = useSupabase();
115
+ const [currentUser, setCurrentUser] = useState(void 0);
116
+ const [userNeedsChange, setUserNeedsChange] = useState(true);
117
+ const [onSignOutCallbacks, setOnSignOutCallbacks] = useState(/* @__PURE__ */ new Map());
118
+ async function registerAsync(register) {
119
+ const response = await supabase.auth.signUp(register);
120
+ if (response.data.user) {
121
+ setCurrentUser((prev) => prev?.id === response.data.user?.id ? prev : response.data.user);
122
+ }
123
+ return response;
124
+ }
125
+ async function signInAsync(username, password) {
126
+ const response_0 = await supabase.auth.signInWithPassword({
127
+ email: username,
128
+ password
129
+ });
130
+ if (response_0.data.user) {
131
+ setCurrentUser((prev_0) => prev_0?.id === response_0.data.user?.id ? prev_0 : response_0.data.user);
132
+ }
133
+ return response_0;
134
+ }
135
+ const signOutAsync = useCallback(async () => {
136
+ const response_1 = await supabase.auth.signOut();
137
+ if (!response_1.error) {
138
+ Array.from(onSignOutCallbacks.values()).forEach((cb) => cb());
139
+ }
140
+ return response_1;
141
+ }, [supabase.auth, onSignOutCallbacks]);
142
+ function onSignOut(action) {
143
+ const id = newUuid();
144
+ setOnSignOutCallbacks((x) => new Map(x).set(id, action));
145
+ return id;
146
+ }
147
+ function removeOnSignOut(id_0) {
148
+ setOnSignOutCallbacks((x_0) => {
149
+ const map = new Map(x_0);
150
+ map.delete(id_0);
151
+ return map;
152
+ });
153
+ }
154
+ async function refreshAsync() {
155
+ }
156
+ useEffect(() => {
157
+ const {
158
+ data: {
159
+ subscription
160
+ }
161
+ } = supabase.auth.onAuthStateChange((event) => {
162
+ if (event === "SIGNED_IN" || event === "SIGNED_OUT") {
163
+ setUserNeedsChange(true);
164
+ }
165
+ });
166
+ return () => subscription.unsubscribe();
167
+ }, [supabase.auth]);
168
+ useEffect(() => {
169
+ if (!userNeedsChange) return;
170
+ let cancelled = false;
171
+ async function fetchSessionWithTimeout() {
172
+ try {
173
+ const timeoutPromise = new Promise((_, reject) => {
174
+ setTimeout(() => {
175
+ reject(new Error(`Session fetch timed out after ${SESSION_FETCH_TIMEOUT_MS}ms`));
176
+ }, SESSION_FETCH_TIMEOUT_MS);
177
+ });
178
+ const result = await Promise.race([supabase.auth.getSession(), timeoutPromise]);
179
+ if (cancelled) return;
180
+ const newUser = result?.data?.session?.user ?? null;
181
+ setCurrentUser((prev_2) => {
182
+ if (newUser === null) return null;
183
+ if (prev_2?.id === newUser?.id) return prev_2;
184
+ return newUser;
185
+ });
186
+ setUserNeedsChange(false);
187
+ } catch (error) {
188
+ if (cancelled) return;
189
+ console.error("Failed to get session (timeout or error):", error);
190
+ setCurrentUser((prev_1) => prev_1 === null ? prev_1 : null);
191
+ setUserNeedsChange(false);
192
+ }
193
+ }
194
+ fetchSessionWithTimeout();
195
+ return () => {
196
+ cancelled = true;
197
+ };
198
+ }, [userNeedsChange, supabase.auth]);
199
+ const isUserReady = isUsable(currentUser);
200
+ const {
201
+ data: profile,
202
+ isLoading: profileLoading
203
+ } = useDbQueryById("core.Profile", currentUser?.id ?? "", {
204
+ enabled: isUserReady
205
+ });
206
+ const {
207
+ data: directAccess,
208
+ isLoading: directAccessLoading
209
+ } = useDbQuery("core.UserAccess", {
210
+ where: {
211
+ userId: currentUser?.id
212
+ },
213
+ enabled: isUserReady,
214
+ realtime: true
215
+ });
216
+ const {
217
+ data: userGroups,
218
+ isLoading: userGroupsLoading
219
+ } = useDbQuery("core.UserGroup", {
220
+ where: {
221
+ userId: currentUser?.id
222
+ },
223
+ enabled: isUserReady,
224
+ realtime: true
225
+ });
226
+ const {
227
+ data: groups,
228
+ isLoading: groupsLoading
229
+ } = useDbQuery("core.Group", {
230
+ where: {
231
+ isActive: 1
232
+ },
233
+ enabled: isUserReady,
234
+ realtime: true
235
+ });
236
+ const groupIds = useMemo2(() => userGroups?.map((ug) => ug.groupId) ?? [], [userGroups]);
237
+ const groupsMap = useMemo2(() => new Map(groups?.map((g) => [g.id, g]) ?? []), [groups]);
238
+ const {
239
+ data: groupAccess,
240
+ isLoading: groupAccessLoading
241
+ } = useDbQuery("core.GroupAccessKey", {
242
+ where: groupIds.length > 0 ? {
243
+ groupId: {
244
+ in: groupIds
245
+ }
246
+ } : void 0,
247
+ enabled: groupIds.length > 0,
248
+ realtime: true
249
+ });
250
+ const prevProfileStatusRef = useRef2(void 0);
251
+ useEffect(() => {
252
+ const currentStatus = profile?.status;
253
+ const prevStatus = prevProfileStatusRef.current;
254
+ if (prevStatus === "active" && (currentStatus === "archived" || currentStatus === "suspended")) {
255
+ signOutAsync();
256
+ }
257
+ prevProfileStatusRef.current = currentStatus;
258
+ }, [profile?.status, signOutAsync]);
259
+ const allAccessKeys = useMemo2(() => {
260
+ const keys = [];
261
+ directAccess?.forEach((a) => {
262
+ if (isNotExpired(a.expiresAt)) {
263
+ keys.push({
264
+ accessKey: a.accessKey,
265
+ effect: a.effect ?? "allow",
266
+ source: "direct",
267
+ expiresAt: a.expiresAt
268
+ });
269
+ }
270
+ });
271
+ const activeGroupIds = new Set(userGroups?.filter((ug_0) => {
272
+ const group = groupsMap.get(ug_0.groupId);
273
+ return group?.isActive === 1 && isNotExpired(ug_0.expiresAt);
274
+ }).map((ug_1) => ug_1.groupId) ?? []);
275
+ groupAccess?.forEach((ga) => {
276
+ if (activeGroupIds.has(ga.groupId) && isNotExpired(ga.expiresAt)) {
277
+ keys.push({
278
+ accessKey: ga.accessKey,
279
+ effect: ga.effect ?? "allow",
280
+ source: "group",
281
+ expiresAt: ga.expiresAt
282
+ });
283
+ }
284
+ });
285
+ return keys;
286
+ }, [directAccess, userGroups, groupsMap, groupAccess]);
287
+ const combinedAccess = useMemo2(() => {
288
+ const uniqueKeys = /* @__PURE__ */ new Set();
289
+ for (const item of allAccessKeys) {
290
+ if (item.accessKey && item.effect === "allow") {
291
+ uniqueKeys.add(item.accessKey);
292
+ }
293
+ }
294
+ return Array.from(uniqueKeys);
295
+ }, [allAccessKeys]);
296
+ const effectivePermissions = useMemo2(() => {
297
+ const permissions = [];
298
+ for (const item_0 of allAccessKeys) {
299
+ if (item_0.effect !== "allow") continue;
300
+ const parts = item_0.accessKey.split(":");
301
+ if (parts.length === 3) {
302
+ const [resourceType, resourceId, permission] = parts;
303
+ permissions.push({
304
+ resourceType,
305
+ resourceId,
306
+ permission,
307
+ permissionLevel: getPermissionLevel(permission),
308
+ source: item_0.source,
309
+ inheritedFrom: null,
310
+ expiresAt: item_0.expiresAt ?? null
311
+ });
312
+ }
313
+ }
314
+ return permissions;
315
+ }, [allAccessKeys]);
316
+ const profileStatus = profile?.status;
317
+ const isArchived = profileStatus === "archived";
318
+ const isSuspended = profileStatus === "suspended";
319
+ const hasAccess = useCallback((key) => {
320
+ if (isArchived || isSuspended) return false;
321
+ if (!isUsable(combinedAccess)) return false;
322
+ if (combinedAccess.includes("*:*:*")) return true;
323
+ if (combinedAccess.includes(key)) return true;
324
+ if (!isUsable(key)) return true;
325
+ for (const pattern of combinedAccess) {
326
+ if (matchesAccessPattern(pattern, key)) return true;
327
+ }
328
+ const parts_0 = key.split(":");
329
+ if (parts_0.length === 3) {
330
+ const [type, id_1, action_0] = parts_0;
331
+ const requiredLevel = getPermissionLevel(action_0);
332
+ const hasPermission = effectivePermissions.some((p) => p.resourceType === type && p.resourceId === id_1 && p.permissionLevel >= requiredLevel);
333
+ if (hasPermission) return true;
334
+ }
335
+ return false;
336
+ }, [combinedAccess, effectivePermissions, isArchived, isSuspended]);
337
+ const isAccessLoading = directAccessLoading || userGroupsLoading || groupsLoading || groupAccessLoading;
338
+ const authState = useMemo2(() => ({
339
+ hasAccess,
340
+ user: currentUser,
341
+ profile,
342
+ access: combinedAccess,
343
+ effectivePermissions,
344
+ profileStatus,
345
+ isArchived,
346
+ isSuspended,
347
+ isLoading: currentUser === null ? false : profileLoading || isAccessLoading || currentUser === void 0,
348
+ signInAsync,
349
+ signOutAsync,
350
+ onSignOut,
351
+ removeOnSignOut,
352
+ registerAsync,
353
+ refreshAsync
354
+ }), [hasAccess, currentUser, profile, combinedAccess, effectivePermissions, profileStatus, isArchived, isSuspended, profileLoading, isAccessLoading]);
355
+ return /* @__PURE__ */ jsx(setupAuthContext.Provider, { value: authState, children });
356
+ }
357
+
358
+ // src/auth/context/PermissionContext.tsx
359
+ import { createContext as createContext2, useCallback as useCallback2, useContext, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
360
+ import { jsx as jsx2 } from "react/jsx-runtime";
78
361
  function getCacheKey(userId, entityType, entityId) {
79
362
  return `${userId || "anon"}:${entityType}:${entityId}`;
80
363
  }
@@ -189,13 +472,13 @@ function PermissionProvider({
189
472
  const supabase = useSupabase();
190
473
  const setupAuth = useContext(setupAuthContext);
191
474
  const user = setupAuth?.user;
192
- const cacheRef = useRef2(/* @__PURE__ */ new Map());
193
- const pendingLookupsRef = useRef2(/* @__PURE__ */ new Set());
194
- const inFlightRef = useRef2(/* @__PURE__ */ new Set());
195
- const batchTimerRef = useRef2(null);
196
- const [isLoading, setIsLoading] = useState(false);
197
- const [, forceUpdate] = useState(0);
198
- const cleanupExpiredEntries = useCallback(() => {
475
+ const cacheRef = useRef3(/* @__PURE__ */ new Map());
476
+ const pendingLookupsRef = useRef3(/* @__PURE__ */ new Set());
477
+ const inFlightRef = useRef3(/* @__PURE__ */ new Set());
478
+ const batchTimerRef = useRef3(null);
479
+ const [isLoading, setIsLoading] = useState2(false);
480
+ const [, forceUpdate] = useState2(0);
481
+ const cleanupExpiredEntries = useCallback2(() => {
199
482
  const now = Date.now();
200
483
  const cache = cacheRef.current;
201
484
  let hasExpired = false;
@@ -209,11 +492,11 @@ function PermissionProvider({
209
492
  forceUpdate((prev) => prev + 1);
210
493
  }
211
494
  }, []);
212
- useEffect(() => {
495
+ useEffect2(() => {
213
496
  const cleanupInterval = setInterval(cleanupExpiredEntries, 60 * 1e3);
214
497
  return () => clearInterval(cleanupInterval);
215
498
  }, [cleanupExpiredEntries]);
216
- const executeBatchLookup = useCallback(async () => {
499
+ const executeBatchLookup = useCallback2(async () => {
217
500
  const pending = Array.from(pendingLookupsRef.current);
218
501
  pendingLookupsRef.current.clear();
219
502
  if (pending.length === 0 || !user?.id) {
@@ -273,7 +556,7 @@ function PermissionProvider({
273
556
  setIsLoading(false);
274
557
  }
275
558
  }, [supabase, user?.id]);
276
- const scheduleBatchLookup = useCallback(() => {
559
+ const scheduleBatchLookup = useCallback2(() => {
277
560
  if (batchTimerRef.current) {
278
561
  clearTimeout(batchTimerRef.current);
279
562
  }
@@ -282,7 +565,7 @@ function PermissionProvider({
282
565
  executeBatchLookup();
283
566
  }, BATCH_DELAY_MS);
284
567
  }, [executeBatchLookup]);
285
- const getPermission = useCallback((entityType_0, entityId) => {
568
+ const getPermission = useCallback2((entityType_0, entityId) => {
286
569
  const key_4 = getCacheKey(user?.id, entityType_0, entityId);
287
570
  const cache_2 = cacheRef.current;
288
571
  const cached = cache_2.get(key_4);
@@ -296,7 +579,7 @@ function PermissionProvider({
296
579
  }
297
580
  return loadingPermission;
298
581
  }, [scheduleBatchLookup, user?.id]);
299
- const checkPermission = useCallback((entityType_1, entityId_0, action) => {
582
+ const checkPermission = useCallback2((entityType_1, entityId_0, action) => {
300
583
  const permission = getPermission(entityType_1, entityId_0);
301
584
  if (permission.isLoading) {
302
585
  return false;
@@ -318,7 +601,7 @@ function PermissionProvider({
318
601
  return false;
319
602
  }
320
603
  }, [getPermission]);
321
- const prefetchPermissions = useCallback(async (entities_0) => {
604
+ const prefetchPermissions = useCallback2(async (entities_0) => {
322
605
  if (!user?.id || entities_0.length === 0) {
323
606
  return;
324
607
  }
@@ -375,12 +658,12 @@ function PermissionProvider({
375
658
  setIsLoading(false);
376
659
  }
377
660
  }, [supabase, user?.id]);
378
- const invalidatePermission = useCallback((entityType_2, entityId_1) => {
661
+ const invalidatePermission = useCallback2((entityType_2, entityId_1) => {
379
662
  const key_8 = getCacheKey(user?.id, entityType_2, entityId_1);
380
663
  cacheRef.current.delete(key_8);
381
664
  forceUpdate((prev_2) => prev_2 + 1);
382
665
  }, [user?.id]);
383
- const parseScopedAccessKey = useCallback((key_9) => {
666
+ const parseScopedAccessKey = useCallback2((key_9) => {
384
667
  if (!key_9 || typeof key_9 !== "string") {
385
668
  return null;
386
669
  }
@@ -408,7 +691,7 @@ function PermissionProvider({
408
691
  entityId: entityId_2
409
692
  };
410
693
  }, []);
411
- useEffect(() => {
694
+ useEffect2(() => {
412
695
  if (!user?.id) {
413
696
  return;
414
697
  }
@@ -436,7 +719,7 @@ function PermissionProvider({
436
719
  supabase.removeChannel(channel);
437
720
  };
438
721
  }, [supabase, user?.id, invalidatePermission, parseScopedAccessKey]);
439
- useEffect(() => {
722
+ useEffect2(() => {
440
723
  cacheRef.current.clear();
441
724
  pendingLookupsRef.current.clear();
442
725
  inFlightRef.current.clear();
@@ -446,21 +729,21 @@ function PermissionProvider({
446
729
  }
447
730
  forceUpdate((prev_3) => prev_3 + 1);
448
731
  }, [user?.id]);
449
- useEffect(() => {
732
+ useEffect2(() => {
450
733
  return () => {
451
734
  if (batchTimerRef.current) {
452
735
  clearTimeout(batchTimerRef.current);
453
736
  }
454
737
  };
455
738
  }, []);
456
- const value = useMemo2(() => ({
739
+ const value = useMemo3(() => ({
457
740
  getPermission,
458
741
  checkPermission,
459
742
  prefetchPermissions,
460
743
  invalidatePermission,
461
744
  isLoading
462
745
  }), [getPermission, checkPermission, prefetchPermissions, invalidatePermission, isLoading]);
463
- return /* @__PURE__ */ jsx(permissionContext.Provider, { value, children });
746
+ return /* @__PURE__ */ jsx2(permissionContext.Provider, { value, children });
464
747
  }
465
748
  function usePermissions() {
466
749
  const context = useContext(permissionContext);
@@ -470,371 +753,6 @@ function usePermissions() {
470
753
  return context;
471
754
  }
472
755
 
473
- // src/auth/context/AuthProvider.tsx
474
- import { isUsable, newUuid } from "@pol-studios/utils";
475
- import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
476
- import { jsx as jsx2 } from "react/jsx-runtime";
477
- var SESSION_FETCH_TIMEOUT_MS = 3e3;
478
- function getPermissionLevel(action) {
479
- switch (action.toLowerCase()) {
480
- case "view":
481
- case "read":
482
- return 1;
483
- case "edit":
484
- case "write":
485
- return 2;
486
- case "admin":
487
- case "delete":
488
- case "share":
489
- return 3;
490
- default:
491
- return 0;
492
- }
493
- }
494
- var profileQuery = typedSupabase?.schema("core").from("Profile").select("*, UserAccess(accessKey), status").single();
495
- function isNotExpired(expiresAt) {
496
- return !expiresAt || new Date(expiresAt) > /* @__PURE__ */ new Date();
497
- }
498
- function AuthProvider({
499
- children,
500
- enableEntityPermissions = false
501
- }) {
502
- const supabase = useSupabase();
503
- const [currentUser, setCurrentUser] = useState2(void 0);
504
- const [userNeedsChange, setUserNeedsChange] = useState2(true);
505
- const [onSignOutCallbacks, setOnSignOutCallbacks] = useState2(/* @__PURE__ */ new Map());
506
- async function registerAsync(register) {
507
- const response = await supabase.auth.signUp(register);
508
- setCurrentUser((prev) => {
509
- const newUser = response.data.user;
510
- if (prev?.id === newUser?.id) {
511
- return prev;
512
- }
513
- return newUser;
514
- });
515
- return response;
516
- }
517
- async function signInAsync(username, password) {
518
- const response_0 = await supabase.auth.signInWithPassword({
519
- email: username,
520
- password
521
- });
522
- if (response_0.data) {
523
- setCurrentUser((prev_0) => {
524
- const newUser_0 = response_0.data.user;
525
- if (prev_0?.id === newUser_0?.id) {
526
- return prev_0;
527
- }
528
- return newUser_0;
529
- });
530
- }
531
- return response_0;
532
- }
533
- async function signOutAsync() {
534
- const response_1 = await supabase.auth.signOut();
535
- if (isUsable(response_1.error) === false) {
536
- Array.from(onSignOutCallbacks.values()).forEach((x) => {
537
- x();
538
- });
539
- }
540
- return response_1;
541
- }
542
- function onSignOut(action) {
543
- const id = newUuid();
544
- setOnSignOutCallbacks((x_0) => new Map(x_0).set(id, action));
545
- return id;
546
- }
547
- function removeOnSignOut(id_0) {
548
- setOnSignOutCallbacks((x_1) => {
549
- const map = new Map(x_1);
550
- map.delete(id_0);
551
- return map;
552
- });
553
- }
554
- async function refreshAsync() {
555
- }
556
- useEffect2(() => {
557
- const request = supabase.auth.onAuthStateChange((event) => {
558
- if (event === "SIGNED_IN" || event === "SIGNED_OUT") {
559
- setUserNeedsChange(true);
560
- }
561
- });
562
- return () => {
563
- request.data.subscription.unsubscribe();
564
- };
565
- }, [supabase.auth]);
566
- useEffect2(() => {
567
- if (userNeedsChange === false) return;
568
- let cancelled = false;
569
- async function fetchSessionWithTimeout() {
570
- try {
571
- const timeoutPromise = new Promise((_, reject) => {
572
- setTimeout(() => {
573
- reject(new Error(`Session fetch timed out after ${SESSION_FETCH_TIMEOUT_MS}ms`));
574
- }, SESSION_FETCH_TIMEOUT_MS);
575
- });
576
- const result = await Promise.race([supabase.auth.getSession(), timeoutPromise]);
577
- if (cancelled) return;
578
- const newUser_1 = result?.data?.session?.user ?? null;
579
- setCurrentUser((prev_2) => {
580
- if (newUser_1 === null) return null;
581
- if (prev_2?.id === newUser_1?.id) {
582
- return prev_2;
583
- }
584
- return newUser_1;
585
- });
586
- setUserNeedsChange(false);
587
- } catch (error) {
588
- if (cancelled) return;
589
- console.error("Failed to get session (timeout or error):", error);
590
- setCurrentUser((prev_1) => prev_1 === null ? prev_1 : null);
591
- setUserNeedsChange(false);
592
- }
593
- }
594
- fetchSessionWithTimeout();
595
- return () => {
596
- cancelled = true;
597
- };
598
- }, [userNeedsChange, supabase.auth]);
599
- const {
600
- data: profileData,
601
- isLoading: profileLoading,
602
- refetch: refetchProfile
603
- } = useDbQuery("core.Profile", {
604
- where: {
605
- id: currentUser?.id
606
- },
607
- enabled: isUsable(currentUser)
608
- });
609
- const profile = profileData?.[0] ?? null;
610
- const {
611
- data: directAccess,
612
- isLoading: directAccessLoading,
613
- refetch: refetchDirectAccess
614
- } = useDbQuery("core.UserAccess", {
615
- where: {
616
- userId: currentUser?.id
617
- },
618
- enabled: isUsable(currentUser)
619
- });
620
- const {
621
- data: userGroups,
622
- isLoading: userGroupsLoading,
623
- refetch: refetchUserGroups
624
- } = useDbQuery("core.UserGroup", {
625
- where: {
626
- userId: currentUser?.id
627
- },
628
- enabled: isUsable(currentUser)
629
- });
630
- const {
631
- data: groups,
632
- isLoading: groupsLoading,
633
- refetch: refetchGroups
634
- } = useDbQuery("core.Group", {
635
- where: {
636
- isActive: 1
637
- },
638
- // PowerSync stores booleans as 0/1
639
- enabled: isUsable(currentUser)
640
- });
641
- const groupIds = useMemo3(() => userGroups?.map((ug) => ug.groupId) ?? [], [userGroups]);
642
- const groupsMap = useMemo3(() => new Map(groups?.map((g) => [g.id, g]) ?? []), [groups]);
643
- const {
644
- data: groupAccess,
645
- isLoading: groupAccessLoading,
646
- refetch: refetchGroupAccess
647
- } = useDbQuery("core.GroupAccessKey", {
648
- where: groupIds.length > 0 ? {
649
- groupId: {
650
- in: groupIds
651
- }
652
- } : void 0,
653
- enabled: groupIds.length > 0
654
- });
655
- const refetchAccessData = useCallback2(() => {
656
- refetchDirectAccess();
657
- refetchUserGroups();
658
- refetchGroups();
659
- refetchGroupAccess();
660
- }, [refetchDirectAccess, refetchUserGroups, refetchGroups, refetchGroupAccess]);
661
- const userGroupIdsRef = useRef3(/* @__PURE__ */ new Set());
662
- useEffect2(() => {
663
- if (userGroups) {
664
- const groupIdSet = /* @__PURE__ */ new Set();
665
- for (const ug_0 of userGroups) {
666
- if (ug_0.groupId) {
667
- groupIdSet.add(typeof ug_0.groupId === "number" ? ug_0.groupId : parseInt(ug_0.groupId, 10));
668
- }
669
- }
670
- userGroupIdsRef.current = groupIdSet;
671
- }
672
- }, [userGroups]);
673
- useEffect2(() => {
674
- if (!currentUser?.id) return;
675
- const channel = supabase.channel(`user-access-keys-${currentUser.id}`).on("postgres_changes", {
676
- event: "*",
677
- schema: "core",
678
- table: "UserAccess",
679
- filter: `userId=eq.${currentUser.id}`
680
- }, () => {
681
- refetchAccessData();
682
- }).on("postgres_changes", {
683
- event: "*",
684
- schema: "core",
685
- table: "UserGroup",
686
- filter: `userId=eq.${currentUser.id}`
687
- }, () => {
688
- refetchAccessData();
689
- }).on("postgres_changes", {
690
- event: "*",
691
- schema: "core",
692
- table: "GroupAccessKey"
693
- }, (payload) => {
694
- const groupId = payload.new?.groupId || payload.old?.groupId;
695
- if (groupId && userGroupIdsRef.current.has(groupId)) {
696
- refetchAccessData();
697
- }
698
- }).on("postgres_changes", {
699
- event: "UPDATE",
700
- schema: "core",
701
- table: "Group"
702
- }, (payload_0) => {
703
- const oldActive = payload_0.old?.isActive;
704
- const newActive = payload_0.new?.isActive;
705
- const groupId_0 = payload_0.new?.id;
706
- if (oldActive !== newActive && groupId_0 && userGroupIdsRef.current.has(groupId_0)) {
707
- refetchAccessData();
708
- }
709
- }).subscribe();
710
- return () => {
711
- channel.unsubscribe();
712
- supabase.removeChannel(channel);
713
- };
714
- }, [supabase, currentUser?.id, refetchAccessData]);
715
- useEffect2(() => {
716
- if (!currentUser?.id) return;
717
- const profileChannel = supabase.channel(`profile-status-${currentUser.id}`).on("postgres_changes", {
718
- event: "UPDATE",
719
- schema: "core",
720
- table: "Profile",
721
- filter: `id=eq.${currentUser.id}`
722
- }, (payload_1) => {
723
- const newStatus = payload_1.new?.status;
724
- const oldStatus = payload_1.old?.status;
725
- if (oldStatus === "active" && (newStatus === "archived" || newStatus === "suspended")) {
726
- signOutAsync();
727
- }
728
- refetchProfile();
729
- }).subscribe();
730
- return () => {
731
- profileChannel.unsubscribe();
732
- supabase.removeChannel(profileChannel);
733
- };
734
- }, [supabase, currentUser?.id, refetchProfile]);
735
- const allAccessKeys = useMemo3(() => {
736
- const keys = [];
737
- directAccess?.forEach((a) => {
738
- if (isNotExpired(a.expiresAt)) {
739
- keys.push({
740
- accessKey: a.accessKey,
741
- effect: a.effect ?? "allow",
742
- source: "direct",
743
- expiresAt: a.expiresAt
744
- });
745
- }
746
- });
747
- const activeGroupIds = new Set(userGroups?.filter((ug_1) => {
748
- const group = groupsMap.get(ug_1.groupId);
749
- return group?.isActive === 1 && isNotExpired(ug_1.expiresAt);
750
- }).map((ug_2) => ug_2.groupId) ?? []);
751
- groupAccess?.forEach((ga) => {
752
- if (activeGroupIds.has(ga.groupId) && isNotExpired(ga.expiresAt)) {
753
- keys.push({
754
- accessKey: ga.accessKey,
755
- effect: ga.effect ?? "allow",
756
- source: "group",
757
- expiresAt: ga.expiresAt
758
- });
759
- }
760
- });
761
- return keys;
762
- }, [directAccess, userGroups, groupsMap, groupAccess]);
763
- const combinedAccess = useMemo3(() => {
764
- const uniqueKeys = /* @__PURE__ */ new Set();
765
- for (const item of allAccessKeys) {
766
- if (item.accessKey && item.effect === "allow") {
767
- uniqueKeys.add(item.accessKey);
768
- }
769
- }
770
- return Array.from(uniqueKeys);
771
- }, [allAccessKeys]);
772
- const effectivePermissions = useMemo3(() => {
773
- const permissions = [];
774
- for (const item_0 of allAccessKeys) {
775
- if (item_0.effect !== "allow") continue;
776
- const parts = item_0.accessKey.split(":");
777
- if (parts.length === 3) {
778
- const [resourceType, resourceId, permission] = parts;
779
- const permissionLevel = getPermissionLevel(permission);
780
- permissions.push({
781
- resourceType,
782
- resourceId,
783
- permission,
784
- permissionLevel,
785
- source: item_0.source,
786
- inheritedFrom: null,
787
- expiresAt: item_0.expiresAt ?? null
788
- });
789
- }
790
- }
791
- return permissions;
792
- }, [allAccessKeys]);
793
- const profileStatus = profile?.status;
794
- const isArchived = profileStatus === "archived";
795
- const isSuspended = profileStatus === "suspended";
796
- const hasAccess = useCallback2((key) => {
797
- if (isArchived || isSuspended) {
798
- return false;
799
- }
800
- const accessGiven = combinedAccess;
801
- if (isUsable(accessGiven) === false) return false;
802
- if (accessGiven.includes("*:*:*")) return true;
803
- if (accessGiven.includes(key)) return true;
804
- if (isUsable(key) === false) return true;
805
- const parts_0 = key.split(":");
806
- if (parts_0.length === 3) {
807
- const [type, id_1, action_0] = parts_0;
808
- const requiredLevel = getPermissionLevel(action_0);
809
- const hasPermission = effectivePermissions.some((p) => p.resourceType === type && p.resourceId === id_1 && p.permissionLevel >= requiredLevel);
810
- if (hasPermission) {
811
- return true;
812
- }
813
- }
814
- return false;
815
- }, [combinedAccess, effectivePermissions, isArchived, isSuspended]);
816
- const isAccessLoading = directAccessLoading || userGroupsLoading || groupsLoading || groupAccessLoading;
817
- const authStateWithLoading = useMemo3(() => ({
818
- hasAccess,
819
- user: currentUser,
820
- profile,
821
- access: combinedAccess,
822
- effectivePermissions,
823
- profileStatus,
824
- isArchived,
825
- isSuspended,
826
- isLoading: currentUser === null ? false : profileLoading || isAccessLoading || currentUser === void 0,
827
- signInAsync,
828
- signOutAsync,
829
- onSignOut,
830
- removeOnSignOut,
831
- registerAsync,
832
- refreshAsync
833
- }), [profile, profileLoading, isAccessLoading, currentUser, combinedAccess, effectivePermissions, profileStatus, isArchived, isSuspended, hasAccess]);
834
- const content = enableEntityPermissions ? /* @__PURE__ */ jsx2(PermissionProvider, { children }) : children;
835
- return /* @__PURE__ */ jsx2(setupAuthContext.Provider, { value: authStateWithLoading, children: content });
836
- }
837
-
838
756
  // src/auth/context/UserMetadataContext.tsx
839
757
  import { c as _c2 } from "react/compiler-runtime";
840
758
  import { createContext as createContext3, useContext as useContext2, useEffect as useEffect3, useMemo as useMemo4, useState as useState3, useCallback as useCallback3, useRef as useRef4 } from "react";
@@ -1176,11 +1094,11 @@ export {
1176
1094
  isTimeoutError,
1177
1095
  useDbQuery2 as useDbQuery,
1178
1096
  setupAuthContext,
1097
+ AuthProvider,
1179
1098
  permissionContext,
1180
1099
  entityPermissionContext,
1181
1100
  PermissionProvider,
1182
1101
  usePermissions,
1183
- AuthProvider,
1184
1102
  userMetadataContext,
1185
1103
  UserMetadataProvider,
1186
1104
  useUserMetadata,
@@ -1188,4 +1106,4 @@ export {
1188
1106
  useSetUserMetadata,
1189
1107
  useUserMetadataState
1190
1108
  };
1191
- //# sourceMappingURL=chunk-YQUNORJD.js.map
1109
+ //# sourceMappingURL=chunk-MZLEDWJF.js.map