supastash 0.1.27 → 0.1.28
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/hooks/syncStatus/index.d.ts +18 -0
- package/dist/hooks/syncStatus/index.d.ts.map +1 -0
- package/dist/hooks/syncStatus/index.js +34 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/store/syncStatus.d.ts +24 -0
- package/dist/store/syncStatus.d.ts.map +1 -0
- package/dist/store/syncStatus.js +23 -0
- package/dist/utils/query/helpers/mainQueryHelpers.d.ts.map +1 -1
- package/dist/utils/query/helpers/mainQueryHelpers.js +4 -0
- package/dist/utils/query/remoteQuery/supabaseQuery.d.ts.map +1 -1
- package/dist/utils/query/remoteQuery/supabaseQuery.js +6 -0
- package/dist/utils/refreshScreenCalls.d.ts.map +1 -1
- package/dist/utils/refreshScreenCalls.js +1 -0
- package/dist/utils/sync/pushLocal/deleteChunks.d.ts.map +1 -1
- package/dist/utils/sync/pushLocal/deleteChunks.js +15 -3
- package/dist/utils/sync/pushLocal/sendUnsyncedToSupabase.d.ts.map +1 -1
- package/dist/utils/sync/pushLocal/sendUnsyncedToSupabase.js +2 -6
- package/dist/utils/sync/pushLocal/uploadChunk.d.ts.map +1 -1
- package/dist/utils/sync/pushLocal/uploadChunk.js +16 -1
- package/dist/utils/sync/queryStatus.d.ts +40 -0
- package/dist/utils/sync/queryStatus.d.ts.map +1 -0
- package/dist/utils/sync/queryStatus.js +93 -0
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook that returns the current global sync status across all tracked Supastash tables.
|
|
3
|
+
*
|
|
4
|
+
* - Listens for the "updateSyncStatus" event from the event bus.
|
|
5
|
+
* - Recomputes sync status whenever the event is emitted (e.g. after CRUD operations).
|
|
6
|
+
* - Status can be:
|
|
7
|
+
* - "pending" → at least one table has pending sync rows
|
|
8
|
+
* - "error" → at least one table has failed sync rows
|
|
9
|
+
* - "synced" → all tracked tables are fully synced
|
|
10
|
+
*
|
|
11
|
+
* @returns {"pending" | "error" | "synced"} The current sync status
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const syncStatus = useSupastashSyncStatus();
|
|
15
|
+
* if (syncStatus === "pending") showSyncingIndicator();
|
|
16
|
+
*/
|
|
17
|
+
export declare function useSupastashSyncStatus(): "error" | "pending" | "synced";
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/syncStatus/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,mCAiBrC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { supastashEventBus } from "../../utils/events/eventBus";
|
|
3
|
+
import { getSupastashStatus } from "../../utils/sync/queryStatus";
|
|
4
|
+
/**
|
|
5
|
+
* React hook that returns the current global sync status across all tracked Supastash tables.
|
|
6
|
+
*
|
|
7
|
+
* - Listens for the "updateSyncStatus" event from the event bus.
|
|
8
|
+
* - Recomputes sync status whenever the event is emitted (e.g. after CRUD operations).
|
|
9
|
+
* - Status can be:
|
|
10
|
+
* - "pending" → at least one table has pending sync rows
|
|
11
|
+
* - "error" → at least one table has failed sync rows
|
|
12
|
+
* - "synced" → all tracked tables are fully synced
|
|
13
|
+
*
|
|
14
|
+
* @returns {"pending" | "error" | "synced"} The current sync status
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const syncStatus = useSupastashSyncStatus();
|
|
18
|
+
* if (syncStatus === "pending") showSyncingIndicator();
|
|
19
|
+
*/
|
|
20
|
+
export function useSupastashSyncStatus() {
|
|
21
|
+
const [syncStatus, setSyncStatus] = useState("synced");
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const refreshSyncStatus = () => {
|
|
24
|
+
const status = getSupastashStatus();
|
|
25
|
+
setSyncStatus(status);
|
|
26
|
+
};
|
|
27
|
+
refreshSyncStatus();
|
|
28
|
+
supastashEventBus.on("updateSyncStatus", refreshSyncStatus);
|
|
29
|
+
return () => {
|
|
30
|
+
supastashEventBus.off("updateSyncStatus", refreshSyncStatus);
|
|
31
|
+
};
|
|
32
|
+
}, []);
|
|
33
|
+
return syncStatus;
|
|
34
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { getSupastashDb } from "./db/dbInitializer";
|
|
|
4
4
|
export { useSupastashData } from "./hooks/supastashData";
|
|
5
5
|
export { useSupastash } from "./hooks/supastashLogic";
|
|
6
6
|
export { syncAllTables, syncTable } from "./hooks/syncEngine";
|
|
7
|
+
export { useSupastashSyncStatus } from "./hooks/syncStatus";
|
|
7
8
|
export { supastashEventBus } from "./utils/events/eventBus";
|
|
8
9
|
export { supastash } from "./utils/query/builder";
|
|
9
10
|
export { dropAllTables, dropTable, wipeAllTables, wipeOldDataForAllTables, wipeOldDataForATable, wipeTable, } from "./utils/schema/wipeTables";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EACL,aAAa,EACb,SAAS,EACT,aAAa,EACb,uBAAuB,EACvB,oBAAoB,EACpB,SAAS,GACV,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,eAAe,GAChB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,+BAA+B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EACL,aAAa,EACb,SAAS,EACT,aAAa,EACb,uBAAuB,EACvB,oBAAoB,EACpB,SAAS,GACV,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,eAAe,GAChB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,+BAA+B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export { useSupastashData } from "./hooks/supastashData";
|
|
|
5
5
|
//export { useSupastashLiteQuery } from "./hooks/supastashLiteQuery";
|
|
6
6
|
export { useSupastash } from "./hooks/supastashLogic";
|
|
7
7
|
export { syncAllTables, syncTable } from "./hooks/syncEngine";
|
|
8
|
+
export { useSupastashSyncStatus } from "./hooks/syncStatus";
|
|
8
9
|
export { supastashEventBus } from "./utils/events/eventBus";
|
|
9
10
|
export { supastash } from "./utils/query/builder";
|
|
10
11
|
export { dropAllTables, dropTable, wipeAllTables, wipeOldDataForAllTables, wipeOldDataForATable, wipeTable, } from "./utils/schema/wipeTables";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A map tracking sync status for each row in each table.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* {
|
|
6
|
+
* "table1": {
|
|
7
|
+
* "1": "pending",
|
|
8
|
+
* "2": "success",
|
|
9
|
+
* "3": "error"
|
|
10
|
+
* },
|
|
11
|
+
* "table2": {
|
|
12
|
+
* "a": "success",
|
|
13
|
+
* "b": "pending"
|
|
14
|
+
* }
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* This structure means:
|
|
18
|
+
* - `syncStatusMap.get("table1")?.get("1")` would return "pending"
|
|
19
|
+
* - Outer key = table name
|
|
20
|
+
* - Inner key = row ID (as string)
|
|
21
|
+
* - Value = sync status of that row
|
|
22
|
+
*/
|
|
23
|
+
export declare const syncStatusMap: Map<string, Map<string, "error" | "pending" | "success">>;
|
|
24
|
+
//# sourceMappingURL=syncStatus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncStatus.d.ts","sourceRoot":"","sources":["../../src/store/syncStatus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,aAAa,2DAGvB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A map tracking sync status for each row in each table.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* {
|
|
6
|
+
* "table1": {
|
|
7
|
+
* "1": "pending",
|
|
8
|
+
* "2": "success",
|
|
9
|
+
* "3": "error"
|
|
10
|
+
* },
|
|
11
|
+
* "table2": {
|
|
12
|
+
* "a": "success",
|
|
13
|
+
* "b": "pending"
|
|
14
|
+
* }
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* This structure means:
|
|
18
|
+
* - `syncStatusMap.get("table1")?.get("1")` would return "pending"
|
|
19
|
+
* - Outer key = table name
|
|
20
|
+
* - Inner key = row ID (as string)
|
|
21
|
+
* - Value = sync status of that row
|
|
22
|
+
*/
|
|
23
|
+
export const syncStatusMap = new Map();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mainQueryHelpers.d.ts","sourceRoot":"","sources":["../../../../src/utils/query/helpers/mainQueryHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAEL,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACf,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"mainQueryHelpers.d.ts","sourceRoot":"","sources":["../../../../src/utils/query/helpers/mainQueryHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAEL,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACf,MAAM,4BAA4B,CAAC;AASpC,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,GACZ,IAAI,CAUN;AAED,wBAAgB,eAAe,CAAC,CAAC,EAC/B,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,GACtB,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,CAc5B;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,SAAS,WAAW,EAAE,CAAC,EAAE,CAAC,EAC3E,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAChD,YAAY,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,GAC7C,CAAC,KAAK,GAAG;IAAE,aAAa,CAAC,EAAE,cAAc,CAAA;CAAE,CAAC,GAAG,IAAI,CAwBrD;AAyBD,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;AAmFD,wBAAsB,eAAe,CACnC,CAAC,SAAS,WAAW,EACrB,CAAC,SAAS,OAAO,EACjB,CAAC,EACD,CAAC,EAED,KAAK,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC7B,OAAO,CAAC;IACT,WAAW,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACjD,YAAY,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;CAChD,CAAC,CA2CD"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { isOnline } from "../../../utils/connection";
|
|
2
2
|
import { log, logWarn } from "../../../utils/logs";
|
|
3
|
+
import { getQueryStatusFromDb } from "../../../utils/sync/queryStatus";
|
|
4
|
+
import { supastashEventBus } from "../../events/eventBus";
|
|
3
5
|
import { generateUUIDv4 } from "../../genUUID";
|
|
4
6
|
import { queryLocalDb } from "../localDbQuery";
|
|
5
7
|
import { querySupabase } from "../remoteQuery/supabaseQuery";
|
|
@@ -98,6 +100,8 @@ async function processBatch() {
|
|
|
98
100
|
if (!isConnected) {
|
|
99
101
|
const offlineRetries = (calledOfflineRetries.get(opKey) || 0) + 1;
|
|
100
102
|
if (offlineRetries > MAX_OFFLINE_RETRIES) {
|
|
103
|
+
await getQueryStatusFromDb(state.table);
|
|
104
|
+
supastashEventBus.emit("updateSyncStatus");
|
|
101
105
|
logWarn(`[Supastash] Gave up on ${opKey} after ${MAX_OFFLINE_RETRIES} offline retries`);
|
|
102
106
|
reject(new Error(`Offline retry limit exceeded for ${opKey}`));
|
|
103
107
|
break;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supabaseQuery.d.ts","sourceRoot":"","sources":["../../../../src/utils/query/remoteQuery/supabaseQuery.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,cAAc,EACf,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"supabaseQuery.d.ts","sourceRoot":"","sources":["../../../../src/utils/query/remoteQuery/supabaseQuery.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,cAAc,EACf,MAAM,4BAA4B,CAAC;AAUpC;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC,EACzD,KAAK,EAAE,cAAc,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC,EACxC,SAAS,UAAQ,GAChB,OAAO,CAAC,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAuNpC"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { getSupastashConfig } from "../../../core/config";
|
|
2
2
|
import { getSupastashDb } from "../../../db/dbInitializer";
|
|
3
|
+
import { getQueryStatusFromDb } from "../../../utils/sync/queryStatus";
|
|
4
|
+
import { supastashEventBus } from "../../events/eventBus";
|
|
3
5
|
import { refreshScreen } from "../../refreshScreenCalls";
|
|
4
6
|
import { getSafeValue } from "../../serializer";
|
|
5
7
|
import { updateLocalSyncedAt } from "../../syncUpdate";
|
|
@@ -125,6 +127,10 @@ export async function querySupabase(state, isBatched = false) {
|
|
|
125
127
|
}
|
|
126
128
|
const result = await filterQuery;
|
|
127
129
|
const db = await getSupastashDb();
|
|
130
|
+
if (result.error) {
|
|
131
|
+
await getQueryStatusFromDb(table);
|
|
132
|
+
supastashEventBus.emit("updateSyncStatus");
|
|
133
|
+
}
|
|
128
134
|
if (!result.error &&
|
|
129
135
|
method !== "select" &&
|
|
130
136
|
type !== "remoteOnly" &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refreshScreenCalls.d.ts","sourceRoot":"","sources":["../../src/utils/refreshScreenCalls.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"refreshScreenCalls.d.ts","sourceRoot":"","sources":["../../src/utils/refreshScreenCalls.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAejD"}
|
|
@@ -11,6 +11,7 @@ export function refreshScreen(table) {
|
|
|
11
11
|
const timeout = setTimeout(() => {
|
|
12
12
|
supastashEventBus.emit(`refresh:${table}`);
|
|
13
13
|
supastashEventBus.emit(`supastash:refreshZustand:${table}`);
|
|
14
|
+
supastashEventBus.emit("updateSyncStatus");
|
|
14
15
|
timesFetched.delete(table);
|
|
15
16
|
debounceMap.delete(table);
|
|
16
17
|
}, timeoutMs);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deleteChunks.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pushLocal/deleteChunks.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"deleteChunks.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pushLocal/deleteChunks.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAyEzD;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,WAAW,EAAE,iBAW/B"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { getSupastashConfig } from "../../../core/config";
|
|
2
2
|
import { getSupastashDb } from "../../../db/dbInitializer";
|
|
3
|
+
import { supastashEventBus } from "../../events/eventBus";
|
|
3
4
|
import log from "../../logs";
|
|
5
|
+
import { setQueryStatus } from "../queryStatus";
|
|
4
6
|
import { parseStringifiedFields } from "./parseFields";
|
|
5
7
|
const CHUNK_SIZE = 500;
|
|
6
8
|
/**
|
|
@@ -14,7 +16,10 @@ async function permanentlyDeleteChunkLocally(table, chunk) {
|
|
|
14
16
|
await db.runAsync(`DELETE FROM ${table} WHERE id = ?`, [row.id]);
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
|
-
function errorHandler(error, table, attempts) {
|
|
19
|
+
function errorHandler(error, table, attempts, toDelete) {
|
|
20
|
+
for (const row of toDelete) {
|
|
21
|
+
setQueryStatus(row.id, table, "error");
|
|
22
|
+
}
|
|
18
23
|
log(`Delete attempt ${attempts + 1} failed for a delete operation on table ${table}`, error);
|
|
19
24
|
}
|
|
20
25
|
/**
|
|
@@ -33,10 +38,14 @@ async function deleteChunk(table, chunk) {
|
|
|
33
38
|
while (attempts < 3) {
|
|
34
39
|
const { error } = await supabase.from(table).upsert(toDelete);
|
|
35
40
|
if (!error) {
|
|
41
|
+
for (const row of toDelete) {
|
|
42
|
+
setQueryStatus(row.id, table, "success");
|
|
43
|
+
}
|
|
44
|
+
supastashEventBus.emit("updateSyncStatus");
|
|
36
45
|
await permanentlyDeleteChunkLocally(table, toDelete);
|
|
37
46
|
break;
|
|
38
47
|
}
|
|
39
|
-
errorHandler(error, table, attempts);
|
|
48
|
+
errorHandler(error, table, attempts, toDelete);
|
|
40
49
|
attempts++;
|
|
41
50
|
await new Promise((res) => setTimeout(res, 1000 * Math.pow(2, attempts)));
|
|
42
51
|
}
|
|
@@ -47,7 +56,10 @@ async function deleteChunk(table, chunk) {
|
|
|
47
56
|
* @param unsyncedRecords - The unsynced records to delete
|
|
48
57
|
*/
|
|
49
58
|
export async function deleteData(table, unsyncedRecords) {
|
|
50
|
-
const cleanRecords = unsyncedRecords.map(({ synced_at, ...rest }) =>
|
|
59
|
+
const cleanRecords = unsyncedRecords.map(({ synced_at, ...rest }) => {
|
|
60
|
+
setQueryStatus(rest.id, table, "pending");
|
|
61
|
+
return parseStringifiedFields(rest);
|
|
62
|
+
});
|
|
51
63
|
for (let i = 0; i < cleanRecords.length; i += CHUNK_SIZE) {
|
|
52
64
|
const chunk = cleanRecords.slice(i, i + CHUNK_SIZE);
|
|
53
65
|
await deleteChunk(table, chunk);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sendUnsyncedToSupabase.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pushLocal/sendUnsyncedToSupabase.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,EACrD,MAAM,CAAC,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"sendUnsyncedToSupabase.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pushLocal/sendUnsyncedToSupabase.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,EACrD,MAAM,CAAC,EAAE,MAAM,EAAE,iBAiClB"}
|
|
@@ -27,12 +27,8 @@ export async function pushLocalDataToRemote(table, onPushToRemote, noSync) {
|
|
|
27
27
|
await uploadData(table, data, onPushToRemote);
|
|
28
28
|
refreshScreen(table);
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
deleted_at: item.deleted_at,
|
|
33
|
-
}));
|
|
34
|
-
if (payloadForDeletedData && payloadForDeletedData.length > 0) {
|
|
35
|
-
await deleteData(table, payloadForDeletedData);
|
|
30
|
+
if (deletedData && deletedData.length > 0) {
|
|
31
|
+
await deleteData(table, deletedData);
|
|
36
32
|
}
|
|
37
33
|
}
|
|
38
34
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploadChunk.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pushLocal/uploadChunk.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"uploadChunk.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pushLocal/uploadChunk.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AA8KzD;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,WAAW,EAAE,EAC9B,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,iBAYtD"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { getSupastashConfig } from "../../../core/config";
|
|
2
|
+
import { supastashEventBus } from "../../events/eventBus";
|
|
2
3
|
import log, { logError } from "../../logs";
|
|
3
4
|
import { supabaseClientErr } from "../../supabaseClientErr";
|
|
4
5
|
import { updateLocalSyncedAt } from "../../syncUpdate";
|
|
6
|
+
import { setQueryStatus } from "../queryStatus";
|
|
5
7
|
import { parseStringifiedFields } from "./parseFields";
|
|
6
8
|
const RANDOM_OLD_DATE = new Date("2000-01-01").toISOString();
|
|
7
9
|
const CHUNK_SIZE = 500;
|
|
@@ -12,6 +14,9 @@ async function updateSyncStatus(table, rows) {
|
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
16
|
function errorHandler(error, table, toUpsert, attempts) {
|
|
17
|
+
for (const row of toUpsert) {
|
|
18
|
+
setQueryStatus(row.id, table, "error");
|
|
19
|
+
}
|
|
15
20
|
if (attempts === 5) {
|
|
16
21
|
log(`[Supastash] Upsert attempt ${attempts} failed for table ${table} \n
|
|
17
22
|
Error: ${error.message} \n
|
|
@@ -67,6 +72,7 @@ async function uploadChunk(table, chunk, onPushToRemote) {
|
|
|
67
72
|
}
|
|
68
73
|
else {
|
|
69
74
|
const { synced_at, ...rest } = row;
|
|
75
|
+
setQueryStatus(rest.id, table, "pending");
|
|
70
76
|
toUpsert.push(rest);
|
|
71
77
|
}
|
|
72
78
|
}
|
|
@@ -85,6 +91,9 @@ async function uploadChunk(table, chunk, onPushToRemote) {
|
|
|
85
91
|
break;
|
|
86
92
|
}
|
|
87
93
|
if (result) {
|
|
94
|
+
for (const row of toUpsert) {
|
|
95
|
+
setQueryStatus(row.id, table, "success");
|
|
96
|
+
}
|
|
88
97
|
success = true;
|
|
89
98
|
}
|
|
90
99
|
else {
|
|
@@ -95,6 +104,10 @@ async function uploadChunk(table, chunk, onPushToRemote) {
|
|
|
95
104
|
else {
|
|
96
105
|
const { error } = await supabase.from(table).upsert(toUpsert);
|
|
97
106
|
if (!error) {
|
|
107
|
+
for (const row of toUpsert) {
|
|
108
|
+
setQueryStatus(row.id, table, "success");
|
|
109
|
+
}
|
|
110
|
+
supastashEventBus.emit("updateSyncStatus");
|
|
98
111
|
success = true;
|
|
99
112
|
}
|
|
100
113
|
else {
|
|
@@ -122,7 +135,9 @@ async function uploadChunk(table, chunk, onPushToRemote) {
|
|
|
122
135
|
* @param unsyncedRecords - The unsynced records to upload
|
|
123
136
|
*/
|
|
124
137
|
export async function uploadData(table, unsyncedRecords, onPushToRemote) {
|
|
125
|
-
const cleanRecords = unsyncedRecords.map(({ synced_at, deleted_at, ...rest }) =>
|
|
138
|
+
const cleanRecords = unsyncedRecords.map(({ synced_at, deleted_at, ...rest }) => {
|
|
139
|
+
return parseStringifiedFields(rest);
|
|
140
|
+
});
|
|
126
141
|
for (let i = 0; i < cleanRecords.length; i += CHUNK_SIZE) {
|
|
127
142
|
const chunk = cleanRecords.slice(i, i + CHUNK_SIZE);
|
|
128
143
|
await uploadChunk(table, chunk, onPushToRemote);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sets the sync status of a query (row) in a specific table.
|
|
3
|
+
* Automatically removes the entry if the status is 'success'.
|
|
4
|
+
*
|
|
5
|
+
* @param rowId - The unique ID of the row
|
|
6
|
+
* @param table - The name of the table
|
|
7
|
+
* @param status - One of: "pending", "success", or "error"
|
|
8
|
+
*/
|
|
9
|
+
export declare function setQueryStatus(rowId: string, table: string, status: "pending" | "success" | "error"): void;
|
|
10
|
+
/**
|
|
11
|
+
* Gets the sync status of a table from the database.
|
|
12
|
+
*
|
|
13
|
+
* @param table - Table name
|
|
14
|
+
*/
|
|
15
|
+
export declare function getQueryStatusFromDb(table: string): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Gets the aggregate sync status of a specific table.
|
|
18
|
+
*
|
|
19
|
+
* @param table - Table name
|
|
20
|
+
* @returns An object with:
|
|
21
|
+
* - size: total tracked rows
|
|
22
|
+
* - errors: number of rows in "error" status
|
|
23
|
+
* - pending: number of rows in "pending" status
|
|
24
|
+
* - status: overall table status: "success" | "pending" | "error"
|
|
25
|
+
*/
|
|
26
|
+
export declare function getTableStatus(table: string): {
|
|
27
|
+
size: number | undefined;
|
|
28
|
+
errors: number;
|
|
29
|
+
pending: number;
|
|
30
|
+
status: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Gets the global sync status across all tracked tables.
|
|
34
|
+
*
|
|
35
|
+
* @returns "error" if any table has errors,
|
|
36
|
+
* "pending" if any table is syncing,
|
|
37
|
+
* "synced" if all tables are fully synced
|
|
38
|
+
*/
|
|
39
|
+
export declare function getSupastashStatus(): "error" | "pending" | "synced";
|
|
40
|
+
//# sourceMappingURL=queryStatus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryStatus.d.ts","sourceRoot":"","sources":["../../../src/utils/sync/queryStatus.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,QAYxC;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,iBAgBvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM;;;;;EAwB3C;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,mCAcjC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { getSupastashDb } from "../../db/dbInitializer";
|
|
2
|
+
import { syncStatusMap } from "../../store/syncStatus";
|
|
3
|
+
/**
|
|
4
|
+
* Sets the sync status of a query (row) in a specific table.
|
|
5
|
+
* Automatically removes the entry if the status is 'success'.
|
|
6
|
+
*
|
|
7
|
+
* @param rowId - The unique ID of the row
|
|
8
|
+
* @param table - The name of the table
|
|
9
|
+
* @param status - One of: "pending", "success", or "error"
|
|
10
|
+
*/
|
|
11
|
+
export function setQueryStatus(rowId, table, status) {
|
|
12
|
+
if (status === "success") {
|
|
13
|
+
syncStatusMap.get(table)?.delete(rowId);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (syncStatusMap.has(table)) {
|
|
17
|
+
syncStatusMap.get(table)?.set(rowId, status);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
syncStatusMap.set(table, new Map());
|
|
21
|
+
syncStatusMap.get(table)?.set(rowId, status);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Gets the sync status of a table from the database.
|
|
26
|
+
*
|
|
27
|
+
* @param table - Table name
|
|
28
|
+
*/
|
|
29
|
+
export async function getQueryStatusFromDb(table) {
|
|
30
|
+
const db = await getSupastashDb();
|
|
31
|
+
const tableData = await db.getAllAsync(`SELECT id FROM ${table} WHERE synced_at IS NULL`);
|
|
32
|
+
let tableMap = syncStatusMap.get(table);
|
|
33
|
+
if (!tableMap) {
|
|
34
|
+
tableMap = new Map();
|
|
35
|
+
syncStatusMap.set(table, tableMap);
|
|
36
|
+
}
|
|
37
|
+
for (const row of tableData) {
|
|
38
|
+
tableMap.set(row.id, "pending");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Gets the aggregate sync status of a specific table.
|
|
43
|
+
*
|
|
44
|
+
* @param table - Table name
|
|
45
|
+
* @returns An object with:
|
|
46
|
+
* - size: total tracked rows
|
|
47
|
+
* - errors: number of rows in "error" status
|
|
48
|
+
* - pending: number of rows in "pending" status
|
|
49
|
+
* - status: overall table status: "success" | "pending" | "error"
|
|
50
|
+
*/
|
|
51
|
+
export function getTableStatus(table) {
|
|
52
|
+
const tableStatus = syncStatusMap.get(table);
|
|
53
|
+
const size = tableStatus?.size;
|
|
54
|
+
const statusArray = Array.from(tableStatus?.values() || []);
|
|
55
|
+
let errorCount = 0;
|
|
56
|
+
let pendingCount = 0;
|
|
57
|
+
statusArray.forEach((status) => {
|
|
58
|
+
if (status === "error") {
|
|
59
|
+
errorCount++;
|
|
60
|
+
}
|
|
61
|
+
else if (status === "pending") {
|
|
62
|
+
pendingCount++;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
let status = "success";
|
|
66
|
+
if (pendingCount > 0) {
|
|
67
|
+
status = "pending";
|
|
68
|
+
}
|
|
69
|
+
else if (errorCount > 0) {
|
|
70
|
+
status = "error";
|
|
71
|
+
}
|
|
72
|
+
return { size, errors: errorCount, pending: pendingCount, status };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Gets the global sync status across all tracked tables.
|
|
76
|
+
*
|
|
77
|
+
* @returns "error" if any table has errors,
|
|
78
|
+
* "pending" if any table is syncing,
|
|
79
|
+
* "synced" if all tables are fully synced
|
|
80
|
+
*/
|
|
81
|
+
export function getSupastashStatus() {
|
|
82
|
+
const tables = Array.from(syncStatusMap.keys());
|
|
83
|
+
for (const table of tables) {
|
|
84
|
+
const tableStatus = getTableStatus(table);
|
|
85
|
+
if (tableStatus.status === "error") {
|
|
86
|
+
return "error";
|
|
87
|
+
}
|
|
88
|
+
if (tableStatus.status === "pending") {
|
|
89
|
+
return "pending";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return "synced";
|
|
93
|
+
}
|