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 addToCache(state: SupastashQuery<CrudMethods, boolean, any>): void;
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,EACL,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;AAiBD,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,CAAC,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,QAQ1E;AAqDD,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
+ {"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, logError } from "../../../utils/logs";
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
- const stateCache = new Map();
50
- const runningStates = new Set();
51
- const calledOfflineRetries = new Map();
49
+ let batchQueue = [];
50
+ let isProcessing = false;
51
+ let batchTimer = null;
52
+ const BATCH_DELAY = 10;
52
53
  const retryCount = new Map();
53
- const MAX_RETRIES = 2;
54
- const MAX_OFFLINE_RETRIES = 10;
55
- const getOpKey = (state) => `${state.method}:${state.table}:${state.id}`;
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((res) => setTimeout(res, ms));
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 addToCache(state) {
60
- const opKey = getOpKey(state);
61
- if (runningStates.has(opKey))
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
- runningStates.add(opKey);
64
- stateCache.set(opKey, state);
65
- runRemoteQuery(opKey);
78
+ batchTimer = setTimeout(() => {
79
+ processBatch();
80
+ batchTimer = null;
81
+ }, BATCH_DELAY);
66
82
  }
67
- async function runRemoteQuery(opKey) {
68
- const state = stateCache.get(opKey);
69
- if (!state)
83
+ async function processBatch() {
84
+ if (isProcessing || batchQueue.length === 0)
70
85
  return;
71
- retryCount.set(opKey, retryCount.get(opKey) ?? 0);
72
- try {
73
- while ((retryCount.get(opKey) ?? 0) <= MAX_RETRIES) {
74
- const isConnected = await isOnline();
75
- if (!isConnected) {
76
- const offlineRetries = (calledOfflineRetries.get(opKey) || 0) + 1;
77
- if (offlineRetries > MAX_OFFLINE_RETRIES) {
78
- logError(`[Supastash] Gave up on ${opKey} after ${MAX_OFFLINE_RETRIES} offline retries`);
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
- calledOfflineRetries.set(opKey, offlineRetries);
82
- await delay(1000);
83
- continue;
84
- }
85
- calledOfflineRetries.delete(opKey);
86
- const { error } = await querySupabase({ ...state }, true);
87
- if (!error) {
88
- log(`[Supastash] Synced item on ${state.table} with ${state.method} to supabase`);
89
- break;
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
- const currentRetry = (retryCount.get(opKey) ?? 0) + 1;
92
- logError(`[Supastash] Remote sync failed: ${opKey} (${currentRetry}/${MAX_RETRIES}) → ${error.message}`);
93
- retryCount.set(opKey, currentRetry);
94
- await delay(100 * currentRetry);
129
+ }
130
+ catch (error) {
131
+ reject(error);
95
132
  }
96
133
  }
97
- catch (error) {
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
- addToCache(state);
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":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supastash",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",