@legendapp/state 3.0.0-alpha.2 → 3.0.0-alpha.21
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/CHANGELOG.md +831 -1
- package/LICENSE +21 -1
- package/README.md +141 -1
- package/babel.js +0 -2
- package/babel.mjs +0 -2
- package/helpers/trackHistory.js +2 -2
- package/helpers/trackHistory.mjs +2 -2
- package/index.d.mts +45 -32
- package/index.d.ts +45 -32
- package/index.js +234 -156
- package/index.mjs +234 -156
- package/package.json +6 -1
- package/persist-plugins/async-storage.d.mts +2 -2
- package/persist-plugins/async-storage.d.ts +2 -2
- package/persist-plugins/indexeddb.js +1 -1
- package/persist-plugins/indexeddb.mjs +1 -1
- package/react.d.mts +17 -14
- package/react.d.ts +17 -14
- package/react.js +55 -30
- package/react.mjs +56 -31
- package/sync-plugins/_transformObjectFields.d.mts +31 -0
- package/sync-plugins/_transformObjectFields.d.ts +31 -0
- package/sync-plugins/_transformObjectFields.js +114 -0
- package/sync-plugins/_transformObjectFields.mjs +110 -0
- package/sync-plugins/crud.d.mts +14 -23
- package/sync-plugins/crud.d.ts +14 -23
- package/sync-plugins/crud.js +205 -134
- package/sync-plugins/crud.mjs +206 -135
- package/sync-plugins/firebase.d.mts +26 -0
- package/sync-plugins/firebase.d.ts +26 -0
- package/sync-plugins/firebase.js +365 -0
- package/sync-plugins/firebase.mjs +360 -0
- package/sync-plugins/keel.d.mts +24 -8
- package/sync-plugins/keel.d.ts +24 -8
- package/sync-plugins/keel.js +32 -16
- package/sync-plugins/keel.mjs +32 -16
- package/sync-plugins/supabase.d.mts +1 -1
- package/sync-plugins/supabase.d.ts +1 -1
- package/sync-plugins/supabase.js +5 -4
- package/sync-plugins/supabase.mjs +6 -5
- package/sync-plugins/tanstack-query.d.mts +2 -2
- package/sync-plugins/tanstack-query.d.ts +2 -2
- package/sync-plugins/tanstack-query.js +3 -2
- package/sync-plugins/tanstack-query.mjs +3 -2
- package/sync-plugins/tanstack-react-query.d.mts +1 -1
- package/sync-plugins/tanstack-react-query.d.ts +1 -1
- package/sync.d.mts +38 -185
- package/sync.d.ts +38 -185
- package/sync.js +354 -296
- package/sync.mjs +356 -297
- package/types/babel.d.ts +12 -1
- package/.DS_Store +0 -0
- /package/config/{enable_GetSet.d.mts → enable$GetSet.d.mts} +0 -0
- /package/config/{enable_GetSet.d.ts → enable$GetSet.d.ts} +0 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { observable, symbolDelete, isString, isArray, isObject, computeSelector, isFunction, isNullOrUndefined, isPromise, isNumber, when } from '@legendapp/state';
|
|
2
|
+
import { syncedCrud } from '@legendapp/state/sync-plugins/crud';
|
|
3
|
+
import { getAuth } from 'firebase/auth';
|
|
4
|
+
import { ref, getDatabase, query, orderByChild, startAt, update, onValue, onChildAdded, onChildChanged, onChildRemoved, serverTimestamp, remove, push } from 'firebase/database';
|
|
5
|
+
|
|
6
|
+
// src/sync-plugins/firebase.ts
|
|
7
|
+
var validateMap;
|
|
8
|
+
function transformObjectFields(dataIn, map) {
|
|
9
|
+
if (process.env.NODE_ENV === "development") {
|
|
10
|
+
validateMap(map);
|
|
11
|
+
}
|
|
12
|
+
let ret = dataIn;
|
|
13
|
+
if (dataIn) {
|
|
14
|
+
if (dataIn === symbolDelete)
|
|
15
|
+
return dataIn;
|
|
16
|
+
if (isString(dataIn)) {
|
|
17
|
+
return map[dataIn];
|
|
18
|
+
}
|
|
19
|
+
ret = {};
|
|
20
|
+
const dict = Object.keys(map).length === 1 && map["_dict"];
|
|
21
|
+
for (const key in dataIn) {
|
|
22
|
+
let v = dataIn[key];
|
|
23
|
+
if (dict) {
|
|
24
|
+
ret[key] = transformObjectFields(v, dict);
|
|
25
|
+
} else {
|
|
26
|
+
const mapped = map[key];
|
|
27
|
+
if (mapped === void 0) {
|
|
28
|
+
if (key !== "@") {
|
|
29
|
+
ret[key] = v;
|
|
30
|
+
if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") {
|
|
31
|
+
console.error("A fatal field transformation error has occurred", key, dataIn, map);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} else if (mapped !== null) {
|
|
35
|
+
if (v !== void 0 && v !== null) {
|
|
36
|
+
if (map[key + "_val"]) {
|
|
37
|
+
const mapChild = map[key + "_val"];
|
|
38
|
+
if (isArray(v)) {
|
|
39
|
+
v = v.map((vChild) => mapChild[vChild]);
|
|
40
|
+
} else {
|
|
41
|
+
v = mapChild[v];
|
|
42
|
+
}
|
|
43
|
+
} else if (map[key + "_arr"] && isArray(v)) {
|
|
44
|
+
const mapChild = map[key + "_arr"];
|
|
45
|
+
v = v.map((vChild) => transformObjectFields(vChild, mapChild));
|
|
46
|
+
} else if (isObject(v)) {
|
|
47
|
+
if (map[key + "_obj"]) {
|
|
48
|
+
v = transformObjectFields(v, map[key + "_obj"]);
|
|
49
|
+
} else if (map[key + "_dict"]) {
|
|
50
|
+
const mapChild = map[key + "_dict"];
|
|
51
|
+
const out = {};
|
|
52
|
+
for (const keyChild in v) {
|
|
53
|
+
out[keyChild] = transformObjectFields(v[keyChild], mapChild);
|
|
54
|
+
}
|
|
55
|
+
v = out;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
ret[mapped] = v;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return ret;
|
|
65
|
+
}
|
|
66
|
+
var invertedMaps = /* @__PURE__ */ new WeakMap();
|
|
67
|
+
function invertFieldMap(obj) {
|
|
68
|
+
const existing = invertedMaps.get(obj);
|
|
69
|
+
if (existing)
|
|
70
|
+
return existing;
|
|
71
|
+
const target = {};
|
|
72
|
+
for (const key in obj) {
|
|
73
|
+
const val = obj[key];
|
|
74
|
+
if (key === "_dict") {
|
|
75
|
+
target[key] = invertFieldMap(val);
|
|
76
|
+
} else if (key.endsWith("_obj") || key.endsWith("_dict") || key.endsWith("_arr") || key.endsWith("_val")) {
|
|
77
|
+
const keyMapped = obj[key.replace(/_obj|_dict|_arr|_val$/, "")];
|
|
78
|
+
const suffix = key.match(/_obj|_dict|_arr|_val$/)[0];
|
|
79
|
+
target[keyMapped + suffix] = invertFieldMap(val);
|
|
80
|
+
} else if (typeof val === "string") {
|
|
81
|
+
target[val] = key;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
invertedMaps.set(obj, target);
|
|
85
|
+
return target;
|
|
86
|
+
}
|
|
87
|
+
if (process.env.NODE_ENV === "development") {
|
|
88
|
+
validateMap = function(record) {
|
|
89
|
+
const values = Object.values(record).filter((value) => {
|
|
90
|
+
if (isObject(value)) {
|
|
91
|
+
validateMap(value);
|
|
92
|
+
} else {
|
|
93
|
+
return isString(value);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
const uniques = Array.from(new Set(values));
|
|
97
|
+
if (values.length !== uniques.length) {
|
|
98
|
+
console.error("Field transform map has duplicate values", record, values.length, uniques.length);
|
|
99
|
+
}
|
|
100
|
+
return record;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/sync-plugins/firebase.ts
|
|
105
|
+
var isEnabled$ = observable(true);
|
|
106
|
+
var firebaseConfig = {};
|
|
107
|
+
function configureSyncedFirebase(config) {
|
|
108
|
+
const { enabled, ...rest } = config;
|
|
109
|
+
Object.assign(firebaseConfig, rest);
|
|
110
|
+
if (enabled !== void 0) {
|
|
111
|
+
isEnabled$.set(enabled);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function joinPaths(str1, str2) {
|
|
115
|
+
return str2 ? [str1, str2].join("/").replace(/\/\//g, "/") : str1;
|
|
116
|
+
}
|
|
117
|
+
var fns = {
|
|
118
|
+
isInitialized: () => {
|
|
119
|
+
try {
|
|
120
|
+
return !!getAuth().app;
|
|
121
|
+
} catch (e) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
getCurrentUser: () => {
|
|
126
|
+
var _a;
|
|
127
|
+
return (_a = getAuth().currentUser) == null ? void 0 : _a.uid;
|
|
128
|
+
},
|
|
129
|
+
ref: (path) => ref(getDatabase(), path),
|
|
130
|
+
orderByChild: (ref, child, start) => query(ref, orderByChild(child), startAt(start)),
|
|
131
|
+
update: (ref, object) => update(ref, object),
|
|
132
|
+
once: (ref, callback, callbackError) => {
|
|
133
|
+
let unsubscribe;
|
|
134
|
+
const cb = (snap) => {
|
|
135
|
+
if (unsubscribe) {
|
|
136
|
+
unsubscribe();
|
|
137
|
+
unsubscribe = void 0;
|
|
138
|
+
}
|
|
139
|
+
callback(snap);
|
|
140
|
+
};
|
|
141
|
+
unsubscribe = onValue(ref, cb, callbackError);
|
|
142
|
+
return unsubscribe;
|
|
143
|
+
},
|
|
144
|
+
onChildAdded,
|
|
145
|
+
onChildChanged,
|
|
146
|
+
onChildRemoved,
|
|
147
|
+
onValue,
|
|
148
|
+
serverTimestamp,
|
|
149
|
+
remove: remove,
|
|
150
|
+
onAuthStateChanged: (cb) => getAuth().onAuthStateChanged(cb),
|
|
151
|
+
generateId: () => push(ref(getDatabase())).key
|
|
152
|
+
};
|
|
153
|
+
function syncedFirebase(props) {
|
|
154
|
+
props = { ...firebaseConfig, ...props };
|
|
155
|
+
const saving$ = observable({});
|
|
156
|
+
const pendingOutgoing$ = observable({});
|
|
157
|
+
const pendingIncoming$ = observable({});
|
|
158
|
+
let didList = false;
|
|
159
|
+
const {
|
|
160
|
+
refPath,
|
|
161
|
+
query,
|
|
162
|
+
fieldId,
|
|
163
|
+
realtime,
|
|
164
|
+
requireAuth,
|
|
165
|
+
readonly,
|
|
166
|
+
transform: transformProp,
|
|
167
|
+
fieldTransforms,
|
|
168
|
+
waitFor,
|
|
169
|
+
waitForSet,
|
|
170
|
+
...rest
|
|
171
|
+
} = props;
|
|
172
|
+
const { fieldCreatedAt, changesSince } = props;
|
|
173
|
+
const asType = props.as || "value";
|
|
174
|
+
const fieldUpdatedAt = props.fieldUpdatedAt || "@";
|
|
175
|
+
const computeRef = (lastSync) => {
|
|
176
|
+
const pathFirebase = refPath(fns.getCurrentUser());
|
|
177
|
+
let ref = fns.ref(pathFirebase);
|
|
178
|
+
if (query) {
|
|
179
|
+
ref = query(ref);
|
|
180
|
+
}
|
|
181
|
+
if (changesSince === "last-sync" && lastSync && fieldUpdatedAt && isNumber(lastSync)) {
|
|
182
|
+
ref = fns.orderByChild(ref, fieldUpdatedAt, lastSync + 1);
|
|
183
|
+
}
|
|
184
|
+
return ref;
|
|
185
|
+
};
|
|
186
|
+
const list = async ({ lastSync, onError }) => {
|
|
187
|
+
const ref = computeRef(lastSync);
|
|
188
|
+
return new Promise((resolve) => {
|
|
189
|
+
fns.once(
|
|
190
|
+
ref,
|
|
191
|
+
async (snap) => {
|
|
192
|
+
const val = snap.val();
|
|
193
|
+
let values = [];
|
|
194
|
+
if (!isNullOrUndefined(val)) {
|
|
195
|
+
values = asType === "value" ? [val] : Object.entries(val).map(([key, value]) => {
|
|
196
|
+
if (fieldId && !value[fieldId]) {
|
|
197
|
+
value[fieldId] = key;
|
|
198
|
+
}
|
|
199
|
+
return value;
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
didList = true;
|
|
203
|
+
resolve(values);
|
|
204
|
+
},
|
|
205
|
+
onError
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
const subscribe = realtime ? ({ lastSync, update: update2, onError }) => {
|
|
210
|
+
const ref = computeRef(lastSync);
|
|
211
|
+
let unsubscribes;
|
|
212
|
+
if (asType === "value") {
|
|
213
|
+
const onValue2 = (snap) => {
|
|
214
|
+
if (!didList)
|
|
215
|
+
return;
|
|
216
|
+
const val = snap.val();
|
|
217
|
+
if (saving$[""].get()) {
|
|
218
|
+
pendingIncoming$[""].set(val);
|
|
219
|
+
} else {
|
|
220
|
+
update2({
|
|
221
|
+
value: [val],
|
|
222
|
+
mode: "set"
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
unsubscribes = [fns.onValue(ref, onValue2, onError)];
|
|
227
|
+
} else {
|
|
228
|
+
const onChildChange = (snap) => {
|
|
229
|
+
if (!didList)
|
|
230
|
+
return;
|
|
231
|
+
const key = snap.key;
|
|
232
|
+
const val = snap.val();
|
|
233
|
+
if (saving$[key].get()) {
|
|
234
|
+
pendingIncoming$[key].set(val);
|
|
235
|
+
} else {
|
|
236
|
+
update2({
|
|
237
|
+
value: [val],
|
|
238
|
+
mode: "assign"
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
const onChildDelete = (snap) => {
|
|
243
|
+
if (!didList)
|
|
244
|
+
return;
|
|
245
|
+
const key = snap.key;
|
|
246
|
+
update2({
|
|
247
|
+
value: [{ [key]: symbolDelete }],
|
|
248
|
+
mode: "assign"
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
unsubscribes = [
|
|
252
|
+
fns.onChildAdded(ref, onChildChange, onError),
|
|
253
|
+
fns.onChildChanged(ref, onChildChange, onError),
|
|
254
|
+
fns.onChildRemoved(ref, onChildDelete, onError)
|
|
255
|
+
];
|
|
256
|
+
}
|
|
257
|
+
return () => {
|
|
258
|
+
unsubscribes.forEach((fn) => fn());
|
|
259
|
+
};
|
|
260
|
+
} : void 0;
|
|
261
|
+
const addUpdatedAt = (input) => {
|
|
262
|
+
if (fieldUpdatedAt) {
|
|
263
|
+
input[fieldUpdatedAt] = serverTimestamp();
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
const addCreatedAt = (input) => {
|
|
267
|
+
if (fieldCreatedAt && !input[fieldCreatedAt]) {
|
|
268
|
+
input[fieldCreatedAt] = serverTimestamp();
|
|
269
|
+
}
|
|
270
|
+
return addUpdatedAt(input);
|
|
271
|
+
};
|
|
272
|
+
const upsert = async (input) => {
|
|
273
|
+
const id = fieldId ? input[fieldId] : "";
|
|
274
|
+
if (saving$[id].get()) {
|
|
275
|
+
pendingOutgoing$[id].set(input);
|
|
276
|
+
} else {
|
|
277
|
+
saving$[id].set(true);
|
|
278
|
+
const path = joinPaths(refPath(fns.getCurrentUser()), fieldId ? id : "");
|
|
279
|
+
await fns.update(fns.ref(path), input);
|
|
280
|
+
saving$[id].set(false);
|
|
281
|
+
flushAfterSave();
|
|
282
|
+
}
|
|
283
|
+
return when(
|
|
284
|
+
() => !pendingOutgoing$[id].get(),
|
|
285
|
+
() => {
|
|
286
|
+
const value = pendingIncoming$[id].get();
|
|
287
|
+
if (value) {
|
|
288
|
+
pendingIncoming$[id].delete();
|
|
289
|
+
return value;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
};
|
|
294
|
+
const flushAfterSave = () => {
|
|
295
|
+
const outgoing = pendingOutgoing$.get();
|
|
296
|
+
Object.values(outgoing).forEach((value) => {
|
|
297
|
+
upsert(value);
|
|
298
|
+
});
|
|
299
|
+
pendingOutgoing$.set({});
|
|
300
|
+
};
|
|
301
|
+
const create = readonly ? void 0 : (input) => {
|
|
302
|
+
addCreatedAt(input);
|
|
303
|
+
return upsert(input);
|
|
304
|
+
};
|
|
305
|
+
const update = readonly ? void 0 : (input) => {
|
|
306
|
+
addUpdatedAt(input);
|
|
307
|
+
return upsert(input);
|
|
308
|
+
};
|
|
309
|
+
const deleteFn = readonly ? void 0 : (input) => {
|
|
310
|
+
const path = joinPaths(
|
|
311
|
+
refPath(fns.getCurrentUser()),
|
|
312
|
+
fieldId && asType !== "value" ? input[fieldId] : ""
|
|
313
|
+
);
|
|
314
|
+
return fns.remove(fns.ref(path));
|
|
315
|
+
};
|
|
316
|
+
let isAuthedIfRequired$;
|
|
317
|
+
if (requireAuth) {
|
|
318
|
+
if (fns.isInitialized()) {
|
|
319
|
+
isAuthedIfRequired$ = observable(false);
|
|
320
|
+
fns.onAuthStateChanged((user) => {
|
|
321
|
+
isAuthedIfRequired$.set(!!user);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
let transform = transformProp;
|
|
326
|
+
if (fieldTransforms) {
|
|
327
|
+
const inverted = invertFieldMap(fieldTransforms);
|
|
328
|
+
transform = {
|
|
329
|
+
load(value, method) {
|
|
330
|
+
const fieldTransformed = transformObjectFields(value, inverted);
|
|
331
|
+
return (transformProp == null ? void 0 : transformProp.load) ? transformProp.load(fieldTransformed, method) : fieldTransformed;
|
|
332
|
+
},
|
|
333
|
+
save(value) {
|
|
334
|
+
const transformed = (transformProp == null ? void 0 : transformProp.save) ? transformProp.save(value) : value;
|
|
335
|
+
if (isPromise(transformed)) {
|
|
336
|
+
return transformed.then((transformedValue) => {
|
|
337
|
+
return transformObjectFields(transformedValue, fieldTransforms);
|
|
338
|
+
});
|
|
339
|
+
} else {
|
|
340
|
+
return transformObjectFields(transformed, fieldTransforms);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
return syncedCrud({
|
|
346
|
+
...rest,
|
|
347
|
+
list,
|
|
348
|
+
subscribe,
|
|
349
|
+
create,
|
|
350
|
+
update,
|
|
351
|
+
delete: deleteFn,
|
|
352
|
+
waitFor: () => isEnabled$.get() && (isAuthedIfRequired$ ? isAuthedIfRequired$.get() : true) && (waitFor ? computeSelector(waitFor) : true),
|
|
353
|
+
waitForSet: (params) => isEnabled$.get() && (isAuthedIfRequired$ ? isAuthedIfRequired$.get() : true) && (waitForSet ? isFunction(waitForSet) ? waitForSet(params) : waitForSet : true),
|
|
354
|
+
generateId: fns.generateId,
|
|
355
|
+
transform,
|
|
356
|
+
as: asType
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export { configureSyncedFirebase, invertFieldMap, syncedFirebase, transformObjectFields };
|
package/sync-plugins/keel.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SyncedOptions } from '@legendapp/state/sync';
|
|
1
|
+
import { SyncedGetSetSubscribeBaseParams, SyncedOptions, SyncedSetParams } from '@legendapp/state/sync';
|
|
2
2
|
import { SyncedCrudPropsBase, CrudAsOption, SyncedCrudReturnType, SyncedCrudPropsSingle, CrudResult, SyncedCrudPropsMany } from '@legendapp/state/sync-plugins/crud';
|
|
3
3
|
|
|
4
4
|
interface KeelObjectBase {
|
|
@@ -39,7 +39,7 @@ interface KeelListParams<Where = {}> {
|
|
|
39
39
|
before?: string;
|
|
40
40
|
}
|
|
41
41
|
interface KeelRealtimePlugin {
|
|
42
|
-
subscribe: (realtimeKey: string,
|
|
42
|
+
subscribe: (realtimeKey: string, params: SyncedGetSetSubscribeBaseParams) => void;
|
|
43
43
|
setLatestChange: (realtimeKey: string, time: Date) => void;
|
|
44
44
|
}
|
|
45
45
|
interface SyncedKeelConfiguration extends Omit<SyncedCrudPropsBase<any>, keyof SyncedOptions | 'create' | 'update' | 'delete' | 'onSaved' | 'updatePartial' | 'fieldCreatedAt' | 'fieldUpdatedAt' | 'generateId'> {
|
|
@@ -55,20 +55,32 @@ interface SyncedKeelConfiguration extends Omit<SyncedCrudPropsBase<any>, keyof S
|
|
|
55
55
|
realtimePlugin?: KeelRealtimePlugin;
|
|
56
56
|
as?: Exclude<CrudAsOption, 'value'>;
|
|
57
57
|
enabled?: boolean;
|
|
58
|
-
onError?: (params:
|
|
58
|
+
onError?: (params: {
|
|
59
|
+
type: 'create' | 'update' | 'delete';
|
|
60
|
+
params: SyncedSetParams<any>;
|
|
61
|
+
input: any;
|
|
62
|
+
action: string;
|
|
63
|
+
error: APIResult<any>['error'];
|
|
64
|
+
}) => void;
|
|
59
65
|
}
|
|
60
|
-
interface SyncedKeelPropsManyBase<TRemote
|
|
66
|
+
interface SyncedKeelPropsManyBase<TRemote extends {
|
|
67
|
+
id: string;
|
|
68
|
+
}, TLocal, AOption extends CrudAsOption> extends Omit<SyncedCrudPropsMany<TRemote, TLocal, AOption>, 'list'> {
|
|
61
69
|
first?: number;
|
|
62
70
|
get?: never;
|
|
63
71
|
}
|
|
64
|
-
interface SyncedKeelPropsManyWhere<TRemote
|
|
72
|
+
interface SyncedKeelPropsManyWhere<TRemote extends {
|
|
73
|
+
id: string;
|
|
74
|
+
}, TLocal, AOption extends CrudAsOption, Where extends Record<string, any>> extends SyncedKeelPropsManyBase<TRemote, TLocal, AOption> {
|
|
65
75
|
list?: (params: KeelListParams<NoInfer<Where>>) => Promise<CrudResult<APIResult<{
|
|
66
76
|
results: TRemote[];
|
|
67
77
|
pageInfo: any;
|
|
68
78
|
}>>>;
|
|
69
79
|
where?: Where | (() => Where);
|
|
70
80
|
}
|
|
71
|
-
interface SyncedKeelPropsManyNoWhere<TRemote
|
|
81
|
+
interface SyncedKeelPropsManyNoWhere<TRemote extends {
|
|
82
|
+
id: string;
|
|
83
|
+
}, TLocal, AOption extends CrudAsOption> extends SyncedKeelPropsManyBase<TRemote, TLocal, AOption> {
|
|
72
84
|
list?: (params: KeelListParams<{}>) => Promise<CrudResult<APIResult<{
|
|
73
85
|
results: TRemote[];
|
|
74
86
|
pageInfo: any;
|
|
@@ -76,8 +88,12 @@ interface SyncedKeelPropsManyNoWhere<TRemote, TLocal, AOption extends CrudAsOpti
|
|
|
76
88
|
where?: never | {};
|
|
77
89
|
}
|
|
78
90
|
type HasAnyKeys<T> = keyof T extends never ? false : true;
|
|
79
|
-
type SyncedKeelPropsMany<TRemote
|
|
80
|
-
|
|
91
|
+
type SyncedKeelPropsMany<TRemote extends {
|
|
92
|
+
id: string;
|
|
93
|
+
}, TLocal, AOption extends CrudAsOption, Where extends Record<string, any>> = HasAnyKeys<Where> extends true ? SyncedKeelPropsManyWhere<TRemote, TLocal, AOption, Where> : SyncedKeelPropsManyNoWhere<TRemote, TLocal, AOption>;
|
|
94
|
+
interface SyncedKeelPropsSingle<TRemote extends {
|
|
95
|
+
id: string;
|
|
96
|
+
}, TLocal> extends Omit<SyncedCrudPropsSingle<TRemote, TLocal>, 'get'> {
|
|
81
97
|
get?: (params: KeelGetParams) => Promise<APIResult<TRemote>>;
|
|
82
98
|
first?: never;
|
|
83
99
|
where?: never;
|
package/sync-plugins/keel.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SyncedOptions } from '@legendapp/state/sync';
|
|
1
|
+
import { SyncedGetSetSubscribeBaseParams, SyncedOptions, SyncedSetParams } from '@legendapp/state/sync';
|
|
2
2
|
import { SyncedCrudPropsBase, CrudAsOption, SyncedCrudReturnType, SyncedCrudPropsSingle, CrudResult, SyncedCrudPropsMany } from '@legendapp/state/sync-plugins/crud';
|
|
3
3
|
|
|
4
4
|
interface KeelObjectBase {
|
|
@@ -39,7 +39,7 @@ interface KeelListParams<Where = {}> {
|
|
|
39
39
|
before?: string;
|
|
40
40
|
}
|
|
41
41
|
interface KeelRealtimePlugin {
|
|
42
|
-
subscribe: (realtimeKey: string,
|
|
42
|
+
subscribe: (realtimeKey: string, params: SyncedGetSetSubscribeBaseParams) => void;
|
|
43
43
|
setLatestChange: (realtimeKey: string, time: Date) => void;
|
|
44
44
|
}
|
|
45
45
|
interface SyncedKeelConfiguration extends Omit<SyncedCrudPropsBase<any>, keyof SyncedOptions | 'create' | 'update' | 'delete' | 'onSaved' | 'updatePartial' | 'fieldCreatedAt' | 'fieldUpdatedAt' | 'generateId'> {
|
|
@@ -55,20 +55,32 @@ interface SyncedKeelConfiguration extends Omit<SyncedCrudPropsBase<any>, keyof S
|
|
|
55
55
|
realtimePlugin?: KeelRealtimePlugin;
|
|
56
56
|
as?: Exclude<CrudAsOption, 'value'>;
|
|
57
57
|
enabled?: boolean;
|
|
58
|
-
onError?: (params:
|
|
58
|
+
onError?: (params: {
|
|
59
|
+
type: 'create' | 'update' | 'delete';
|
|
60
|
+
params: SyncedSetParams<any>;
|
|
61
|
+
input: any;
|
|
62
|
+
action: string;
|
|
63
|
+
error: APIResult<any>['error'];
|
|
64
|
+
}) => void;
|
|
59
65
|
}
|
|
60
|
-
interface SyncedKeelPropsManyBase<TRemote
|
|
66
|
+
interface SyncedKeelPropsManyBase<TRemote extends {
|
|
67
|
+
id: string;
|
|
68
|
+
}, TLocal, AOption extends CrudAsOption> extends Omit<SyncedCrudPropsMany<TRemote, TLocal, AOption>, 'list'> {
|
|
61
69
|
first?: number;
|
|
62
70
|
get?: never;
|
|
63
71
|
}
|
|
64
|
-
interface SyncedKeelPropsManyWhere<TRemote
|
|
72
|
+
interface SyncedKeelPropsManyWhere<TRemote extends {
|
|
73
|
+
id: string;
|
|
74
|
+
}, TLocal, AOption extends CrudAsOption, Where extends Record<string, any>> extends SyncedKeelPropsManyBase<TRemote, TLocal, AOption> {
|
|
65
75
|
list?: (params: KeelListParams<NoInfer<Where>>) => Promise<CrudResult<APIResult<{
|
|
66
76
|
results: TRemote[];
|
|
67
77
|
pageInfo: any;
|
|
68
78
|
}>>>;
|
|
69
79
|
where?: Where | (() => Where);
|
|
70
80
|
}
|
|
71
|
-
interface SyncedKeelPropsManyNoWhere<TRemote
|
|
81
|
+
interface SyncedKeelPropsManyNoWhere<TRemote extends {
|
|
82
|
+
id: string;
|
|
83
|
+
}, TLocal, AOption extends CrudAsOption> extends SyncedKeelPropsManyBase<TRemote, TLocal, AOption> {
|
|
72
84
|
list?: (params: KeelListParams<{}>) => Promise<CrudResult<APIResult<{
|
|
73
85
|
results: TRemote[];
|
|
74
86
|
pageInfo: any;
|
|
@@ -76,8 +88,12 @@ interface SyncedKeelPropsManyNoWhere<TRemote, TLocal, AOption extends CrudAsOpti
|
|
|
76
88
|
where?: never | {};
|
|
77
89
|
}
|
|
78
90
|
type HasAnyKeys<T> = keyof T extends never ? false : true;
|
|
79
|
-
type SyncedKeelPropsMany<TRemote
|
|
80
|
-
|
|
91
|
+
type SyncedKeelPropsMany<TRemote extends {
|
|
92
|
+
id: string;
|
|
93
|
+
}, TLocal, AOption extends CrudAsOption, Where extends Record<string, any>> = HasAnyKeys<Where> extends true ? SyncedKeelPropsManyWhere<TRemote, TLocal, AOption, Where> : SyncedKeelPropsManyNoWhere<TRemote, TLocal, AOption>;
|
|
94
|
+
interface SyncedKeelPropsSingle<TRemote extends {
|
|
95
|
+
id: string;
|
|
96
|
+
}, TLocal> extends Omit<SyncedCrudPropsSingle<TRemote, TLocal>, 'get'> {
|
|
81
97
|
get?: (params: KeelGetParams) => Promise<APIResult<TRemote>>;
|
|
82
98
|
first?: never;
|
|
83
99
|
where?: never;
|
package/sync-plugins/keel.js
CHANGED
|
@@ -65,9 +65,9 @@ function configureSyncedKeel(config) {
|
|
|
65
65
|
const oldFn = queries[key];
|
|
66
66
|
queries[key] = (i) => {
|
|
67
67
|
const realtimeKey = [key, ...Object.values(i.where || {})].filter((value) => value && typeof value !== "object").join("/");
|
|
68
|
-
const subscribe = (
|
|
68
|
+
const subscribe = (params) => {
|
|
69
69
|
if (realtimeKey) {
|
|
70
|
-
return realtimePlugin.subscribe(realtimeKey,
|
|
70
|
+
return realtimePlugin.subscribe(realtimeKey, params);
|
|
71
71
|
}
|
|
72
72
|
};
|
|
73
73
|
return oldFn(i).then((ret) => {
|
|
@@ -128,7 +128,9 @@ function syncedKeel(props) {
|
|
|
128
128
|
first,
|
|
129
129
|
where: whereParam,
|
|
130
130
|
waitFor,
|
|
131
|
+
waitForSet,
|
|
131
132
|
fieldDeleted,
|
|
133
|
+
mode,
|
|
132
134
|
...rest
|
|
133
135
|
} = props;
|
|
134
136
|
const { changesSince } = props;
|
|
@@ -137,8 +139,15 @@ function syncedKeel(props) {
|
|
|
137
139
|
const subscribeFnKey$ = state.observable("");
|
|
138
140
|
const fieldCreatedAt = "createdAt";
|
|
139
141
|
const fieldUpdatedAt = "updatedAt";
|
|
142
|
+
const setupSubscribe = (doSubscribe, subscribeKey, lastSync) => {
|
|
143
|
+
subscribeFn = doSubscribe;
|
|
144
|
+
subscribeFnKey$.set(subscribeKey);
|
|
145
|
+
if (realtimePlugin && lastSync) {
|
|
146
|
+
realtimePlugin.setLatestChange(subscribeKey, new Date(lastSync));
|
|
147
|
+
}
|
|
148
|
+
};
|
|
140
149
|
const list = listParam ? async (listParams) => {
|
|
141
|
-
const { lastSync
|
|
150
|
+
const { lastSync } = listParams;
|
|
142
151
|
const queryBySync = !!lastSync && changesSince === "last-sync";
|
|
143
152
|
const where = Object.assign(
|
|
144
153
|
queryBySync ? { updatedAt: { after: new Date(lastSync + 1) } } : {},
|
|
@@ -147,8 +156,7 @@ function syncedKeel(props) {
|
|
|
147
156
|
const params = { where, first };
|
|
148
157
|
const { results, subscribe: subscribe2, subscribeKey } = await getAllPages(listParam, params);
|
|
149
158
|
if (subscribe2) {
|
|
150
|
-
|
|
151
|
-
subscribeFnKey$.set(subscribeKey);
|
|
159
|
+
setupSubscribe(() => subscribe2(listParams), subscribeKey, lastSync);
|
|
152
160
|
}
|
|
153
161
|
return results;
|
|
154
162
|
} : void 0;
|
|
@@ -156,8 +164,7 @@ function syncedKeel(props) {
|
|
|
156
164
|
const { refresh } = getParams;
|
|
157
165
|
const { data, error, subscribe: subscribe2, subscribeKey } = await getParam({ refresh });
|
|
158
166
|
if (subscribe2) {
|
|
159
|
-
|
|
160
|
-
subscribeFnKey$.set(subscribeKey);
|
|
167
|
+
setupSubscribe(() => subscribe2(getParams), subscribeKey);
|
|
161
168
|
}
|
|
162
169
|
if (error) {
|
|
163
170
|
throw new Error(error.message);
|
|
@@ -176,19 +183,27 @@ function syncedKeel(props) {
|
|
|
176
183
|
}
|
|
177
184
|
}
|
|
178
185
|
};
|
|
179
|
-
const handleSetError = async (error, params,
|
|
186
|
+
const handleSetError = async (error, params, input, fn, from) => {
|
|
180
187
|
var _a, _b, _c;
|
|
181
188
|
const { retryNum, cancelRetry, update: update2 } = params;
|
|
182
|
-
if (
|
|
189
|
+
if (from === "create" && ((_a = error.message) == null ? void 0 : _a.includes("for the unique")) && ((_b = error.message) == null ? void 0 : _b.includes("must be unique"))) {
|
|
183
190
|
if (__DEV__) {
|
|
184
191
|
console.log("Creating duplicate data already saved, just ignore.");
|
|
185
192
|
}
|
|
193
|
+
cancelRetry();
|
|
186
194
|
update2({
|
|
187
195
|
value: {},
|
|
188
196
|
mode: "assign"
|
|
189
197
|
});
|
|
198
|
+
} else if (from === "delete") {
|
|
199
|
+
if (error.message === "record not found") {
|
|
200
|
+
if (__DEV__) {
|
|
201
|
+
console.log("Deleting non-existing data, just ignore.");
|
|
202
|
+
}
|
|
203
|
+
cancelRetry();
|
|
204
|
+
}
|
|
190
205
|
} else if (error.type === "bad_request") {
|
|
191
|
-
(_c = keelConfig.onError) == null ? void 0 : _c.call(keelConfig, error);
|
|
206
|
+
(_c = keelConfig.onError) == null ? void 0 : _c.call(keelConfig, { error, params, input, type: from, action: fn.name || fn.toString() });
|
|
192
207
|
if (retryNum > 4) {
|
|
193
208
|
cancelRetry();
|
|
194
209
|
}
|
|
@@ -201,7 +216,7 @@ function syncedKeel(props) {
|
|
|
201
216
|
const create = createParam ? async (input, params) => {
|
|
202
217
|
const { data, error } = await createParam(convertObjectToCreate(input));
|
|
203
218
|
if (error) {
|
|
204
|
-
handleSetError(error, params,
|
|
219
|
+
await handleSetError(error, params, input, createParam, "create");
|
|
205
220
|
}
|
|
206
221
|
return data;
|
|
207
222
|
} : void 0;
|
|
@@ -214,15 +229,15 @@ function syncedKeel(props) {
|
|
|
214
229
|
if (!state.isEmpty(values)) {
|
|
215
230
|
const { data, error } = await updateParam({ where: { id }, values });
|
|
216
231
|
if (error) {
|
|
217
|
-
handleSetError(error, params,
|
|
232
|
+
await handleSetError(error, params, input, updateParam, "update");
|
|
218
233
|
}
|
|
219
234
|
return data;
|
|
220
235
|
}
|
|
221
236
|
} : void 0;
|
|
222
|
-
const deleteFn = deleteParam ? async (
|
|
223
|
-
const { data, error } = await deleteParam({ id });
|
|
237
|
+
const deleteFn = deleteParam ? async (value, params) => {
|
|
238
|
+
const { data, error } = await deleteParam({ id: value.id });
|
|
224
239
|
if (error) {
|
|
225
|
-
handleSetError(error, params,
|
|
240
|
+
await handleSetError(error, params, value, deleteParam, "delete");
|
|
226
241
|
}
|
|
227
242
|
return data;
|
|
228
243
|
} : void 0;
|
|
@@ -238,13 +253,14 @@ function syncedKeel(props) {
|
|
|
238
253
|
return crud.syncedCrud({
|
|
239
254
|
...rest,
|
|
240
255
|
as: asType,
|
|
256
|
+
mode: mode || "merge",
|
|
241
257
|
list,
|
|
242
258
|
create,
|
|
243
259
|
update,
|
|
244
260
|
delete: deleteFn,
|
|
245
261
|
waitFor: () => isEnabled$.get() && (waitFor ? state.computeSelector(waitFor) : true),
|
|
262
|
+
waitForSet: (params) => isEnabled$.get() && (waitForSet ? state.isFunction(waitForSet) ? waitForSet(params) : waitForSet : true),
|
|
246
263
|
onSaved,
|
|
247
|
-
onSavedUpdate: "createdUpdatedAt",
|
|
248
264
|
fieldCreatedAt,
|
|
249
265
|
fieldUpdatedAt,
|
|
250
266
|
fieldDeleted: fieldDeleted || "deleted",
|