supastash 0.1.21 → 0.1.23
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.
|
@@ -5,7 +5,7 @@ export declare function assignInsertIds<R>(payload: R | R[] | null): R | R[] | n
|
|
|
5
5
|
export declare function getCommonError<U extends boolean, T extends CrudMethods, R, Z>(table: string, method: CrudMethods, localResult: MethodReturnTypeMap<U, Z>[T] | null, remoteResult: SupabaseQueryReturn<U, Z> | null): (Error & {
|
|
6
6
|
supabaseError?: PostgrestError;
|
|
7
7
|
}) | null;
|
|
8
|
-
export declare function
|
|
8
|
+
export declare function queueRemoteCall<T extends CrudMethods, U extends boolean, R>(state: SupastashQuery<T, U, R>): Promise<boolean>;
|
|
9
9
|
export declare function runSyncStrategy<T extends CrudMethods, U extends boolean, R, Z>(state: SupastashQuery<T, U, R>): Promise<{
|
|
10
10
|
localResult: MethodReturnTypeMap<U, Z>[T] | null;
|
|
11
11
|
remoteResult: SupabaseQueryReturn<U, Z> | null;
|
|
@@ -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,
|
|
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;AAOpC,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;AA+ED,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,CA6CD"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isOnline } from "../../../utils/connection";
|
|
2
|
-
import { log,
|
|
2
|
+
import { log, logWarn } from "../../../utils/logs";
|
|
3
3
|
import { generateUUIDv4 } from "../../genUUID";
|
|
4
4
|
import { queryLocalDb } from "../localDbQuery";
|
|
5
5
|
import { querySupabase } from "../remoteQuery/supabaseQuery";
|
|
@@ -46,62 +46,92 @@ export function getCommonError(table, method, localResult, remoteResult) {
|
|
|
46
46
|
}
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
let batchQueue = [];
|
|
50
|
+
let isProcessing = false;
|
|
51
|
+
let batchTimer = null;
|
|
52
|
+
const BATCH_DELAY = 10;
|
|
52
53
|
const retryCount = new Map();
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const
|
|
54
|
+
const calledOfflineRetries = new Map();
|
|
55
|
+
const successfulCalls = new Set();
|
|
56
|
+
const MAX_RETRIES = 3;
|
|
57
|
+
const MAX_OFFLINE_RETRIES = 5;
|
|
56
58
|
function delay(ms) {
|
|
57
|
-
return new Promise((
|
|
59
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
60
|
+
}
|
|
61
|
+
function generateOpKey(state) {
|
|
62
|
+
return `${state.table}_${state.method}_${state.id}_${JSON.stringify(state.payload)}`;
|
|
58
63
|
}
|
|
59
|
-
export function
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
export function queueRemoteCall(state) {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const opKey = generateOpKey(state);
|
|
67
|
+
if (successfulCalls.has(opKey)) {
|
|
68
|
+
resolve(true);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
batchQueue.push({ state, opKey, resolve, reject });
|
|
72
|
+
scheduleBatch();
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function scheduleBatch() {
|
|
76
|
+
if (batchTimer)
|
|
62
77
|
return;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
78
|
+
batchTimer = setTimeout(() => {
|
|
79
|
+
processBatch();
|
|
80
|
+
batchTimer = null;
|
|
81
|
+
}, BATCH_DELAY);
|
|
66
82
|
}
|
|
67
|
-
async function
|
|
68
|
-
|
|
69
|
-
if (!state)
|
|
83
|
+
async function processBatch() {
|
|
84
|
+
if (isProcessing || batchQueue.length === 0)
|
|
70
85
|
return;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
isProcessing = true;
|
|
87
|
+
const batch = [...batchQueue];
|
|
88
|
+
batchQueue = [];
|
|
89
|
+
for (const call of batch) {
|
|
90
|
+
const { state, opKey, resolve, reject } = call;
|
|
91
|
+
if (successfulCalls.has(opKey)) {
|
|
92
|
+
resolve(true);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
while ((retryCount.get(opKey) ?? 0) <= MAX_RETRIES) {
|
|
97
|
+
const isConnected = await isOnline();
|
|
98
|
+
if (!isConnected) {
|
|
99
|
+
const offlineRetries = (calledOfflineRetries.get(opKey) || 0) + 1;
|
|
100
|
+
if (offlineRetries > MAX_OFFLINE_RETRIES) {
|
|
101
|
+
logWarn(`[Supastash] Gave up on ${opKey} after ${MAX_OFFLINE_RETRIES} offline retries`);
|
|
102
|
+
reject(new Error(`Offline retry limit exceeded for ${opKey}`));
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
calledOfflineRetries.set(opKey, offlineRetries);
|
|
106
|
+
await delay(1000);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
calledOfflineRetries.delete(opKey);
|
|
110
|
+
const { error } = await querySupabase({ ...state }, true);
|
|
111
|
+
if (!error) {
|
|
112
|
+
successfulCalls.add(opKey);
|
|
113
|
+
retryCount.delete(opKey);
|
|
114
|
+
log(`[Supastash] Synced item on ${state.table} with ${state.method} to supabase`);
|
|
115
|
+
resolve(true);
|
|
79
116
|
break;
|
|
80
117
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
118
|
+
else {
|
|
119
|
+
const currentRetries = retryCount.get(opKey) ?? 0;
|
|
120
|
+
retryCount.set(opKey, currentRetries + 1);
|
|
121
|
+
if (currentRetries >= MAX_RETRIES) {
|
|
122
|
+
logWarn(`[Supastash] Gave up on ${opKey} after ${MAX_RETRIES} retries`);
|
|
123
|
+
reject(new Error(`Max retries exceeded for ${opKey}`));
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
await delay(1000 * (currentRetries + 1));
|
|
127
|
+
}
|
|
90
128
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
await delay(100 * currentRetry);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
reject(error);
|
|
95
132
|
}
|
|
96
133
|
}
|
|
97
|
-
|
|
98
|
-
logError(`[Supastash] Error running remote query: ${opKey} → ${error}`);
|
|
99
|
-
}
|
|
100
|
-
finally {
|
|
101
|
-
retryCount.delete(opKey);
|
|
102
|
-
stateCache.delete(opKey);
|
|
103
|
-
runningStates.delete(opKey);
|
|
104
|
-
}
|
|
134
|
+
isProcessing = false;
|
|
105
135
|
}
|
|
106
136
|
export async function runSyncStrategy(state) {
|
|
107
137
|
const { type } = state;
|
|
@@ -131,7 +161,9 @@ export async function runSyncStrategy(state) {
|
|
|
131
161
|
remoteResult = await querySupabase(state);
|
|
132
162
|
}
|
|
133
163
|
else {
|
|
134
|
-
|
|
164
|
+
queueRemoteCall(state).catch((error) => {
|
|
165
|
+
console.error(`[Supastash] Failed to sync ${state.table}:`, error);
|
|
166
|
+
});
|
|
135
167
|
}
|
|
136
168
|
break;
|
|
137
169
|
case "remoteFirst":
|