@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.
- package/dist/auth/context.js +21 -12786
- package/dist/auth/context.js.map +1 -1
- package/dist/auth/guards.js +12 -7640
- package/dist/auth/guards.js.map +1 -1
- package/dist/auth/hooks.js +25 -10591
- package/dist/auth/hooks.js.map +1 -1
- package/dist/auth/index.js +43 -13008
- package/dist/auth/index.js.map +1 -1
- package/dist/canvas-75Y7XMF3.js +1541 -0
- package/dist/canvas-75Y7XMF3.js.map +1 -0
- package/dist/chunk-2IFGILT3.js +532 -0
- package/dist/chunk-2IFGILT3.js.map +1 -0
- package/dist/chunk-3M2U6TXH.js +928 -0
- package/dist/chunk-3M2U6TXH.js.map +1 -0
- package/dist/chunk-3PJTNH2L.js +2778 -0
- package/dist/chunk-3PJTNH2L.js.map +1 -0
- package/dist/chunk-5ZYAEGCJ.js +416 -0
- package/dist/chunk-5ZYAEGCJ.js.map +1 -0
- package/dist/chunk-7HG6G25H.js +710 -0
- package/dist/chunk-7HG6G25H.js.map +1 -0
- package/dist/chunk-7XT7K4QT.js +2687 -0
- package/dist/chunk-7XT7K4QT.js.map +1 -0
- package/dist/chunk-AWFMICFV.js +158 -0
- package/dist/chunk-AWFMICFV.js.map +1 -0
- package/dist/chunk-BRTW7CO5.js +1467 -0
- package/dist/chunk-BRTW7CO5.js.map +1 -0
- package/dist/chunk-EL45Z26M.js +4194 -0
- package/dist/chunk-EL45Z26M.js.map +1 -0
- package/dist/chunk-ERGF2FCE.js +903 -0
- package/dist/chunk-ERGF2FCE.js.map +1 -0
- package/dist/chunk-GK7B66LY.js +135 -0
- package/dist/chunk-GK7B66LY.js.map +1 -0
- package/dist/chunk-GQI6WJGI.js +172 -0
- package/dist/chunk-GQI6WJGI.js.map +1 -0
- package/dist/chunk-H6365JPC.js +1858 -0
- package/dist/chunk-H6365JPC.js.map +1 -0
- package/dist/chunk-J4ZVCXZ4.js +1 -0
- package/dist/chunk-J4ZVCXZ4.js.map +1 -0
- package/dist/chunk-JUVE3DWY.js +433 -0
- package/dist/chunk-JUVE3DWY.js.map +1 -0
- package/dist/chunk-O3K7R32P.js +7555 -0
- package/dist/chunk-O3K7R32P.js.map +1 -0
- package/dist/chunk-P4UZ7IXC.js +42 -0
- package/dist/chunk-P4UZ7IXC.js.map +1 -0
- package/dist/chunk-SEY5UO2T.js +89 -0
- package/dist/chunk-SEY5UO2T.js.map +1 -0
- package/dist/chunk-USJYMRUO.js +86 -0
- package/dist/chunk-USJYMRUO.js.map +1 -0
- package/dist/chunk-XX3IWSPM.js +189 -0
- package/dist/chunk-XX3IWSPM.js.map +1 -0
- package/dist/chunk-Y3INY2CS.js +14 -0
- package/dist/chunk-Y3INY2CS.js.map +1 -0
- package/dist/chunk-ZTSBF536.js +1927 -0
- package/dist/chunk-ZTSBF536.js.map +1 -0
- package/dist/client/index.js +13 -141
- package/dist/client/index.js.map +1 -1
- package/dist/dist-NDNRSNOG.js +521 -0
- package/dist/dist-NDNRSNOG.js.map +1 -0
- package/dist/gen/index.js +186 -1280
- package/dist/gen/index.js.map +1 -1
- package/dist/hooks/index.js +21 -8694
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.js +403 -47848
- package/dist/index.js.map +1 -1
- package/dist/index.native.js +400 -25048
- package/dist/index.native.js.map +1 -1
- package/dist/index.web.js +576 -43769
- package/dist/index.web.js.map +1 -1
- package/dist/mutation/index.js +44 -4675
- package/dist/mutation/index.js.map +1 -1
- package/dist/parser/index.js +45 -3697
- package/dist/parser/index.js.map +1 -1
- package/dist/pdf-3TIGQRLA.js +20336 -0
- package/dist/pdf-3TIGQRLA.js.map +1 -0
- package/dist/query/index.js +31 -13175
- package/dist/query/index.js.map +1 -1
- package/dist/realtime/index.js +45 -12431
- package/dist/realtime/index.js.map +1 -1
- package/dist/types/index.js +9 -0
- 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
|