prostgles-client 4.0.360 → 4.0.362
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/FunctionQueuer.js +51 -49
- package/dist/SyncedTable/SyncedTable.d.ts +3 -108
- package/dist/SyncedTable/SyncedTable.d.ts.map +1 -1
- package/dist/SyncedTable/SyncedTable.js +1 -628
- package/dist/SyncedTable/createSync.d.ts +3 -2
- package/dist/SyncedTable/createSync.d.ts.map +1 -1
- package/dist/SyncedTable/createSync.js +16 -9
- package/dist/SyncedTable/createSyncDataStore.d.ts +1 -1
- package/dist/SyncedTable/createSyncDataStore.d.ts.map +1 -1
- package/dist/SyncedTable/createSyncDataStore.js +2 -3
- package/dist/SyncedTable/createSyncStateUtils.d.ts +7 -4
- package/dist/SyncedTable/createSyncStateUtils.d.ts.map +1 -1
- package/dist/SyncedTable/createSyncStateUtils.js +32 -38
- package/dist/SyncedTable/createSyncSubscriptionManager.js +6 -10
- package/dist/auth/useAuthState.d.ts +2 -2
- package/dist/auth/useAuthState.d.ts.map +1 -1
- package/dist/auth/useAuthState.js +4 -5
- package/dist/getAuthHandler.js +7 -9
- package/dist/getDbHandler.d.ts +2 -6
- package/dist/getDbHandler.d.ts.map +1 -1
- package/dist/getDbHandler.js +37 -71
- package/dist/getMethods.js +1 -1
- package/dist/getSqlHandler.js +6 -8
- package/dist/getSubscriptionHandler.js +5 -5
- package/dist/getSyncHandlerV2.d.ts.map +1 -1
- package/dist/getSyncHandlerV2.js +3 -4
- package/dist/hooks/reactImports.js +2 -3
- package/dist/hooks/useAsyncEffectQueue.js +3 -4
- package/dist/hooks/useEffectAsync.js +1 -2
- package/dist/hooks/useEffectDeep.js +1 -1
- package/dist/hooks/useFetch.js +1 -1
- package/dist/hooks/useProstglesClient.d.ts +1 -1
- package/dist/hooks/useProstglesClient.d.ts.map +1 -1
- package/dist/hooks/useProstglesClient.js +5 -8
- package/dist/hooks/useSubscribe.js +1 -1
- package/dist/hooks/useSync.js +1 -1
- package/dist/{prostgles-full.d.ts → index.d.ts} +1 -3
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -1
- package/dist/{prostgles-full-cdn.d.ts → index.umd.d.ts} +1 -1
- package/dist/index.umd.d.ts.map +1 -0
- package/dist/index.umd.js +1 -0
- package/dist/prostgles.d.ts +3 -3
- package/dist/prostgles.d.ts.map +1 -1
- package/dist/prostgles.js +11 -13
- package/package.json +3 -4
- package/tsconfig.json +5 -15
- package/webpack.dev.js +14 -15
- package/webpack.prod.js +2 -3
- package/dist/SyncedTable/getMultiSyncSubscription.d.ts +0 -12
- package/dist/SyncedTable/getMultiSyncSubscription.d.ts.map +0 -1
- package/dist/SyncedTable/getMultiSyncSubscription.js +0 -60
- package/dist/SyncedTable/getSingleSyncSubscription.d.ts +0 -1
- package/dist/SyncedTable/getSingleSyncSubscription.d.ts.map +0 -1
- package/dist/SyncedTable/getSingleSyncSubscription.js +0 -1
- package/dist/getSyncHandler.d.ts +0 -10
- package/dist/getSyncHandler.d.ts.map +0 -1
- package/dist/getSyncHandler.js +0 -199
- package/dist/index.no-sync.js +0 -1
- package/dist/prostgles-full-cdn.d.ts.map +0 -1
- package/dist/prostgles-full-cdn.js +0 -8
- package/dist/prostgles-full.d.ts.map +0 -1
- package/dist/prostgles-full.js +0 -38
|
@@ -1,635 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.quickClone = exports.mergeDeep =
|
|
3
|
+
exports.quickClone = exports.mergeDeep = void 0;
|
|
4
4
|
const prostgles_types_1 = require("prostgles-types");
|
|
5
|
-
const getMultiSyncSubscription_1 = require("./getMultiSyncSubscription");
|
|
6
|
-
const WAL_1 = require("prostgles-types/dist/WAL");
|
|
7
5
|
const hasWnd = typeof window !== "undefined";
|
|
8
|
-
class SyncedTable {
|
|
9
|
-
/**
|
|
10
|
-
* add debug mode to fix sudden no data and sync listeners bug
|
|
11
|
-
*/
|
|
12
|
-
set multiSubscriptions(mSubs) {
|
|
13
|
-
this._multiSubscriptions = mSubs.slice(0);
|
|
14
|
-
}
|
|
15
|
-
get multiSubscriptions() {
|
|
16
|
-
return this._multiSubscriptions;
|
|
17
|
-
}
|
|
18
|
-
set singleSubscriptions(sSubs) {
|
|
19
|
-
this._singleSubscriptions = sSubs.slice(0);
|
|
20
|
-
}
|
|
21
|
-
get singleSubscriptions() {
|
|
22
|
-
return this._singleSubscriptions;
|
|
23
|
-
}
|
|
24
|
-
constructor(options) {
|
|
25
|
-
var _a;
|
|
26
|
-
this.throttle = 100;
|
|
27
|
-
this.batch_size = 50;
|
|
28
|
-
this.columns = [];
|
|
29
|
-
this._multiSubscriptions = [];
|
|
30
|
-
this._singleSubscriptions = [];
|
|
31
|
-
this.itemsMap = new Map();
|
|
32
|
-
this.isSynced = false;
|
|
33
|
-
/**
|
|
34
|
-
* Notifies multi subs with ALL data + deltas. Attaches handles on data if required
|
|
35
|
-
* @param newData -> updates. Must include id_fields + updates
|
|
36
|
-
*/
|
|
37
|
-
this._notifySubscribers = (changes = []) => {
|
|
38
|
-
var _a, _b;
|
|
39
|
-
if (!this.isSynced) {
|
|
40
|
-
(_a = this.onDebug) === null || _a === void 0 ? void 0 : _a.call(this, { command: "notifySubscribers", data: [], info: "not synced yet" });
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
(_b = this.onDebug) === null || _b === void 0 ? void 0 : _b.call(this, { command: "notifySubscribers", data: changes });
|
|
45
|
-
}
|
|
46
|
-
/* Deleted items (changes = []) do not trigger singleSubscriptions notify because it might break things */
|
|
47
|
-
const items = [], deltas = [], ids = [];
|
|
48
|
-
changes.map(({ idObj, newItem, delta }) => {
|
|
49
|
-
/* Single subs do not care about the filter */
|
|
50
|
-
this.singleSubscriptions
|
|
51
|
-
.filter((s) => this.matchesIdObj(s.idObj, idObj))
|
|
52
|
-
.map(async (s) => {
|
|
53
|
-
try {
|
|
54
|
-
await s.notify(newItem, delta);
|
|
55
|
-
}
|
|
56
|
-
catch (e) {
|
|
57
|
-
console.error("SyncedTable failed to notify: ", e);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
/* Preparing data for multi subs */
|
|
61
|
-
if (this.matchesFilter(newItem)) {
|
|
62
|
-
items.push(newItem);
|
|
63
|
-
deltas.push(delta);
|
|
64
|
-
ids.push(idObj);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
if (this.multiSubscriptions.length) {
|
|
68
|
-
const allItems = [], allDeltas = [];
|
|
69
|
-
this.getItems().map((d) => {
|
|
70
|
-
allItems.push({ ...d });
|
|
71
|
-
const dIdx = items.findIndex((_d) => this.matchesIdObj(d, _d));
|
|
72
|
-
allDeltas.push(deltas[dIdx]);
|
|
73
|
-
});
|
|
74
|
-
/* Multisubs must not forget about the original filter */
|
|
75
|
-
this.multiSubscriptions.map(async (s) => {
|
|
76
|
-
try {
|
|
77
|
-
await s.notify(allItems, allDeltas);
|
|
78
|
-
}
|
|
79
|
-
catch (e) {
|
|
80
|
-
console.error("SyncedTable failed to notify: ", e);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
this.unsubscribe = (onChange) => {
|
|
86
|
-
this.singleSubscriptions = this.singleSubscriptions.filter((s) => s._onChange !== onChange);
|
|
87
|
-
this.multiSubscriptions = this.multiSubscriptions.filter((s) => s._onChange !== onChange);
|
|
88
|
-
return "ok";
|
|
89
|
-
};
|
|
90
|
-
this.unsync = () => {
|
|
91
|
-
var _a;
|
|
92
|
-
(_a = this.dbSync) === null || _a === void 0 ? void 0 : _a.unsync();
|
|
93
|
-
};
|
|
94
|
-
this.destroy = () => {
|
|
95
|
-
this.unsync();
|
|
96
|
-
this.multiSubscriptions = [];
|
|
97
|
-
this.singleSubscriptions = [];
|
|
98
|
-
this.itemsMap.clear();
|
|
99
|
-
};
|
|
100
|
-
this.delete = async (item, from_server = false) => {
|
|
101
|
-
var _a;
|
|
102
|
-
const idObj = this.getIdObj(item);
|
|
103
|
-
this.setItem(idObj, true, true);
|
|
104
|
-
if (!from_server && ((_a = this.tableHandler) === null || _a === void 0 ? void 0 : _a.delete)) {
|
|
105
|
-
await this.tableHandler.delete(idObj);
|
|
106
|
-
}
|
|
107
|
-
this._notifySubscribers();
|
|
108
|
-
return true;
|
|
109
|
-
};
|
|
110
|
-
/**
|
|
111
|
-
* Ensures that all object keys match valid column names
|
|
112
|
-
*/
|
|
113
|
-
this.checkItemCols = (item) => {
|
|
114
|
-
if (this.columns.length) {
|
|
115
|
-
const badCols = Object.keys({ ...item }).filter((k) => !this.columns.find((c) => c.name === k));
|
|
116
|
-
if (badCols.length) {
|
|
117
|
-
throw `Unexpected columns in sync item update: ` + badCols.join(", ");
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
/**
|
|
122
|
-
* Upserts data locally -> notify subs -> sends to server if required
|
|
123
|
-
* synced_field is populated if data is not from server
|
|
124
|
-
* @param items <{ idObj: object, delta: object }[]> Data items that changed
|
|
125
|
-
* @param from_server : <boolean> If false then updates will be sent to server
|
|
126
|
-
*/
|
|
127
|
-
this.upsert = async (items, from_server = false) => {
|
|
128
|
-
var _a, _b;
|
|
129
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
130
|
-
if ((!items || !items.length) && !from_server)
|
|
131
|
-
throw "No data provided for upsert";
|
|
132
|
-
/* If data has been deleted then wait for it to sync with server before continuing */
|
|
133
|
-
// if(from_server && this.getDeleted().length){
|
|
134
|
-
// await this.syncDeleted();
|
|
135
|
-
// }
|
|
136
|
-
const results = [];
|
|
137
|
-
let status;
|
|
138
|
-
const walItems = [];
|
|
139
|
-
await Promise.all(items.map(async (item, i) => {
|
|
140
|
-
var _a;
|
|
141
|
-
// let d = { ...item.idObj, ...item.delta };
|
|
142
|
-
const idObj = { ...item.idObj };
|
|
143
|
-
let delta = { ...item.delta };
|
|
144
|
-
/* Convert undefined to null because:
|
|
145
|
-
1) JSON.stringify drops these keys
|
|
146
|
-
2) Postgres does not have undefined
|
|
147
|
-
*/
|
|
148
|
-
Object.keys(delta).map((k) => {
|
|
149
|
-
if (delta[k] === undefined)
|
|
150
|
-
delta[k] = null;
|
|
151
|
-
});
|
|
152
|
-
if (!from_server) {
|
|
153
|
-
this.checkItemCols({ ...item.delta, ...item.idObj });
|
|
154
|
-
}
|
|
155
|
-
const oldItem = this.getItem(idObj);
|
|
156
|
-
/* Calc delta if missing or if from server */
|
|
157
|
-
if ((from_server || (0, prostgles_types_1.isEmpty)(delta)) && !(0, prostgles_types_1.isEmpty)(oldItem)) {
|
|
158
|
-
delta = this.getDelta(oldItem || {}, delta);
|
|
159
|
-
}
|
|
160
|
-
/* Add synced if local update */
|
|
161
|
-
/** Will need to check client clock shift */
|
|
162
|
-
if (!from_server) {
|
|
163
|
-
delta[this.synced_field] = Date.now();
|
|
164
|
-
}
|
|
165
|
-
let newItem = { ...oldItem, ...delta, ...idObj };
|
|
166
|
-
if (oldItem && !from_server) {
|
|
167
|
-
/**
|
|
168
|
-
* Merge deep
|
|
169
|
-
*/
|
|
170
|
-
if ((_a = item.opts) === null || _a === void 0 ? void 0 : _a.deepMerge) {
|
|
171
|
-
newItem = (0, exports.mergeDeep)({ ...oldItem, ...idObj }, { ...delta });
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
/* Update existing -> Expecting delta */
|
|
175
|
-
if (oldItem) {
|
|
176
|
-
status =
|
|
177
|
-
oldItem[this.synced_field] < newItem[this.synced_field] ? "updated" : "unchanged";
|
|
178
|
-
/* Insert new item */
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
status = "inserted";
|
|
182
|
-
}
|
|
183
|
-
this.setItem(newItem);
|
|
184
|
-
// if(!status) throw "changeInfo status missing"
|
|
185
|
-
const changeInfo = { idObj, delta, oldItem, newItem, status, from_server };
|
|
186
|
-
// const idStr = this.getIdStr(idObj);
|
|
187
|
-
/* IF Local updates then Keep any existing oldItem to revert to the earliest working item */
|
|
188
|
-
if (!from_server) {
|
|
189
|
-
/* Patch server data if necessary and update separately to account for errors */
|
|
190
|
-
// let updatedWithPatch = false;
|
|
191
|
-
// if(this.columns && this.columns.length && (this.patchText || this.patchJSON)){
|
|
192
|
-
// // const jCols = this.columns.filter(c => c.data_type === "json")
|
|
193
|
-
// const txtCols = this.columns.filter(c => c.data_type === "text");
|
|
194
|
-
// if(this.patchText && txtCols.length && this.db[this.name].update){
|
|
195
|
-
// let patchedDelta;
|
|
196
|
-
// txtCols.map(c => {
|
|
197
|
-
// if(c.name in changeInfo.delta){
|
|
198
|
-
// patchedDelta = patchedDelta || {
|
|
199
|
-
// ...changeInfo.delta,
|
|
200
|
-
// }
|
|
201
|
-
// patchedDelta[c.name] = getTextPatch(changeInfo.oldItem[c.name], changeInfo.delta[c.name]);
|
|
202
|
-
// }
|
|
203
|
-
// });
|
|
204
|
-
// if(patchedDelta){
|
|
205
|
-
// try {
|
|
206
|
-
// await this.db[this.name].update(idObj, patchedDelta);
|
|
207
|
-
// updatedWithPatch = true;
|
|
208
|
-
// } catch(e) {
|
|
209
|
-
// console.log("failed to patch update", e)
|
|
210
|
-
// }
|
|
211
|
-
// }
|
|
212
|
-
// // console.log("json-stable-stringify ???")
|
|
213
|
-
// }
|
|
214
|
-
// }
|
|
215
|
-
walItems.push({
|
|
216
|
-
initial: oldItem,
|
|
217
|
-
current: { ...newItem },
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
if (!(0, prostgles_types_1.isEmpty)(changeInfo.delta)) {
|
|
221
|
-
results.push(changeInfo);
|
|
222
|
-
}
|
|
223
|
-
/* TODO: Deletes from server */
|
|
224
|
-
// if(allow_deletes){
|
|
225
|
-
// items = this.getItems();
|
|
226
|
-
// }
|
|
227
|
-
return true;
|
|
228
|
-
})).catch((err) => {
|
|
229
|
-
console.error("SyncedTable failed upsert: ", err);
|
|
230
|
-
});
|
|
231
|
-
(_a = this.notifyWal) === null || _a === void 0 ? void 0 : _a.addData(results.map((d) => ({ initial: d.oldItem, current: d.newItem })));
|
|
232
|
-
/* Push to server */
|
|
233
|
-
if (!from_server && walItems.length) {
|
|
234
|
-
(_b = this.wal) === null || _b === void 0 ? void 0 : _b.addData(walItems);
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
/**
|
|
238
|
-
* Sets the current data
|
|
239
|
-
*/
|
|
240
|
-
this.setItems = (_items) => {
|
|
241
|
-
const items = (0, exports.quickClone)(_items);
|
|
242
|
-
this.itemsMap = new Map(items.map((item) => {
|
|
243
|
-
const id = this.getIdStr(item);
|
|
244
|
-
return [id, { ...item }];
|
|
245
|
-
}));
|
|
246
|
-
};
|
|
247
|
-
/**
|
|
248
|
-
* Returns the current data ordered by synced_field ASC and matching the main filter;
|
|
249
|
-
*/
|
|
250
|
-
this.getItems = () => {
|
|
251
|
-
let items = [];
|
|
252
|
-
items = Array.from(this.itemsMap.values()).map((d) => ({ ...d }));
|
|
253
|
-
const syncFields = [this.synced_field, ...this.id_fields.sort()];
|
|
254
|
-
items = items
|
|
255
|
-
.filter((d) => {
|
|
256
|
-
return !this.filter || !(0, prostgles_types_1.getKeys)(this.filter).find((key) => d[key] !== this.filter[key]);
|
|
257
|
-
})
|
|
258
|
-
.sort((a, b) => syncFields
|
|
259
|
-
.map((key) => (a[key] < b[key] ? -1
|
|
260
|
-
: a[key] > b[key] ? 1
|
|
261
|
-
: 0))
|
|
262
|
-
.find((v) => v));
|
|
263
|
-
return (0, exports.quickClone)(items);
|
|
264
|
-
};
|
|
265
|
-
/**
|
|
266
|
-
* Sync data request
|
|
267
|
-
*/
|
|
268
|
-
this.getBatch = ({ from_synced, to_synced, offset, limit } = { offset: 0, limit: undefined }) => {
|
|
269
|
-
const items = this.getItems();
|
|
270
|
-
let res = items
|
|
271
|
-
.map((c) => ({ ...c }))
|
|
272
|
-
.filter((c) => (!Number.isFinite(from_synced) || +c[this.synced_field] >= +from_synced) &&
|
|
273
|
-
(!Number.isFinite(to_synced) || +c[this.synced_field] <= +to_synced));
|
|
274
|
-
if (offset || limit) {
|
|
275
|
-
res = res.splice(offset !== null && offset !== void 0 ? offset : 0, limit || res.length);
|
|
276
|
-
}
|
|
277
|
-
return res;
|
|
278
|
-
};
|
|
279
|
-
const { name, filter = {}, onReady, onDebug, db, select = "*", onError } = options;
|
|
280
|
-
this.name = name;
|
|
281
|
-
this.filter = filter;
|
|
282
|
-
this.select = select;
|
|
283
|
-
if (onDebug) {
|
|
284
|
-
this.onDebug = (evt) => onDebug({
|
|
285
|
-
...evt,
|
|
286
|
-
type: "sync",
|
|
287
|
-
tableName: name,
|
|
288
|
-
channelName: (0, prostgles_types_1.getSyncChannelName)({ filter, select, tableName: name }),
|
|
289
|
-
options,
|
|
290
|
-
});
|
|
291
|
-
this.onDebug({ command: "create", data: { name, filter, select } });
|
|
292
|
-
}
|
|
293
|
-
const tableHandler = db[name];
|
|
294
|
-
if (!tableHandler) {
|
|
295
|
-
throw `${name} table not found in db`;
|
|
296
|
-
}
|
|
297
|
-
this.db = db;
|
|
298
|
-
const { _sync, _syncInfo } = tableHandler;
|
|
299
|
-
if (!_sync || !_syncInfo) {
|
|
300
|
-
throw `${name} table does not support sync`;
|
|
301
|
-
}
|
|
302
|
-
const { id_fields, synced_field, throttle = 100, batch_size = 50 } = _syncInfo;
|
|
303
|
-
if (!id_fields.length || !synced_field) {
|
|
304
|
-
throw "id_fields/synced_field missing";
|
|
305
|
-
}
|
|
306
|
-
this.id_fields = id_fields;
|
|
307
|
-
this.synced_field = synced_field;
|
|
308
|
-
this.batch_size = batch_size;
|
|
309
|
-
this.throttle = throttle;
|
|
310
|
-
this.multiSubscriptions = [];
|
|
311
|
-
this.singleSubscriptions = [];
|
|
312
|
-
this.onError =
|
|
313
|
-
onError ||
|
|
314
|
-
function (err) {
|
|
315
|
-
console.error("Sync internal error: ", err);
|
|
316
|
-
};
|
|
317
|
-
const onSyncRequest = (syncBatchParams) => {
|
|
318
|
-
var _a;
|
|
319
|
-
let clientSyncInfo = { c_lr: undefined, c_fr: undefined, c_count: 0 };
|
|
320
|
-
const batch = this.getBatch(syncBatchParams);
|
|
321
|
-
if (batch.length) {
|
|
322
|
-
clientSyncInfo = {
|
|
323
|
-
c_fr: this.getRowSyncObj(batch[0]),
|
|
324
|
-
c_lr: this.getRowSyncObj(batch[batch.length - 1]),
|
|
325
|
-
c_count: batch.length,
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
(_a = this.onDebug) === null || _a === void 0 ? void 0 : _a.call(this, { command: "onUpdates", data: { syncBatchParams, batch, clientSyncInfo } });
|
|
329
|
-
return clientSyncInfo;
|
|
330
|
-
}, onPullRequest = async (syncBatchParams) => {
|
|
331
|
-
var _a;
|
|
332
|
-
// if(this.getDeleted().length){
|
|
333
|
-
// await this.syncDeleted();
|
|
334
|
-
// }
|
|
335
|
-
const data = this.getBatch(syncBatchParams);
|
|
336
|
-
await ((_a = this.onDebug) === null || _a === void 0 ? void 0 : _a.call(this, { command: "onPullRequest", data: { syncBatchParams, data } }));
|
|
337
|
-
return { data };
|
|
338
|
-
}, onUpdates = async (onUpdatesParams) => {
|
|
339
|
-
var _a, _b;
|
|
340
|
-
await ((_a = this.onDebug) === null || _a === void 0 ? void 0 : _a.call(this, { command: "onUpdates", data: { onUpdatesParams } }));
|
|
341
|
-
if ("err" in onUpdatesParams && onUpdatesParams.err) {
|
|
342
|
-
(_b = this.onError) === null || _b === void 0 ? void 0 : _b.call(this, onUpdatesParams.err);
|
|
343
|
-
}
|
|
344
|
-
else if ("isSynced" in onUpdatesParams && onUpdatesParams.isSynced && !this.isSynced) {
|
|
345
|
-
this.isSynced = onUpdatesParams.isSynced;
|
|
346
|
-
const items = this.getItems().map((d) => ({ ...d }));
|
|
347
|
-
this.setItems([]);
|
|
348
|
-
const updateItems = items.map((d) => ({
|
|
349
|
-
idObj: this.getIdObj(d),
|
|
350
|
-
delta: { ...d },
|
|
351
|
-
}));
|
|
352
|
-
await this.upsert(updateItems, true);
|
|
353
|
-
}
|
|
354
|
-
else if ("data" in onUpdatesParams) {
|
|
355
|
-
/* Delta left empty so we can prepare it here */
|
|
356
|
-
const updateItems = onUpdatesParams.data.map((d) => {
|
|
357
|
-
return {
|
|
358
|
-
idObj: this.getIdObj(d),
|
|
359
|
-
delta: d,
|
|
360
|
-
};
|
|
361
|
-
});
|
|
362
|
-
await this.upsert(updateItems, true);
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
console.error("Unexpected onUpdates");
|
|
366
|
-
}
|
|
367
|
-
return true;
|
|
368
|
-
};
|
|
369
|
-
const opts = {
|
|
370
|
-
id_fields,
|
|
371
|
-
synced_field,
|
|
372
|
-
throttle,
|
|
373
|
-
};
|
|
374
|
-
_sync(filter, { select }, { onSyncRequest, onPullRequest, onUpdates }).then((s) => {
|
|
375
|
-
this.dbSync = s;
|
|
376
|
-
function confirmExit() {
|
|
377
|
-
return "Data may be lost. Are you sure?";
|
|
378
|
-
}
|
|
379
|
-
/**
|
|
380
|
-
* Some syncs can be read only. Any changes are local
|
|
381
|
-
*/
|
|
382
|
-
this.wal = new WAL_1.WAL({
|
|
383
|
-
...opts,
|
|
384
|
-
batch_size,
|
|
385
|
-
onSendStart: () => {
|
|
386
|
-
if (hasWnd)
|
|
387
|
-
window.onbeforeunload = confirmExit;
|
|
388
|
-
},
|
|
389
|
-
onSend: async (data, walData) => {
|
|
390
|
-
const _data = walData.map((d) => d.current);
|
|
391
|
-
if (!_data.length)
|
|
392
|
-
return [];
|
|
393
|
-
return s.syncData(data);
|
|
394
|
-
}, //, deletedData);,
|
|
395
|
-
onSendEnd: () => {
|
|
396
|
-
if (hasWnd)
|
|
397
|
-
window.onbeforeunload = null;
|
|
398
|
-
},
|
|
399
|
-
});
|
|
400
|
-
this.notifyWal = new WAL_1.WAL({
|
|
401
|
-
...opts,
|
|
402
|
-
batch_size: Infinity,
|
|
403
|
-
throttle: 5,
|
|
404
|
-
onSend: async (_, fullItems) => {
|
|
405
|
-
this._notifySubscribers(fullItems.map((d) => {
|
|
406
|
-
var _a;
|
|
407
|
-
return ({
|
|
408
|
-
delta: this.getDelta((_a = d.initial) !== null && _a !== void 0 ? _a : {}, d.current),
|
|
409
|
-
idObj: this.getIdObj(d.current),
|
|
410
|
-
newItem: d.current,
|
|
411
|
-
});
|
|
412
|
-
}));
|
|
413
|
-
},
|
|
414
|
-
});
|
|
415
|
-
onReady();
|
|
416
|
-
});
|
|
417
|
-
(_a = tableHandler.getColumns) === null || _a === void 0 ? void 0 : _a.call(tableHandler).then((cols) => {
|
|
418
|
-
this.columns = cols;
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
static create(opts) {
|
|
422
|
-
return new Promise((resolve, reject) => {
|
|
423
|
-
try {
|
|
424
|
-
const res = new SyncedTable({
|
|
425
|
-
...opts,
|
|
426
|
-
onReady: () => {
|
|
427
|
-
setTimeout(() => {
|
|
428
|
-
resolve(res);
|
|
429
|
-
}, 0);
|
|
430
|
-
},
|
|
431
|
-
onError: (err) => {
|
|
432
|
-
console.error("Sync internal error: ", err);
|
|
433
|
-
reject(err);
|
|
434
|
-
},
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
catch (err) {
|
|
438
|
-
reject(err);
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
/**
|
|
443
|
-
* Returns a sync handler to all records within the SyncedTable instance
|
|
444
|
-
* @param onChange change listener <(items: object[], delta: object[]) => any >
|
|
445
|
-
* @param handlesOnData If true then $upsert and $unsync handles will be added on each data item. True by default;
|
|
446
|
-
*/
|
|
447
|
-
sync(onChange, handlesOnData = true) {
|
|
448
|
-
const { sub, handles } = getMultiSyncSubscription_1.getMultiSyncSubscription.bind(this)({
|
|
449
|
-
onChange: onChange,
|
|
450
|
-
handlesOnData,
|
|
451
|
-
});
|
|
452
|
-
this.multiSubscriptions.push(sub);
|
|
453
|
-
setTimeout(() => {
|
|
454
|
-
const items = this.getItems();
|
|
455
|
-
sub.notify(items, items);
|
|
456
|
-
}, 0);
|
|
457
|
-
return Object.freeze({ ...handles });
|
|
458
|
-
}
|
|
459
|
-
makeSingleSyncHandles(idObj, onChange) {
|
|
460
|
-
const handles = {
|
|
461
|
-
$get: () => this.getItem(idObj),
|
|
462
|
-
$find: (idObject) => this.getItem(idObject),
|
|
463
|
-
$unsync: () => {
|
|
464
|
-
return this.unsubscribe(onChange);
|
|
465
|
-
},
|
|
466
|
-
$delete: () => {
|
|
467
|
-
return this.delete(idObj);
|
|
468
|
-
},
|
|
469
|
-
$update: (newData, opts) => {
|
|
470
|
-
/* DROPPED SYNC BUG */
|
|
471
|
-
if (!this.singleSubscriptions.length && !this.multiSubscriptions.length) {
|
|
472
|
-
console.warn("No sync listeners");
|
|
473
|
-
}
|
|
474
|
-
return this.upsert([{ idObj, delta: newData, opts }]);
|
|
475
|
-
},
|
|
476
|
-
$cloneSync: (onChange) => this.syncOne(idObj, onChange),
|
|
477
|
-
// TODO: add clone sync hook
|
|
478
|
-
// $useCloneSync: () => {
|
|
479
|
-
// const handles = this.syncOne<T, Full>(idObj, item => {
|
|
480
|
-
// setItem()
|
|
481
|
-
// });
|
|
482
|
-
// return handles.$unsync;
|
|
483
|
-
// },
|
|
484
|
-
$cloneMultiSync: (onChange) => this.sync(onChange, true),
|
|
485
|
-
};
|
|
486
|
-
return handles;
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Returns a sync handler to a specific record within the SyncedTable instance
|
|
490
|
-
* @param idObj object containing the target id_fields properties
|
|
491
|
-
* @param onChange change listener <(item: object, delta: object) => any >
|
|
492
|
-
* @param handlesOnData If true then $update, $delete and $unsync handles will be added on the data item. True by default;
|
|
493
|
-
*/
|
|
494
|
-
syncOne(idObj, onChange, handlesOnData = true) {
|
|
495
|
-
const handles = this.makeSingleSyncHandles(idObj, onChange);
|
|
496
|
-
const sub = {
|
|
497
|
-
_onChange: onChange,
|
|
498
|
-
idObj,
|
|
499
|
-
handlesOnData,
|
|
500
|
-
handles,
|
|
501
|
-
notify: (data, delta) => {
|
|
502
|
-
const newData = { ...data };
|
|
503
|
-
if (handlesOnData) {
|
|
504
|
-
newData.$get = handles.$get;
|
|
505
|
-
newData.$find = handles.$find;
|
|
506
|
-
newData.$update = handles.$update;
|
|
507
|
-
newData.$delete = handles.$delete;
|
|
508
|
-
newData.$unsync = handles.$unsync;
|
|
509
|
-
newData.$cloneSync = handles.$cloneSync;
|
|
510
|
-
}
|
|
511
|
-
return onChange(newData, delta);
|
|
512
|
-
},
|
|
513
|
-
};
|
|
514
|
-
this.singleSubscriptions.push(sub);
|
|
515
|
-
setTimeout(() => {
|
|
516
|
-
const existingData = handles.$get();
|
|
517
|
-
if (existingData) {
|
|
518
|
-
sub.notify(existingData, existingData);
|
|
519
|
-
}
|
|
520
|
-
}, 0);
|
|
521
|
-
return Object.freeze({ ...handles });
|
|
522
|
-
}
|
|
523
|
-
getIdStr(d) {
|
|
524
|
-
return this.id_fields
|
|
525
|
-
.sort()
|
|
526
|
-
.map((key) => `${d[key] || ""}`)
|
|
527
|
-
.join(".");
|
|
528
|
-
}
|
|
529
|
-
getIdObj(d) {
|
|
530
|
-
const res = {};
|
|
531
|
-
this.id_fields.sort().map((key) => {
|
|
532
|
-
res[key] = d[key];
|
|
533
|
-
});
|
|
534
|
-
return res;
|
|
535
|
-
}
|
|
536
|
-
getRowSyncObj(d) {
|
|
537
|
-
const res = {};
|
|
538
|
-
[this.synced_field, ...this.id_fields].sort().map((key) => {
|
|
539
|
-
res[key] = d[key];
|
|
540
|
-
});
|
|
541
|
-
return res;
|
|
542
|
-
}
|
|
543
|
-
matchesFilter(item) {
|
|
544
|
-
return Boolean(item &&
|
|
545
|
-
(!this.filter ||
|
|
546
|
-
(0, prostgles_types_1.isEmpty)(this.filter) ||
|
|
547
|
-
!Object.keys(this.filter).find((k) => this.filter[k] !== item[k])));
|
|
548
|
-
}
|
|
549
|
-
matchesIdObj(a, b) {
|
|
550
|
-
return Boolean(a && b && !this.id_fields.sort().find((k) => a[k] !== b[k]));
|
|
551
|
-
}
|
|
552
|
-
// TODO: offline-first deletes if allow_delete = true
|
|
553
|
-
// setDeleted(idObj, fullArray){
|
|
554
|
-
// let deleted: object[] = [];
|
|
555
|
-
// if(fullArray) deleted = fullArray;
|
|
556
|
-
// else {
|
|
557
|
-
// deleted = this.getDeleted();
|
|
558
|
-
// deleted.push(idObj);
|
|
559
|
-
// }
|
|
560
|
-
// if(hasWnd) window.localStorage.setItem(this.name + "_$$psql$$_deleted", <any>deleted);
|
|
561
|
-
// }
|
|
562
|
-
// getDeleted(){
|
|
563
|
-
// const delStr = if(hasWnd) window.localStorage.getItem(this.name + "_$$psql$$_deleted") || '[]';
|
|
564
|
-
// return JSON.parse(delStr);
|
|
565
|
-
// }
|
|
566
|
-
// syncDeleted = async () => {
|
|
567
|
-
// try {
|
|
568
|
-
// await Promise.all(this.getDeleted().map(async idObj => {
|
|
569
|
-
// return this.db[this.name].delete(idObj);
|
|
570
|
-
// }));
|
|
571
|
-
// this.setDeleted(null, []);
|
|
572
|
-
// return true;
|
|
573
|
-
// } catch(e){
|
|
574
|
-
// throw e;
|
|
575
|
-
// }
|
|
576
|
-
// }
|
|
577
|
-
/**
|
|
578
|
-
* Returns properties that are present in {n} and are different to {o}
|
|
579
|
-
* @param o current full data item
|
|
580
|
-
* @param n new data item
|
|
581
|
-
*/
|
|
582
|
-
getDelta(o, n) {
|
|
583
|
-
if ((0, prostgles_types_1.isEmpty)(o))
|
|
584
|
-
return { ...n };
|
|
585
|
-
return Object.fromEntries(Object.entries({ ...n })
|
|
586
|
-
.filter(([k]) => !this.id_fields.includes(k))
|
|
587
|
-
.map(([k, v]) => {
|
|
588
|
-
if (!(0, prostgles_types_1.isEqual)(v, o[k])) {
|
|
589
|
-
const vClone = (0, prostgles_types_1.isObject)(v) ? { ...v }
|
|
590
|
-
: Array.isArray(v) ? v.slice(0)
|
|
591
|
-
: v;
|
|
592
|
-
return [k, vClone];
|
|
593
|
-
}
|
|
594
|
-
})
|
|
595
|
-
.filter(prostgles_types_1.isDefined));
|
|
596
|
-
}
|
|
597
|
-
deleteAll() {
|
|
598
|
-
this.getItems().map((d) => this.delete(d));
|
|
599
|
-
}
|
|
600
|
-
get tableHandler() {
|
|
601
|
-
const tblHandler = this.db[this.name];
|
|
602
|
-
if ((tblHandler === null || tblHandler === void 0 ? void 0 : tblHandler.update) && tblHandler.updateBatch) {
|
|
603
|
-
return tblHandler;
|
|
604
|
-
}
|
|
605
|
-
return undefined;
|
|
606
|
-
}
|
|
607
|
-
/* Returns an item by idObj from the local store */
|
|
608
|
-
getItem(idObj) {
|
|
609
|
-
const d = this.itemsMap.get(this.getIdStr(idObj));
|
|
610
|
-
return (0, exports.quickClone)(d);
|
|
611
|
-
}
|
|
612
|
-
/**
|
|
613
|
-
*
|
|
614
|
-
* @param item data to be inserted/updated/deleted. Must include id_fields
|
|
615
|
-
* @param index (optional) index within array
|
|
616
|
-
* @param isFullData
|
|
617
|
-
* @param deleteItem
|
|
618
|
-
*/
|
|
619
|
-
setItem(_item, isFullData = false, deleteItem = false) {
|
|
620
|
-
var _a;
|
|
621
|
-
const item = (0, exports.quickClone)(_item);
|
|
622
|
-
const id = this.getIdStr(item);
|
|
623
|
-
if (deleteItem) {
|
|
624
|
-
this.itemsMap.delete(id);
|
|
625
|
-
}
|
|
626
|
-
else {
|
|
627
|
-
const existing = (_a = this.itemsMap.get(id)) !== null && _a !== void 0 ? _a : {};
|
|
628
|
-
this.itemsMap.set(id, isFullData ? { ...item } : { ...existing, ...item });
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
exports.SyncedTable = SyncedTable;
|
|
633
6
|
const mergeDeep = (_target, _source) => {
|
|
634
7
|
const target = _target ? (0, exports.quickClone)(_target) : _target;
|
|
635
8
|
const source = _source ? (0, exports.quickClone)(_source) : _source;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { type AnyObject } from "prostgles-types";
|
|
1
2
|
import { WAL } from "prostgles-types/dist/WAL";
|
|
2
3
|
import { type SyncedTableOptions } from "./SyncedTable";
|
|
3
4
|
import type { Socket } from "socket.io-client";
|
|
4
5
|
export declare const createSync: (socket: Socket, options: Omit<SyncedTableOptions, "onReady">) => Promise<{
|
|
5
|
-
sync: <T extends
|
|
6
|
-
syncOne: <T extends
|
|
6
|
+
sync: <T extends AnyObject = AnyObject>(onChange: import("./SyncedTable").MultiChangeListener<T>, handlesOnData?: boolean) => import("./SyncedTable").MultiSyncHandles<T>;
|
|
7
|
+
syncOne: <T extends AnyObject = AnyObject, Full extends boolean = false>(idObj: Partial<T>, onChange: import("./SyncedTable").SingleChangeListener<T, Full>, handlesOnData?: boolean) => import("./SyncedTable").SingleSyncHandles<T, Full>;
|
|
7
8
|
notifyWal: WAL;
|
|
8
9
|
}>;
|
|
9
10
|
//# sourceMappingURL=createSync.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSync.d.ts","sourceRoot":"","sources":["../../lib/SyncedTable/createSync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"createSync.d.ts","sourceRoot":"","sources":["../../lib/SyncedTable/createSync.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EAIf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,GAAG,EAAgB,MAAM,0BAA0B,CAAC;AAI7D,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,eAAO,MAAM,UAAU,GAAU,QAAQ,MAAM,EAAE,SAAS,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC;;;;EAiO5F,CAAC"}
|