@pol-studios/db 1.0.9 → 1.0.10

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 (80) hide show
  1. package/dist/auth/context.js +21 -12786
  2. package/dist/auth/context.js.map +1 -1
  3. package/dist/auth/guards.js +12 -7640
  4. package/dist/auth/guards.js.map +1 -1
  5. package/dist/auth/hooks.js +25 -10591
  6. package/dist/auth/hooks.js.map +1 -1
  7. package/dist/auth/index.js +43 -13008
  8. package/dist/auth/index.js.map +1 -1
  9. package/dist/canvas-75Y7XMF3.js +1541 -0
  10. package/dist/canvas-75Y7XMF3.js.map +1 -0
  11. package/dist/chunk-2IFGILT3.js +532 -0
  12. package/dist/chunk-2IFGILT3.js.map +1 -0
  13. package/dist/chunk-3M2U6TXH.js +928 -0
  14. package/dist/chunk-3M2U6TXH.js.map +1 -0
  15. package/dist/chunk-3PJTNH2L.js +2778 -0
  16. package/dist/chunk-3PJTNH2L.js.map +1 -0
  17. package/dist/chunk-5ZYAEGCJ.js +416 -0
  18. package/dist/chunk-5ZYAEGCJ.js.map +1 -0
  19. package/dist/chunk-7HG6G25H.js +710 -0
  20. package/dist/chunk-7HG6G25H.js.map +1 -0
  21. package/dist/chunk-7XT7K4QT.js +2687 -0
  22. package/dist/chunk-7XT7K4QT.js.map +1 -0
  23. package/dist/chunk-AWFMICFV.js +158 -0
  24. package/dist/chunk-AWFMICFV.js.map +1 -0
  25. package/dist/chunk-BRTW7CO5.js +1467 -0
  26. package/dist/chunk-BRTW7CO5.js.map +1 -0
  27. package/dist/chunk-EL45Z26M.js +4194 -0
  28. package/dist/chunk-EL45Z26M.js.map +1 -0
  29. package/dist/chunk-ERGF2FCE.js +903 -0
  30. package/dist/chunk-ERGF2FCE.js.map +1 -0
  31. package/dist/chunk-GK7B66LY.js +135 -0
  32. package/dist/chunk-GK7B66LY.js.map +1 -0
  33. package/dist/chunk-GQI6WJGI.js +172 -0
  34. package/dist/chunk-GQI6WJGI.js.map +1 -0
  35. package/dist/chunk-H6365JPC.js +1858 -0
  36. package/dist/chunk-H6365JPC.js.map +1 -0
  37. package/dist/chunk-J4ZVCXZ4.js +1 -0
  38. package/dist/chunk-J4ZVCXZ4.js.map +1 -0
  39. package/dist/chunk-JUVE3DWY.js +433 -0
  40. package/dist/chunk-JUVE3DWY.js.map +1 -0
  41. package/dist/chunk-O3K7R32P.js +7555 -0
  42. package/dist/chunk-O3K7R32P.js.map +1 -0
  43. package/dist/chunk-P4UZ7IXC.js +42 -0
  44. package/dist/chunk-P4UZ7IXC.js.map +1 -0
  45. package/dist/chunk-SEY5UO2T.js +89 -0
  46. package/dist/chunk-SEY5UO2T.js.map +1 -0
  47. package/dist/chunk-USJYMRUO.js +86 -0
  48. package/dist/chunk-USJYMRUO.js.map +1 -0
  49. package/dist/chunk-XX3IWSPM.js +189 -0
  50. package/dist/chunk-XX3IWSPM.js.map +1 -0
  51. package/dist/chunk-Y3INY2CS.js +14 -0
  52. package/dist/chunk-Y3INY2CS.js.map +1 -0
  53. package/dist/chunk-ZTSBF536.js +1927 -0
  54. package/dist/chunk-ZTSBF536.js.map +1 -0
  55. package/dist/client/index.js +13 -141
  56. package/dist/client/index.js.map +1 -1
  57. package/dist/dist-NDNRSNOG.js +521 -0
  58. package/dist/dist-NDNRSNOG.js.map +1 -0
  59. package/dist/gen/index.js +186 -1280
  60. package/dist/gen/index.js.map +1 -1
  61. package/dist/hooks/index.js +21 -8694
  62. package/dist/hooks/index.js.map +1 -1
  63. package/dist/index.js +403 -47848
  64. package/dist/index.js.map +1 -1
  65. package/dist/index.native.js +400 -25048
  66. package/dist/index.native.js.map +1 -1
  67. package/dist/index.web.js +576 -43769
  68. package/dist/index.web.js.map +1 -1
  69. package/dist/mutation/index.js +44 -4675
  70. package/dist/mutation/index.js.map +1 -1
  71. package/dist/parser/index.js +45 -3697
  72. package/dist/parser/index.js.map +1 -1
  73. package/dist/pdf-3TIGQRLA.js +20336 -0
  74. package/dist/pdf-3TIGQRLA.js.map +1 -0
  75. package/dist/query/index.js +31 -13175
  76. package/dist/query/index.js.map +1 -1
  77. package/dist/realtime/index.js +45 -12431
  78. package/dist/realtime/index.js.map +1 -1
  79. package/dist/types/index.js +9 -0
  80. package/package.json +3 -3
