@pol-studios/db 1.0.10 → 1.0.12

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