@pol-studios/db 1.0.54 → 1.0.56

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 (77) hide show
  1. package/dist/{DataLayerContext-C7cJtiO8.d.ts → DataLayerContext-BYZtDD0g.d.ts} +1 -1
  2. package/dist/auth/context.js +6 -4
  3. package/dist/auth/hooks.js +7 -5
  4. package/dist/auth/index.js +7 -5
  5. package/dist/{chunk-FIAXWEBK.js → chunk-4EO55YV2.js} +10 -7
  6. package/dist/chunk-4EO55YV2.js.map +1 -0
  7. package/dist/{chunk-DP3YEVSX.js → chunk-6SDH7M7J.js} +26 -10
  8. package/dist/chunk-6SDH7M7J.js.map +1 -0
  9. package/dist/{chunk-UJWETW36.js → chunk-AKIRHA4Q.js} +527 -418
  10. package/dist/chunk-AKIRHA4Q.js.map +1 -0
  11. package/dist/{chunk-2XS2PM62.js → chunk-DDL63KLQ.js} +388 -107
  12. package/dist/chunk-DDL63KLQ.js.map +1 -0
  13. package/dist/{chunk-YA6MUTA7.js → chunk-FI6JAD5G.js} +3 -3
  14. package/dist/{chunk-WQLIGVQR.js → chunk-GWYTROSD.js} +98 -1
  15. package/dist/chunk-GWYTROSD.js.map +1 -0
  16. package/dist/chunk-JOULSXOI.js +415 -0
  17. package/dist/chunk-JOULSXOI.js.map +1 -0
  18. package/dist/{chunk-OKYHI6JG.js → chunk-LF3V3ERS.js} +3 -3
  19. package/dist/{chunk-FMYXG4VN.js → chunk-MEBT5YHA.js} +2 -2
  20. package/dist/{chunk-BZSAPFFB.js → chunk-N4KK5G5T.js} +116 -18
  21. package/dist/chunk-N4KK5G5T.js.map +1 -0
  22. package/dist/chunk-QYAFI34Q.js +64 -0
  23. package/dist/chunk-QYAFI34Q.js.map +1 -0
  24. package/dist/{chunk-3Q74DK5K.js → chunk-VYFAMTHI.js} +2 -2
  25. package/dist/chunk-W7PERM66.js +215 -0
  26. package/dist/chunk-W7PERM66.js.map +1 -0
  27. package/dist/{chunk-ZGQ7Q4ZU.js → chunk-WM25QE7E.js} +2 -2
  28. package/dist/{chunk-HZIVE5AZ.js → chunk-YRIPM2AN.js} +253 -338
  29. package/dist/chunk-YRIPM2AN.js.map +1 -0
  30. package/dist/chunk-YUX6RGLZ.js +1858 -0
  31. package/dist/chunk-YUX6RGLZ.js.map +1 -0
  32. package/dist/{chunk-Z3EJX3VG.js → chunk-Z456IHCB.js} +3 -3
  33. package/dist/core/index.d.ts +24 -1
  34. package/dist/{executor-YJw4m7Q7.d.ts → executor-D15yjeMo.d.ts} +20 -0
  35. package/dist/hooks/index.d.ts +3 -3
  36. package/dist/hooks/index.js +4 -2
  37. package/dist/{index-jVYdTeWx.d.ts → index-CFUuTzXO.d.ts} +1 -1
  38. package/dist/index.d.ts +5 -5
  39. package/dist/index.js +16 -14
  40. package/dist/index.native.d.ts +62 -8
  41. package/dist/index.native.js +16 -14
  42. package/dist/index.web.d.ts +10 -9
  43. package/dist/index.web.js +30 -19
  44. package/dist/index.web.js.map +1 -1
  45. package/dist/mutation/index.js +3 -3
  46. package/dist/parser/index.js +3 -3
  47. package/dist/powersync-bridge/index.d.ts +1 -1
  48. package/dist/query/index.d.ts +4 -83
  49. package/dist/query/index.js +17 -7
  50. package/dist/realtime/index.d.ts +80 -1
  51. package/dist/realtime/index.js +14 -12
  52. package/dist/realtime/index.js.map +1 -1
  53. package/dist/select-parser-BAV7fOaM.d.ts +144 -0
  54. package/dist/types/index.d.ts +3 -3
  55. package/dist/types/index.js +4 -4
  56. package/dist/{useDbCount-DHLJzmkO.d.ts → useDbCount-Ckb-FhZk.d.ts} +1 -1
  57. package/dist/{useResolveFeedback-B0UcYWVI.d.ts → useResolveFeedback-CuUkdHoR.d.ts} +13 -29
  58. package/dist/with-auth/index.js +9 -7
  59. package/dist/with-auth/index.js.map +1 -1
  60. package/package.json +9 -4
  61. package/dist/chunk-2XS2PM62.js.map +0 -1
  62. package/dist/chunk-BZSAPFFB.js.map +0 -1
  63. package/dist/chunk-CTRY7JDP.js +0 -4112
  64. package/dist/chunk-CTRY7JDP.js.map +0 -1
  65. package/dist/chunk-DP3YEVSX.js.map +0 -1
  66. package/dist/chunk-FIAXWEBK.js.map +0 -1
  67. package/dist/chunk-HZIVE5AZ.js.map +0 -1
  68. package/dist/chunk-INEUG6MC.js +0 -521
  69. package/dist/chunk-INEUG6MC.js.map +0 -1
  70. package/dist/chunk-UJWETW36.js.map +0 -1
  71. package/dist/chunk-WQLIGVQR.js.map +0 -1
  72. /package/dist/{chunk-YA6MUTA7.js.map → chunk-FI6JAD5G.js.map} +0 -0
  73. /package/dist/{chunk-OKYHI6JG.js.map → chunk-LF3V3ERS.js.map} +0 -0
  74. /package/dist/{chunk-FMYXG4VN.js.map → chunk-MEBT5YHA.js.map} +0 -0
  75. /package/dist/{chunk-3Q74DK5K.js.map → chunk-VYFAMTHI.js.map} +0 -0
  76. /package/dist/{chunk-ZGQ7Q4ZU.js.map → chunk-WM25QE7E.js.map} +0 -0
  77. /package/dist/{chunk-Z3EJX3VG.js.map → chunk-Z456IHCB.js.map} +0 -0
