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.
- package/dist/core/schemaManager/index.js +0 -3
- package/dist/utils/connection.d.ts +1 -0
- package/dist/utils/connection.d.ts.map +1 -1
- package/dist/utils/connection.js +21 -1
- package/dist/utils/fetchData/createTable.d.ts.map +1 -1
- package/dist/utils/fetchData/createTable.js +8 -1
- package/dist/utils/query/builder/mainQuery.js +2 -2
- package/dist/utils/query/helpers/localDb/insertMany.d.ts.map +1 -1
- package/dist/utils/query/helpers/localDb/insertMany.js +0 -3
- package/dist/utils/query/helpers/localDb/upsertMany.d.ts.map +1 -1
- package/dist/utils/query/helpers/localDb/upsertMany.js +0 -3
- package/dist/utils/query/helpers/queueRemote.d.ts.map +1 -1
- package/dist/utils/query/helpers/queueRemote.js +32 -8
- package/dist/utils/sync/pullFromRemote/updateLocalDb.js +1 -1
- package/dist/utils/sync/pushLocal/getAllUnsyncedData.d.ts.map +1 -1
- package/dist/utils/sync/pushLocal/getAllUnsyncedData.js +10 -3
- package/package.json +1 -1
|
@@ -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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/utils/connection.ts"],"names":[],"mappings":"AAEA,wBAAsB,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,
|
|
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"}
|
package/dist/utils/connection.js
CHANGED
|
@@ -3,5 +3,25 @@ export async function isOnline() {
|
|
|
3
3
|
if (!NetInfo)
|
|
4
4
|
return true;
|
|
5
5
|
const networkState = await NetInfo.fetch();
|
|
6
|
-
|
|
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;
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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;
|
|
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
|
|
14
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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 (
|
|
91
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
34
|
+
numberOfErrors.set(table, (numberOfErrors.get(table) || 0) + 1);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
30
37
|
}
|
|
31
38
|
tableSchemaData.set(table, data);
|
|
32
39
|
}
|