supastash 0.1.46 → 0.1.48

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.
@@ -110,7 +110,6 @@ async function createStandardIndexes(db, table, columns) {
110
110
  const existingCols = Array.isArray(pragmaRows)
111
111
  ? pragmaRows.map((r) => r.name)
112
112
  : [];
113
- await db.execAsync("BEGIN");
114
113
  try {
115
114
  for (const col of columns) {
116
115
  if (existingCols.includes(col)) {
@@ -120,10 +119,8 @@ async function createStandardIndexes(db, table, columns) {
120
119
  }
121
120
  }
122
121
  }
123
- await db.execAsync("COMMIT");
124
122
  }
125
123
  catch (error) {
126
- await db.execAsync("ROLLBACK");
127
124
  logError(`[Supastash] Error creating standard indexes for ${table}`, error);
128
125
  }
129
126
  }
@@ -1,2 +1,3 @@
1
1
  export declare function isOnline(): Promise<boolean>;
2
+ export declare function isNetworkError(err: any): boolean;
2
3
  //# sourceMappingURL=connection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/utils/connection.ts"],"names":[],"mappings":"AAEA,wBAAsB,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,CAIjD"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/utils/connection.ts"],"names":[],"mappings":"AAEA,wBAAsB,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,CAKjD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAehD"}
