@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,2687 @@
1
+ import {
2
+ normalizeFilter,
3
+ useDbAdvanceFilterQuery
4
+ } from "./chunk-7HG6G25H.js";
5
+ import {
6
+ OperationLog
7
+ } from "./chunk-BRTW7CO5.js";
8
+ import {
9
+ useDbQuery,
10
+ useDbUpsert
11
+ } from "./chunk-GK7B66LY.js";
12
+ import {
13
+ getSupabaseUrl
14
+ } from "./chunk-Y3INY2CS.js";
15
+ import {
16
+ PostgrestParser
17
+ } from "./chunk-JUVE3DWY.js";
18
+ import {
19
+ chunkArray,
20
+ diff,
21
+ getObjectChanges,
22
+ isNullOrWhitespace,
23
+ isUsable,
24
+ newUuid,
25
+ omit,
26
+ require_moment
27
+ } from "./chunk-O3K7R32P.js";
28
+ import {
29
+ buildNormalizedQuery,
30
+ encode,
31
+ normalizeResponse,
32
+ useDeleteItem,
33
+ useDeleteManyMutation,
34
+ useInsertMutation,
35
+ useQueriesForTableLoader,
36
+ useQuery,
37
+ useUpsertItem
38
+ } from "./chunk-H6365JPC.js";
39
+ import {
40
+ UserSessionId,
41
+ useSupabase
42
+ } from "./chunk-AWFMICFV.js";
43
+ import {
44
+ __require,
45
+ __toESM
46
+ } from "./chunk-P4UZ7IXC.js";
47
+
48
+ // src/useDbDelete.ts
49
+ import { useMutation } from "@tanstack/react-query";
50
+ function useDbDelete(relation, primaryKeys = ["id"]) {
51
+ const tableName = typeof relation === "object" ? relation.table : relation;
52
+ const schemaName = typeof relation === "object" ? relation.schema : "public";
53
+ const supabase = useSupabase();
54
+ const deleteItem = useDeleteItem({
55
+ primaryKeys: primaryKeys.map((k) => String(k)),
56
+ table: tableName,
57
+ schema: schemaName
58
+ });
59
+ const deleteMutation = useMutation({
60
+ mutationFn: async (item) => {
61
+ if (Array.isArray(item)) {
62
+ throw new Error("Item cannot be an array.");
63
+ }
64
+ const deleteQuery = supabase.schema(schemaName).from(tableName).delete();
65
+ primaryKeys.forEach((key) => {
66
+ const keyStr = String(key);
67
+ const value = item[keyStr];
68
+ if (isUsable(value)) {
69
+ deleteQuery.eq(keyStr, value);
70
+ }
71
+ });
72
+ const response = await deleteQuery.select().single();
73
+ if (response.error) {
74
+ throw response.error;
75
+ }
76
+ if (response.data) {
77
+ deleteItem(response.data);
78
+ } else {
79
+ deleteItem(item);
80
+ }
81
+ return response.data;
82
+ }
83
+ });
84
+ return deleteMutation;
85
+ }
86
+
87
+ // src/useDbInfiniteQuery.ts
88
+ import {
89
+ useInfiniteQuery
90
+ } from "@tanstack/react-query";
91
+ import { useMemo, useRef } from "react";
92
+ function useDbInfiniteQuery(query, countPerLoad, config) {
93
+ const initialQueryKey = encode(query, false).join("-");
94
+ const lastKnownQuery = useRef(initialQueryKey);
95
+ const currentPageNumber = useRef(1);
96
+ if (lastKnownQuery.current != initialQueryKey) {
97
+ lastKnownQuery.current = initialQueryKey;
98
+ currentPageNumber.current = 1;
99
+ }
100
+ const isFetching = useRef(false);
101
+ const queryKey = useMemo(
102
+ () => encode(query, false),
103
+ [initialQueryKey, config?.crossOrganization]
104
+ );
105
+ const getQuery = useInfiniteQuery({
106
+ ...config,
107
+ queryKey,
108
+ queryFn: async ({ pageParam, signal }) => {
109
+ let adjustableQuery = query;
110
+ const pageNumber = pageParam;
111
+ if (config?.onQuery && config?.enableOnQuery) {
112
+ config?.onQuery({ query: adjustableQuery, pageParam: pageNumber });
113
+ } else {
114
+ adjustableQuery = adjustableQuery.range(
115
+ (pageNumber - 1) * countPerLoad,
116
+ pageNumber * countPerLoad - 1
117
+ );
118
+ }
119
+ adjustableQuery = adjustableQuery.abortSignal(signal);
120
+ updatedCache.current = false;
121
+ const response = await adjustableQuery;
122
+ currentPageNumber.current = pageNumber;
123
+ if (response.error) {
124
+ throw response.error;
125
+ } else {
126
+ return response;
127
+ }
128
+ },
129
+ initialPageParam: 1,
130
+ getNextPageParam: (response, allResponses, lastParam) => {
131
+ const pageParam = lastParam;
132
+ const resp = response;
133
+ if (allResponses.length * countPerLoad >= (resp?.count ?? 0)) {
134
+ return void 0;
135
+ }
136
+ return pageParam + 1;
137
+ }
138
+ });
139
+ const updatedCache = useRef(true);
140
+ return useMemo(
141
+ () => {
142
+ const pages = getQuery.data?.pages;
143
+ return {
144
+ ...getQuery,
145
+ count: pages?.[pages.length - 1]?.count,
146
+ data: pages?.flatMap((x) => x.data ?? [])
147
+ };
148
+ },
149
+ [getQuery.data, currentPageNumber.current]
150
+ );
151
+ }
152
+
153
+ // src/useDbInsert.ts
154
+ import { useMutation as useMutation2 } from "@tanstack/react-query";
155
+ function useDbInsert(relation, primaryKeys = ["id"], query, mutationOption) {
156
+ const supabase = useSupabase();
157
+ const tableName = typeof relation === "object" ? relation.table : relation;
158
+ const schemaName = typeof relation === "object" ? String(relation.schema) : "public";
159
+ const primaryKeysAsStrings = primaryKeys.map((k) => String(k));
160
+ const mutation = useInsertMutation(
161
+ supabase.schema(schemaName).from(tableName),
162
+ primaryKeysAsStrings,
163
+ query,
164
+ mutationOption
165
+ );
166
+ const updateMutation = useMutation2({
167
+ mutationFn: async (item) => {
168
+ const primaryKeysNull = primaryKeys.filter((x) => x in item === false || isUsable(item[x]) === false).map((k) => String(k));
169
+ const response = await mutation.mutateAsync([
170
+ omit(item, primaryKeysNull)
171
+ ]);
172
+ mutation.reset();
173
+ return response?.[0];
174
+ }
175
+ });
176
+ return updateMutation;
177
+ }
178
+
179
+ // src/useDbMultiDelete.ts
180
+ import { useMutation as useMutation3 } from "@tanstack/react-query";
181
+ function useDbMultiDelete(relation, primaryKeys = ["id"]) {
182
+ const tableName = typeof relation === "object" ? relation.table : relation;
183
+ const schemaName = typeof relation === "object" ? String(relation.schema) : "public";
184
+ const supabase = useSupabase();
185
+ const primaryKeysAsStrings = primaryKeys.map((k) => String(k));
186
+ const deleteManyMutation = useDeleteManyMutation(
187
+ supabase.schema(schemaName).from(tableName),
188
+ primaryKeysAsStrings
189
+ );
190
+ const deleteMutation = useMutation3({
191
+ mutationFn: async (item) => {
192
+ if (Array.isArray(item)) {
193
+ const response = await deleteManyMutation.mutateAsync(item);
194
+ return response;
195
+ } else {
196
+ throw new Error("Item must be an array.");
197
+ }
198
+ }
199
+ });
200
+ return deleteMutation;
201
+ }
202
+
203
+ // src/useDbMultiUpsert.ts
204
+ import { useMutation as useMutation4 } from "@tanstack/react-query";
205
+ function useDbMultiUpsert(relation, primaryKeys = ["id"], query = "*") {
206
+ const supabase = useSupabase();
207
+ const tableName = typeof relation === "object" ? relation.table : relation;
208
+ const schemaName = typeof relation === "object" ? String(relation.schema) : "public";
209
+ const upsertItem = useUpsertItem({
210
+ primaryKeys,
211
+ table: relation,
212
+ schema: "public"
213
+ });
214
+ const primaryKeysAsStrings = primaryKeys.map((k) => String(k));
215
+ async function upsertItemAction(item) {
216
+ let task;
217
+ if (Object.keys(item).length !== primaryKeys.length && primaryKeys.every((key) => key in item && isUsable(item[key]))) {
218
+ const updateObject = omit(item, primaryKeysAsStrings);
219
+ task = supabase.schema(schemaName).from(tableName).update(updateObject);
220
+ primaryKeys.forEach((key) => {
221
+ task = task.eq(String(key), item[key]);
222
+ });
223
+ } else {
224
+ task = supabase.schema(schemaName).from(tableName).insert(omit(item, primaryKeysAsStrings));
225
+ }
226
+ const response = await task.select(query);
227
+ if (response.error) {
228
+ throw response.error;
229
+ }
230
+ return response.data[0];
231
+ }
232
+ const updateMutation = useMutation4({
233
+ mutationFn: async (item) => {
234
+ if (Array.isArray(item)) {
235
+ const data = await Promise.all(item.map((x) => upsertItemAction(x)));
236
+ data.filter((x) => isUsable(x)).forEach((x) => upsertItem(x));
237
+ return data;
238
+ }
239
+ throw new Error("Item must be an array.");
240
+ }
241
+ });
242
+ return updateMutation;
243
+ }
244
+
245
+ // src/useDbPartialAdvanceQuery.ts
246
+ import { useLayoutEffect, useMemo as useMemo2, useState } from "react";
247
+ import { useSessionStorageState } from "@pol-studios/hooks/storage";
248
+ function useDbPartialAdvanceQuery(query, itemCountPerPage, config) {
249
+ const initialQuery = encode(query, false);
250
+ const [id, setId] = useState(window.location.pathname);
251
+ const [currentPage, setCurrentPage] = useSessionStorageState(
252
+ `${id}-currentPage`,
253
+ 1
254
+ );
255
+ const rangedQuery = useMemo2(
256
+ () => {
257
+ const page = currentPage ?? 1;
258
+ return query.range(
259
+ (page - 1) * itemCountPerPage,
260
+ page * itemCountPerPage - 1
261
+ );
262
+ },
263
+ [query, currentPage, itemCountPerPage]
264
+ );
265
+ const [baseQuery, filter, setFilters] = useDbAdvanceFilterQuery(
266
+ rangedQuery,
267
+ {
268
+ ...config,
269
+ filterKey: config?.filterKey,
270
+ count: "exact",
271
+ key: (currentPage ?? 1).toString()
272
+ }
273
+ );
274
+ const filterKey = JSON.stringify(omit(filter, ["pagination"]));
275
+ const select = initialQuery[4].split("&").find(
276
+ (x) => x.startsWith("select=")
277
+ );
278
+ useLayoutEffect(() => {
279
+ const newId = [
280
+ initialQuery[3],
281
+ select,
282
+ initialQuery[5],
283
+ initialQuery[6],
284
+ initialQuery[7],
285
+ initialQuery[8],
286
+ filterKey
287
+ ].join("-");
288
+ console.log({ newId, id });
289
+ setId(newId);
290
+ }, [
291
+ initialQuery[3],
292
+ select,
293
+ initialQuery[5],
294
+ initialQuery[6],
295
+ initialQuery[7],
296
+ initialQuery[8],
297
+ filterKey
298
+ ]);
299
+ const safeFetchNextPage = () => {
300
+ setCurrentPage((currentPage2) => (currentPage2 ?? 1) + 1);
301
+ };
302
+ const fetchPreviousPage = () => {
303
+ setCurrentPage((currentPage2) => (currentPage2 ?? 1) - 1);
304
+ };
305
+ const pageCount = Math.max(
306
+ Math.ceil(
307
+ (baseQuery.count ?? 0) / itemCountPerPage
308
+ ),
309
+ 1
310
+ );
311
+ const request = {
312
+ ...baseQuery,
313
+ clarification: baseQuery.clarification,
314
+ // Explicitly pass through clarification
315
+ fetchPreviousPage,
316
+ fetchNextPage: safeFetchNextPage,
317
+ currentPage: currentPage ?? 1,
318
+ setCurrentPage,
319
+ data: baseQuery.data ? toPagedResponse(
320
+ baseQuery.data,
321
+ currentPage ?? 1,
322
+ baseQuery.count ?? baseQuery.data.length,
323
+ itemCountPerPage
324
+ ) : null,
325
+ pageCount,
326
+ hasNextPage: (currentPage ?? 1) < pageCount,
327
+ hasPreviousPage: (currentPage ?? 1) > 1,
328
+ count: baseQuery.count ?? baseQuery.data?.length
329
+ };
330
+ return [
331
+ request,
332
+ filter,
333
+ setFilters
334
+ ];
335
+ }
336
+ function toPagedResponse(results, currentPage, totalCount, itemPerPage) {
337
+ const newPage = {
338
+ Items: results,
339
+ CurrentPage: currentPage,
340
+ ItemCount: totalCount,
341
+ MaxCountPerPage: itemPerPage,
342
+ PageCount: Math.max(Math.ceil(totalCount / itemPerPage), 1)
343
+ };
344
+ return newPage;
345
+ }
346
+
347
+ // src/useDbPartialQuery.ts
348
+ import { useMemo as useMemo3 } from "react";
349
+ import { useSessionStorageState as useSessionStorageState2 } from "@pol-studios/hooks/storage";
350
+ function useDbPartialQuery(query, itemCountPerPage, config) {
351
+ const initialQuery = encode(query, false);
352
+ const id = useMemo3(
353
+ () => [
354
+ initialQuery[7],
355
+ initialQuery[8]
356
+ ].join("-"),
357
+ [
358
+ initialQuery[7],
359
+ initialQuery[8]
360
+ ]
361
+ );
362
+ const [currentPage, setCurrentPage] = useSessionStorageState2(id, 1);
363
+ const rangedQuery = useMemo3(
364
+ () => {
365
+ const page = currentPage ?? 1;
366
+ return query.range(
367
+ (page - 1) * itemCountPerPage,
368
+ page * itemCountPerPage - 1
369
+ );
370
+ },
371
+ [query, currentPage, itemCountPerPage]
372
+ );
373
+ const baseQuery = useDbQuery(rangedQuery, config);
374
+ const safeFetchNextPage = () => {
375
+ setCurrentPage((currentPage2) => currentPage2 + 1);
376
+ };
377
+ const fetchPreviousPage = () => {
378
+ setCurrentPage((currentPage2) => currentPage2 - 1);
379
+ };
380
+ const pageCount = Math.max(
381
+ Math.ceil(
382
+ (baseQuery.count ?? 0) / itemCountPerPage
383
+ ),
384
+ 1
385
+ );
386
+ return {
387
+ ...baseQuery,
388
+ fetchPreviousPage,
389
+ fetchNextPage: safeFetchNextPage,
390
+ currentPage: currentPage ?? 1,
391
+ setCurrentPage,
392
+ data: baseQuery.data ? toPagedResponse2(
393
+ baseQuery.data,
394
+ currentPage ?? 1,
395
+ baseQuery.count ?? baseQuery.data.length,
396
+ itemCountPerPage
397
+ ) : null,
398
+ pageCount,
399
+ hasNextPage: (currentPage ?? 1) < pageCount,
400
+ hasPreviousPage: (currentPage ?? 1) > 1
401
+ };
402
+ }
403
+ function toPagedResponse2(results, currentPage, totalCount, itemPerPage) {
404
+ const newPage = {
405
+ Items: results,
406
+ CurrentPage: currentPage,
407
+ ItemCount: totalCount,
408
+ MaxCountPerPage: itemPerPage,
409
+ PageCount: Math.max(Math.ceil(totalCount / itemPerPage), 1)
410
+ };
411
+ return newPage;
412
+ }
413
+
414
+ // src/useDbRealtime.ts
415
+ import { useEffect, useState as useState2, useRef as useRef2 } from "react";
416
+ import {
417
+ REALTIME_POSTGRES_CHANGES_LISTEN_EVENT
418
+ } from "@supabase/supabase-js";
419
+ import { useDelayedValue } from "@pol-studios/hooks/state";
420
+ function useDbRealtime(key, query, table, primaryKeys, options) {
421
+ const supabase = useSupabase();
422
+ const tableNameValue = typeof table === "string" ? table : table.table;
423
+ const schemaValue = typeof table === "string" ? "public" : table.schema;
424
+ const queriesForTable = useQueriesForTableLoader(tableNameValue);
425
+ const deleteItem = useDeleteItem({
426
+ primaryKeys,
427
+ table: tableNameValue,
428
+ schema: schemaValue
429
+ });
430
+ const upsertItem = useUpsertItem({
431
+ primaryKeys,
432
+ table: tableNameValue,
433
+ schema: schemaValue
434
+ });
435
+ const [realtimeState, setRealtimeState] = useState2("CLOSED");
436
+ const [retryTick, setRetryTick] = useState2(0);
437
+ const channelRef = useRef2(null);
438
+ const lastRetryTimeRef = useRef2(0);
439
+ const retryCountRef = useRef2(0);
440
+ const enabled = options?.enabled ?? true;
441
+ const filter = options?.filter;
442
+ const onChange = options?.onChange;
443
+ const debouncedKey = useDelayedValue(key, 50);
444
+ useEffect(() => {
445
+ if (!enabled) {
446
+ if (channelRef.current) {
447
+ channelRef.current.unsubscribe();
448
+ supabase.removeChannel(channelRef.current);
449
+ channelRef.current = null;
450
+ }
451
+ setRealtimeState("CLOSED");
452
+ return;
453
+ }
454
+ const now = Date.now();
455
+ const timeSinceLastRetry = now - lastRetryTimeRef.current;
456
+ const minRetryInterval = 2e3;
457
+ if (timeSinceLastRetry < minRetryInterval && retryTick > 0) {
458
+ return;
459
+ }
460
+ if (channelRef.current) {
461
+ channelRef.current.unsubscribe();
462
+ supabase.removeChannel(channelRef.current);
463
+ channelRef.current = null;
464
+ }
465
+ lastRetryTimeRef.current = now;
466
+ retryCountRef.current += 1;
467
+ const channel = supabase.channel(newUuid()).on(
468
+ "postgres_changes",
469
+ {
470
+ event: "*",
471
+ schema: schemaValue,
472
+ table: tableNameValue,
473
+ filter
474
+ },
475
+ async (payload) => {
476
+ let data = payload.new ?? payload.old;
477
+ if (payload.new && Object.keys(payload.new).length > 0) {
478
+ const selectQuery = buildNormalizedQuery({
479
+ queriesForTable,
480
+ query
481
+ });
482
+ if (payload.eventType !== REALTIME_POSTGRES_CHANGES_LISTEN_EVENT.DELETE && selectQuery) {
483
+ if (selectQuery.groupedUserQueryPaths?.every(
484
+ (x) => x.path in data
485
+ ) === false) {
486
+ const qb = supabase.schema(payload.schema).from(payload.table).select(selectQuery.selectQuery);
487
+ for (const pk of primaryKeys) {
488
+ qb.eq(pk.toString(), data[pk]);
489
+ }
490
+ const res = await qb.single();
491
+ if (res.data) {
492
+ data = normalizeResponse(
493
+ selectQuery.groupedPaths,
494
+ res.data
495
+ );
496
+ }
497
+ }
498
+ }
499
+ if ("changedBySessionId" in data) {
500
+ const sessionId = data["changedBySessionId"];
501
+ if (sessionId !== UserSessionId) {
502
+ await upsertItem(data);
503
+ }
504
+ } else {
505
+ await upsertItem(data);
506
+ }
507
+ }
508
+ if (payload.eventType === REALTIME_POSTGRES_CHANGES_LISTEN_EVENT.DELETE) {
509
+ await deleteItem(payload.old);
510
+ }
511
+ if (payload.errors?.length > 0) {
512
+ setRealtimeState("CHANNEL_ERROR");
513
+ }
514
+ if (onChange) {
515
+ onChange(payload);
516
+ }
517
+ }
518
+ ).on(
519
+ "postgres_changes",
520
+ {
521
+ event: "DELETE",
522
+ schema: schemaValue,
523
+ table: tableNameValue
524
+ },
525
+ async (payload) => {
526
+ if (payload.eventType === REALTIME_POSTGRES_CHANGES_LISTEN_EVENT.DELETE) {
527
+ await deleteItem(payload.old);
528
+ }
529
+ }
530
+ ).subscribe((status) => {
531
+ setRealtimeState(status);
532
+ if (status === "SUBSCRIBED") {
533
+ retryCountRef.current = 0;
534
+ }
535
+ });
536
+ channelRef.current = channel;
537
+ return () => {
538
+ if (channelRef.current) {
539
+ channelRef.current.unsubscribe();
540
+ supabase.removeChannel(channelRef.current);
541
+ channelRef.current = null;
542
+ }
543
+ };
544
+ }, [
545
+ supabase,
546
+ tableNameValue,
547
+ schemaValue,
548
+ enabled,
549
+ filter,
550
+ query,
551
+ primaryKeys.join(","),
552
+ retryTick,
553
+ debouncedKey
554
+ ]);
555
+ useEffect(() => {
556
+ if (!enabled || realtimeState === "SUBSCRIBED") {
557
+ retryCountRef.current = 0;
558
+ return;
559
+ }
560
+ const baseDelay = 5e3;
561
+ const maxDelay = 6e4;
562
+ const delay = Math.min(
563
+ baseDelay * Math.pow(2, retryCountRef.current),
564
+ maxDelay
565
+ );
566
+ const id = setTimeout(() => {
567
+ setRetryTick((t) => t + 1);
568
+ }, delay);
569
+ return () => clearTimeout(id);
570
+ }, [realtimeState, enabled]);
571
+ return realtimeState;
572
+ }
573
+
574
+ // src/useDbRealtimeQuery.tsx
575
+ import { useMemo as useMemo4 } from "react";
576
+ function convertFilterToRealtimeQuery(filters) {
577
+ function convert(filter) {
578
+ if (!filter) return "";
579
+ if ("path" in filter) {
580
+ if (filter.path.includes(".")) return "";
581
+ let valueString = filter.value?.toString() ?? "null";
582
+ if (filter.operator === "in") {
583
+ valueString = '("' + valueString.slice(2, valueString.length - 2).split(",").join('","') + '")';
584
+ }
585
+ return `${filter.path}=${filter.negate ? "not." : ""}${filter.operator}.${valueString}`;
586
+ } else {
587
+ if (filter.or) {
588
+ return `(${filter.or.map((f) => convert(f)).join("|")})`;
589
+ } else if (filter.and) {
590
+ return `(${filter.and.map((f) => convert(f)).join("&")})`;
591
+ }
592
+ }
593
+ return "";
594
+ }
595
+ return convert(filters[0]);
596
+ }
597
+ function useDbRealtimeQuery(query, config, primaryKeys = ["id"]) {
598
+ const parser = useMemo4(
599
+ () => new PostgrestParser(query),
600
+ [query]
601
+ );
602
+ const request = useQuery(
603
+ query,
604
+ useMemo4(() => config, [config])
605
+ );
606
+ const queryKey = encode(query, false);
607
+ const filterString = convertFilterToRealtimeQuery(parser.filters);
608
+ let filter = request.data && typeof request.data === "object" && "id" in request.data ? "id=eq." + request.data.id : filterString;
609
+ if (filter.includes('in.(\\"\\")')) {
610
+ filter = void 0;
611
+ }
612
+ if (isNullOrWhitespace(filter)) {
613
+ filter = void 0;
614
+ }
615
+ const selectStatement = parser.select;
616
+ const realtimeStatus = useDbRealtime(
617
+ queryKey.join("-"),
618
+ selectStatement,
619
+ { schema: parser.schema, table: parser.table },
620
+ primaryKeys,
621
+ {
622
+ filter,
623
+ enabled: typeof config?.enabled === "boolean" ? config.enabled : true
624
+ }
625
+ );
626
+ const outputRealtimeStatus = request.isFetching ? "Loading..." : realtimeStatus;
627
+ return {
628
+ ...request,
629
+ data: request.data,
630
+ realtimeStatus: outputRealtimeStatus,
631
+ isRealtimeConnected: realtimeStatus == "SUBSCRIBED",
632
+ isRealtimeLoading: request.isFetching
633
+ };
634
+ }
635
+
636
+ // src/useDbUpdate.ts
637
+ import { useMutation as useMutation5 } from "@tanstack/react-query";
638
+ function useDbUpdate(relation, primaryKeys = ["id"], query, mutationOption) {
639
+ const supabase = useSupabase();
640
+ const tableName = typeof relation === "object" ? relation.table : relation;
641
+ const schemaName = typeof relation === "object" ? String(relation.schema) : "public";
642
+ const primaryKeysAsStrings = primaryKeys.map((k) => String(k));
643
+ const upsertItem = useUpsertItem({
644
+ primaryKeys: primaryKeysAsStrings,
645
+ table: relation,
646
+ schema: "public"
647
+ });
648
+ const updateMutation = useMutation5({
649
+ mutationFn: async (item) => {
650
+ let response = null;
651
+ if (primaryKeys.every((x) => x in item && isUsable(item[x]))) {
652
+ const query2 = supabase.schema(schemaName).from(tableName).update(omit(item, primaryKeysAsStrings));
653
+ primaryKeys.forEach((x) => {
654
+ query2.eq(String(x), item[x]);
655
+ });
656
+ const queryResponse = await query2.select().single().throwOnError();
657
+ if (queryResponse.data) {
658
+ response = queryResponse.data;
659
+ upsertItem(response);
660
+ }
661
+ }
662
+ return response;
663
+ }
664
+ });
665
+ return updateMutation;
666
+ }
667
+
668
+ // src/useMutationSuccess.ts
669
+ import { useEffect as useEffect3, useRef as useRef3 } from "react";
670
+ function useMutationSuccess(mutation, options = {}) {
671
+ const {
672
+ successMessage,
673
+ entityName,
674
+ enabled = true,
675
+ onSuccess
676
+ } = options;
677
+ const previousSuccessRef = useRef3(false);
678
+ const previousDataRef = useRef3(void 0);
679
+ useEffect3(() => {
680
+ if (!enabled) return;
681
+ const isSuccess = mutation.isSuccess;
682
+ const hasNewData = mutation.data !== previousDataRef.current;
683
+ if (isSuccess && hasNewData && !previousSuccessRef.current) {
684
+ const message = successMessage || getDefaultSuccessMessage(entityName);
685
+ if (typeof window !== "undefined") {
686
+ try {
687
+ const { toast } = __require("@pol-studios/hooks");
688
+ toast.toast({
689
+ title: "Success",
690
+ description: message
691
+ });
692
+ } catch {
693
+ }
694
+ } else {
695
+ try {
696
+ const { Toast } = __require("toastify-react-native");
697
+ Toast.success(message);
698
+ } catch {
699
+ }
700
+ }
701
+ onSuccess?.();
702
+ }
703
+ previousSuccessRef.current = isSuccess;
704
+ previousDataRef.current = mutation.data;
705
+ }, [mutation.isSuccess, mutation.data, enabled, successMessage, entityName, onSuccess]);
706
+ return mutation;
707
+ }
708
+ function getDefaultSuccessMessage(entityName) {
709
+ if (!entityName) {
710
+ return "Saved successfully";
711
+ }
712
+ return `${entityName} saved successfully`;
713
+ }
714
+ function useMutationSuccessRN(mutation, options = {}) {
715
+ return useMutationSuccess(mutation, options);
716
+ }
717
+
718
+ // src/adapters/types.ts
719
+ var ADAPTER_STRATEGIES = {
720
+ POWERSYNC: "powersync",
721
+ SUPABASE: "supabase",
722
+ CACHED: "cached",
723
+ HYBRID: "hybrid",
724
+ AUTO: "auto"
725
+ };
726
+
727
+ // src/adapters/registry.ts
728
+ var AdapterRegistry = class {
729
+ /**
730
+ * Create a new adapter registry
731
+ *
732
+ * @param config - Data layer configuration with table strategies
733
+ */
734
+ constructor(config) {
735
+ this.config = config;
736
+ }
737
+ /**
738
+ * Cache of created adapters by table name
739
+ */
740
+ adapters = /* @__PURE__ */ new Map();
741
+ /**
742
+ * PowerSync adapter instance (set during initialization)
743
+ */
744
+ powerSyncAdapter = null;
745
+ /**
746
+ * Supabase adapter instance (set during initialization)
747
+ */
748
+ supabaseAdapter = null;
749
+ /**
750
+ * Cached adapter instance (wraps Supabase with TanStack Query)
751
+ */
752
+ cachedAdapter = null;
753
+ /**
754
+ * Dependencies for creating adapters
755
+ */
756
+ deps = null;
757
+ /**
758
+ * Whether the registry has been initialized with adapters
759
+ */
760
+ _isInitialized = false;
761
+ /**
762
+ * Auto-detector instance for automatic backend selection
763
+ */
764
+ autoDetector = null;
765
+ /**
766
+ * Listeners for backend change events
767
+ */
768
+ backendChangeListeners = /* @__PURE__ */ new Set();
769
+ /**
770
+ * Last auto-detection result for debugging and status
771
+ */
772
+ lastDetectionResult = null;
773
+ // ===========================================================================
774
+ // Initialization
775
+ // ===========================================================================
776
+ /**
777
+ * Check if the registry has been initialized
778
+ */
779
+ get isInitialized() {
780
+ return this._isInitialized;
781
+ }
782
+ /**
783
+ * Initialize the registry with dependencies.
784
+ * Called by DataLayerProvider when PowerSync and Supabase are ready.
785
+ *
786
+ * @param deps - Dependencies needed to create adapters
787
+ */
788
+ initialize(deps) {
789
+ this.deps = deps;
790
+ this._isInitialized = true;
791
+ }
792
+ /**
793
+ * Set the PowerSync adapter instance
794
+ *
795
+ * @param adapter - PowerSync adapter implementation
796
+ */
797
+ setPowerSyncAdapter(adapter) {
798
+ this.powerSyncAdapter = adapter;
799
+ }
800
+ /**
801
+ * Set the Supabase adapter instance
802
+ *
803
+ * @param adapter - Supabase adapter implementation
804
+ */
805
+ setSupabaseAdapter(adapter) {
806
+ this.supabaseAdapter = adapter;
807
+ }
808
+ /**
809
+ * Set the Cached adapter instance
810
+ *
811
+ * @param adapter - Cached adapter implementation
812
+ */
813
+ setCachedAdapter(adapter) {
814
+ this.cachedAdapter = adapter;
815
+ }
816
+ /**
817
+ * Initialize auto-detection with a detector instance
818
+ *
819
+ * @param detector - The auto-detector to use
820
+ */
821
+ initializeAutoDetection(detector) {
822
+ this.autoDetector = detector;
823
+ detector.addListener((result) => {
824
+ this.lastDetectionResult = result;
825
+ this.notifyBackendChange(result.recommendedBackend);
826
+ });
827
+ }
828
+ // ===========================================================================
829
+ // Adapter Access
830
+ // ===========================================================================
831
+ /**
832
+ * Get the appropriate adapter for a table based on configuration.
833
+ *
834
+ * The adapter is selected based on the table's strategy in config.tables:
835
+ * - "powersync": Returns PowerSyncAdapter
836
+ * - "supabase": Returns SupabaseAdapter
837
+ * - "cached": Returns CachedAdapter (wrapping Supabase)
838
+ * - "hybrid": Returns HybridAdapter (combining PowerSync + Cached)
839
+ * - "auto": Uses auto-detection to select the best backend
840
+ *
841
+ * For tables not in config, defaults to auto-detection if available,
842
+ * otherwise falls back to SupabaseAdapter.
843
+ *
844
+ * @param table - The table name
845
+ * @returns The appropriate adapter for the table
846
+ * @throws Error if adapters are not initialized
847
+ */
848
+ getAdapter(table) {
849
+ const existing = this.adapters.get(table);
850
+ if (existing) {
851
+ return existing;
852
+ }
853
+ const strategy = this.config.tables[table];
854
+ if (!strategy || strategy.strategy === "auto") {
855
+ return this.getAutoAdapter(strategy);
856
+ }
857
+ const adapter = this.createAdapter(strategy);
858
+ this.adapters.set(table, adapter);
859
+ return adapter;
860
+ }
861
+ /**
862
+ * Get the PowerSync adapter directly
863
+ *
864
+ * @returns PowerSync adapter or null if not initialized
865
+ */
866
+ getPowerSyncAdapter() {
867
+ return this.powerSyncAdapter;
868
+ }
869
+ /**
870
+ * Get the Supabase adapter directly
871
+ *
872
+ * @returns Supabase adapter or null if not initialized
873
+ */
874
+ getSupabaseAdapter() {
875
+ return this.supabaseAdapter;
876
+ }
877
+ /**
878
+ * Get the Cached adapter directly
879
+ *
880
+ * @returns Cached adapter or null if not initialized
881
+ */
882
+ getCachedAdapter() {
883
+ return this.cachedAdapter;
884
+ }
885
+ /**
886
+ * Get all configured table names
887
+ *
888
+ * @returns Array of table names with explicit strategy configuration
889
+ */
890
+ getConfiguredTables() {
891
+ return Object.keys(this.config.tables);
892
+ }
893
+ /**
894
+ * Get the strategy for a specific table
895
+ *
896
+ * @param table - The table name
897
+ * @returns The table strategy or undefined if not configured
898
+ */
899
+ getTableStrategy(table) {
900
+ return this.config.tables[table];
901
+ }
902
+ /**
903
+ * Check if a table uses PowerSync strategy
904
+ *
905
+ * @param table - The table name
906
+ * @returns True if table uses PowerSync or Hybrid strategy
907
+ */
908
+ usesPowerSync(table) {
909
+ const strategy = this.config.tables[table];
910
+ return strategy?.strategy === "powersync" || strategy?.strategy === "hybrid";
911
+ }
912
+ /**
913
+ * Get all tables that use PowerSync
914
+ *
915
+ * @returns Array of table names using PowerSync or Hybrid strategy
916
+ */
917
+ getPowerSyncTables() {
918
+ return Object.entries(this.config.tables).filter(
919
+ ([_, strategy]) => strategy.strategy === "powersync" || strategy.strategy === "hybrid"
920
+ ).map(([table]) => table);
921
+ }
922
+ // ===========================================================================
923
+ // Auto-Detection Methods
924
+ // ===========================================================================
925
+ /**
926
+ * Get adapter using auto-detection
927
+ *
928
+ * @param strategy - Optional auto strategy configuration
929
+ * @returns The automatically selected adapter
930
+ */
931
+ getAutoAdapter(strategy) {
932
+ if (!this.autoDetector) {
933
+ if (!this.supabaseAdapter) {
934
+ throw new Error(
935
+ "No auto-detector configured and Supabase adapter not available. Either initialize auto-detection or set adapters explicitly."
936
+ );
937
+ }
938
+ return this.supabaseAdapter;
939
+ }
940
+ const detection = this.autoDetector.detect();
941
+ this.lastDetectionResult = detection;
942
+ if (detection.recommendedBackend === "powersync") {
943
+ if (!this.powerSyncAdapter) {
944
+ if (!this.supabaseAdapter) {
945
+ throw new Error(
946
+ "Neither PowerSync nor Supabase adapters are available."
947
+ );
948
+ }
949
+ return this.supabaseAdapter;
950
+ }
951
+ return this.powerSyncAdapter;
952
+ }
953
+ if (!this.supabaseAdapter) {
954
+ throw new Error(
955
+ "Supabase adapter not available and PowerSync not recommended."
956
+ );
957
+ }
958
+ return this.supabaseAdapter;
959
+ }
960
+ /**
961
+ * Subscribe to backend changes
962
+ *
963
+ * @param callback - Function called when recommended backend changes
964
+ * @returns Unsubscribe function
965
+ */
966
+ onBackendChange(callback) {
967
+ this.backendChangeListeners.add(callback);
968
+ return () => {
969
+ this.backendChangeListeners.delete(callback);
970
+ };
971
+ }
972
+ /**
973
+ * Notify listeners of backend change
974
+ *
975
+ * @param backend - The new recommended backend
976
+ */
977
+ notifyBackendChange(backend) {
978
+ this.backendChangeListeners.forEach((callback) => {
979
+ try {
980
+ callback(backend);
981
+ } catch (error) {
982
+ console.error("Error in backend change listener:", error);
983
+ }
984
+ });
985
+ }
986
+ /**
987
+ * Get the last auto-detection result
988
+ *
989
+ * @returns Last detection result or null if never detected
990
+ */
991
+ getLastDetectionResult() {
992
+ return this.lastDetectionResult;
993
+ }
994
+ /**
995
+ * Get the auto-detector instance
996
+ *
997
+ * @returns Auto-detector instance or null if not initialized
998
+ */
999
+ getAutoDetector() {
1000
+ return this.autoDetector;
1001
+ }
1002
+ // ===========================================================================
1003
+ // Private Methods
1004
+ // ===========================================================================
1005
+ /**
1006
+ * Create an adapter based on the strategy type
1007
+ *
1008
+ * @param strategy - The table strategy configuration
1009
+ * @returns The created adapter
1010
+ * @throws Error if the required base adapter is not initialized
1011
+ */
1012
+ createAdapter(strategy) {
1013
+ switch (strategy.strategy) {
1014
+ case "powersync":
1015
+ if (!this.powerSyncAdapter) {
1016
+ throw new Error(
1017
+ "PowerSync adapter not initialized. Ensure PowerSyncAdapter is set before accessing PowerSync tables."
1018
+ );
1019
+ }
1020
+ return this.powerSyncAdapter;
1021
+ case "supabase":
1022
+ if (!this.supabaseAdapter) {
1023
+ throw new Error(
1024
+ "Supabase adapter not initialized. Ensure SupabaseAdapter is set before accessing Supabase tables."
1025
+ );
1026
+ }
1027
+ return this.supabaseAdapter;
1028
+ case "cached":
1029
+ if (this.cachedAdapter) {
1030
+ return this.cachedAdapter;
1031
+ }
1032
+ throw new Error(
1033
+ "CachedAdapter not yet implemented. This feature will be available in Wave 2. For now, use 'supabase' strategy as a fallback."
1034
+ );
1035
+ case "hybrid":
1036
+ throw new Error(
1037
+ "HybridAdapter not yet implemented. This feature will be available in Wave 2. For now, use 'powersync' or 'supabase' strategy as a fallback."
1038
+ );
1039
+ default:
1040
+ if (!this.supabaseAdapter) {
1041
+ throw new Error(
1042
+ "Supabase adapter not initialized. Ensure SupabaseAdapter is set before accessing tables."
1043
+ );
1044
+ }
1045
+ return this.supabaseAdapter;
1046
+ }
1047
+ }
1048
+ // ===========================================================================
1049
+ // Utility Methods
1050
+ // ===========================================================================
1051
+ /**
1052
+ * Clear all cached adapters.
1053
+ * Useful when configuration changes and adapters need to be recreated.
1054
+ */
1055
+ clearCache() {
1056
+ this.adapters.clear();
1057
+ }
1058
+ /**
1059
+ * Reset the registry to uninitialized state.
1060
+ * Used during cleanup or testing.
1061
+ */
1062
+ reset() {
1063
+ this.adapters.clear();
1064
+ this.powerSyncAdapter = null;
1065
+ this.supabaseAdapter = null;
1066
+ this.cachedAdapter = null;
1067
+ this.deps = null;
1068
+ this._isInitialized = false;
1069
+ this.autoDetector = null;
1070
+ this.backendChangeListeners.clear();
1071
+ this.lastDetectionResult = null;
1072
+ }
1073
+ /**
1074
+ * Dispose all adapters and clean up resources.
1075
+ * Called when the DataLayerProvider unmounts.
1076
+ */
1077
+ dispose() {
1078
+ this.reset();
1079
+ }
1080
+ /**
1081
+ * Get debug information about the registry state
1082
+ */
1083
+ getDebugInfo() {
1084
+ return {
1085
+ isInitialized: this._isInitialized,
1086
+ hasPowerSync: this.powerSyncAdapter !== null,
1087
+ hasSupabase: this.supabaseAdapter !== null,
1088
+ hasCached: this.cachedAdapter !== null,
1089
+ cachedAdapterCount: this.adapters.size,
1090
+ configuredTableCount: Object.keys(this.config.tables).length,
1091
+ powerSyncTables: this.getPowerSyncTables(),
1092
+ hasAutoDetector: this.autoDetector !== null,
1093
+ lastDetectionResult: this.lastDetectionResult
1094
+ };
1095
+ }
1096
+ };
1097
+ function createAdapterRegistry(config) {
1098
+ return new AdapterRegistry(config);
1099
+ }
1100
+
1101
+ // src/adapters/auto-detector.ts
1102
+ var BackendStatus = /* @__PURE__ */ ((BackendStatus2) => {
1103
+ BackendStatus2["AVAILABLE"] = "available";
1104
+ BackendStatus2["INITIALIZING"] = "initializing";
1105
+ BackendStatus2["UNAVAILABLE"] = "unavailable";
1106
+ return BackendStatus2;
1107
+ })(BackendStatus || {});
1108
+ var AdapterAutoDetector = class {
1109
+ constructor(powerSyncDb, supabase, options = {}) {
1110
+ this.powerSyncDb = powerSyncDb;
1111
+ this.supabase = supabase;
1112
+ this.options = {
1113
+ preferPowerSync: options.preferPowerSync ?? true,
1114
+ statusCheckTimeout: options.statusCheckTimeout ?? 1e3
1115
+ };
1116
+ }
1117
+ options;
1118
+ listeners = /* @__PURE__ */ new Set();
1119
+ lastResult = null;
1120
+ // ===========================================================================
1121
+ // Main Detection Methods
1122
+ // ===========================================================================
1123
+ /**
1124
+ * Detect backend availability and recommend best option.
1125
+ *
1126
+ * The detection logic follows this priority:
1127
+ * 1. If preferPowerSync is true and PowerSync is available, use PowerSync
1128
+ * 2. If PowerSync is initializing and online with Supabase available, use Supabase temporarily
1129
+ * 3. If online with Supabase available, use Supabase
1130
+ * 4. If offline but PowerSync available, use PowerSync (offline mode)
1131
+ * 5. Default to Supabase as fallback
1132
+ *
1133
+ * @returns Detection result with recommendation and reasoning
1134
+ */
1135
+ detect() {
1136
+ const powerSyncStatus = this.detectPowerSyncStatus();
1137
+ const supabaseStatus = this.detectSupabaseStatus();
1138
+ const isOnline = this.checkOnlineStatus();
1139
+ let recommendedBackend;
1140
+ let reason;
1141
+ if (this.options.preferPowerSync && powerSyncStatus === "available" /* AVAILABLE */) {
1142
+ recommendedBackend = "powersync";
1143
+ reason = "PowerSync is available and preferred for offline-first experience";
1144
+ } else if (powerSyncStatus === "initializing" /* INITIALIZING */ && isOnline && supabaseStatus === "available" /* AVAILABLE */) {
1145
+ recommendedBackend = "supabase";
1146
+ reason = "PowerSync is initializing; using Supabase temporarily";
1147
+ } else if (supabaseStatus === "available" /* AVAILABLE */ && isOnline) {
1148
+ recommendedBackend = "supabase";
1149
+ reason = "Using Supabase direct connection";
1150
+ } else if (powerSyncStatus === "available" /* AVAILABLE */) {
1151
+ recommendedBackend = "powersync";
1152
+ reason = "Offline mode using PowerSync local data";
1153
+ } else {
1154
+ recommendedBackend = "supabase";
1155
+ reason = "No confirmed available backend; defaulting to Supabase";
1156
+ }
1157
+ const result = {
1158
+ powerSyncStatus,
1159
+ supabaseStatus,
1160
+ recommendedBackend,
1161
+ isOnline,
1162
+ reason
1163
+ };
1164
+ if (this.hasResultChanged(result)) {
1165
+ this.lastResult = result;
1166
+ this.notifyListeners(result);
1167
+ }
1168
+ return result;
1169
+ }
1170
+ /**
1171
+ * Check if PowerSync is available.
1172
+ *
1173
+ * PowerSync is considered available if we have a database instance.
1174
+ * The actual sync status would be checked via PowerSync's status API
1175
+ * when the full SDK is integrated.
1176
+ *
1177
+ * @returns PowerSync backend status
1178
+ */
1179
+ detectPowerSyncStatus() {
1180
+ if (!this.powerSyncDb) {
1181
+ return "unavailable" /* UNAVAILABLE */;
1182
+ }
1183
+ try {
1184
+ if (typeof this.powerSyncDb.getAll === "function") {
1185
+ return "available" /* AVAILABLE */;
1186
+ }
1187
+ return "initializing" /* INITIALIZING */;
1188
+ } catch {
1189
+ return "initializing" /* INITIALIZING */;
1190
+ }
1191
+ }
1192
+ /**
1193
+ * Check if Supabase is available.
1194
+ *
1195
+ * Supabase is considered available if we have a client instance.
1196
+ * The actual network connectivity is checked separately via checkOnlineStatus().
1197
+ *
1198
+ * @returns Supabase backend status
1199
+ */
1200
+ detectSupabaseStatus() {
1201
+ if (!this.supabase) {
1202
+ return "unavailable" /* UNAVAILABLE */;
1203
+ }
1204
+ try {
1205
+ if (typeof this.supabase.from === "function") {
1206
+ return "available" /* AVAILABLE */;
1207
+ }
1208
+ return "unavailable" /* UNAVAILABLE */;
1209
+ } catch {
1210
+ return "unavailable" /* UNAVAILABLE */;
1211
+ }
1212
+ }
1213
+ /**
1214
+ * Check if device is online.
1215
+ *
1216
+ * Uses navigator.onLine in browser environments.
1217
+ * Returns true by default for non-browser environments (React Native
1218
+ * would need NetInfo integration via dependency injection).
1219
+ *
1220
+ * @returns Whether the device has network connectivity
1221
+ */
1222
+ checkOnlineStatus() {
1223
+ if (typeof window !== "undefined" && typeof navigator !== "undefined") {
1224
+ return navigator.onLine;
1225
+ }
1226
+ return true;
1227
+ }
1228
+ // ===========================================================================
1229
+ // Instance Management Methods
1230
+ // ===========================================================================
1231
+ /**
1232
+ * Update PowerSync instance (e.g., when it becomes available).
1233
+ *
1234
+ * @param db - New PowerSync database instance or null
1235
+ */
1236
+ setPowerSync(db) {
1237
+ this.powerSyncDb = db;
1238
+ }
1239
+ /**
1240
+ * Update Supabase instance.
1241
+ *
1242
+ * @param supabase - New Supabase client instance or null
1243
+ */
1244
+ setSupabase(supabase) {
1245
+ this.supabase = supabase;
1246
+ }
1247
+ /**
1248
+ * Get current PowerSync instance.
1249
+ *
1250
+ * @returns Current PowerSync database instance or null
1251
+ */
1252
+ getPowerSync() {
1253
+ return this.powerSyncDb;
1254
+ }
1255
+ /**
1256
+ * Get current Supabase instance.
1257
+ *
1258
+ * @returns Current Supabase client instance or null
1259
+ */
1260
+ getSupabase() {
1261
+ return this.supabase;
1262
+ }
1263
+ // ===========================================================================
1264
+ // Options Management
1265
+ // ===========================================================================
1266
+ /**
1267
+ * Update detector options.
1268
+ *
1269
+ * @param options - New options to merge with existing
1270
+ */
1271
+ setOptions(options) {
1272
+ this.options = {
1273
+ ...this.options,
1274
+ ...options
1275
+ };
1276
+ }
1277
+ /**
1278
+ * Get current detector options.
1279
+ *
1280
+ * @returns Current detector options
1281
+ */
1282
+ getOptions() {
1283
+ return { ...this.options };
1284
+ }
1285
+ // ===========================================================================
1286
+ // Listener Management
1287
+ // ===========================================================================
1288
+ /**
1289
+ * Add a listener for backend change events.
1290
+ *
1291
+ * @param listener - Callback to invoke when detection result changes
1292
+ * @returns Function to remove the listener
1293
+ */
1294
+ addListener(listener) {
1295
+ this.listeners.add(listener);
1296
+ return () => this.listeners.delete(listener);
1297
+ }
1298
+ /**
1299
+ * Remove a listener for backend change events.
1300
+ *
1301
+ * @param listener - Listener to remove
1302
+ */
1303
+ removeListener(listener) {
1304
+ this.listeners.delete(listener);
1305
+ }
1306
+ /**
1307
+ * Get the last detection result.
1308
+ *
1309
+ * @returns Last detection result or null if never detected
1310
+ */
1311
+ getLastResult() {
1312
+ return this.lastResult;
1313
+ }
1314
+ // ===========================================================================
1315
+ // Private Helper Methods
1316
+ // ===========================================================================
1317
+ /**
1318
+ * Check if the detection result has changed from the last result.
1319
+ */
1320
+ hasResultChanged(result) {
1321
+ if (!this.lastResult) {
1322
+ return true;
1323
+ }
1324
+ return this.lastResult.powerSyncStatus !== result.powerSyncStatus || this.lastResult.supabaseStatus !== result.supabaseStatus || this.lastResult.recommendedBackend !== result.recommendedBackend || this.lastResult.isOnline !== result.isOnline;
1325
+ }
1326
+ /**
1327
+ * Notify all listeners of a detection result change.
1328
+ */
1329
+ notifyListeners(result) {
1330
+ Array.from(this.listeners).forEach((listener) => {
1331
+ try {
1332
+ listener(result);
1333
+ } catch (error) {
1334
+ console.error("Error in backend change listener:", error);
1335
+ }
1336
+ });
1337
+ }
1338
+ };
1339
+ function createAdapterAutoDetector(powerSyncDb, supabase, options) {
1340
+ return new AdapterAutoDetector(powerSyncDb, supabase, options);
1341
+ }
1342
+
1343
+ // src/adapters/supabase-adapter.ts
1344
+ var SupabaseAdapter = class {
1345
+ constructor(supabase, schema) {
1346
+ this.supabase = supabase;
1347
+ this.schema = schema;
1348
+ }
1349
+ name = "supabase";
1350
+ capabilities = {
1351
+ supportsSubscribe: true,
1352
+ supportsOffline: false,
1353
+ supportsCache: false,
1354
+ supportsSync: false
1355
+ };
1356
+ // ===========================================================================
1357
+ // Private Helper - Table Name Parsing
1358
+ // ===========================================================================
1359
+ /**
1360
+ * Parse a table identifier to extract schema and table name.
1361
+ * Handles schema-qualified names like "core.Profile" -> { schema: "core", tableName: "Profile" }
1362
+ *
1363
+ * @param table - The table name, optionally schema-qualified (e.g., "users" or "core.Profile")
1364
+ * @returns Object with schema (defaults to "public") and tableName
1365
+ */
1366
+ parseTableIdentifier(table) {
1367
+ if (table.includes(".")) {
1368
+ const [schema, ...rest] = table.split(".");
1369
+ return { schema, tableName: rest.join(".") };
1370
+ }
1371
+ return { schema: "public", tableName: table };
1372
+ }
1373
+ // ===========================================================================
1374
+ // Query Operations
1375
+ // ===========================================================================
1376
+ /**
1377
+ * Execute a query and return results with optional count
1378
+ *
1379
+ * @param table - The table name to query
1380
+ * @param options - Query options (select, where, orderBy, limit, offset)
1381
+ * @returns Promise resolving to query results with optional count
1382
+ */
1383
+ async query(table, options) {
1384
+ const { select = "*", where, orderBy, limit, offset } = options;
1385
+ const { schema, tableName } = this.parseTableIdentifier(table);
1386
+ let query = this.supabase.schema(schema).from(tableName).select(select, { count: "exact" });
1387
+ if (where) {
1388
+ query = this.applyWhereClause(query, where);
1389
+ }
1390
+ if (orderBy && orderBy.length > 0) {
1391
+ for (const order of orderBy) {
1392
+ query = query.order(order.field, {
1393
+ ascending: order.direction === "asc"
1394
+ });
1395
+ }
1396
+ }
1397
+ if (limit !== void 0 && offset !== void 0) {
1398
+ query = query.range(offset, offset + limit - 1);
1399
+ } else if (limit !== void 0) {
1400
+ query = query.limit(limit);
1401
+ }
1402
+ const { data, error, count } = await query;
1403
+ if (error) {
1404
+ throw new Error(`Supabase query error on ${table}: ${error.message}`);
1405
+ }
1406
+ return {
1407
+ data: data ?? [],
1408
+ count: count ?? void 0
1409
+ };
1410
+ }
1411
+ /**
1412
+ * Query a single record by ID
1413
+ *
1414
+ * @param table - The table name to query
1415
+ * @param id - The record ID
1416
+ * @param options - Optional query options (mainly for select)
1417
+ * @returns Promise resolving to the record or null if not found
1418
+ */
1419
+ async queryById(table, id, options) {
1420
+ const select = options?.select ?? "*";
1421
+ const { schema, tableName } = this.parseTableIdentifier(table);
1422
+ const { data, error } = await this.supabase.schema(schema).from(tableName).select(select).eq("id", id).single();
1423
+ if (error) {
1424
+ if (error.code === "PGRST116") {
1425
+ return null;
1426
+ }
1427
+ throw new Error(`Supabase queryById error on ${table}: ${error.message}`);
1428
+ }
1429
+ return data;
1430
+ }
1431
+ // ===========================================================================
1432
+ // Mutation Operations
1433
+ // ===========================================================================
1434
+ /**
1435
+ * Insert a new record
1436
+ *
1437
+ * @param table - The table name
1438
+ * @param data - The data to insert
1439
+ * @returns Promise resolving to the inserted record
1440
+ */
1441
+ async insert(table, data) {
1442
+ const { schema, tableName } = this.parseTableIdentifier(table);
1443
+ const { data: insertedData, error } = await this.supabase.schema(schema).from(tableName).insert(data).select().single();
1444
+ if (error) {
1445
+ throw new Error(`Supabase insert error on ${table}: ${error.message}`);
1446
+ }
1447
+ return insertedData;
1448
+ }
1449
+ /**
1450
+ * Update an existing record by ID
1451
+ *
1452
+ * @param table - The table name
1453
+ * @param id - The record ID to update
1454
+ * @param data - The data to update
1455
+ * @returns Promise resolving to the updated record
1456
+ */
1457
+ async update(table, id, data) {
1458
+ const { schema, tableName } = this.parseTableIdentifier(table);
1459
+ const { data: updatedData, error } = await this.supabase.schema(schema).from(tableName).update(data).eq("id", id).select().single();
1460
+ if (error) {
1461
+ throw new Error(`Supabase update error on ${table}: ${error.message}`);
1462
+ }
1463
+ return updatedData;
1464
+ }
1465
+ /**
1466
+ * Upsert (insert or update) a record
1467
+ *
1468
+ * @param table - The table name
1469
+ * @param data - The data to upsert
1470
+ * @returns Promise resolving to the upserted record
1471
+ */
1472
+ async upsert(table, data) {
1473
+ const { schema, tableName } = this.parseTableIdentifier(table);
1474
+ const { data: upsertedData, error } = await this.supabase.schema(schema).from(tableName).upsert(data, { onConflict: "id" }).select().single();
1475
+ if (error) {
1476
+ throw new Error(`Supabase upsert error on ${table}: ${error.message}`);
1477
+ }
1478
+ return upsertedData;
1479
+ }
1480
+ /**
1481
+ * Delete a record by ID
1482
+ *
1483
+ * @param table - The table name
1484
+ * @param id - The record ID to delete
1485
+ * @returns Promise that resolves when deletion is complete
1486
+ */
1487
+ async delete(table, id) {
1488
+ const { schema, tableName } = this.parseTableIdentifier(table);
1489
+ const { error } = await this.supabase.schema(schema).from(tableName).delete().eq("id", id);
1490
+ if (error) {
1491
+ throw new Error(`Supabase delete error on ${table}: ${error.message}`);
1492
+ }
1493
+ }
1494
+ // ===========================================================================
1495
+ // Subscription (Real-time)
1496
+ // ===========================================================================
1497
+ /**
1498
+ * Subscribe to real-time changes on a query
1499
+ *
1500
+ * @param table - The table name to watch
1501
+ * @param options - Query options to filter what to watch
1502
+ * @param callback - Function called with updated data
1503
+ * @returns Unsubscribe function
1504
+ */
1505
+ subscribe(table, options, callback) {
1506
+ const { schema, tableName } = this.parseTableIdentifier(table);
1507
+ const channelName = `v3:${table}:${Date.now()}`;
1508
+ const channel = this.supabase.channel(channelName).on(
1509
+ "postgres_changes",
1510
+ {
1511
+ event: "*",
1512
+ schema,
1513
+ table: tableName
1514
+ },
1515
+ async () => {
1516
+ try {
1517
+ const { data } = await this.query(table, options);
1518
+ callback(data);
1519
+ } catch (error) {
1520
+ console.error(
1521
+ `Supabase subscription refetch error on ${table}:`,
1522
+ error
1523
+ );
1524
+ }
1525
+ }
1526
+ ).subscribe();
1527
+ return () => {
1528
+ this.supabase.removeChannel(channel);
1529
+ };
1530
+ }
1531
+ // ===========================================================================
1532
+ // Private Helper Methods
1533
+ // ===========================================================================
1534
+ /**
1535
+ * Apply where clause filters to a Supabase query
1536
+ *
1537
+ * Converts WhereClause to Supabase filter methods:
1538
+ * - Direct value: .eq(field, value)
1539
+ * - { in: [...] }: .in(field, values)
1540
+ * - { gt: n }: .gt(field, n)
1541
+ * - { gte: n }: .gte(field, n)
1542
+ * - { lt: n }: .lt(field, n)
1543
+ * - { lte: n }: .lte(field, n)
1544
+ * - { like: s }: .ilike(field, s)
1545
+ * - { is: null }: .is(field, null)
1546
+ * - { neq: v }: .neq(field, v) or .not(field, "is", null)
1547
+ * - { notIn: [...] }: .not().in(field, values)
1548
+ *
1549
+ * @param query - The Supabase query builder
1550
+ * @param where - The where clause to apply
1551
+ * @returns The query with filters applied
1552
+ */
1553
+ applyWhereClause(query, where) {
1554
+ for (const [field, condition] of Object.entries(where)) {
1555
+ if (condition === null) {
1556
+ query = query.is(field, null);
1557
+ } else if (typeof condition === "string" || typeof condition === "number" || typeof condition === "boolean") {
1558
+ query = query.eq(field, condition);
1559
+ } else if (typeof condition === "object") {
1560
+ const operators = condition;
1561
+ if (operators.in !== void 0) {
1562
+ query = query.in(field, operators.in);
1563
+ }
1564
+ if (operators.gt !== void 0) {
1565
+ query = query.gt(field, operators.gt);
1566
+ }
1567
+ if (operators.gte !== void 0) {
1568
+ query = query.gte(field, operators.gte);
1569
+ }
1570
+ if (operators.lt !== void 0) {
1571
+ query = query.lt(field, operators.lt);
1572
+ }
1573
+ if (operators.lte !== void 0) {
1574
+ query = query.lte(field, operators.lte);
1575
+ }
1576
+ if (operators.like !== void 0) {
1577
+ query = query.ilike(field, operators.like);
1578
+ }
1579
+ if (operators.is !== void 0) {
1580
+ query = query.is(field, null);
1581
+ }
1582
+ if (operators.neq !== void 0) {
1583
+ if (operators.neq === null) {
1584
+ query = query.not(field, "is", null);
1585
+ } else {
1586
+ query = query.neq(field, operators.neq);
1587
+ }
1588
+ }
1589
+ if (operators.notIn !== void 0) {
1590
+ query = query.not(field, "in", `(${operators.notIn.join(",")})`);
1591
+ }
1592
+ }
1593
+ }
1594
+ return query;
1595
+ }
1596
+ };
1597
+ function createSupabaseAdapter(supabase, schema) {
1598
+ return new SupabaseAdapter(supabase, schema);
1599
+ }
1600
+
1601
+ // src/supabase-functions.tsx
1602
+ async function getErrorBody(response) {
1603
+ const stream = response.error.context.body;
1604
+ const reader = stream.getReader();
1605
+ const { value } = await reader.read();
1606
+ const text = new TextDecoder().decode(value);
1607
+ let parsed;
1608
+ try {
1609
+ parsed = JSON.parse(text);
1610
+ } catch {
1611
+ parsed = text;
1612
+ }
1613
+ return parsed;
1614
+ }
1615
+
1616
+ // src/useAI.ts
1617
+ function useAI() {
1618
+ const supabase = useSupabase();
1619
+ const generate = async (prompt, context, options) => {
1620
+ const response = await supabase.functions.invoke("ai", {
1621
+ body: { prompt, context, options }
1622
+ });
1623
+ if (response.error) {
1624
+ throw new Error(`Failed to invoke AI function: ${response.error.message}`);
1625
+ }
1626
+ if (response.data?.error) {
1627
+ throw new Error(response.data.error);
1628
+ }
1629
+ if (!response.data?.success) {
1630
+ throw new Error(response.data?.error || "Unknown error occurred");
1631
+ }
1632
+ if (!response.data?.data?.response) {
1633
+ throw new Error("No response from AI");
1634
+ }
1635
+ return response.data.data.response;
1636
+ };
1637
+ return { generate };
1638
+ }
1639
+
1640
+ // src/useServerAvailability.ts
1641
+ import { useState as useState3, useEffect as useEffect4, useCallback } from "react";
1642
+ var CHECK_TIMEOUT = 1e4;
1643
+ var CHECK_INTERVAL = 3e4;
1644
+ function useServerAvailability(options) {
1645
+ const { enabled = true, checkInterval = CHECK_INTERVAL, timeout = CHECK_TIMEOUT } = options || {};
1646
+ const [isAvailable, setIsAvailable] = useState3(null);
1647
+ const [isChecking, setIsChecking] = useState3(false);
1648
+ const [lastChecked, setLastChecked] = useState3(null);
1649
+ const [error, setError] = useState3(null);
1650
+ const checkAvailability = useCallback(async () => {
1651
+ if (!enabled) return;
1652
+ setIsChecking(true);
1653
+ setError(null);
1654
+ try {
1655
+ const supabaseUrl = getSupabaseUrl();
1656
+ const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY;
1657
+ if (!supabaseUrl) {
1658
+ throw new Error("Supabase URL is not configured");
1659
+ }
1660
+ if (!supabaseAnonKey) {
1661
+ throw new Error("Supabase anon key is not configured");
1662
+ }
1663
+ const controller = new AbortController();
1664
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
1665
+ const healthCheckPromise = fetch(`${supabaseUrl}/rest/v1/`, {
1666
+ method: "HEAD",
1667
+ headers: {
1668
+ apikey: supabaseAnonKey
1669
+ },
1670
+ signal: controller.signal
1671
+ }).then((response) => {
1672
+ clearTimeout(timeoutId);
1673
+ return response.status < 500;
1674
+ }).catch((err) => {
1675
+ clearTimeout(timeoutId);
1676
+ if (err.name === "AbortError") {
1677
+ return false;
1678
+ }
1679
+ return false;
1680
+ });
1681
+ const available = await healthCheckPromise;
1682
+ setIsAvailable(available);
1683
+ setLastChecked(/* @__PURE__ */ new Date());
1684
+ setError(null);
1685
+ } catch (err) {
1686
+ const error2 = err instanceof Error ? err : new Error("Unknown error checking server availability");
1687
+ setIsAvailable(false);
1688
+ setError(error2);
1689
+ setLastChecked(/* @__PURE__ */ new Date());
1690
+ } finally {
1691
+ setIsChecking(false);
1692
+ }
1693
+ }, [enabled, timeout]);
1694
+ useEffect4(() => {
1695
+ if (enabled) {
1696
+ checkAvailability();
1697
+ }
1698
+ }, [enabled, checkAvailability]);
1699
+ useEffect4(() => {
1700
+ if (!enabled || isAvailable !== false) return;
1701
+ const intervalId = setInterval(() => {
1702
+ checkAvailability();
1703
+ }, checkInterval);
1704
+ return () => clearInterval(intervalId);
1705
+ }, [enabled, isAvailable, checkInterval, checkAvailability]);
1706
+ return {
1707
+ isAvailable,
1708
+ isChecking,
1709
+ lastChecked,
1710
+ error,
1711
+ checkAvailability
1712
+ };
1713
+ }
1714
+
1715
+ // src/useSearchQuery.ts
1716
+ import { useMemo as useMemo5 } from "react";
1717
+ import React from "react";
1718
+ function useSearchQuery(baseQuery, searchText, columns, options = { isOptional: true, additionCondition: [] }) {
1719
+ const original = React.useRef(null);
1720
+ if (original.current?.lastQuery !== baseQuery) {
1721
+ const b = baseQuery;
1722
+ const urlValue = b.url;
1723
+ const url = urlValue ? typeof urlValue === "string" ? new URL(urlValue) : new URL(urlValue.toString()) : new URL("http://localhost");
1724
+ const method = b.method ?? "GET";
1725
+ const headers = b.headers ?? {};
1726
+ const body = method !== "GET" && method !== "HEAD" ? b.body ?? null : null;
1727
+ const signal = b.signal ?? null;
1728
+ original.current = {
1729
+ lastQuery: baseQuery,
1730
+ original: {
1731
+ url,
1732
+ method,
1733
+ headers,
1734
+ body,
1735
+ signal
1736
+ }
1737
+ };
1738
+ }
1739
+ return useMemo5(() => {
1740
+ const qb = baseQuery;
1741
+ const { url, method, body, signal } = original.current.original;
1742
+ qb.url = new URL(url.toString());
1743
+ qb.method = method;
1744
+ if (method !== "GET" && method !== "HEAD") {
1745
+ qb.body = body;
1746
+ } else {
1747
+ delete qb.body;
1748
+ }
1749
+ qb.signal = signal;
1750
+ const term = searchText.trim();
1751
+ if (term) {
1752
+ const clauses = columns.map((col) => {
1753
+ const [tbl, colName] = col.split(".");
1754
+ return `${colName ?? tbl}.ilike.%${escapeLikePattern(term)}%`;
1755
+ });
1756
+ clauses.push(...options.additionCondition.filter(Boolean));
1757
+ qb.or(
1758
+ clauses.join(","),
1759
+ {
1760
+ referencedTable: columns.find((c) => c.includes("."))?.split(".")[0]
1761
+ }
1762
+ );
1763
+ } else if (!options.isOptional) {
1764
+ const conds = options.additionCondition.filter(Boolean);
1765
+ conds?.length ? qb.or(conds.join(",")) : qb.limit(0);
1766
+ }
1767
+ return qb;
1768
+ }, [
1769
+ baseQuery,
1770
+ searchText,
1771
+ columns.join(","),
1772
+ options.isOptional,
1773
+ options.additionCondition.join(",")
1774
+ ]);
1775
+ }
1776
+ function escapeLikePattern(text) {
1777
+ return text?.replace(/[%_]/g, "\\$&");
1778
+ }
1779
+
1780
+ // src/useLiveChangeTracking.ts
1781
+ import { useEffect as useEffect5, useState as useState4 } from "react";
1782
+ var import_moment = __toESM(require_moment(), 1);
1783
+ function useLiveChangeTracking(tableName, item, action) {
1784
+ const [changeLog, setChangeLog] = useState4(null);
1785
+ const [oldItem, setOldItem] = useState4(null);
1786
+ const supabase = useSupabase();
1787
+ useEffect5(() => {
1788
+ setChangeLog(null);
1789
+ if (isUsable(item) === false) return;
1790
+ if (isUsable(oldItem) === false) {
1791
+ setOldItem(item);
1792
+ return;
1793
+ }
1794
+ const itemId = item["id"];
1795
+ if (isUsable(itemId) === false) return;
1796
+ const changes = getObjectChanges(oldItem, item);
1797
+ if (Object.keys(changes).length === 0) return;
1798
+ supabase.schema("core").from("OperationLog").select().filter("tableName", "eq", tableName).filter("recordId", "eq", itemId).order("changeAt", { ascending: false }).limit(1).single().then((x) => {
1799
+ if (isUsable(x.data) === false) return;
1800
+ const data = x.data;
1801
+ if (data.sessionId === UserSessionId) return;
1802
+ const changeLog2 = {
1803
+ modifiedByUserId: data.changeBy,
1804
+ changedOn: import_moment.default.utc(data.changeAt),
1805
+ changes
1806
+ };
1807
+ setChangeLog(changeLog2);
1808
+ });
1809
+ setOldItem(item);
1810
+ }, [item]);
1811
+ useEffect5(() => {
1812
+ if (isUsable(changeLog) && action) {
1813
+ action(changeLog.changes, changeLog);
1814
+ }
1815
+ }, [changeLog]);
1816
+ return changeLog;
1817
+ }
1818
+
1819
+ // src/useLatestOperationLog.ts
1820
+ function useLatestOperationLog(tableName, recordId) {
1821
+ const supabase = useSupabase();
1822
+ return useDbQuery(
1823
+ supabase.schema("core").from("OperationLog").select(OperationLog.defaultQuery).eq("recordId", String(recordId)).eq("tableName", tableName).order("changeAt", { ascending: false }).limit(1).maybeSingle(),
1824
+ {
1825
+ refetchOnMount: true,
1826
+ refetchOnWindowFocus: true,
1827
+ staleTime: 0,
1828
+ enabled: isUsable(recordId)
1829
+ }
1830
+ );
1831
+ }
1832
+
1833
+ // src/useAutosaveState.ts
1834
+ import {
1835
+ useCallback as useCallback2,
1836
+ useEffect as useEffect6,
1837
+ useMemo as useMemo6,
1838
+ useRef as useRef5,
1839
+ useState as useState5
1840
+ } from "react";
1841
+ function newTimeActivity(date) {
1842
+ return {
1843
+ activityDate: date.clone().startOf("day").toISOString(true),
1844
+ description: "",
1845
+ hasRequestBillableChange: false,
1846
+ hasRequestVerification: false,
1847
+ hours: 0,
1848
+ userNotes: "",
1849
+ adminNotes: "",
1850
+ userId: null,
1851
+ clientId: null,
1852
+ projectId: null,
1853
+ workingPhaseId: null,
1854
+ roleId: null,
1855
+ taskId: null,
1856
+ subtaskId: null,
1857
+ weeklyTimesheetId: null,
1858
+ id: null,
1859
+ BillingDetails: {
1860
+ status: "Unknown",
1861
+ id: null,
1862
+ orderHint: "",
1863
+ chargeTableId: null,
1864
+ cosmosId: null,
1865
+ amount: 0,
1866
+ rate: 0,
1867
+ type: "TimeActivity"
1868
+ }
1869
+ };
1870
+ }
1871
+ function getNewValue(action, prevState) {
1872
+ return typeof action === "function" ? action(prevState) : action;
1873
+ }
1874
+ function useAutosaveState(relation, defaultValue, options) {
1875
+ const [value, setValue] = useState5(() => defaultValue);
1876
+ const [baseValue, setBaseValue] = useState5(() => defaultValue);
1877
+ const pendingChangesRef = useRef5(/* @__PURE__ */ new Map());
1878
+ const currentId = useRef5(null);
1879
+ const newOptions = useMemo6(
1880
+ () => options ? { ...options, primaryKeys: options.primaryKeys ?? ["id"] } : { delay: 200, primaryKeys: ["id"] },
1881
+ [options]
1882
+ );
1883
+ const upsertMutation = useDbUpsert(relation, newOptions.primaryKeys);
1884
+ const saveEntityChanges = useCallback2(async (entityId) => {
1885
+ const pending = pendingChangesRef.current.get(entityId);
1886
+ if (!pending) return;
1887
+ const changes = diff(pending.baseValue, pending.currentValue);
1888
+ if (Object.keys(changes).length === 0) {
1889
+ pendingChangesRef.current.delete(entityId);
1890
+ return;
1891
+ }
1892
+ const patch = { ...changes, id: entityId };
1893
+ const finalPatch = newOptions.transformBeforeMutation ? newOptions.transformBeforeMutation(patch) : patch;
1894
+ await upsertMutation.mutateAsync(finalPatch);
1895
+ pendingChangesRef.current.delete(entityId);
1896
+ }, [upsertMutation, newOptions.transformBeforeMutation]);
1897
+ const updateValue = useCallback2((update) => {
1898
+ setValue((prev) => {
1899
+ const newValue = getNewValue(update, prev);
1900
+ const entityId = newValue.id;
1901
+ if (!entityId) return newValue;
1902
+ let pending = pendingChangesRef.current.get(entityId);
1903
+ if (!pending) {
1904
+ pending = {
1905
+ baseValue,
1906
+ currentValue: newValue,
1907
+ timeoutId: null
1908
+ };
1909
+ pendingChangesRef.current.set(entityId, pending);
1910
+ } else {
1911
+ pending.currentValue = newValue;
1912
+ }
1913
+ if (pending.timeoutId) {
1914
+ clearTimeout(pending.timeoutId);
1915
+ }
1916
+ pending.timeoutId = setTimeout(() => {
1917
+ saveEntityChanges(entityId);
1918
+ }, newOptions.delay);
1919
+ return newValue;
1920
+ });
1921
+ }, [baseValue, saveEntityChanges, newOptions.delay]);
1922
+ const setNewEntity = useCallback2((newEntity) => {
1923
+ setValue(newEntity);
1924
+ setBaseValue(newEntity);
1925
+ currentId.current = newEntity.id;
1926
+ }, []);
1927
+ useEffect6(() => {
1928
+ return () => {
1929
+ pendingChangesRef.current.forEach((pending) => {
1930
+ if (pending.timeoutId) clearTimeout(pending.timeoutId);
1931
+ });
1932
+ };
1933
+ }, []);
1934
+ const saveCurrentValue = useCallback2(async () => {
1935
+ if (currentId.current) {
1936
+ await saveEntityChanges(currentId.current);
1937
+ }
1938
+ }, [saveEntityChanges]);
1939
+ return [
1940
+ value,
1941
+ updateValue,
1942
+ upsertMutation,
1943
+ setValue,
1944
+ setNewEntity,
1945
+ saveCurrentValue
1946
+ ];
1947
+ }
1948
+
1949
+ // src/useToastError.ts
1950
+ import { useEffect as useEffect7 } from "react";
1951
+ import { useToast } from "@pol-studios/hooks/toast";
1952
+ function useToastError(mutation) {
1953
+ const toast = useToast();
1954
+ useEffect7(() => {
1955
+ if (isUsable(mutation.error) === false) return;
1956
+ toast.toast({ title: "Error", description: mutation.error.message });
1957
+ }, [mutation.error]);
1958
+ return mutation;
1959
+ }
1960
+
1961
+ // src/advance-query.tsx
1962
+ async function executeAdvanceQuery(supabase, query, filterLayer) {
1963
+ const parser = new PostgrestParser(query);
1964
+ const filterGroup = {
1965
+ id: filterLayer.id,
1966
+ op: filterLayer.op,
1967
+ not: filterLayer.not,
1968
+ filters: filterLayer.filters.map(normalizeFilter).filter((f) => f !== null)
1969
+ };
1970
+ const pagination = { offset: void 0, limit: void 0 };
1971
+ const sort = [...filterLayer.sort || []];
1972
+ const searchParams = Array.from(parser.searchParams.entries());
1973
+ searchParams.forEach(([k, v]) => {
1974
+ if (k.includes("offset")) {
1975
+ pagination.offset = Number(v);
1976
+ return;
1977
+ }
1978
+ if (k.includes("limit")) {
1979
+ pagination.limit = Number(v);
1980
+ return;
1981
+ }
1982
+ if (k.includes("order")) {
1983
+ const orderColumns = v.split(",");
1984
+ orderColumns.forEach((x) => {
1985
+ const values2 = x.split(".");
1986
+ sort.push({
1987
+ field: values2[0],
1988
+ direction: values2[1] === "asc" ? "asc" : "desc"
1989
+ });
1990
+ });
1991
+ return;
1992
+ }
1993
+ if (v.includes(".") === false) return;
1994
+ const values = v.split(".");
1995
+ const column = k;
1996
+ const rawCondition = values[0];
1997
+ let condition = "";
1998
+ const value = values[1];
1999
+ if (column == "select") return;
2000
+ switch (rawCondition) {
2001
+ case "eq":
2002
+ condition = "=";
2003
+ break;
2004
+ case "in":
2005
+ condition = "in";
2006
+ break;
2007
+ case "lt":
2008
+ condition = "<";
2009
+ break;
2010
+ case "gt":
2011
+ condition = ">";
2012
+ break;
2013
+ case "lte":
2014
+ condition = "<=";
2015
+ break;
2016
+ case "gte":
2017
+ condition = ">=";
2018
+ break;
2019
+ case "is":
2020
+ condition = "is";
2021
+ break;
2022
+ case "ilike":
2023
+ condition = "ilike";
2024
+ break;
2025
+ case "neq":
2026
+ condition = "=";
2027
+ break;
2028
+ case "like":
2029
+ condition = "contains";
2030
+ break;
2031
+ }
2032
+ filterGroup.filters.push({
2033
+ id: `filter_${column}_${Date.now()}`,
2034
+ field: column,
2035
+ op: condition,
2036
+ value,
2037
+ display: `${column} ${condition} ${value}`
2038
+ });
2039
+ });
2040
+ const stripUIProperties = (obj) => {
2041
+ return JSON.parse(JSON.stringify(obj, (key, value) => {
2042
+ if (key === "display" || key === "info" || key === "options") {
2043
+ return void 0;
2044
+ }
2045
+ return value;
2046
+ }));
2047
+ };
2048
+ const cleanedBody = stripUIProperties({
2049
+ table: parser.table,
2050
+ schema: parser.schema,
2051
+ select: parser.select,
2052
+ filters: filterGroup,
2053
+ pagination,
2054
+ sort,
2055
+ count: "exact"
2056
+ });
2057
+ const response = await supabase.functions.invoke("query", {
2058
+ body: cleanedBody,
2059
+ method: "POST"
2060
+ });
2061
+ if (response.error) {
2062
+ return { data: null, count: null, error: response.error };
2063
+ }
2064
+ return { data: response.data?.data ?? null, count: response.data?.count ?? null, error: null };
2065
+ }
2066
+
2067
+ // src/storage/useStorageUrl.ts
2068
+ var import_moment2 = __toESM(require_moment(), 1);
2069
+ import { useRef as useRef6 } from "react";
2070
+ import { useIndexedDB } from "@pol-studios/hooks/storage";
2071
+ var cacheVersions = /* @__PURE__ */ new Map();
2072
+ var cacheVersionListeners = /* @__PURE__ */ new Map();
2073
+ var retryAttempts = /* @__PURE__ */ new Map();
2074
+ function useStorageUrl() {
2075
+ const db = useIndexedDB({
2076
+ dbName: "polstudios",
2077
+ storeName: "cached-urls"
2078
+ });
2079
+ const storedUrls = useRef6(/* @__PURE__ */ new Map());
2080
+ const supabase = useSupabase();
2081
+ function getCacheVersion(entity) {
2082
+ const key = `${entity.bucketId}${entity.path}`;
2083
+ return cacheVersions.get(key) || 0;
2084
+ }
2085
+ function subscribeToCacheVersion(entity, callback) {
2086
+ const key = `${entity.bucketId}${entity.path}`;
2087
+ if (!cacheVersionListeners.has(key)) {
2088
+ cacheVersionListeners.set(key, /* @__PURE__ */ new Set());
2089
+ }
2090
+ cacheVersionListeners.get(key).add(callback);
2091
+ return () => {
2092
+ cacheVersionListeners.get(key)?.delete(callback);
2093
+ };
2094
+ }
2095
+ async function expireCache(entity) {
2096
+ const key = `${entity.bucketId}${entity.path}`;
2097
+ const dbKeys = await db.getAllKeys();
2098
+ const keysToDelete = Array.from(
2099
+ new Set(
2100
+ [...Array.from(storedUrls.current.keys()), ...dbKeys].filter(
2101
+ (value) => value.startsWith(key)
2102
+ )
2103
+ )
2104
+ );
2105
+ await Promise.all(
2106
+ keysToDelete.map(
2107
+ async (x) => {
2108
+ storedUrls.current.delete(x);
2109
+ await db.removeItem(x);
2110
+ }
2111
+ )
2112
+ );
2113
+ const currentVersion = cacheVersions.get(key) || 0;
2114
+ cacheVersions.set(key, currentVersion + 1);
2115
+ const listeners = cacheVersionListeners.get(key);
2116
+ if (listeners) {
2117
+ listeners.forEach((callback) => {
2118
+ callback();
2119
+ });
2120
+ }
2121
+ }
2122
+ async function baseFetchUrl(entity, options, isPublic = false) {
2123
+ if (isUsable(entity) === false) return;
2124
+ const optionsString = JSON.stringify(options);
2125
+ if (isUsable(entity.bucketId) === false) {
2126
+ return;
2127
+ }
2128
+ if (isUsable(entity.path) === false) {
2129
+ return;
2130
+ }
2131
+ const key = `${entity.bucketId}${entity.path}${optionsString ?? ""}-cached-url`;
2132
+ const inMemoryItem = storedUrls.current.get(key);
2133
+ let item = inMemoryItem;
2134
+ if (isUsable(inMemoryItem) === false) {
2135
+ item = await db.getItem(key) ?? void 0;
2136
+ if (isUsable(item)) {
2137
+ }
2138
+ } else {
2139
+ }
2140
+ if (isUsable(item) && (0, import_moment2.default)(item.expiresOn).isAfter((0, import_moment2.default)().add(-1 * 60, "seconds"))) {
2141
+ return item.url;
2142
+ }
2143
+ const base = supabase.storage.from(entity.bucketId);
2144
+ let download = options?.download;
2145
+ if (typeof options?.download === "string") {
2146
+ const ext = entity.path.split(".").pop() ?? "";
2147
+ download = options?.download.endsWith(ext) ? options?.download : options?.download + "." + ext;
2148
+ }
2149
+ const newOptions = options ? { ...options, download } : void 0;
2150
+ const retryKey = `${entity.bucketId}/${entity.path}`;
2151
+ const currentRetries = retryAttempts.get(retryKey) || 0;
2152
+ let url;
2153
+ if (isPublic) {
2154
+ url = base.getPublicUrl(entity.path, newOptions).data.publicUrl;
2155
+ } else {
2156
+ try {
2157
+ console.log("Creating signed URL for", entity.path);
2158
+ const result = await base.createSignedUrl(entity.path, 60 * 100, newOptions);
2159
+ url = result.data?.signedUrl;
2160
+ if (isUsable(url)) {
2161
+ retryAttempts.delete(retryKey);
2162
+ } else if (currentRetries < 3) {
2163
+ retryAttempts.set(retryKey, currentRetries + 1);
2164
+ throw new Error("Failed to get signed URL");
2165
+ }
2166
+ } catch (error) {
2167
+ if (currentRetries < 3) {
2168
+ retryAttempts.set(retryKey, currentRetries + 1);
2169
+ const delay = Math.min(1e3 * Math.pow(2, currentRetries), 5e3);
2170
+ await new Promise((resolve) => setTimeout(resolve, delay));
2171
+ return baseFetchUrl(entity, options, isPublic);
2172
+ }
2173
+ retryAttempts.delete(retryKey);
2174
+ return void 0;
2175
+ }
2176
+ }
2177
+ if (isUsable(url) === false) return url;
2178
+ const cachedUrl = {
2179
+ key,
2180
+ url,
2181
+ expiresOn: (0, import_moment2.default)().add(60 * 100, "seconds").toISOString(true)
2182
+ };
2183
+ storedUrls.current.set(key, cachedUrl);
2184
+ db.setItem(key, cachedUrl);
2185
+ return url;
2186
+ }
2187
+ async function fetchUrl(entity, options) {
2188
+ return baseFetchUrl(entity, options, false);
2189
+ }
2190
+ async function fetchPublicUrl(entity, options) {
2191
+ return baseFetchUrl(entity, options, true);
2192
+ }
2193
+ async function prefetchImage(entity, options) {
2194
+ const url = await fetchUrl(entity, options);
2195
+ if (url) {
2196
+ new Image().src = url;
2197
+ }
2198
+ }
2199
+ async function fetchUrls(entities, options) {
2200
+ const results = /* @__PURE__ */ new Map();
2201
+ if (entities.length === 0) return results;
2202
+ const optionsString = JSON.stringify(options);
2203
+ const expirySeconds = 60 * 100;
2204
+ const byBucket = /* @__PURE__ */ new Map();
2205
+ for (const entity of entities) {
2206
+ if (!isUsable(entity.bucketId) || !isUsable(entity.path)) continue;
2207
+ const list = byBucket.get(entity.bucketId) ?? [];
2208
+ list.push(entity);
2209
+ byBucket.set(entity.bucketId, list);
2210
+ }
2211
+ for (const [bucketId, bucketEntities] of byBucket) {
2212
+ const uncached = [];
2213
+ for (const entity of bucketEntities) {
2214
+ const key = `${entity.bucketId}${entity.path}${optionsString ?? ""}-cached-url`;
2215
+ const entityKey = `${entity.bucketId}/${entity.path}`;
2216
+ let item = storedUrls.current.get(key);
2217
+ if (!isUsable(item)) {
2218
+ item = await db.getItem(key) ?? void 0;
2219
+ }
2220
+ if (isUsable(item) && (0, import_moment2.default)(item.expiresOn).isAfter((0, import_moment2.default)().add(-1 * 60, "seconds"))) {
2221
+ results.set(entityKey, item.url);
2222
+ } else {
2223
+ uncached.push(entity);
2224
+ }
2225
+ }
2226
+ if (uncached.length > 0) {
2227
+ const paths = uncached.map((e) => e.path);
2228
+ const base = supabase.storage.from(bucketId);
2229
+ try {
2230
+ console.log("Signed URLs created");
2231
+ const { data, error } = await base.createSignedUrls(paths, expirySeconds);
2232
+ if (!error && data) {
2233
+ const expiresOn = (0, import_moment2.default)().add(expirySeconds, "seconds").toISOString(true);
2234
+ for (let i = 0; i < uncached.length; i++) {
2235
+ const entity = uncached[i];
2236
+ const urlData = data[i];
2237
+ const entityKey = `${entity.bucketId}/${entity.path}`;
2238
+ const cacheKey = `${entity.bucketId}${entity.path}${optionsString ?? ""}-cached-url`;
2239
+ if (urlData?.signedUrl) {
2240
+ results.set(entityKey, urlData.signedUrl);
2241
+ const cachedUrl = {
2242
+ key: cacheKey,
2243
+ url: urlData.signedUrl,
2244
+ expiresOn
2245
+ };
2246
+ storedUrls.current.set(cacheKey, cachedUrl);
2247
+ db.setItem(cacheKey, cachedUrl);
2248
+ } else {
2249
+ results.set(entityKey, void 0);
2250
+ }
2251
+ }
2252
+ } else {
2253
+ for (const entity of uncached) {
2254
+ const entityKey = `${entity.bucketId}/${entity.path}`;
2255
+ const url = await fetchUrl(entity, options);
2256
+ results.set(entityKey, url);
2257
+ }
2258
+ }
2259
+ } catch {
2260
+ for (const entity of uncached) {
2261
+ const entityKey = `${entity.bucketId}/${entity.path}`;
2262
+ const url = await fetchUrl(entity, options);
2263
+ results.set(entityKey, url);
2264
+ }
2265
+ }
2266
+ }
2267
+ }
2268
+ return results;
2269
+ }
2270
+ return { fetchUrl, fetchUrls, prefetchImage, fetchPublicUrl, expireCache, getCacheVersion, subscribeToCacheVersion };
2271
+ }
2272
+
2273
+ // src/storage/useStoragePath.ts
2274
+ import { useEffect as useEffect8, useState as useState6 } from "react";
2275
+ function getContentType(metadata, path) {
2276
+ if (metadata?.contentType) {
2277
+ return metadata.contentType;
2278
+ }
2279
+ const ext = path.split(".").pop()?.toUpperCase();
2280
+ return ext || "Unknown";
2281
+ }
2282
+ function useStoragePath(storagePath, bucketId, options = {}) {
2283
+ const { fetchMetadata = true, transform, download } = options;
2284
+ const [url, setUrl] = useState6(null);
2285
+ const [error, setError] = useState6(null);
2286
+ const { fetchUrl } = useStorageUrl();
2287
+ const supabase = useSupabase();
2288
+ useEffect8(() => {
2289
+ if (!isUsable(storagePath) || !isUsable(bucketId)) {
2290
+ setUrl(null);
2291
+ return;
2292
+ }
2293
+ let cancelled = false;
2294
+ fetchUrl({ bucketId, path: storagePath }, { transform, download }).then((signedUrl) => {
2295
+ if (!cancelled) {
2296
+ setUrl(signedUrl ?? null);
2297
+ setError(null);
2298
+ }
2299
+ }).catch((err) => {
2300
+ if (!cancelled) {
2301
+ setError(err instanceof Error ? err : new Error(String(err)));
2302
+ setUrl(null);
2303
+ }
2304
+ });
2305
+ return () => {
2306
+ cancelled = true;
2307
+ };
2308
+ }, [storagePath, bucketId, JSON.stringify(transform), download]);
2309
+ const storageQuery = supabase.schema("storage").from("objects").select("metadata").eq("bucket_id", bucketId).eq("name", storagePath ?? "").maybeSingle();
2310
+ const metadataRequest = useDbQuery(
2311
+ storageQuery,
2312
+ {
2313
+ enabled: fetchMetadata && isUsable(storagePath) && isUsable(bucketId)
2314
+ }
2315
+ );
2316
+ const metadata = metadataRequest.data?.metadata ?? null;
2317
+ const contentType = getContentType(metadata, storagePath ?? "");
2318
+ const isLoading = isUsable(storagePath) && url === null && error === null || fetchMetadata && metadataRequest.isFetching;
2319
+ return {
2320
+ url,
2321
+ metadata,
2322
+ contentType,
2323
+ isLoading,
2324
+ error
2325
+ };
2326
+ }
2327
+
2328
+ // src/storage/bucketConfig.ts
2329
+ var BUCKETS = {
2330
+ AVATARS: "avatars",
2331
+ FIXTURE_CATALOG_COVER: "fixture-catalog-cover",
2332
+ FIXTURE_ATTACHMENTS: "fixture-attachments",
2333
+ UNIT_DOCUMENTATION: "unit-documentation-attachments",
2334
+ DATASHEETS: "datasheets",
2335
+ PATTERNS: "patterns",
2336
+ LOGOS: "logos",
2337
+ RECEIPTS: "receipts",
2338
+ TICKET_ATTACHMENTS: "ticket-attachment",
2339
+ PROCESS_RESULTS: "process-results",
2340
+ DATA_EXCHANGE_IMPORTS: "data-exchange-imports",
2341
+ EMAIL_TEMPLATES: "email-templates",
2342
+ EMAIL_TEMPLATES_ASSETS: "email-templates-assets"
2343
+ };
2344
+
2345
+ // src/utilities/query-utils.ts
2346
+ function createInCondition(column, values) {
2347
+ let condition = "";
2348
+ if (values.length > 2e3) {
2349
+ throw new Error("Too many values to create in condition");
2350
+ }
2351
+ chunkArray(values, 99).forEach((x, index) => {
2352
+ if (index == 0) {
2353
+ condition = `${column}.in.(${x.join(",")})`;
2354
+ return;
2355
+ }
2356
+ condition += `,${column}.in.(${x.join(",")})`;
2357
+ });
2358
+ return condition;
2359
+ }
2360
+
2361
+ // src/contexts/LiveChangeContext.tsx
2362
+ import { createContext } from "react";
2363
+ import { jsx } from "react/jsx-runtime";
2364
+ var LiveChangeContext = createContext({});
2365
+ var LiveChangeContextProvider = ({
2366
+ children,
2367
+ changeLog
2368
+ }) => {
2369
+ const supabase = useSupabase();
2370
+ const userRequest = useDbQuery(
2371
+ supabase.schema("core").from("Profile").select().eq("id", changeLog?.modifiedByUserId).single(),
2372
+ {
2373
+ enabled: isUsable(changeLog?.modifiedByUserId),
2374
+ crossOrganization: true
2375
+ }
2376
+ );
2377
+ return /* @__PURE__ */ jsx(
2378
+ LiveChangeContext.Provider,
2379
+ {
2380
+ value: {
2381
+ changeLog,
2382
+ lastModifiedUserName: userRequest.data?.firstName + " " + userRequest.data?.lastName
2383
+ },
2384
+ children
2385
+ }
2386
+ );
2387
+ };
2388
+
2389
+ // src/changelog/useChangelogMutations.ts
2390
+ var useUpsertChangelog = () => {
2391
+ return useDbUpsert(
2392
+ "Changelog",
2393
+ ["id"],
2394
+ `
2395
+ *,
2396
+ ChangelogEntry (
2397
+ *,
2398
+ ChangelogMedia (*)
2399
+ )
2400
+ `
2401
+ );
2402
+ };
2403
+ var useDeleteChangelog = () => {
2404
+ return useDbDelete("Changelog");
2405
+ };
2406
+ var useUpsertChangelogEntry = () => {
2407
+ return useDbUpsert("ChangelogEntry", ["id"], "*, ChangelogMedia (*)");
2408
+ };
2409
+ var useMultiUpsertChangelogEntries = () => {
2410
+ return useDbMultiUpsert("ChangelogEntry", ["id"], "*, ChangelogMedia (*)");
2411
+ };
2412
+ var useDeleteChangelogEntry = () => {
2413
+ return useDbDelete("ChangelogEntry");
2414
+ };
2415
+ var useMultiDeleteChangelogEntries = () => {
2416
+ return useDbMultiDelete("ChangelogEntry");
2417
+ };
2418
+ var useUpsertChangelogMedia = () => {
2419
+ return useDbUpsert("ChangelogMedia", ["id"], "*");
2420
+ };
2421
+ var useDeleteChangelogMedia = () => {
2422
+ return useDbDelete("ChangelogMedia");
2423
+ };
2424
+ var useUploadChangelogMedia = () => {
2425
+ return { mutateAsync: async () => ({ path: "", publicUrl: "" }) };
2426
+ };
2427
+
2428
+ // src/changelog/useChangelogQuery.ts
2429
+ var useChangelogs = (options) => {
2430
+ const supabase = useSupabase();
2431
+ const query = supabase.from("Changelog").select(
2432
+ `
2433
+ *,
2434
+ ChangelogEntry (
2435
+ *,
2436
+ ChangelogMedia (*)
2437
+ )
2438
+ `
2439
+ ).order("releaseDate", { ascending: false }).order("order", { foreignTable: "ChangelogEntry", ascending: true }).order("order", { foreignTable: "ChangelogEntry.ChangelogMedia", ascending: true });
2440
+ if (options?.realtime) {
2441
+ return useDbRealtimeQuery(query, { crossOrganization: true });
2442
+ }
2443
+ return useDbQuery(query, { crossOrganization: true });
2444
+ };
2445
+ var usePublishedChangelogs = (options) => {
2446
+ const supabase = useSupabase();
2447
+ const query = supabase.from("Changelog").select(
2448
+ `
2449
+ *,
2450
+ ChangelogEntry (
2451
+ *,
2452
+ ChangelogMedia (*)
2453
+ )
2454
+ `
2455
+ ).eq("isPublished", true).order("releaseDate", { ascending: false }).order("order", { foreignTable: "ChangelogEntry", ascending: true }).order("order", { foreignTable: "ChangelogEntry.ChangelogMedia", ascending: true });
2456
+ if (options?.realtime) {
2457
+ return useDbRealtimeQuery(query, { crossOrganization: true });
2458
+ }
2459
+ return useDbQuery(query, { crossOrganization: true });
2460
+ };
2461
+ var useChangelogById = (id) => {
2462
+ const supabase = useSupabase();
2463
+ return useDbQuery(
2464
+ supabase.from("Changelog").select(
2465
+ `
2466
+ *,
2467
+ ChangelogEntry (
2468
+ *,
2469
+ ChangelogMedia (*)
2470
+ )
2471
+ `
2472
+ ).eq("id", id).order("order", { foreignTable: "ChangelogEntry", ascending: true }).order("order", { foreignTable: "ChangelogEntry.ChangelogMedia", ascending: true }).single(),
2473
+ {
2474
+ enabled: !!id,
2475
+ crossOrganization: true
2476
+ }
2477
+ );
2478
+ };
2479
+ var useChangelogBySlug = (slug) => {
2480
+ const supabase = useSupabase();
2481
+ return useDbQuery(
2482
+ supabase.from("Changelog").select(
2483
+ `
2484
+ *,
2485
+ ChangelogEntry (
2486
+ *,
2487
+ ChangelogMedia (*)
2488
+ )
2489
+ `
2490
+ ).eq("slug", slug).order("order", { foreignTable: "ChangelogEntry", ascending: true }).order("order", { foreignTable: "ChangelogEntry.ChangelogMedia", ascending: true }).single(),
2491
+ {
2492
+ enabled: !!slug,
2493
+ crossOrganization: true
2494
+ }
2495
+ );
2496
+ };
2497
+ var useChangelogEntries = (changelogId) => {
2498
+ const supabase = useSupabase();
2499
+ return useDbQuery(
2500
+ supabase.from("ChangelogEntry").select("*, ChangelogMedia (*)").eq("changelogId", changelogId).order("order", { ascending: true }).order("order", { foreignTable: "ChangelogMedia", ascending: true }),
2501
+ {
2502
+ enabled: !!changelogId,
2503
+ crossOrganization: true
2504
+ }
2505
+ );
2506
+ };
2507
+ var useChangelogMedia = (entryId) => {
2508
+ const supabase = useSupabase();
2509
+ return useDbQuery(
2510
+ supabase.from("ChangelogMedia").select("*").eq("changelogEntryId", entryId).order("order", { ascending: true }),
2511
+ {
2512
+ enabled: !!entryId,
2513
+ crossOrganization: true
2514
+ }
2515
+ );
2516
+ };
2517
+
2518
+ // src/nl-training/useFeedbackList.ts
2519
+ import { useQuery as useQuery2 } from "@tanstack/react-query";
2520
+ function useFeedbackList(params = {}) {
2521
+ const supabase = useSupabase();
2522
+ return useQuery2({
2523
+ queryKey: ["feedback-list", params],
2524
+ queryFn: async () => {
2525
+ const searchParams = new URLSearchParams();
2526
+ if (params.resolved !== void 0) {
2527
+ searchParams.append("resolved", String(params.resolved));
2528
+ }
2529
+ if (params.status) {
2530
+ searchParams.append("status", params.status);
2531
+ }
2532
+ if (params.limit) {
2533
+ searchParams.append("limit", String(params.limit));
2534
+ }
2535
+ if (params.offset) {
2536
+ searchParams.append("offset", String(params.offset));
2537
+ }
2538
+ if (params.userId) {
2539
+ searchParams.append("userId", params.userId);
2540
+ }
2541
+ if (params.startDate) {
2542
+ searchParams.append("startDate", params.startDate);
2543
+ }
2544
+ if (params.endDate) {
2545
+ searchParams.append("endDate", params.endDate);
2546
+ }
2547
+ const { data, error } = await supabase.functions.invoke("query", {
2548
+ body: {},
2549
+ method: "GET",
2550
+ headers: {
2551
+ "X-Path": `/query/feedback?${searchParams.toString()}`
2552
+ }
2553
+ });
2554
+ if (error) throw error;
2555
+ return data;
2556
+ }
2557
+ });
2558
+ }
2559
+
2560
+ // src/nl-training/useSubmitFeedback.ts
2561
+ import { useMutation as useMutation6, useQueryClient } from "@tanstack/react-query";
2562
+ function useSubmitFeedback() {
2563
+ const supabase = useSupabase();
2564
+ const queryClient = useQueryClient();
2565
+ return useMutation6({
2566
+ mutationFn: async (params) => {
2567
+ const { data, error } = await supabase.functions.invoke("query", {
2568
+ body: params,
2569
+ method: "POST",
2570
+ headers: {
2571
+ "X-Path": "/query/feedback"
2572
+ }
2573
+ });
2574
+ if (error) throw error;
2575
+ return data;
2576
+ },
2577
+ onSuccess: () => {
2578
+ queryClient.invalidateQueries({ queryKey: ["feedback-list"] });
2579
+ }
2580
+ });
2581
+ }
2582
+
2583
+ // src/nl-training/useApplyFeedback.ts
2584
+ import { useMutation as useMutation7, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
2585
+ function useApplyFeedback() {
2586
+ const supabase = useSupabase();
2587
+ const queryClient = useQueryClient2();
2588
+ return useMutation7({
2589
+ mutationFn: async (memoryId) => {
2590
+ const { data, error } = await supabase.functions.invoke("query", {
2591
+ body: {},
2592
+ method: "POST",
2593
+ headers: {
2594
+ "X-Path": `/query/feedback/${memoryId}/apply`
2595
+ }
2596
+ });
2597
+ if (error) throw error;
2598
+ return data;
2599
+ },
2600
+ onSuccess: () => {
2601
+ queryClient.invalidateQueries({ queryKey: ["feedback-list"] });
2602
+ }
2603
+ });
2604
+ }
2605
+
2606
+ // src/nl-training/useResolveFeedback.ts
2607
+ import { useMutation as useMutation8, useQueryClient as useQueryClient3 } from "@tanstack/react-query";
2608
+ function useResolveFeedback() {
2609
+ const supabase = useSupabase();
2610
+ const queryClient = useQueryClient3();
2611
+ return useMutation8({
2612
+ mutationFn: async (memoryId) => {
2613
+ const { data, error } = await supabase.functions.invoke("query", {
2614
+ body: {},
2615
+ method: "POST",
2616
+ headers: {
2617
+ "X-Path": `/query/feedback/${memoryId}/resolve`
2618
+ }
2619
+ });
2620
+ if (error) throw error;
2621
+ return data;
2622
+ },
2623
+ onSuccess: () => {
2624
+ queryClient.invalidateQueries({ queryKey: ["feedback-list"] });
2625
+ }
2626
+ });
2627
+ }
2628
+
2629
+ export {
2630
+ useDbDelete,
2631
+ useDbInfiniteQuery,
2632
+ useDbInsert,
2633
+ useDbMultiDelete,
2634
+ useDbMultiUpsert,
2635
+ useDbPartialAdvanceQuery,
2636
+ useDbPartialQuery,
2637
+ useDbRealtime,
2638
+ convertFilterToRealtimeQuery,
2639
+ useDbRealtimeQuery,
2640
+ useDbUpdate,
2641
+ useMutationSuccess,
2642
+ useMutationSuccessRN,
2643
+ ADAPTER_STRATEGIES,
2644
+ AdapterRegistry,
2645
+ createAdapterRegistry,
2646
+ BackendStatus,
2647
+ AdapterAutoDetector,
2648
+ createAdapterAutoDetector,
2649
+ SupabaseAdapter,
2650
+ createSupabaseAdapter,
2651
+ getErrorBody,
2652
+ useAI,
2653
+ useServerAvailability,
2654
+ useSearchQuery,
2655
+ useLiveChangeTracking,
2656
+ useLatestOperationLog,
2657
+ newTimeActivity,
2658
+ useAutosaveState,
2659
+ useToastError,
2660
+ executeAdvanceQuery,
2661
+ useStorageUrl,
2662
+ useStoragePath,
2663
+ BUCKETS,
2664
+ createInCondition,
2665
+ LiveChangeContext,
2666
+ LiveChangeContextProvider,
2667
+ useUpsertChangelog,
2668
+ useDeleteChangelog,
2669
+ useUpsertChangelogEntry,
2670
+ useMultiUpsertChangelogEntries,
2671
+ useDeleteChangelogEntry,
2672
+ useMultiDeleteChangelogEntries,
2673
+ useUpsertChangelogMedia,
2674
+ useDeleteChangelogMedia,
2675
+ useUploadChangelogMedia,
2676
+ useChangelogs,
2677
+ usePublishedChangelogs,
2678
+ useChangelogById,
2679
+ useChangelogBySlug,
2680
+ useChangelogEntries,
2681
+ useChangelogMedia,
2682
+ useFeedbackList,
2683
+ useSubmitFeedback,
2684
+ useApplyFeedback,
2685
+ useResolveFeedback
2686
+ };
2687
+ //# sourceMappingURL=chunk-7XT7K4QT.js.map