@@ -0,0 +1,903 @@
1
+ import {
2
+ useDbQuery,
3
+ useDbUpsert
4
+ } from "./chunk-GK7B66LY.js";
5
+ import {
6
+ isUsable,
7
+ newUuid
8
+ } from "./chunk-O3K7R32P.js";
9
+ import {
10
+ typedSupabase,
11
+ useSupabase
12
+ } from "./chunk-AWFMICFV.js";
13
+
14
+ // src/auth/context/setupAuthContext.tsx
15
+ import { createContext } from "react";
16
+ import { jsx } from "react/jsx-runtime";
17
+ var setupAuthContext = createContext({});
18
+ function SetupAuthContextProvider({
19
+ children,
20
+ auth
21
+ }) {
22
+ return /* @__PURE__ */ jsx(setupAuthContext.Provider, { value: auth, children });
23
+ }
24
+
25
+ // src/auth/context/PermissionContext.tsx
26
+ import {
27
+ createContext as createContext2,
28
+ useCallback,
29
+ useContext,
30
+ useEffect,
31
+ useMemo,
32
+ useRef,
33
+ useState
34
+ } from "react";
35
+ import { jsx as jsx2 } from "react/jsx-runtime";
36
+ function getCacheKey(userId, entityType, entityId) {
37
+ return `${userId || "anon"}:${entityType}:${entityId}`;
38
+ }
39
+ var loadingPermission = {
40
+ canView: false,
41
+ canAdminView: false,
42
+ canEdit: false,
43
+ canCreate: false,
44
+ canDelete: false,
45
+ canShare: false,
46
+ permissionLevel: null,
47
+ isLoading: true
48
+ };
49
+ var noPermission = {
50
+ canView: false,
51
+ canAdminView: false,
52
+ canEdit: false,
53
+ canCreate: false,
54
+ canDelete: false,
55
+ canShare: false,
56
+ permissionLevel: null,
57
+ isLoading: false,
58
+ isDenied: false
59
+ };
60
+ var deniedPermission = {
61
+ canView: false,
62
+ canAdminView: false,
63
+ canEdit: false,
64
+ canCreate: false,
65
+ canDelete: false,
66
+ canShare: false,
67
+ permissionLevel: null,
68
+ isLoading: false,
69
+ isDenied: true
70
+ };
71
+ function mapPermissionLevel(level) {
72
+ if (!level) {
73
+ return noPermission;
74
+ }
75
+ const normalizedLevel = level.toLowerCase();
76
+ switch (normalizedLevel) {
77
+ // Legacy format: ReadOnly, New format: view
78
+ case "readonly":
79
+ case "view":
80
+ return {
81
+ canView: true,
82
+ canAdminView: false,
83
+ canEdit: false,
84
+ canCreate: false,
85
+ canDelete: false,
86
+ canShare: false,
87
+ permissionLevel: "ReadOnly",
88
+ isLoading: false,
89
+ isDenied: false
90
+ };
91
+ // Legacy format: AdminReadOnly (no new equivalent, keep for backwards compatibility)
92
+ case "adminreadonly":
93
+ return {
94
+ canView: true,
95
+ canAdminView: true,
96
+ canEdit: false,
97
+ canCreate: false,
98
+ canDelete: false,
99
+ canShare: false,
100
+ permissionLevel: "AdminReadOnly",
101
+ isLoading: false,
102
+ isDenied: false
103
+ };
104
+ // Legacy format: ReadWrite, New format: edit
105
+ case "readwrite":
106
+ case "edit":
107
+ return {
108
+ canView: true,
109
+ canAdminView: false,
110
+ canEdit: true,
111
+ canCreate: true,
112
+ canDelete: false,
113
+ canShare: false,
114
+ permissionLevel: "ReadWrite",
115
+ isLoading: false,
116
+ isDenied: false
117
+ };
118
+ // Legacy format: Admin, New format: admin
119
+ case "admin":
120
+ return {
121
+ canView: true,
122
+ canAdminView: true,
123
+ canEdit: true,
124
+ canCreate: true,
125
+ canDelete: true,
126
+ canShare: true,
127
+ permissionLevel: "Admin",
128
+ isLoading: false,
129
+ isDenied: false
130
+ };
131
+ // New format: denied - explicit access denial
132
+ case "denied":
133
+ return deniedPermission;
134
+ default:
135
+ console.warn(`Unknown permission level: ${level}`);
136
+ return noPermission;
137
+ }
138
+ }
139
+ var permissionContext = createContext2(
140
+ {}
141
+ );
142
+ var entityPermissionContext = permissionContext;
143
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
144
+ var ERROR_CACHE_TTL_MS = 30 * 1e3;
145
+ var BATCH_DELAY_MS = 50;
146
+ function PermissionProvider({ children }) {
147
+ const supabase = useSupabase();
148
+ const setupAuth = useContext(setupAuthContext);
149
+ const user = setupAuth?.user;
150
+ const cacheRef = useRef(/* @__PURE__ */ new Map());
151
+ const pendingLookupsRef = useRef(/* @__PURE__ */ new Set());
152
+ const inFlightRef = useRef(/* @__PURE__ */ new Set());
153
+ const batchTimerRef = useRef(null);
154
+ const [isLoading, setIsLoading] = useState(false);
155
+ const [, forceUpdate] = useState(0);
156
+ const cleanupExpiredEntries = useCallback(() => {
157
+ const now = Date.now();
158
+ const cache = cacheRef.current;
159
+ let hasExpired = false;
160
+ for (const [key, entry] of cache.entries()) {
161
+ if (entry.expiresAt < now) {
162
+ cache.delete(key);
163
+ hasExpired = true;
164
+ }
165
+ }
166
+ if (hasExpired) {
167
+ forceUpdate((prev) => prev + 1);
168
+ }
169
+ }, []);
170
+ useEffect(() => {
171
+ const cleanupInterval = setInterval(cleanupExpiredEntries, 60 * 1e3);
172
+ return () => clearInterval(cleanupInterval);
173
+ }, [cleanupExpiredEntries]);
174
+ const executeBatchLookup = useCallback(async () => {
175
+ const pending = Array.from(pendingLookupsRef.current);
176
+ pendingLookupsRef.current.clear();
177
+ if (pending.length === 0 || !user?.id) {
178
+ return;
179
+ }
180
+ pending.forEach((k) => inFlightRef.current.add(k));
181
+ setIsLoading(true);
182
+ try {
183
+ const entities = pending.map((key) => {
184
+ const parts = key.split(":");
185
+ const entityType = parts[1];
186
+ const entityIdStr = parts[2];
187
+ return {
188
+ entity_type: entityType,
189
+ entity_id: parseInt(entityIdStr, 10)
190
+ };
191
+ });
192
+ const { data, error } = await supabase.rpc(
193
+ "get_user_entity_permissions",
194
+ {
195
+ p_user_id: user.id,
196
+ p_entities: entities
197
+ }
198
+ );
199
+ if (error) {
200
+ console.error("Failed to fetch entity permissions:", error);
201
+ const cache = cacheRef.current;
202
+ const now = Date.now();
203
+ for (const key of pending) {
204
+ cache.set(key, {
205
+ permission: noPermission,
206
+ expiresAt: now + ERROR_CACHE_TTL_MS
207
+ });
208
+ }
209
+ } else if (data) {
210
+ const cache = cacheRef.current;
211
+ const now = Date.now();
212
+ const resultsMap = /* @__PURE__ */ new Map();
213
+ const results = data;
214
+ for (const result of results) {
215
+ const key = getCacheKey(
216
+ user?.id,
217
+ result.entity_type,
218
+ result.entity_id
219
+ );
220
+ resultsMap.set(key, result.permission);
221
+ }
222
+ for (const key of pending) {
223
+ const permissionLevel = resultsMap.get(key) || null;
224
+ cache.set(key, {
225
+ permission: mapPermissionLevel(permissionLevel),
226
+ expiresAt: now + CACHE_TTL_MS
227
+ });
228
+ }
229
+ }
230
+ forceUpdate((prev) => prev + 1);
231
+ } catch (err) {
232
+ console.error("Unexpected error fetching entity permissions:", err);
233
+ } finally {
234
+ pending.forEach((k) => inFlightRef.current.delete(k));
235
+ setIsLoading(false);
236
+ }
237
+ }, [supabase, user?.id]);
238
+ const scheduleBatchLookup = useCallback(() => {
239
+ if (batchTimerRef.current) {
240
+ clearTimeout(batchTimerRef.current);
241
+ }
242
+ batchTimerRef.current = setTimeout(() => {
243
+ batchTimerRef.current = null;
244
+ executeBatchLookup();
245
+ }, BATCH_DELAY_MS);
246
+ }, [executeBatchLookup]);
247
+ const getPermission = useCallback(
248
+ (entityType, entityId) => {
249
+ const key = getCacheKey(user?.id, entityType, entityId);
250
+ const cache = cacheRef.current;
251
+ const cached = cache.get(key);
252
+ const now = Date.now();
253
+ if (cached && cached.expiresAt > now) {
254
+ return cached.permission;
255
+ }
256
+ if (!pendingLookupsRef.current.has(key) && !inFlightRef.current.has(key)) {
257
+ pendingLookupsRef.current.add(key);
258
+ scheduleBatchLookup();
259
+ }
260
+ return loadingPermission;
261
+ },
262
+ [scheduleBatchLookup, user?.id]
263
+ );
264
+ const checkPermission = useCallback(
265
+ (entityType, entityId, action) => {
266
+ const permission = getPermission(entityType, entityId);
267
+ if (permission.isLoading) {
268
+ return false;
269
+ }
270
+ switch (action) {
271
+ case "view":
272
+ return permission.canView;
273
+ case "adminView":
274
+ return permission.canAdminView;
275
+ case "edit":
276
+ return permission.canEdit;
277
+ case "create":
278
+ return permission.canCreate;
279
+ case "delete":
280
+ return permission.canDelete;
281
+ case "share":
282
+ return permission.canShare;
283
+ default:
284
+ return false;
285
+ }
286
+ },
287
+ [getPermission]
288
+ );
289
+ const prefetchPermissions = useCallback(
290
+ async (entities) => {
291
+ if (!user?.id || entities.length === 0) {
292
+ return;
293
+ }
294
+ const cache = cacheRef.current;
295
+ const now = Date.now();
296
+ const toFetch = entities.filter((entity) => {
297
+ const key = getCacheKey(user?.id, entity.entityType, entity.entityId);
298
+ const cached = cache.get(key);
299
+ const isPending = pendingLookupsRef.current.has(key);
300
+ const isInFlight = inFlightRef.current.has(key);
301
+ return !isPending && !isInFlight && (!cached || cached.expiresAt <= now);
302
+ });
303
+ if (toFetch.length === 0) {
304
+ return;
305
+ }
306
+ setIsLoading(true);
307
+ try {
308
+ const entitiesParam = toFetch.map((e) => ({
309
+ entity_type: e.entityType,
310
+ entity_id: e.entityId
311
+ }));
312
+ const { data, error } = await supabase.rpc(
313
+ "get_user_entity_permissions",
314
+ {
315
+ p_user_id: user.id,
316
+ p_entities: entitiesParam
317
+ }
318
+ );
319
+ if (error) {
320
+ console.error("Failed to prefetch entity permissions:", error);
321
+ return;
322
+ }
323
+ if (data) {
324
+ const cacheTimestamp = Date.now();
325
+ const resultsMap = /* @__PURE__ */ new Map();
326
+ const results = data;
327
+ for (const result of results) {
328
+ const key = getCacheKey(
329
+ user?.id,
330
+ result.entity_type,
331
+ result.entity_id
332
+ );
333
+ resultsMap.set(key, result.permission);
334
+ }
335
+ for (const entity of toFetch) {
336
+ const key = getCacheKey(
337
+ user?.id,
338
+ entity.entityType,
339
+ entity.entityId
340
+ );
341
+ const permissionLevel = resultsMap.get(key) || null;
342
+ cache.set(key, {
343
+ permission: mapPermissionLevel(permissionLevel),
344
+ expiresAt: cacheTimestamp + CACHE_TTL_MS
345
+ });
346
+ }
347
+ forceUpdate((prev) => prev + 1);
348
+ }
349
+ } catch (err) {
350
+ console.error("Unexpected error prefetching entity permissions:", err);
351
+ } finally {
352
+ setIsLoading(false);
353
+ }
354
+ },
355
+ [supabase, user?.id]
356
+ );
357
+ const invalidatePermission = useCallback(
358
+ (entityType, entityId) => {
359
+ const key = getCacheKey(user?.id, entityType, entityId);
360
+ cacheRef.current.delete(key);
361
+ forceUpdate((prev) => prev + 1);
362
+ },
363
+ [user?.id]
364
+ );
365
+ const parseScopedAccessKey = useCallback(
366
+ (key) => {
367
+ if (!key || typeof key !== "string") {
368
+ return null;
369
+ }
370
+ const parts = key.split(":");
371
+ if (parts.length < 2) {
372
+ return null;
373
+ }
374
+ const entityType = parts[0];
375
+ const entityId = parseInt(parts[1], 10);
376
+ if (isNaN(entityId)) {
377
+ return null;
378
+ }
379
+ const entityTypeMap = {
380
+ client: "Client",
381
+ project: "Project",
382
+ database: "ProjectDatabase",
383
+ projectdatabase: "ProjectDatabase"
384
+ };
385
+ const normalizedEntityType = entityTypeMap[entityType.toLowerCase()];
386
+ if (!normalizedEntityType) {
387
+ return null;
388
+ }
389
+ return { entityType: normalizedEntityType, entityId };
390
+ },
391
+ []
392
+ );
393
+ useEffect(() => {
394
+ if (!user?.id) {
395
+ return;
396
+ }
397
+ const channel = supabase.channel(`entity-permissions-${user.id}`).on(
398
+ "postgres_changes",
399
+ {
400
+ event: "*",
401
+ schema: "core",
402
+ table: "UserAccess",
403
+ filter: `userId=eq.${user.id}`
404
+ },
405
+ (payload) => {
406
+ if (payload.new && typeof payload.new === "object" && "scopedAccessKey" in payload.new && typeof payload.new.scopedAccessKey === "string") {
407
+ const parsed = parseScopedAccessKey(payload.new.scopedAccessKey);
408
+ if (parsed) {
409
+ invalidatePermission(parsed.entityType, parsed.entityId);
410
+ }
411
+ }
412
+ if (payload.old && typeof payload.old === "object" && "scopedAccessKey" in payload.old && typeof payload.old.scopedAccessKey === "string") {
413
+ const parsed = parseScopedAccessKey(payload.old.scopedAccessKey);
414
+ if (parsed) {
415
+ invalidatePermission(parsed.entityType, parsed.entityId);
416
+ }
417
+ }
418
+ }
419
+ ).subscribe();
420
+ return () => {
421
+ channel.unsubscribe();
422
+ supabase.removeChannel(channel);
423
+ };
424
+ }, [supabase, user?.id, invalidatePermission, parseScopedAccessKey]);
425
+ useEffect(() => {
426
+ cacheRef.current.clear();
427
+ pendingLookupsRef.current.clear();
428
+ inFlightRef.current.clear();
429
+ if (batchTimerRef.current) {
430
+ clearTimeout(batchTimerRef.current);
431
+ batchTimerRef.current = null;
432
+ }
433
+ forceUpdate((prev) => prev + 1);
434
+ }, [user?.id]);
435
+ useEffect(() => {
436
+ return () => {
437
+ if (batchTimerRef.current) {
438
+ clearTimeout(batchTimerRef.current);
439
+ }
440
+ };
441
+ }, []);
442
+ const value = useMemo(
443
+ () => ({
444
+ getPermission,
445
+ checkPermission,
446
+ prefetchPermissions,
447
+ invalidatePermission,
448
+ isLoading
449
+ }),
450
+ [
451
+ getPermission,
452
+ checkPermission,
453
+ prefetchPermissions,
454
+ invalidatePermission,
455
+ isLoading
456
+ ]
457
+ );
458
+ return /* @__PURE__ */ jsx2(permissionContext.Provider, { value, children });
459
+ }
460
+ function usePermissions() {
461
+ const context = useContext(permissionContext);
462
+ if (!context || Object.keys(context).length === 0) {
463
+ throw new Error("usePermissions must be used within a PermissionProvider");
464
+ }
465
+ return context;
466
+ }
467
+
468
+ // src/auth/context/AuthProvider.tsx
469
+ import {
470
+ useCallback as useCallback2,
471
+ useEffect as useEffect2,
472
+ useMemo as useMemo2,
473
+ useRef as useRef2,
474
+ useState as useState2
475
+ } from "react";
476
+ import { jsx as jsx3 } from "react/jsx-runtime";
477
+ var profileQuery = typedSupabase?.schema("core").from("Profile").select("*, UserAccess(accessKey), status").single();
478
+ function AuthProvider({
479
+ children,
480
+ enableEntityPermissions = false
481
+ }) {
482
+ const supabase = useSupabase();
483
+ const [currentUser, setCurrentUser] = useState2(
484
+ void 0
485
+ );
486
+ const [userNeedsChange, setUserNeedsChange] = useState2(true);
487
+ const [onSignOutCallbacks, setOnSignOutCallbacks] = useState2(
488
+ /* @__PURE__ */ new Map()
489
+ );
490
+ async function registerAsync(register) {
491
+ const response = await supabase.auth.signUp(register);
492
+ setCurrentUser(response.data.user);
493
+ return response;
494
+ }
495
+ async function signInAsync(username, password) {
496
+ const response = await supabase.auth.signInWithPassword({
497
+ email: username,
498
+ password
499
+ });
500
+ if (response.data) {
501
+ setCurrentUser(response.data.user);
502
+ }
503
+ return response;
504
+ }
505
+ async function signOutAsync() {
506
+ const response = await supabase.auth.signOut();
507
+ if (isUsable(response.error) === false) {
508
+ Array.from(onSignOutCallbacks.values()).forEach((x) => {
509
+ x();
510
+ });
511
+ }
512
+ return response;
513
+ }
514
+ function onSignOut(action) {
515
+ const id = newUuid();
516
+ setOnSignOutCallbacks((x) => new Map(x).set(id, action));
517
+ return id;
518
+ }
519
+ function removeOnSignOut(id) {
520
+ setOnSignOutCallbacks((x) => {
521
+ const map = new Map(x);
522
+ map.delete(id);
523
+ return map;
524
+ });
525
+ }
526
+ async function refreshAsync() {
527
+ }
528
+ useEffect2(() => {
529
+ const request = supabase.auth.onAuthStateChange((event) => {
530
+ if (event === "SIGNED_IN" || event === "SIGNED_OUT") {
531
+ setUserNeedsChange(true);
532
+ }
533
+ });
534
+ return () => {
535
+ request.data.subscription.unsubscribe();
536
+ };
537
+ }, [supabase.auth]);
538
+ useEffect2(() => {
539
+ if (userNeedsChange === false) return;
540
+ supabase.auth.getSession().then((x) => {
541
+ setCurrentUser(x?.data?.session?.user ?? null);
542
+ setUserNeedsChange(false);
543
+ });
544
+ }, [userNeedsChange]);
545
+ const profileRequest = useDbQuery(
546
+ supabase.schema("core").from("Profile").select("*, UserAccess(accessKey), status").eq("id", currentUser?.id).limit(1).maybeSingle(),
547
+ {
548
+ enabled: isUsable(currentUser),
549
+ crossOrganization: true
550
+ }
551
+ );
552
+ const accessKeysRequest = useDbQuery(
553
+ supabase.schema("core").rpc("get_user_access_keys", {
554
+ user_id: currentUser?.id
555
+ }),
556
+ {
557
+ enabled: isUsable(currentUser),
558
+ crossOrganization: true
559
+ }
560
+ );
561
+ const refetchAccessKeys = useCallback2(() => {
562
+ accessKeysRequest.refetch();
563
+ }, [accessKeysRequest.refetch]);
564
+ const userGroupIdsRef = useRef2(/* @__PURE__ */ new Set());
565
+ useEffect2(() => {
566
+ if (accessKeysRequest.data) {
567
+ const groupIds = /* @__PURE__ */ new Set();
568
+ for (const item of accessKeysRequest.data) {
569
+ if (item.source === "group" && item.source_id) {
570
+ groupIds.add(item.source_id);
571
+ }
572
+ }
573
+ userGroupIdsRef.current = groupIds;
574
+ }
575
+ }, [accessKeysRequest.data]);
576
+ useEffect2(() => {
577
+ if (!currentUser?.id) return;
578
+ const channel = supabase.channel(`user-access-keys-${currentUser.id}`).on(
579
+ "postgres_changes",
580
+ {
581
+ event: "*",
582
+ schema: "core",
583
+ table: "UserAccess",
584
+ filter: `userId=eq.${currentUser.id}`
585
+ },
586
+ () => {
587
+ refetchAccessKeys();
588
+ }
589
+ ).on(
590
+ "postgres_changes",
591
+ {
592
+ event: "*",
593
+ schema: "core",
594
+ table: "UserGroup",
595
+ filter: `userId=eq.${currentUser.id}`
596
+ },
597
+ () => {
598
+ refetchAccessKeys();
599
+ }
600
+ ).on(
601
+ "postgres_changes",
602
+ {
603
+ event: "*",
604
+ schema: "core",
605
+ table: "GroupAccessKey"
606
+ },
607
+ (payload) => {
608
+ const groupId = payload.new?.groupId || payload.old?.groupId;
609
+ if (groupId && userGroupIdsRef.current.has(groupId)) {
610
+ refetchAccessKeys();
611
+ }
612
+ }
613
+ ).on(
614
+ "postgres_changes",
615
+ {
616
+ event: "UPDATE",
617
+ schema: "core",
618
+ table: "Group"
619
+ },
620
+ (payload) => {
621
+ const oldActive = payload.old?.isActive;
622
+ const newActive = payload.new?.isActive;
623
+ const groupId = payload.new?.id;
624
+ if (oldActive !== newActive && groupId && userGroupIdsRef.current.has(groupId)) {
625
+ refetchAccessKeys();
626
+ }
627
+ }
628
+ ).subscribe();
629
+ return () => {
630
+ channel.unsubscribe();
631
+ supabase.removeChannel(channel);
632
+ };
633
+ }, [supabase, currentUser?.id, refetchAccessKeys]);
634
+ useEffect2(() => {
635
+ if (!currentUser?.id) return;
636
+ const profileChannel = supabase.channel(`profile-status-${currentUser.id}`).on(
637
+ "postgres_changes",
638
+ {
639
+ event: "UPDATE",
640
+ schema: "core",
641
+ table: "Profile",
642
+ filter: `id=eq.${currentUser.id}`
643
+ },
644
+ (payload) => {
645
+ const newStatus = payload.new?.status;
646
+ const oldStatus = payload.old?.status;
647
+ if (oldStatus === "active" && (newStatus === "archived" || newStatus === "suspended")) {
648
+ signOutAsync();
649
+ }
650
+ profileRequest.refetch();
651
+ }
652
+ ).subscribe();
653
+ return () => {
654
+ profileChannel.unsubscribe();
655
+ supabase.removeChannel(profileChannel);
656
+ };
657
+ }, [supabase, currentUser?.id, profileRequest.refetch]);
658
+ const combinedAccess = useMemo2(() => {
659
+ if (accessKeysRequest.data) {
660
+ const uniqueKeys = /* @__PURE__ */ new Set();
661
+ for (const item of accessKeysRequest.data) {
662
+ if (item.access_key) {
663
+ uniqueKeys.add(item.access_key);
664
+ }
665
+ }
666
+ return Array.from(uniqueKeys);
667
+ }
668
+ return profileRequest.data?.UserAccess?.map((x) => x.accessKey) || [];
669
+ }, [accessKeysRequest.data, profileRequest.data?.UserAccess]);
670
+ const profileStatus = profileRequest.data?.status;
671
+ const isArchived = profileStatus === "archived";
672
+ const isSuspended = profileStatus === "suspended";
673
+ const hasAccess = useCallback2(
674
+ (key) => {
675
+ if (isArchived || isSuspended) {
676
+ return false;
677
+ }
678
+ const accessGiven = combinedAccess;
679
+ if (isUsable(accessGiven) === false) return false;
680
+ if (accessGiven.includes("owner")) return true;
681
+ if (accessGiven.includes(key)) return true;
682
+ if (isUsable(key) === false) return true;
683
+ return false;
684
+ },
685
+ [combinedAccess, isArchived, isSuspended]
686
+ );
687
+ const authStateWithLoading = useMemo2(
688
+ () => ({
689
+ hasAccess,
690
+ user: currentUser,
691
+ profile: profileRequest.data,
692
+ access: combinedAccess,
693
+ profileStatus,
694
+ isArchived,
695
+ isSuspended,
696
+ isLoading: currentUser === null ? false : profileRequest.isLoading || accessKeysRequest.isLoading || currentUser === void 0,
697
+ signInAsync,
698
+ signOutAsync,
699
+ onSignOut,
700
+ removeOnSignOut,
701
+ registerAsync,
702
+ refreshAsync
703
+ }),
704
+ [
705
+ profileRequest.data,
706
+ profileRequest.isLoading,
707
+ accessKeysRequest.data,
708
+ accessKeysRequest.isLoading,
709
+ currentUser,
710
+ combinedAccess,
711
+ profileStatus,
712
+ isArchived,
713
+ isSuspended,
714
+ hasAccess
715
+ ]
716
+ );
717
+ const content = enableEntityPermissions ? /* @__PURE__ */ jsx3(PermissionProvider, { children }) : children;
718
+ return /* @__PURE__ */ jsx3(setupAuthContext.Provider, { value: authStateWithLoading, children: content });
719
+ }
720
+
721
+ // src/auth/context/UserMetadataContext.tsx
722
+ import {
723
+ createContext as createContext3,
724
+ useContext as useContext2,
725
+ useEffect as useEffect3,
726
+ useMemo as useMemo3,
727
+ useState as useState3,
728
+ useCallback as useCallback3,
729
+ useRef as useRef3
730
+ } from "react";
731
+ import { jsx as jsx4 } from "react/jsx-runtime";
732
+ var UserMetadataQuery = {
733
+ schema: "core",
734
+ table: "UserMetadata",
735
+ defaultQuery: "key, userId, value"
736
+ };
737
+ var userMetadataContext = createContext3(null);
738
+ function UserMetadataProvider({ children }) {
739
+ const supabase = useSupabase();
740
+ const [metadata, setMetadataState] = useState3({});
741
+ const [isLoading, setIsLoading] = useState3(true);
742
+ const [error, setError] = useState3(null);
743
+ const setupAuth = useContext2(setupAuthContext);
744
+ const userId = setupAuth?.user?.id;
745
+ const metadataQuery = useDbQuery(
746
+ supabase.schema("core").from("UserMetadata").select(UserMetadataQuery.defaultQuery).eq("userId", userId).order("key"),
747
+ {
748
+ enabled: isUsable(userId),
749
+ crossOrganization: true
750
+ }
751
+ );
752
+ const upsertMutation = useDbUpsert(
753
+ { table: "UserMetadata", schema: "core" },
754
+ ["userId", "key"],
755
+ UserMetadataQuery.defaultQuery
756
+ );
757
+ const upsertMutationRef = useRef3(upsertMutation);
758
+ upsertMutationRef.current = upsertMutation;
759
+ useEffect3(() => {
760
+ if (metadataQuery.data) {
761
+ const metadataMap = {};
762
+ metadataQuery.data.forEach((item) => {
763
+ metadataMap[item.key] = item.value;
764
+ });
765
+ setMetadataState(metadataMap);
766
+ setIsLoading(false);
767
+ setError(null);
768
+ } else if (metadataQuery.error) {
769
+ setError(metadataQuery.error);
770
+ setIsLoading(false);
771
+ } else if (metadataQuery.isLoading) {
772
+ setIsLoading(true);
773
+ }
774
+ }, [metadataQuery.data, metadataQuery.error, metadataQuery.isLoading]);
775
+ const setMetadata = useCallback3(
776
+ async (key, value) => {
777
+ if (!userId) {
778
+ throw new Error("User not authenticated");
779
+ }
780
+ try {
781
+ await upsertMutationRef.current.mutateAsync({
782
+ userId,
783
+ key,
784
+ value
785
+ });
786
+ setMetadataState((prev) => ({
787
+ ...prev,
788
+ [key]: value
789
+ }));
790
+ } catch (err) {
791
+ setError(err);
792
+ throw err;
793
+ }
794
+ },
795
+ [userId]
796
+ );
797
+ const getMetadata = useCallback3(
798
+ (key) => {
799
+ return metadata[key];
800
+ },
801
+ [metadata]
802
+ );
803
+ const removeMetadata = useCallback3(
804
+ async (key) => {
805
+ if (!userId) {
806
+ throw new Error("User not authenticated");
807
+ }
808
+ try {
809
+ await supabase.schema("core").from("UserMetadata").delete().eq("userId", userId).eq("key", key);
810
+ setMetadataState((prev) => {
811
+ const newState = { ...prev };
812
+ delete newState[key];
813
+ return newState;
814
+ });
815
+ } catch (err) {
816
+ setError(err);
817
+ throw err;
818
+ }
819
+ },
820
+ [userId, supabase]
821
+ );
822
+ const refreshMetadata = useCallback3(async () => {
823
+ await metadataQuery.refetch();
824
+ }, [metadataQuery]);
825
+ const contextValue = useMemo3(
826
+ () => ({
827
+ metadata,
828
+ isLoading,
829
+ error,
830
+ setMetadata,
831
+ getMetadata,
832
+ removeMetadata,
833
+ refreshMetadata
834
+ }),
835
+ [
836
+ metadata,
837
+ isLoading,
838
+ error,
839
+ setMetadata,
840
+ getMetadata,
841
+ removeMetadata,
842
+ refreshMetadata
843
+ ]
844
+ );
845
+ return /* @__PURE__ */ jsx4(userMetadataContext.Provider, { value: contextValue, children });
846
+ }
847
+ function useUserMetadata() {
848
+ const context = useContext2(userMetadataContext);
849
+ if (!context) {
850
+ throw new Error(
851
+ "useUserMetadata must be used within a UserMetadataProvider"
852
+ );
853
+ }
854
+ return context;
855
+ }
856
+ function useUserMetadataValue(key) {
857
+ const { getMetadata } = useUserMetadata();
858
+ return getMetadata(key);
859
+ }
860
+ function useSetUserMetadata() {
861
+ const { setMetadata, removeMetadata } = useUserMetadata();
862
+ return { setMetadata, removeMetadata };
863
+ }
864
+ function useUserMetadataState(key, defaultValue, options) {
865
+ const { metadata, setMetadata, isLoading } = useUserMetadata();
866
+ const serialize = options?.serialize ?? ((value) => JSON.stringify(value));
867
+ const deserialize = options?.deserialize ?? ((value) => JSON.parse(value));
868
+ const currentValue = useMemo3(() => {
869
+ const rawValue = metadata[key];
870
+ if (!rawValue) return defaultValue;
871
+ try {
872
+ return deserialize(rawValue);
873
+ } catch (error) {
874
+ console.warn(`Failed to deserialize metadata for key "${key}":`, error);
875
+ return defaultValue;
876
+ }
877
+ }, [metadata, key, defaultValue, deserialize, isLoading]);
878
+ const setValue = useCallback3(
879
+ async (value) => {
880
+ const serializedValue = serialize(value);
881
+ await setMetadata(key, serializedValue);
882
+ },
883
+ [key, setMetadata, serialize]
884
+ );
885
+ return [currentValue, setValue, isLoading];
886
+ }
887
+
888
+ export {
889
+ setupAuthContext,
890
+ SetupAuthContextProvider,
891
+ permissionContext,
892
+ entityPermissionContext,
893
+ PermissionProvider,
894
+ usePermissions,
895
+ AuthProvider,
896
+ userMetadataContext,
897
+ UserMetadataProvider,
898
+ useUserMetadata,
899
+ useUserMetadataValue,
900
+ useSetUserMetadata,
901
+ useUserMetadataState
902
+ };
903
+ //# sourceMappingURL=chunk-ERGF2FCE.js.map