supastash 0.1.62 → 0.2.0
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/README.md +17 -5
- package/dist/core/config/index.d.ts.map +1 -1
- package/dist/core/config/index.js +5 -1
- package/dist/db/adapters/expo_sqlite.d.ts.map +1 -1
- package/dist/db/adapters/expo_sqlite.js +11 -0
- package/dist/db/adapters/rn_nitro.d.ts.map +1 -1
- package/dist/db/adapters/rn_nitro.js +20 -0
- package/dist/db/adapters/rn_sqlite_storage.d.ts.map +1 -1
- package/dist/db/adapters/rn_sqlite_storage.js +27 -0
- package/dist/hooks/supastashData/fetchCalls.d.ts.map +1 -1
- package/dist/hooks/supastashData/index.d.ts.map +1 -1
- package/dist/hooks/supastashData/index.js +12 -4
- package/dist/hooks/supastashData/realtimeSubscription.js +4 -4
- package/dist/hooks/supastashData/registerSub.d.ts +3 -3
- package/dist/hooks/supastashData/registerSub.d.ts.map +1 -1
- package/dist/hooks/supastashData/registerSub.js +3 -3
- package/dist/hooks/supastashFilters/index.d.ts +2 -2
- package/dist/hooks/supastashFilters/index.d.ts.map +1 -1
- package/dist/hooks/supastashFilters/index.js +6 -4
- package/dist/hooks/supastashLiteQuery/index.d.ts.map +1 -1
- package/dist/hooks/supastashLiteQuery/index.js +12 -2
- package/dist/hooks/syncEngine/pushLocal/index.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/store/tableFilters.d.ts +3 -3
- package/dist/store/tableFilters.d.ts.map +1 -1
- package/dist/store/tx.d.ts +3 -0
- package/dist/store/tx.d.ts.map +1 -0
- package/dist/store/tx.js +1 -0
- package/dist/types/expoSqlite.types.d.ts +3 -13
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/liteQuery.types.d.ts +2 -2
- package/dist/types/query.types.d.ts +24 -1
- package/dist/types/realtimeData.types.d.ts +8 -4
- package/dist/types/supastashConfig.types.d.ts +83 -6
- package/dist/types/syncEngine.types.d.ts +49 -7
- package/dist/utils/errorHandler.d.ts +6 -0
- package/dist/utils/errorHandler.d.ts.map +1 -0
- package/dist/utils/errorHandler.js +8 -0
- package/dist/utils/fetchData/buildFilter.d.ts +8 -4
- package/dist/utils/fetchData/buildFilter.d.ts.map +1 -1
- package/dist/utils/fetchData/createTable.d.ts.map +1 -1
- package/dist/utils/fetchData/createTable.js +3 -46
- package/dist/utils/fetchData/deleteData.d.ts.map +1 -1
- package/dist/utils/fetchData/deleteData.js +6 -3
- package/dist/utils/fetchData/fetchLocalData.d.ts +2 -2
- package/dist/utils/fetchData/fetchLocalData.d.ts.map +1 -1
- package/dist/utils/fetchData/initialFetch.d.ts +2 -2
- package/dist/utils/fetchData/initialFetch.d.ts.map +1 -1
- package/dist/utils/fetchData/liteHelpers.d.ts +2 -2
- package/dist/utils/fetchData/liteHelpers.d.ts.map +1 -1
- package/dist/utils/fetchData/liteHelpers.js +14 -119
- package/dist/utils/fetchData/realTimeCall.js +2 -2
- package/dist/utils/fetchData/receiveData.js +1 -1
- package/dist/utils/query/builder/crud.d.ts +5 -5
- package/dist/utils/query/builder/crud.d.ts.map +1 -1
- package/dist/utils/query/builder/filters.d.ts +36 -12
- package/dist/utils/query/builder/filters.d.ts.map +1 -1
- package/dist/utils/query/builder/filters.js +32 -0
- package/dist/utils/query/builder/index.d.ts +29 -1
- package/dist/utils/query/builder/index.d.ts.map +1 -1
- package/dist/utils/query/builder/index.js +77 -1
- package/dist/utils/query/builder/mainQuery.d.ts.map +1 -1
- package/dist/utils/query/builder/mainQuery.js +17 -2
- package/dist/utils/query/helpers/localDb/getLocalMethod.d.ts +2 -2
- package/dist/utils/query/helpers/localDb/getLocalMethod.d.ts.map +1 -1
- package/dist/utils/query/helpers/localDb/getLocalMethod.js +7 -6
- package/dist/utils/query/helpers/localDb/insertMany.d.ts +3 -0
- package/dist/utils/query/helpers/localDb/insertMany.d.ts.map +1 -1
- package/dist/utils/query/helpers/localDb/insertMany.js +10 -3
- package/dist/utils/query/helpers/localDb/localQueryBuilder.d.ts +6 -6
- package/dist/utils/query/helpers/localDb/localQueryBuilder.d.ts.map +1 -1
- package/dist/utils/query/helpers/localDb/localQueryBuilder.js +17 -10
- package/dist/utils/query/helpers/localDb/upsertMany.d.ts +3 -0
- package/dist/utils/query/helpers/localDb/upsertMany.d.ts.map +1 -1
- package/dist/utils/query/helpers/localDb/upsertMany.js +9 -2
- package/dist/utils/query/helpers/mainQueryHelpers.d.ts.map +1 -1
- package/dist/utils/query/helpers/mainQueryHelpers.js +38 -8
- package/dist/utils/query/helpers/queueRemote.d.ts.map +1 -1
- package/dist/utils/query/helpers/queueRemote.js +33 -24
- package/dist/utils/query/localDbQuery/delete.d.ts +9 -3
- package/dist/utils/query/localDbQuery/delete.d.ts.map +1 -1
- package/dist/utils/query/localDbQuery/delete.js +16 -5
- package/dist/utils/query/localDbQuery/index.d.ts.map +1 -1
- package/dist/utils/query/localDbQuery/index.js +3 -3
- package/dist/utils/query/localDbQuery/insert.d.ts +2 -2
- package/dist/utils/query/localDbQuery/insert.d.ts.map +1 -1
- package/dist/utils/query/localDbQuery/insert.js +8 -5
- package/dist/utils/query/localDbQuery/select.d.ts +2 -2
- package/dist/utils/query/localDbQuery/select.d.ts.map +1 -1
- package/dist/utils/query/localDbQuery/select.js +5 -2
- package/dist/utils/query/localDbQuery/update.d.ts +2 -2
- package/dist/utils/query/localDbQuery/update.d.ts.map +1 -1
- package/dist/utils/query/localDbQuery/update.js +5 -2
- package/dist/utils/query/localDbQuery/upsert.d.ts +2 -2
- package/dist/utils/query/localDbQuery/upsert.d.ts.map +1 -1
- package/dist/utils/query/localDbQuery/upsert.js +8 -3
- package/dist/utils/query/remoteQuery/supabaseQuery.d.ts.map +1 -1
- package/dist/utils/query/remoteQuery/supabaseQuery.js +4 -1
- package/dist/utils/reusedHelpers.d.ts +8 -0
- package/dist/utils/reusedHelpers.d.ts.map +1 -0
- package/dist/utils/reusedHelpers.js +162 -0
- package/dist/utils/schema/createSyncStatus.d.ts +3 -1
- package/dist/utils/schema/createSyncStatus.d.ts.map +1 -1
- package/dist/utils/schema/createSyncStatus.js +30 -3
- package/dist/utils/sync/pullFromRemote/fetchOlder.d.ts +44 -0
- package/dist/utils/sync/pullFromRemote/fetchOlder.d.ts.map +1 -0
- package/dist/utils/sync/pullFromRemote/fetchOlder.js +55 -0
- package/dist/utils/sync/pullFromRemote/fetchOlderHelpers.d.ts +33 -0
- package/dist/utils/sync/pullFromRemote/fetchOlderHelpers.d.ts.map +1 -0
- package/dist/utils/sync/pullFromRemote/fetchOlderHelpers.js +110 -0
- package/dist/utils/sync/pullFromRemote/helpers.d.ts +10 -7
- package/dist/utils/sync/pullFromRemote/helpers.d.ts.map +1 -1
- package/dist/utils/sync/pullFromRemote/helpers.js +20 -14
- package/dist/utils/sync/pullFromRemote/pullData.d.ts +2 -3
- package/dist/utils/sync/pullFromRemote/pullData.d.ts.map +1 -1
- package/dist/utils/sync/pullFromRemote/pullData.js +4 -9
- package/dist/utils/sync/pullFromRemote/pullDeletedData.d.ts +8 -5
- package/dist/utils/sync/pullFromRemote/updateFilter.d.ts +1 -1
- package/dist/utils/sync/pullFromRemote/updateFilter.d.ts.map +1 -1
- package/dist/utils/sync/pullFromRemote/updateFilter.js +5 -3
- package/dist/utils/sync/pullFromRemote/updateLocalDb.d.ts +4 -3
- package/dist/utils/sync/pullFromRemote/updateLocalDb.d.ts.map +1 -1
- package/dist/utils/sync/pullFromRemote/updateLocalDb.js +51 -46
- package/dist/utils/sync/pullFromRemote/validateFilters.d.ts +2 -4
- package/dist/utils/sync/pullFromRemote/validateFilters.d.ts.map +1 -1
- package/dist/utils/sync/pullFromRemote/validateFilters.js +9 -63
- package/dist/utils/sync/pushLocal/deleteChunks.d.ts.map +1 -1
- package/dist/utils/sync/pushLocal/deleteChunks.js +7 -5
- package/dist/utils/sync/pushLocal/getAllUnsyncedData.d.ts.map +1 -1
- package/dist/utils/sync/pushLocal/getAllUnsyncedData.js +12 -44
- package/dist/utils/sync/pushLocal/uploadHelpers.js +1 -1
- package/dist/utils/sync/status/filterKey.d.ts +3 -3
- package/dist/utils/sync/status/filterKey.d.ts.map +1 -1
- package/dist/utils/sync/status/filterKey.js +5 -2
- package/dist/utils/sync/status/remoteSchema.d.ts +4 -0
- package/dist/utils/sync/status/remoteSchema.d.ts.map +1 -0
- package/dist/utils/sync/status/remoteSchema.js +140 -0
- package/dist/utils/sync/status/repo.d.ts +5 -5
- package/dist/utils/sync/status/repo.d.ts.map +1 -1
- package/dist/utils/sync/status/repo.js +29 -23
- package/dist/utils/sync/status/services.d.ts +5 -6
- package/dist/utils/sync/status/services.d.ts.map +1 -1
- package/dist/utils/sync/status/services.js +1 -6
- package/dist/utils/sync/status/syncStatus.d.ts +5 -7
- package/dist/utils/sync/status/syncStatus.d.ts.map +1 -1
- package/dist/utils/sync/status/syncStatus.js +11 -3
- package/package.json +2 -2
- package/dist/types/supastashFilters.types.d.ts +0 -3
|
@@ -29,8 +29,10 @@ export async function pullData({ table, filters, batchId, }) {
|
|
|
29
29
|
});
|
|
30
30
|
const db = await getSupastashDb();
|
|
31
31
|
const syncStatus = await selectSyncStatus(db, table, filters);
|
|
32
|
+
const cfg = getSupastashConfig();
|
|
33
|
+
const tsCol = cfg.replicationMode === "server-side" ? "arrived_at" : "updated_at";
|
|
32
34
|
const updatedRows = await pageThrough({
|
|
33
|
-
tsCol
|
|
35
|
+
tsCol,
|
|
34
36
|
since: syncStatus.last_synced_at,
|
|
35
37
|
table,
|
|
36
38
|
filters,
|
|
@@ -39,7 +41,6 @@ export async function pullData({ table, filters, batchId, }) {
|
|
|
39
41
|
});
|
|
40
42
|
const updatedData = [];
|
|
41
43
|
let deletedIds = [];
|
|
42
|
-
let createdMax = null;
|
|
43
44
|
let updatedMax = null;
|
|
44
45
|
let deletedMax = null;
|
|
45
46
|
for (const r of updatedRows) {
|
|
@@ -47,15 +48,10 @@ export async function pullData({ table, filters, batchId, }) {
|
|
|
47
48
|
logWarn(`[Supastash] Skipped row without id from "${table}"`);
|
|
48
49
|
continue;
|
|
49
50
|
}
|
|
50
|
-
createdMax = returnMaxDate({
|
|
51
|
-
row: r,
|
|
52
|
-
prevMax: createdMax,
|
|
53
|
-
col: "created_at",
|
|
54
|
-
});
|
|
55
51
|
updatedMax = returnMaxDate({
|
|
56
52
|
row: r,
|
|
57
53
|
prevMax: updatedMax,
|
|
58
|
-
col:
|
|
54
|
+
col: tsCol,
|
|
59
55
|
});
|
|
60
56
|
// If the row is deleted, add the id to the deleted ids and update the deleted max
|
|
61
57
|
if (r.deleted_at) {
|
|
@@ -75,7 +71,6 @@ export async function pullData({ table, filters, batchId, }) {
|
|
|
75
71
|
return null;
|
|
76
72
|
}
|
|
77
73
|
const timestamps = {
|
|
78
|
-
createdMax: createdMax?.value ?? null,
|
|
79
74
|
updatedMax: updatedMax?.value ?? null,
|
|
80
75
|
deletedMax: deletedMax?.value ?? null,
|
|
81
76
|
updatedMaxPk: updatedMax?.pk ?? null,
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { PayloadData } from "../../../types/query.types";
|
|
2
|
-
import {
|
|
2
|
+
import { SupastashFilter } from "../../../types/realtimeData.types";
|
|
3
3
|
/**
|
|
4
4
|
* Pulls deleted data from the remote database for a given table
|
|
5
5
|
* @param table - The table to pull deleted data from
|
|
6
6
|
* @returns The deleted data from the table as a map of id to record and the reordered data
|
|
7
7
|
*/
|
|
8
|
-
export declare function pullDeletedData(
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
export declare function pullDeletedData(
|
|
9
|
+
table: string,
|
|
10
|
+
filters?: SupastashFilter[]
|
|
11
|
+
): Promise<{
|
|
12
|
+
deletedDataMap: Map<string, PayloadData>;
|
|
13
|
+
records: PayloadData[];
|
|
11
14
|
} | null>;
|
|
12
|
-
//# sourceMappingURL=pullDeletedData.d.ts.map
|
|
15
|
+
//# sourceMappingURL=pullDeletedData.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateFilter.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pullFromRemote/updateFilter.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"updateFilter.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pullFromRemote/updateFilter.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAMpE;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,iBAwC3D"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { filterTracker, tableFilters, tableFiltersUsed, } from "../../../store/tableFilters";
|
|
2
2
|
import { logWarn } from "../../logs";
|
|
3
|
+
import { ReusedHelpers } from "../../reusedHelpers";
|
|
3
4
|
import { checkIfTableExist } from "../../tableValidator";
|
|
4
|
-
import
|
|
5
|
+
import { warnOnMisMatch } from "./validateFilters";
|
|
5
6
|
/**
|
|
6
7
|
* Updates the filter for the given table
|
|
7
8
|
* Non-hook version of useSupastashFilters
|
|
@@ -26,8 +27,9 @@ export async function updateFilters(filters) {
|
|
|
26
27
|
logWarn(`Table '${table}' does not exist; skipping filters`);
|
|
27
28
|
continue;
|
|
28
29
|
}
|
|
29
|
-
const raw = filters[table] ??
|
|
30
|
-
|
|
30
|
+
const raw = (filters[table] ??
|
|
31
|
+
[]);
|
|
32
|
+
const valid = raw.filter((f) => ReusedHelpers.isValidFilter([f]));
|
|
31
33
|
if (!valid.length) {
|
|
32
34
|
tableFilters.delete(table);
|
|
33
35
|
tableFiltersUsed.delete(table);
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SupastashFilter } from "../../../types/realtimeData.types";
|
|
2
|
+
import { UpsertDataParams } from "../../../types/syncEngine.types";
|
|
2
3
|
/**
|
|
3
4
|
* Updates the local database with the remote changes
|
|
4
5
|
* @param table - The table to update
|
|
5
6
|
*/
|
|
6
|
-
export declare function updateLocalDb(table: string, filters?:
|
|
7
|
+
export declare function updateLocalDb(table: string, filters?: SupastashFilter[], onReceiveData?: (payload: any) => Promise<void>): Promise<void>;
|
|
7
8
|
/**
|
|
8
9
|
* Upserts a record into the local database
|
|
9
10
|
* @param table - The table to upsert the record into
|
|
10
11
|
* @param record - The record to upsert
|
|
11
12
|
* @param exists - Whether the record already exists in the database
|
|
12
13
|
*/
|
|
13
|
-
export declare function upsertData(
|
|
14
|
+
export declare function upsertData({ tx, table, record, doesExist, }: UpsertDataParams): Promise<void>;
|
|
14
15
|
//# sourceMappingURL=updateLocalDb.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateLocalDb.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pullFromRemote/updateLocalDb.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"updateLocalDb.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pullFromRemote/updateLocalDb.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAkBnE;;;GAGG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,eAAe,EAAE,EAC3B,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,iBA6HhD;AAID;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,EAC/B,EAAE,EACF,KAAK,EACL,MAAM,EACN,SAAS,GACV,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqElC"}
|
|
@@ -34,9 +34,8 @@ export async function updateLocalDb(table, filters, onReceiveData) {
|
|
|
34
34
|
const db = await getSupastashDb();
|
|
35
35
|
// Initialize the batch completed map
|
|
36
36
|
RECEIVED_DATA_COMPLETED_MAP[batchId] = {
|
|
37
|
-
|
|
37
|
+
arrived_at: DEFAULT_RECEIVED_DATA_COMPLETED,
|
|
38
38
|
updated_at: DEFAULT_RECEIVED_DATA_COMPLETED,
|
|
39
|
-
deleted_at: DEFAULT_RECEIVED_DATA_COMPLETED,
|
|
40
39
|
};
|
|
41
40
|
let refreshNeeded = false;
|
|
42
41
|
while (true) {
|
|
@@ -59,55 +58,61 @@ export async function updateLocalDb(table, filters, onReceiveData) {
|
|
|
59
58
|
});
|
|
60
59
|
refreshNeeded = !!data?.length || !!deletedIds?.length;
|
|
61
60
|
// Delete records that are no longer in the remote data
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
// Get the local stamp of the records
|
|
71
|
-
let localStamp = new Map();
|
|
72
|
-
if (data?.length) {
|
|
73
|
-
const ids = data.map((r) => r.id).filter(Boolean);
|
|
74
|
-
for (let i = 0; i < ids.length; i += DELETE_CHUNK) {
|
|
75
|
-
const slice = ids.slice(i, i + DELETE_CHUNK);
|
|
76
|
-
const placeholders = slice.map(() => "?").join(", ");
|
|
77
|
-
const rows = await db.getAllAsync(`SELECT id, updated_at FROM ${table} WHERE id IN (${placeholders})`, slice);
|
|
78
|
-
for (const row of rows ?? []) {
|
|
79
|
-
localStamp.set(row.id, row.updated_at ?? null);
|
|
61
|
+
await db.withTransaction(async (tx) => {
|
|
62
|
+
if (deletedIds && deletedIds.length > 0) {
|
|
63
|
+
const ids = deletedIds;
|
|
64
|
+
for (let i = 0; i < ids.length; i += DELETE_CHUNK) {
|
|
65
|
+
const slice = ids.slice(i, i + DELETE_CHUNK);
|
|
66
|
+
const placeholders = slice.map(() => "?").join(", ");
|
|
67
|
+
await tx.runAsync(`DELETE FROM ${table} WHERE id IN (${placeholders})`, slice);
|
|
80
68
|
}
|
|
81
69
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (new Date(localUpdated) >= new Date(remoteUpdated)) {
|
|
94
|
-
// local is newer or same — skip
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
if (onReceiveData) {
|
|
98
|
-
await onReceiveData(record);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
await upsertData(table, record, localStamp.has(record.id));
|
|
70
|
+
// Get the local stamp of the records
|
|
71
|
+
let localStamp = new Map();
|
|
72
|
+
if (data?.length) {
|
|
73
|
+
const ids = data.map((r) => r.id).filter(Boolean);
|
|
74
|
+
for (let i = 0; i < ids.length; i += DELETE_CHUNK) {
|
|
75
|
+
const slice = ids.slice(i, i + DELETE_CHUNK);
|
|
76
|
+
const placeholders = slice.map(() => "?").join(", ");
|
|
77
|
+
const rows = await db.getAllAsync(`SELECT id, updated_at FROM ${table} WHERE id IN (${placeholders})`, slice);
|
|
78
|
+
for (const row of rows ?? []) {
|
|
79
|
+
localStamp.set(row.id, row.updated_at ?? null);
|
|
80
|
+
}
|
|
102
81
|
}
|
|
103
|
-
|
|
104
|
-
|
|
82
|
+
}
|
|
83
|
+
// Update local database with remote changes
|
|
84
|
+
if (data?.length) {
|
|
85
|
+
for (let i = 0; i < data.length; i++) {
|
|
86
|
+
const record = data[i];
|
|
87
|
+
if (!record?.id)
|
|
88
|
+
continue;
|
|
89
|
+
if (deletedIdSet.has(record.id))
|
|
90
|
+
continue;
|
|
91
|
+
const localUpdated = localStamp.get(record.id) ?? DEFAULT_DATE;
|
|
92
|
+
const remoteUpdated = record.updated_at ?? DEFAULT_DATE;
|
|
93
|
+
if (new Date(localUpdated) >= new Date(remoteUpdated)) {
|
|
94
|
+
// local is newer or same — skip
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (onReceiveData) {
|
|
98
|
+
await onReceiveData(record);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
await upsertData({
|
|
102
|
+
tx,
|
|
103
|
+
table,
|
|
104
|
+
record,
|
|
105
|
+
doesExist: localStamp.has(record.id),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if ((i + 1) % BATCH_SIZE === 0) {
|
|
109
|
+
await new Promise((res) => setTimeout(res, 0));
|
|
110
|
+
}
|
|
105
111
|
}
|
|
106
112
|
}
|
|
107
|
-
}
|
|
113
|
+
});
|
|
108
114
|
if (timestamps) {
|
|
109
115
|
await setSupastashSyncStatus(table, filters, {
|
|
110
|
-
lastCreatedAt: timestamps.createdMax,
|
|
111
116
|
lastSyncedAt: timestamps.updatedMax,
|
|
112
117
|
lastDeletedAt: timestamps.deletedMax,
|
|
113
118
|
lastSyncedAtPk: timestamps.updatedMaxPk,
|
|
@@ -138,7 +143,7 @@ const warned = new Map();
|
|
|
138
143
|
* @param record - The record to upsert
|
|
139
144
|
* @param exists - Whether the record already exists in the database
|
|
140
145
|
*/
|
|
141
|
-
export async function upsertData(table, record, doesExist) {
|
|
146
|
+
export async function upsertData({ tx, table, record, doesExist, }) {
|
|
142
147
|
if (!record?.id)
|
|
143
148
|
return;
|
|
144
149
|
let itemExists = !!doesExist;
|
|
@@ -150,7 +155,7 @@ export async function upsertData(table, record, doesExist) {
|
|
|
150
155
|
itemExists = exists;
|
|
151
156
|
}
|
|
152
157
|
try {
|
|
153
|
-
const db = await getSupastashDb();
|
|
158
|
+
const db = tx ?? (await getSupastashDb());
|
|
154
159
|
const columns = await getTableSchema(table);
|
|
155
160
|
const recordToSave = {
|
|
156
161
|
...record,
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
declare function
|
|
3
|
-
export default isValidFilter;
|
|
4
|
-
export declare function warnOnMisMatch<R = any>(table: string, filters: RealtimeFilter<R>[]): void;
|
|
1
|
+
import { SupastashFilter } from "../../../types/realtimeData.types";
|
|
2
|
+
export declare function warnOnMisMatch<R = any>(table: string, filters: SupastashFilter<R>[]): void;
|
|
5
3
|
//# sourceMappingURL=validateFilters.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateFilters.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pullFromRemote/validateFilters.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"validateFilters.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pullFromRemote/validateFilters.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAOpE,wBAAgB,cAAc,CAAC,CAAC,GAAG,GAAG,EACpC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,QA2D9B"}
|
|
@@ -1,68 +1,5 @@
|
|
|
1
1
|
import { filterTracker } from "../../../store/tableFilters";
|
|
2
2
|
import { logWarn } from "../../logs";
|
|
3
|
-
const validOperators = new Set([
|
|
4
|
-
"eq",
|
|
5
|
-
"neq",
|
|
6
|
-
"gt",
|
|
7
|
-
"lt",
|
|
8
|
-
"gte",
|
|
9
|
-
"lte",
|
|
10
|
-
"is",
|
|
11
|
-
"in",
|
|
12
|
-
]);
|
|
13
|
-
function isValidFilter(filters) {
|
|
14
|
-
for (const filter of filters ?? []) {
|
|
15
|
-
if (!filter || typeof filter !== "object") {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
const { column, operator, value } = filter;
|
|
19
|
-
if (typeof column !== "string" || column.trim() === "") {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
if (!validOperators.has(operator)) {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
switch (operator) {
|
|
26
|
-
case "is":
|
|
27
|
-
if (typeof value === "boolean") {
|
|
28
|
-
// normalize
|
|
29
|
-
filter.value = value ? "true" : "false";
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
if (!(value === null ||
|
|
33
|
-
value === "null" ||
|
|
34
|
-
value === "true" ||
|
|
35
|
-
value === "false")) {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
break;
|
|
39
|
-
case "in":
|
|
40
|
-
if (Array.isArray(value)) {
|
|
41
|
-
if (value.length === 0) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
else if (typeof value === "string") {
|
|
46
|
-
const trimmed = value.trim();
|
|
47
|
-
if (trimmed === "" ||
|
|
48
|
-
trimmed.split(",").filter((item) => item.trim() !== "").length === 0) {
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
break;
|
|
56
|
-
default:
|
|
57
|
-
if (value === undefined || value === null) {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
export default isValidFilter;
|
|
66
3
|
const tablesWarned = new Set();
|
|
67
4
|
const debounceWarnTime = 2000;
|
|
68
5
|
let debounceWarnTimeout = null;
|
|
@@ -74,6 +11,15 @@ export function warnOnMisMatch(table, filters) {
|
|
|
74
11
|
for (let i = 0; i < maxLength; i++) {
|
|
75
12
|
const oldFilter = existingFilters[i];
|
|
76
13
|
const newFilter = filters[i];
|
|
14
|
+
if ("or" in oldFilter || "or" in newFilter) {
|
|
15
|
+
if ("or" in oldFilter && "or" in newFilter) {
|
|
16
|
+
if (oldFilter.or.length !== newFilter.or.length) {
|
|
17
|
+
hasMismatch = true;
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
77
23
|
if (!oldFilter || !newFilter) {
|
|
78
24
|
hasMismatch = true;
|
|
79
25
|
break;
|
|
@@ -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;AA6DzD;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,WAAW,EAAE,iBAc/B"}
|
|
@@ -12,11 +12,13 @@ const SQL_CHUNK = 999;
|
|
|
12
12
|
*/
|
|
13
13
|
async function permanentlyDeleteLocally(table, ids) {
|
|
14
14
|
const db = await getSupastashDb();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
await db.withTransaction(async (tx) => {
|
|
16
|
+
for (let i = 0; i < ids.length; i += SQL_CHUNK) {
|
|
17
|
+
const slice = ids.slice(i, i + SQL_CHUNK);
|
|
18
|
+
const placeholders = slice.map(() => "?").join(", ");
|
|
19
|
+
await tx.runAsync(`DELETE FROM ${table} WHERE id IN (${placeholders})`, slice);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
24
|
* Deletes a chunk of data from the remote database
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getAllUnsyncedData.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pushLocal/getAllUnsyncedData.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"getAllUnsyncedData.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/pushLocal/getAllUnsyncedData.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAwCzD;;;;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,59 +1,27 @@
|
|
|
1
|
-
import { getSupastashConfig } from "../../../core/config";
|
|
2
1
|
import { getSupastashDb } from "../../../db/dbInitializer";
|
|
3
|
-
import { tableSchemaData } from "../../../store/tableSchemaData";
|
|
4
2
|
import { getTableSchema } from "../../../utils/getTableSchema";
|
|
5
3
|
import log from "../../../utils/logs";
|
|
6
|
-
import {
|
|
7
|
-
import { supabaseClientErr } from "../../supabaseClientErr";
|
|
8
|
-
const numberOfErrors = new Map();
|
|
4
|
+
import { getRemoteTableSchema } from "../status/remoteSchema";
|
|
9
5
|
const sharedKeysCache = new Map();
|
|
10
6
|
async function getRemoteKeys(table) {
|
|
11
|
-
const config = getSupastashConfig();
|
|
12
|
-
const supabase = config?.supabaseClient;
|
|
13
|
-
if (!supabase) {
|
|
14
|
-
throw new Error(`Supabase client not found, ${supabaseClientErr}`);
|
|
15
|
-
}
|
|
16
7
|
if (sharedKeysCache.has(table)) {
|
|
17
8
|
return sharedKeysCache.get(table);
|
|
18
9
|
}
|
|
19
|
-
|
|
10
|
+
const remoteSchema = await getRemoteTableSchema(table);
|
|
11
|
+
if (!remoteSchema)
|
|
20
12
|
return null;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (!isConnected) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
const { data, error } = await supabase.rpc("get_table_schema", {
|
|
28
|
-
table_name: table,
|
|
29
|
-
});
|
|
30
|
-
if (error) {
|
|
31
|
-
if (!isNetworkError(error)) {
|
|
32
|
-
log(`[Supastash] Error getting remote keys for table ${table} on public schema: ${error.message}
|
|
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`);
|
|
34
|
-
numberOfErrors.set(table, (numberOfErrors.get(table) || 0) + 1);
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
tableSchemaData.set(table, data);
|
|
39
|
-
}
|
|
40
|
-
if (!tableSchemaData.get(table)) {
|
|
13
|
+
const remoteKeys = remoteSchema.map((col) => col.column_name);
|
|
14
|
+
const localSchema = await getTableSchema(table);
|
|
15
|
+
if (!localSchema)
|
|
41
16
|
return null;
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const sharedKeys = keys?.filter((key) => columns.includes(key));
|
|
46
|
-
const missingKeys = columns.filter((column) => !keys?.includes(column) && column !== "synced_at");
|
|
47
|
-
// Inform user of missing keys
|
|
17
|
+
const localKeys = localSchema;
|
|
18
|
+
const sharedKeys = remoteKeys.filter((key) => localKeys.includes(key));
|
|
19
|
+
const missingKeys = remoteKeys.filter((key) => !localKeys.includes(key) && key !== "synced_at" && key !== "arrived_at");
|
|
48
20
|
if (missingKeys.length > 0) {
|
|
49
|
-
log(`[Supastash] Missing keys for table ${table}
|
|
50
|
-
}
|
|
51
|
-
// Return shared keys if they exist
|
|
52
|
-
if (sharedKeys) {
|
|
53
|
-
sharedKeysCache.set(table, sharedKeys);
|
|
54
|
-
return sharedKeys;
|
|
21
|
+
log(`[Supastash] Missing keys for table ${table}: ${missingKeys.join(", ")}`);
|
|
55
22
|
}
|
|
56
|
-
|
|
23
|
+
sharedKeysCache.set(table, sharedKeys);
|
|
24
|
+
return sharedKeys.length ? sharedKeys : null;
|
|
57
25
|
}
|
|
58
26
|
/**
|
|
59
27
|
* Gets all unsynced data from a table
|
|
@@ -220,7 +220,7 @@ export async function rewindAndDropLocal(table, rowId, supabase) {
|
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
async function replaceLocalWithServer(table, serverRow) {
|
|
223
|
-
await upsertData(table, serverRow);
|
|
223
|
+
await upsertData({ table, record: serverRow });
|
|
224
224
|
await updateLocalSyncedAt(table, [serverRow.id]);
|
|
225
225
|
setQueryStatus(serverRow.id, table, "success");
|
|
226
226
|
supastashEventBus.emit("updateSyncStatus");
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SupastashFilter } from "../../../types/realtimeData.types";
|
|
2
2
|
/**
|
|
3
3
|
* Canonicalizes a set of filters
|
|
4
4
|
* @param filters - The filters to canonicalize
|
|
5
5
|
* @returns The canonicalized filters
|
|
6
6
|
*/
|
|
7
|
-
export declare function canonicalizeFilters(filters?:
|
|
7
|
+
export declare function canonicalizeFilters(filters?: SupastashFilter[] | null): string;
|
|
8
8
|
/**
|
|
9
9
|
* Computes the filter key for a given set of filters
|
|
10
10
|
* @param filters - The filters to compute the key for
|
|
11
11
|
* @param ns - The namespace to use for the key
|
|
12
12
|
* @returns The computed filter key
|
|
13
13
|
*/
|
|
14
|
-
export declare function computeFilterKey(filters?:
|
|
14
|
+
export declare function computeFilterKey(filters?: SupastashFilter[] | null, ns?: string): Promise<string>;
|
|
15
15
|
//# sourceMappingURL=filterKey.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filterKey.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/status/filterKey.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"filterKey.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/status/filterKey.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AA4BpE;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,CAAC,EAAE,eAAe,EAAE,GAAG,IAAI,GACjC,MAAM,CAuBR;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,CAAC,EAAE,eAAe,EAAE,GAAG,IAAI,EAClC,EAAE,SAAW,mBAQd"}
|
|
@@ -31,7 +31,9 @@ function normVal(v) {
|
|
|
31
31
|
* @returns The canonicalized filters
|
|
32
32
|
*/
|
|
33
33
|
export function canonicalizeFilters(filters) {
|
|
34
|
-
const list = (filters ?? [])
|
|
34
|
+
const list = (filters ?? [])
|
|
35
|
+
.filter((f) => !!f && typeof f === "object" && "column" in f && "operator" in f)
|
|
36
|
+
.map((f) => ({
|
|
35
37
|
column: String(f.column),
|
|
36
38
|
operator: f.operator,
|
|
37
39
|
value: normVal(f.value),
|
|
@@ -41,7 +43,8 @@ export function canonicalizeFilters(filters) {
|
|
|
41
43
|
return a.column < b.column ? -1 : 1;
|
|
42
44
|
if (a.operator !== b.operator)
|
|
43
45
|
return a.operator < b.operator ? -1 : 1;
|
|
44
|
-
const av = JSON.stringify(a.value)
|
|
46
|
+
const av = JSON.stringify(a.value);
|
|
47
|
+
const bv = JSON.stringify(b.value);
|
|
45
48
|
return av < bv ? -1 : av > bv ? 1 : 0;
|
|
46
49
|
});
|
|
47
50
|
return JSON.stringify(list);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TableSchema } from "../../../types/realtimeData.types";
|
|
2
|
+
export declare function appendSyncedAt(schema: TableSchema[]): TableSchema[];
|
|
3
|
+
export declare function getRemoteTableSchema(table: string): Promise<TableSchema[] | null>;
|
|
4
|
+
//# sourceMappingURL=remoteSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteSchema.d.ts","sourceRoot":"","sources":["../../../../src/utils/sync/status/remoteSchema.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AA4HhE,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,iBASnD;AAID,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,CAkD/B"}
|