@@ -3,5 +3,25 @@ export async function isOnline() {
3
3
  if (!NetInfo)
4
4
  return true;
5
5
  const networkState = await NetInfo.fetch();
6
- return !!networkState.isConnected;
6
+ if (!networkState.isConnected)
7
+ return false;
8
+ return true;
9
+ }
10
+ export function isNetworkError(err) {
11
+ if (!err)
12
+ return false;
13
+ if (err.message === "Failed to fetch")
14
+ return true;
15
+ if (err.message?.includes("Network request failed"))
16
+ return true;
17
+ if (err.message?.includes("NetworkError"))
18
+ return true;
19
+ if (err.message?.includes("timeout"))
20
+ return true;
21
+ if (err.__isOffline === true)
22
+ return true;
23
+ const hasPgCode = typeof err.code === "string" && err.code.length > 0;
24
+ if (!hasPgCode)
25
+ return true;
26
+ return false;
7
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"createTable.d.ts","sourceRoot":"","sources":["../../../src/utils/fetchData/createTable.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AA8CtD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,iBAsCrE"}
1
+ {"version":3,"file":"createTable.d.ts","sourceRoot":"","sources":["../../../src/utils/fetchData/createTable.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAqDtD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,iBAsCrE"}
@@ -1,6 +1,7 @@
1
1
  import { getSupastashConfig } from "../../core/config";
2
2
  import { getSupastashDb } from "../../db/dbInitializer";
3
3
  import { tableSchemaData } from "../../store/tableSchemaData";
4
+ import { isNetworkError, isOnline } from "../connection";
4
5
  import log from "../logs";
5
6
  import { supabaseClientErr } from "../supabaseClientErr";
6
7
  import { checkIfTableExist } from "../tableValidator";
@@ -19,11 +20,17 @@ async function getTableSchema(table) {
19
20
  if (errorCount.get(table) && (errorCount.get(table) || 0) > 3) {
20
21
  return null;
21
22
  }
23
+ const isConnected = await isOnline();
24
+ if (!isConnected) {
25
+ return null;
26
+ }
22
27
  const { data, error } = await supabase.rpc("get_table_schema", {
23
28
  table_name: table,
24
29
  });
25
30
  if (error) {
26
- log(`[Supastash] Error getting table schema for table ${table}: ${error.message}`);
31
+ if (!isNetworkError(error)) {
32
+ log(`[Supastash] Error getting table schema for table ${table}: ${error.message}`);
33
+ }
27
34
  errorCount.set(table, (errorCount.get(table) || 0) + 1);
28
35
  return null;
29
36
  }
@@ -1,4 +1,4 @@
1
- import { logError } from "../../logs";
1
+ import { logWarn } from "../../logs";
2
2
  import { refreshScreen } from "../../refreshScreenCalls";
3
3
  import { assignInsertIds, getCommonError, runSyncStrategy, validatePayloadForSingleInsert, } from "../helpers/mainQueryHelpers";
4
4
  import { validateQuery } from "../helpers/queryValidator";
@@ -42,7 +42,7 @@ export async function queryDb(state) {
42
42
  });
43
43
  }
44
44
  catch (error) {
45
- logError(`[Supastash] ${error instanceof Error ? error.message : String(error)}`);
45
+ logWarn(`[Supastash] ${error instanceof Error ? error.message : String(error)}`);
46
46
  if (state.viewRemoteResult) {
47
47
  return Promise.resolve({
48
48
  remote: null,
@@ -1 +1 @@
1
- {"version":3,"file":"insertMany.d.ts","sourceRoot":"","sources":["../../../../../src/utils/query/helpers/localDb/insertMany.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAIzD,UAAU,aAAa,CAAC,CAAC,GAAG,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAMD,wBAAsB,UAAU,CAAC,CAAC,GAAG,GAAG,EACtC,OAAO,EAAE,CAAC,EAAE,EACZ,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,GACrB,OAAO,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CA4GrB"}
1
+ {"version":3,"file":"insertMany.d.ts","sourceRoot":"","sources":["../../../../../src/utils/query/helpers/localDb/insertMany.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAIzD,UAAU,aAAa,CAAC,CAAC,GAAG,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAMD,wBAAsB,UAAU,CAAC,CAAC,GAAG,GAAG,EACtC,OAAO,EAAE,CAAC,EAAE,EACZ,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,GACrB,OAAO,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAyGrB"}
@@ -62,13 +62,10 @@ export async function insertMany(payload, opts) {
62
62
  await microYield();
63
63
  }
64
64
  };
65
- await db.runAsync("BEGIN");
66
65
  try {
67
66
  await run();
68
- await db.runAsync("COMMIT");
69
67
  }
70
68
  catch (e) {
71
- await db.runAsync("ROLLBACK");
72
69
  throw e;
73
70
  }
74
71
  // 4) Optionally fetch inserted rows (batched) and return in input order
@@ -1 +1 @@
1
- {"version":3,"file":"upsertMany.d.ts","sourceRoot":"","sources":["../../../../../src/utils/query/helpers/localDb/upsertMany.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,cAAc,EACd,QAAQ,EACT,MAAM,+BAA+B,CAAC;AAKvC,UAAU,aAAa,CAAC,CAAC,GAAG,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD,wBAAsB,UAAU,CAAC,CAAC,GAAG,GAAG,EACtC,KAAK,EAAE,CAAC,EAAE,EACV,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,EACtB,KAAK,EAAE,cAAc,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,GAC7C,OAAO,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAuLrB"}
1
+ {"version":3,"file":"upsertMany.d.ts","sourceRoot":"","sources":["../../../../../src/utils/query/helpers/localDb/upsertMany.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,cAAc,EACd,QAAQ,EACT,MAAM,+BAA+B,CAAC;AAKvC,UAAU,aAAa,CAAC,CAAC,GAAG,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD,wBAAsB,UAAU,CAAC,CAAC,GAAG,GAAG,EACtC,KAAK,EAAE,CAAC,EAAE,EACV,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,EACtB,KAAK,EAAE,cAAc,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,GAC7C,OAAO,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAoLrB"}
@@ -119,17 +119,14 @@ export async function upsertMany(items, opts, state) {
119
119
  }
120
120
  }
121
121
  };
122
- await db.runAsync("BEGIN");
123
122
  try {
124
123
  await run();
125
124
  const newState = { ...state, payload: remotePayload };
126
125
  if (remoteCalls.includes(newState.type)) {
127
126
  queueRemoteCall(newState);
128
127
  }
129
- await db.runAsync("COMMIT");
130
128
  }
131
129
  catch (e) {
132
- await db.runAsync("ROLLBACK");
133
130
  throw e;
134
131
  }
135
132
  return returnRows ? upserted : undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"queueRemote.d.ts","sourceRoot":"","sources":["../../../../src/utils/query/helpers/queueRemote.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,cAAc,EACf,MAAM,4BAA4B,CAAC;AA8BpC,wBAAgB,eAAe,CAAC,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,OAAO,EAAE,CAAC,EACzE,KAAK,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC7B,OAAO,CAAC,OAAO,CAAC,CAYlB"}
1
+ {"version":3,"file":"queueRemote.d.ts","sourceRoot":"","sources":["../../../../src/utils/query/helpers/queueRemote.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,cAAc,EACf,MAAM,4BAA4B,CAAC;AA0CpC,wBAAgB,eAAe,CAAC,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,OAAO,EAAE,CAAC,EACzE,KAAK,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC7B,OAAO,CAAC,OAAO,CAAC,CAYlB"}
@@ -7,11 +7,18 @@ let batchQueue = [];
7
7
  let isProcessing = false;
8
8
  let batchTimer = null;
9
9
  const BATCH_DELAY = 10;
10
+ const MAX_RETRIES = 3;
11
+ const MAX_OFFLINE_RETRIES = 5;
10
12
  const retryCount = new Map();
11
13
  const calledOfflineRetries = new Map();
12
14
  const successfulCalls = new Set();
13
- const MAX_RETRIES = 3;
14
- const MAX_OFFLINE_RETRIES = 5;
15
+ const seenFailureLog = new Set();
16
+ function isDuplicateKeyError(error) {
17
+ return (!!error &&
18
+ ((error.code && error.code === "23505") ||
19
+ (typeof error.message === "string" &&
20
+ error.message.toLowerCase().includes("duplicate key"))));
21
+ }
15
22
  function delay(ms) {
16
23
  return new Promise((resolve) => setTimeout(resolve, ms));
17
24
  }
@@ -54,14 +61,17 @@ async function processBatch() {
54
61
  const isConnected = await isOnline();
55
62
  if (!isConnected) {
56
63
  const offlineRetries = (calledOfflineRetries.get(opKey) || 0) + 1;
64
+ calledOfflineRetries.set(opKey, offlineRetries);
57
65
  if (offlineRetries > MAX_OFFLINE_RETRIES) {
66
+ if (!seenFailureLog.has(opKey)) {
67
+ seenFailureLog.add(opKey);
68
+ log(`[Supastash] Offline — persisted locally, will retry later: ${opKey}`);
69
+ }
58
70
  await getQueryStatusFromDb(state.table);
59
71
  supastashEventBus.emit("updateSyncStatus");
60
- logWarn(`[Supastash] Gave up on ${opKey} after ${MAX_OFFLINE_RETRIES} offline retries`);
61
72
  reject(new Error(`Offline retry limit exceeded for ${opKey}`));
62
73
  break;
63
74
  }
64
- calledOfflineRetries.set(opKey, offlineRetries);
65
75
  await delay(1000);
66
76
  continue;
67
77
  }
@@ -77,9 +87,19 @@ async function processBatch() {
77
87
  else {
78
88
  const currentRetries = retryCount.get(opKey) ?? 0;
79
89
  retryCount.set(opKey, currentRetries + 1);
90
+ if (isDuplicateKeyError(error)) {
91
+ if (!seenFailureLog.has(opKey)) {
92
+ seenFailureLog.add(opKey);
93
+ logWarn(`[Supastash] Duplicate key on ${state.table} (op=${state.method}) — seems already synced; will retry on next full sync: id=${state.id ?? "-"}`);
94
+ }
95
+ reject(new Error(`Duplicate key (23505) for ${opKey}`));
96
+ break;
97
+ }
80
98
  if (currentRetries >= MAX_RETRIES) {
81
- logWarn(`[Supastash] Gave up on ${opKey} after ${MAX_RETRIES} retries ERROR: ${error.message} will retry on next sync`);
82
- logWarn("[Supastash] Full error object:", JSON.stringify(error));
99
+ if (!seenFailureLog.has(opKey)) {
100
+ seenFailureLog.add(opKey);
101
+ logWarn(`[Supastash] Gave up on ${opKey} after ${MAX_RETRIES} retries — will retry on next sync`);
102
+ }
83
103
  reject(new Error(`Max retries exceeded for ${opKey}`));
84
104
  break;
85
105
  }
@@ -87,8 +107,12 @@ async function processBatch() {
87
107
  }
88
108
  }
89
109
  }
90
- catch (error) {
91
- reject(error);
110
+ catch (err) {
111
+ if (!seenFailureLog.has(opKey)) {
112
+ seenFailureLog.add(opKey);
113
+ logWarn(`[Supastash] Unexpected error processing ${opKey} — ${String(err.message ?? err)}`);
114
+ }
115
+ reject(err);
92
116
  }
93
117
  }
94
118
  isProcessing = false;
@@ -90,7 +90,7 @@ export async function updateLocalDb(table, filters, onReceiveData) {
90
90
  refreshScreen(table);
91
91
  }
92
92
  catch (error) {
93
- logError(`[Supastash] Error updating local db for ${table}`, error);
93
+ logWarn(`[Supastash] Error updating local db for ${table}`, error);
94
94
  throw error;
95
95
  }
96
96
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"getAllUnsyncedData.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pushLocal/getAllUnsyncedData.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAsEzD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,CAe/B;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,CAM/B"}
1
+ {"version":3,"file":"getAllUnsyncedData.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pushLocal/getAllUnsyncedData.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AA6EzD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,CAe/B;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,CAM/B"}
@@ -3,6 +3,7 @@ import { getSupastashDb } from "../../../db/dbInitializer";
3
3
  import { tableSchemaData } from "../../../store/tableSchemaData";
4
4
  import { getTableSchema } from "../../../utils/getTableSchema";
5
5
  import log from "../../../utils/logs";
6
+ import { isNetworkError, isOnline } from "../../connection";
6
7
  import { supabaseClientErr } from "../../supabaseClientErr";
7
8
  const numberOfErrors = new Map();
8
9
  const sharedKeysCache = new Map();
@@ -19,14 +20,20 @@ async function getRemoteKeys(table) {
19
20
  return null;
20
21
  }
21
22
  if (!tableSchemaData.has(table)) {
23
+ const isConnected = await isOnline();
24
+ if (!isConnected) {
25
+ return null;
26
+ }
22
27
  const { data, error } = await supabase.rpc("get_table_schema", {
23
28
  table_name: table,
24
29
  });
25
30
  if (error) {
26
- log(`[Supastash] Error getting remote keys for table ${table} on public schema: ${error.message}
31
+ if (!isNetworkError(error)) {
32
+ log(`[Supastash] Error getting remote keys for table ${table} on public schema: ${error.message}
27
33
  You can find more information in the Supastash docs: https://0xzekea.github.io/supastash/docs/getting-started#%EF%B8%8F-server-side-setup-for-filtered-pulls`);
28
- numberOfErrors.set(table, (numberOfErrors.get(table) || 0) + 1);
29
- return null;
34
+ numberOfErrors.set(table, (numberOfErrors.get(table) || 0) + 1);
35
+ return null;
36
+ }
30
37
  }
31
38
  tableSchemaData.set(table, data);
32
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supastash",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",