@@ -3,17 +3,20 @@ import {
3
3
  createAdapterRegistry,
4
4
  createSupabaseAdapter,
5
5
  stripSchemaPrefix
6
- } from "./chunk-2XS2PM62.js";
6
+ } from "./chunk-DDL63KLQ.js";
7
7
  import {
8
8
  DataLayerContext,
9
9
  DataLayerCoreContext,
10
+ DataLayerNestingContext,
10
11
  DataLayerStatusContext
11
- } from "./chunk-WQLIGVQR.js";
12
+ } from "./chunk-GWYTROSD.js";
13
+ import {
14
+ QueryExecutor
15
+ } from "./chunk-YRIPM2AN.js";
12
16
  import {
13
- QueryExecutor,
14
17
  extractRelationNames,
15
18
  parseSelect
16
- } from "./chunk-HZIVE5AZ.js";
19
+ } from "./chunk-W7PERM66.js";
17
20
  import {
18
21
  useSupabase
19
22
  } from "./chunk-DMVUEJG2.js";
@@ -1104,19 +1107,27 @@ function transformWithRelations(row, schema, tableName) {
1104
1107
  }
1105
1108
 
1106
1109
  // src/adapters/powersync-adapter.ts
1110
+ import stringify from "fast-json-stable-stringify";
1111
+ var DEFAULT_MAX_SUBSCRIPTIONS = 100;
1107
1112
  var PowerSyncAdapter = class {
1108
1113
  /**
1109
1114
  * Create a new PowerSyncAdapter
1110
1115
  *
1111
1116
  * @param db - PowerSync database instance
1112
1117
  * @param schema - Database schema for relationship resolution
1113
- * @param tableNameResolver - Optional custom resolver for table names to PowerSync aliases
1118
+ * @param options - Optional configuration (tableNameResolver, maxSubscriptions)
1114
1119
  */
1115
- constructor(db, schema, tableNameResolver) {
1120
+ constructor(db, schema, options) {
1116
1121
  this.db = db;
1117
1122
  this.schema = schema;
1118
1123
  this.executor = new QueryExecutor(db, schema);
1119
- this.tableNameResolver = tableNameResolver;
1124
+ if (typeof options === "function") {
1125
+ this.tableNameResolver = options;
1126
+ this.maxSubscriptions = DEFAULT_MAX_SUBSCRIPTIONS;
1127
+ } else {
1128
+ this.tableNameResolver = options?.tableNameResolver;
1129
+ this.maxSubscriptions = options?.maxSubscriptions ?? DEFAULT_MAX_SUBSCRIPTIONS;
1130
+ }
1120
1131
  }
1121
1132
  /**
1122
1133
  * Unique identifier for this adapter type
@@ -1140,6 +1151,31 @@ var PowerSyncAdapter = class {
1140
1151
  * If not provided, uses default auto-alias generation.
1141
1152
  */
1142
1153
  tableNameResolver;
1154
+ /**
1155
+ * Maximum number of subscriptions to keep in the registry.
1156
+ */
1157
+ maxSubscriptions;
1158
+ /**
1159
+ * Subscription deduplication registry.
1160
+ * Maps cache keys (table + serialized options) to shared subscription entries.
1161
+ * This prevents duplicate PowerSync watch() calls when multiple components
1162
+ * subscribe to the same query.
1163
+ *
1164
+ * Uses Map insertion order for LRU eviction - most recently accessed entries
1165
+ * are moved to the end by deleting and re-adding.
1166
+ */
1167
+ subscriptionRegistry = /* @__PURE__ */ new Map();
1168
+ /**
1169
+ * Move a subscription entry to the end of the Map for LRU tracking.
1170
+ * The most recently accessed entries are at the end; oldest are at the start.
1171
+ *
1172
+ * @param key - The subscription cache key
1173
+ * @param entry - The subscription entry to touch
1174
+ */
1175
+ touchSubscription(key, entry) {
1176
+ this.subscriptionRegistry.delete(key);
1177
+ this.subscriptionRegistry.set(key, entry);
1178
+ }
1143
1179
  /**
1144
1180
  * Resolve a table name to its PowerSync alias.
1145
1181
  * Schema-qualified names like "chat.Conversation" become "Conversation" (schema stripped).
@@ -1193,12 +1229,31 @@ var PowerSyncAdapter = class {
1193
1229
  }
1194
1230
  return transformWithRelations(rawResult, this.schema, table);
1195
1231
  }
1232
+ /**
1233
+ * Generate a unique cache key for subscription deduplication.
1234
+ * Combines table name and serialized options to identify identical queries.
1235
+ * Uses sorted keys to ensure consistent serialization regardless of property order.
1236
+ *
1237
+ * @param table - The table name
1238
+ * @param options - Query options
1239
+ * @returns A unique string key for the subscription registry
1240
+ */
1241
+ generateSubscriptionKey(table, options) {
1242
+ return `${table}:${stringify(options)}`;
1243
+ }
1196
1244
  /**
1197
1245
  * Subscribe to changes on a query using PowerSync's native watch() API.
1198
1246
  *
1199
1247
  * This uses PowerSync's reactive query watching which efficiently detects
1200
1248
  * changes to the underlying data and re-executes the query only when needed.
1201
1249
  *
1250
+ * Subscription Deduplication:
1251
+ * Multiple components subscribing to the same query (same table + options)
1252
+ * will share a single underlying PowerSync watch. This optimization:
1253
+ * - Reduces database load by avoiding duplicate watches
1254
+ * - Provides instant data to new subscribers from cached results
1255
+ * - Properly cleans up when all subscribers unsubscribe
1256
+ *
1202
1257
  * If the database doesn't support watch(), falls back to polling every 5 seconds.
1203
1258
  *
1204
1259
  * @param table - The table name to watch
@@ -1211,6 +1266,33 @@ var PowerSyncAdapter = class {
1211
1266
  if (!this.db.watch) {
1212
1267
  return this.subscribeWithPolling(table, options, callback);
1213
1268
  }
1269
+ const cacheKey = this.generateSubscriptionKey(resolvedTable, options);
1270
+ const existingEntry = this.subscriptionRegistry.get(cacheKey);
1271
+ if (existingEntry) {
1272
+ existingEntry.subscribers.add(callback);
1273
+ this.touchSubscription(cacheKey, existingEntry);
1274
+ return () => {
1275
+ existingEntry.subscribers.delete(callback);
1276
+ if (existingEntry.subscribers.size === 0) {
1277
+ existingEntry.unsubscribe();
1278
+ this.subscriptionRegistry.delete(cacheKey);
1279
+ }
1280
+ };
1281
+ }
1282
+ if (this.subscriptionRegistry.size >= this.maxSubscriptions) {
1283
+ let evicted = false;
1284
+ for (const [key, entry2] of this.subscriptionRegistry) {
1285
+ if (entry2.subscribers.size === 0) {
1286
+ entry2.unsubscribe();
1287
+ this.subscriptionRegistry.delete(key);
1288
+ evicted = true;
1289
+ break;
1290
+ }
1291
+ }
1292
+ if (!evicted && typeof __DEV__ !== "undefined" && __DEV__) {
1293
+ console.warn(`[PowerSyncAdapter] Subscription registry exceeded max (${this.maxSubscriptions}) but all entries have active subscribers. Registry size: ${this.subscriptionRegistry.size + 1}`);
1294
+ }
1295
+ }
1214
1296
  const abortController = new AbortController();
1215
1297
  const parsed = parseSelect(options.select ?? "*");
1216
1298
  const builder = this.executor.getBuilder();
@@ -1225,16 +1307,22 @@ var PowerSyncAdapter = class {
1225
1307
  });
1226
1308
  let watchTables = [resolvedTable];
1227
1309
  if (options.select && options.select !== "*") {
1228
- const parsed2 = parseSelect(options.select);
1229
- const relationNames = extractRelationNames(parsed2);
1310
+ const relationNames = extractRelationNames(parsed);
1230
1311
  watchTables = [resolvedTable, ...relationNames.map((r) => this.resolveTableName(r))];
1231
1312
  }
1313
+ const entry = {
1314
+ subscribers: /* @__PURE__ */ new Set([callback]),
1315
+ unsubscribe: () => abortController.abort()
1316
+ };
1317
+ this.subscriptionRegistry.set(cacheKey, entry);
1232
1318
  const schema = this.schema;
1233
1319
  this.db.watch(sql, params, {
1234
1320
  onResult: (results) => {
1235
1321
  const rawData = results.rows?._array ?? [];
1236
1322
  const data = transformResultsFromStorage(rawData, schema, table);
1237
- callback(data);
1323
+ for (const subscriber of entry.subscribers) {
1324
+ subscriber(data);
1325
+ }
1238
1326
  },
1239
1327
  onError: (error) => {
1240
1328
  console.error(`PowerSync subscription error for ${resolvedTable}:`, error);
@@ -1246,7 +1334,11 @@ var PowerSyncAdapter = class {
1246
1334
  // Throttle to prevent excessive re-queries
1247
1335
  });
1248
1336
  return () => {
1249
- abortController.abort();
1337
+ entry.subscribers.delete(callback);
1338
+ if (entry.subscribers.size === 0) {
1339
+ entry.unsubscribe();
1340
+ this.subscriptionRegistry.delete(cacheKey);
1341
+ }
1250
1342
  };
1251
1343
  }
1252
1344
  /**
@@ -1407,7 +1499,7 @@ function createPowerSyncAdapter(db, schema) {
1407
1499
  }
1408
1500
 
1409
1501
  // src/providers/DataLayerProvider.tsx
1410
- import { useState, useEffect, useMemo, useCallback, useRef } from "react";
1502
+ import { useState, useEffect, useMemo, useCallback, useRef, useContext } from "react";
1411
1503
  import { jsx } from "react/jsx-runtime";
1412
1504
  var defaultSyncStatus = {
1413
1505
  isConnected: false,
@@ -1454,6 +1546,12 @@ function DataLayerProvider({
1454
1546
  powerSyncSyncStatus,
1455
1547
  syncControl: externalSyncControl
1456
1548
  }) {
1549
+ const isNested = useContext(DataLayerNestingContext);
1550
+ const hasWarnedNesting = useRef(false);
1551
+ if (isNested && !hasWarnedNesting.current && typeof __DEV__ !== "undefined" && __DEV__) {
1552
+ hasWarnedNesting.current = true;
1553
+ console.warn("[DataLayerProvider] Nested DataLayerProvider detected! This usually indicates a setup issue where DataLayerProvider is wrapped twice. Each DataLayerProvider creates its own registry, which can cause:\n - Queries using wrong adapter (e.g., Supabase instead of PowerSync)\n - Inconsistent cache state\n - Unexpected behavior with sync status\n\nCommon causes:\n - Using both DataLayerWrapper and OfflineDataProvider (OfflineDataProvider already includes DataLayerProvider)\n - Accidentally wrapping layout twice\n\nFix: Remove the outer DataLayerProvider wrapper.");
1554
+ }
1457
1555
  const [registry] = useState(() => createAdapterRegistry(config));
1458
1556
  const powerSyncInstanceRef = useRef(powerSyncInstance);
1459
1557
  powerSyncInstanceRef.current = powerSyncInstance;
@@ -1699,7 +1797,7 @@ function DataLayerProvider({
1699
1797
  const resolvedCore = coreContextValue ?? pendingCoreContextValue;
1700
1798
  const resolvedStatus = statusContextValue ?? pendingStatusContextValue;
1701
1799
  const resolvedContext = contextValue ?? pendingContextValue;
1702
- return /* @__PURE__ */ jsx(DataLayerContext.Provider, { value: resolvedContext, children: /* @__PURE__ */ jsx(DataLayerCoreContext.Provider, { value: resolvedCore, children: /* @__PURE__ */ jsx(DataLayerStatusContext.Provider, { value: resolvedStatus, children }) }) });
1800
+ return /* @__PURE__ */ jsx(DataLayerNestingContext.Provider, { value: true, children: /* @__PURE__ */ jsx(DataLayerContext.Provider, { value: resolvedContext, children: /* @__PURE__ */ jsx(DataLayerCoreContext.Provider, { value: resolvedCore, children: /* @__PURE__ */ jsx(DataLayerStatusContext.Provider, { value: resolvedStatus, children }) }) }) });
1703
1801
  }
1704
1802
 
1705
1803
  // src/storage/use-supabase-upload.tsx
@@ -4630,9 +4728,9 @@ function _temp(err) {
4630
4728
 
4631
4729
  // src/conflicts/useConflictState.ts
4632
4730
  import { c as _c2 } from "react/compiler-runtime";
4633
- import { useContext } from "react";
4731
+ import { useContext as useContext2 } from "react";
4634
4732
  function useConflictState() {
4635
- const context = useContext(ConflictContext);
4733
+ const context = useContext2(ConflictContext);
4636
4734
  if (!context) {
4637
4735
  throw new Error("useConflictState must be used within a ConflictProvider. Wrap your app with <ConflictProvider> to enable conflict management.");
4638
4736
  }
@@ -4640,7 +4738,7 @@ function useConflictState() {
4640
4738
  }
4641
4739
  function usePendingConflicts() {
4642
4740
  const $ = _c2(2);
4643
- const context = useContext(ConflictContext);
4741
+ const context = useContext2(ConflictContext);
4644
4742
  let t0;
4645
4743
  if ($[0] !== context?.pendingConflicts) {
4646
4744
  t0 = context?.pendingConflicts ?? [];
@@ -4652,7 +4750,7 @@ function usePendingConflicts() {
4652
4750
  return t0;
4653
4751
  }
4654
4752
  function useHasConflicts() {
4655
- const context = useContext(ConflictContext);
4753
+ const context = useContext2(ConflictContext);
4656
4754
  return context?.hasConflicts ?? false;
4657
4755
  }
4658
4756
  function useConflictForRecord(table, recordId) {
@@ -4968,4 +5066,4 @@ object-assign/index.js:
4968
5066
  @license MIT
4969
5067
  *)
4970
5068
  */
4971
- //# sourceMappingURL=chunk-BZSAPFFB.js.map
5069
+ //# sourceMappingURL=chunk-N4KK5G5T.js.map