@legendapp/state 3.0.0-alpha.3 → 3.0.0-alpha.30
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/config/enable$GetSet.js +2 -1
- package/config/enable$GetSet.mjs +2 -1
- package/config/enableReactTracking.js +2 -1
- package/config/enableReactTracking.mjs +2 -1
- package/config/enableReactUse.js +2 -1
- package/config/enableReactUse.mjs +2 -1
- package/config/enable_PeekAssign.js +2 -1
- package/config/enable_PeekAssign.mjs +2 -1
- package/config.d.mts +13 -0
- package/config.d.ts +13 -0
- package/config.js +2052 -0
- package/config.mjs +2050 -0
- package/helpers/trackHistory.js +2 -2
- package/helpers/trackHistory.mjs +2 -2
- package/index.d.mts +21 -302
- package/index.d.ts +21 -302
- package/index.js +274 -318
- package/index.mjs +275 -317
- package/observableInterfaces-Dilj6F92.d.mts +282 -0
- package/observableInterfaces-Dilj6F92.d.ts +282 -0
- package/package.json +11 -1
- package/persist-plugins/async-storage.d.mts +6 -3
- package/persist-plugins/async-storage.d.ts +6 -3
- package/persist-plugins/async-storage.js +12 -4
- package/persist-plugins/async-storage.mjs +12 -5
- package/persist-plugins/indexeddb.d.mts +6 -4
- package/persist-plugins/indexeddb.d.ts +6 -4
- package/persist-plugins/indexeddb.js +16 -6
- package/persist-plugins/indexeddb.mjs +16 -7
- package/persist-plugins/mmkv.d.mts +5 -1
- package/persist-plugins/mmkv.d.ts +5 -1
- package/persist-plugins/mmkv.js +14 -5
- package/persist-plugins/mmkv.mjs +14 -6
- package/react.d.mts +18 -14
- package/react.d.ts +18 -14
- package/react.js +57 -32
- package/react.mjs +58 -33
- 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 +15 -23
- package/sync-plugins/crud.d.ts +15 -23
- package/sync-plugins/crud.js +213 -134
- package/sync-plugins/crud.mjs +214 -135
- package/sync-plugins/fetch.js +12 -8
- package/sync-plugins/fetch.mjs +13 -9
- package/sync-plugins/firebase.d.mts +26 -0
- package/sync-plugins/firebase.d.ts +26 -0
- package/sync-plugins/firebase.js +373 -0
- package/sync-plugins/firebase.mjs +368 -0
- package/sync-plugins/keel.d.mts +27 -10
- package/sync-plugins/keel.d.ts +27 -10
- package/sync-plugins/keel.js +40 -21
- package/sync-plugins/keel.mjs +40 -21
- package/sync-plugins/supabase.d.mts +12 -7
- package/sync-plugins/supabase.d.ts +12 -7
- package/sync-plugins/supabase.js +24 -13
- package/sync-plugins/supabase.mjs +25 -14
- 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 +68 -197
- package/sync.d.ts +68 -197
- package/sync.js +448 -283
- package/sync.mjs +454 -289
- 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,368 @@
|
|
|
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 (fieldId && !val[fieldId]) {
|
|
234
|
+
val[fieldId] = key;
|
|
235
|
+
}
|
|
236
|
+
if (saving$[key].get()) {
|
|
237
|
+
pendingIncoming$[key].set(val);
|
|
238
|
+
} else {
|
|
239
|
+
update2({
|
|
240
|
+
value: [val],
|
|
241
|
+
mode: "assign"
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const onChildDelete = (snap) => {
|
|
246
|
+
if (!didList)
|
|
247
|
+
return;
|
|
248
|
+
const key = snap.key;
|
|
249
|
+
const val = snap.val();
|
|
250
|
+
if (fieldId && !val[fieldId]) {
|
|
251
|
+
val[fieldId] = key;
|
|
252
|
+
}
|
|
253
|
+
val[symbolDelete] = true;
|
|
254
|
+
update2({
|
|
255
|
+
value: [val],
|
|
256
|
+
mode: "assign"
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
unsubscribes = [
|
|
260
|
+
fns.onChildAdded(ref, onChildChange, onError),
|
|
261
|
+
fns.onChildChanged(ref, onChildChange, onError),
|
|
262
|
+
fns.onChildRemoved(ref, onChildDelete, onError)
|
|
263
|
+
];
|
|
264
|
+
}
|
|
265
|
+
return () => {
|
|
266
|
+
unsubscribes.forEach((fn) => fn());
|
|
267
|
+
};
|
|
268
|
+
} : void 0;
|
|
269
|
+
const addUpdatedAt = (input) => {
|
|
270
|
+
if (fieldUpdatedAt) {
|
|
271
|
+
input[fieldUpdatedAt] = serverTimestamp();
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
const addCreatedAt = (input) => {
|
|
275
|
+
if (fieldCreatedAt && !input[fieldCreatedAt]) {
|
|
276
|
+
input[fieldCreatedAt] = serverTimestamp();
|
|
277
|
+
}
|
|
278
|
+
return addUpdatedAt(input);
|
|
279
|
+
};
|
|
280
|
+
const upsert = async (input) => {
|
|
281
|
+
const id = fieldId ? input[fieldId] : "";
|
|
282
|
+
if (saving$[id].get()) {
|
|
283
|
+
pendingOutgoing$[id].set(input);
|
|
284
|
+
} else {
|
|
285
|
+
saving$[id].set(true);
|
|
286
|
+
const path = joinPaths(refPath(fns.getCurrentUser()), fieldId ? id : "");
|
|
287
|
+
await fns.update(fns.ref(path), input);
|
|
288
|
+
saving$[id].set(false);
|
|
289
|
+
flushAfterSave();
|
|
290
|
+
}
|
|
291
|
+
return when(
|
|
292
|
+
() => !pendingOutgoing$[id].get(),
|
|
293
|
+
() => {
|
|
294
|
+
const value = pendingIncoming$[id].get();
|
|
295
|
+
if (value) {
|
|
296
|
+
pendingIncoming$[id].delete();
|
|
297
|
+
return value;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
};
|
|
302
|
+
const flushAfterSave = () => {
|
|
303
|
+
const outgoing = pendingOutgoing$.get();
|
|
304
|
+
Object.values(outgoing).forEach((value) => {
|
|
305
|
+
upsert(value);
|
|
306
|
+
});
|
|
307
|
+
pendingOutgoing$.set({});
|
|
308
|
+
};
|
|
309
|
+
const create = readonly ? void 0 : (input) => {
|
|
310
|
+
addCreatedAt(input);
|
|
311
|
+
return upsert(input);
|
|
312
|
+
};
|
|
313
|
+
const update = readonly ? void 0 : (input) => {
|
|
314
|
+
addUpdatedAt(input);
|
|
315
|
+
return upsert(input);
|
|
316
|
+
};
|
|
317
|
+
const deleteFn = readonly ? void 0 : (input) => {
|
|
318
|
+
const path = joinPaths(
|
|
319
|
+
refPath(fns.getCurrentUser()),
|
|
320
|
+
fieldId && asType !== "value" ? input[fieldId] : ""
|
|
321
|
+
);
|
|
322
|
+
return fns.remove(fns.ref(path));
|
|
323
|
+
};
|
|
324
|
+
let isAuthedIfRequired$;
|
|
325
|
+
if (requireAuth) {
|
|
326
|
+
if (fns.isInitialized()) {
|
|
327
|
+
isAuthedIfRequired$ = observable(false);
|
|
328
|
+
fns.onAuthStateChanged((user) => {
|
|
329
|
+
isAuthedIfRequired$.set(!!user);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
let transform = transformProp;
|
|
334
|
+
if (fieldTransforms) {
|
|
335
|
+
const inverted = invertFieldMap(fieldTransforms);
|
|
336
|
+
transform = {
|
|
337
|
+
load(value, method) {
|
|
338
|
+
const fieldTransformed = transformObjectFields(value, inverted);
|
|
339
|
+
return (transformProp == null ? void 0 : transformProp.load) ? transformProp.load(fieldTransformed, method) : fieldTransformed;
|
|
340
|
+
},
|
|
341
|
+
save(value) {
|
|
342
|
+
const transformed = (transformProp == null ? void 0 : transformProp.save) ? transformProp.save(value) : value;
|
|
343
|
+
if (isPromise(transformed)) {
|
|
344
|
+
return transformed.then((transformedValue) => {
|
|
345
|
+
return transformObjectFields(transformedValue, fieldTransforms);
|
|
346
|
+
});
|
|
347
|
+
} else {
|
|
348
|
+
return transformObjectFields(transformed, fieldTransforms);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
return syncedCrud({
|
|
354
|
+
...rest,
|
|
355
|
+
list,
|
|
356
|
+
subscribe,
|
|
357
|
+
create,
|
|
358
|
+
update,
|
|
359
|
+
delete: deleteFn,
|
|
360
|
+
waitFor: () => isEnabled$.get() && (isAuthedIfRequired$ ? isAuthedIfRequired$.get() : true) && (waitFor ? computeSelector(waitFor) : true),
|
|
361
|
+
waitForSet: (params) => isEnabled$.get() && (isAuthedIfRequired$ ? isAuthedIfRequired$.get() : true) && (waitForSet ? isFunction(waitForSet) ? waitForSet(params) : waitForSet : true),
|
|
362
|
+
generateId: fns.generateId,
|
|
363
|
+
transform,
|
|
364
|
+
as: asType
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
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,14 +39,14 @@ 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'> {
|
|
46
46
|
client: {
|
|
47
47
|
auth: {
|
|
48
|
-
refresh: () => Promise<boolean
|
|
49
|
-
isAuthenticated: () => Promise<boolean
|
|
48
|
+
refresh: () => Promise<APIResult<boolean>>;
|
|
49
|
+
isAuthenticated: () => Promise<APIResult<boolean>>;
|
|
50
50
|
};
|
|
51
51
|
api: {
|
|
52
52
|
queries: Record<string, (i: any) => Promise<any>>;
|
|
@@ -55,20 +55,33 @@ 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;
|
|
65
|
+
refreshAuth?: () => void | Promise<void>;
|
|
59
66
|
}
|
|
60
|
-
interface SyncedKeelPropsManyBase<TRemote
|
|
67
|
+
interface SyncedKeelPropsManyBase<TRemote extends {
|
|
68
|
+
id: string;
|
|
69
|
+
}, TLocal, AOption extends CrudAsOption> extends Omit<SyncedCrudPropsMany<TRemote, TLocal, AOption>, 'list'> {
|
|
61
70
|
first?: number;
|
|
62
71
|
get?: never;
|
|
63
72
|
}
|
|
64
|
-
interface SyncedKeelPropsManyWhere<TRemote
|
|
73
|
+
interface SyncedKeelPropsManyWhere<TRemote extends {
|
|
74
|
+
id: string;
|
|
75
|
+
}, TLocal, AOption extends CrudAsOption, Where extends Record<string, any>> extends SyncedKeelPropsManyBase<TRemote, TLocal, AOption> {
|
|
65
76
|
list?: (params: KeelListParams<NoInfer<Where>>) => Promise<CrudResult<APIResult<{
|
|
66
77
|
results: TRemote[];
|
|
67
78
|
pageInfo: any;
|
|
68
79
|
}>>>;
|
|
69
80
|
where?: Where | (() => Where);
|
|
70
81
|
}
|
|
71
|
-
interface SyncedKeelPropsManyNoWhere<TRemote
|
|
82
|
+
interface SyncedKeelPropsManyNoWhere<TRemote extends {
|
|
83
|
+
id: string;
|
|
84
|
+
}, TLocal, AOption extends CrudAsOption> extends SyncedKeelPropsManyBase<TRemote, TLocal, AOption> {
|
|
72
85
|
list?: (params: KeelListParams<{}>) => Promise<CrudResult<APIResult<{
|
|
73
86
|
results: TRemote[];
|
|
74
87
|
pageInfo: any;
|
|
@@ -76,8 +89,12 @@ interface SyncedKeelPropsManyNoWhere<TRemote, TLocal, AOption extends CrudAsOpti
|
|
|
76
89
|
where?: never | {};
|
|
77
90
|
}
|
|
78
91
|
type HasAnyKeys<T> = keyof T extends never ? false : true;
|
|
79
|
-
type SyncedKeelPropsMany<TRemote
|
|
80
|
-
|
|
92
|
+
type SyncedKeelPropsMany<TRemote extends {
|
|
93
|
+
id: string;
|
|
94
|
+
}, TLocal, AOption extends CrudAsOption, Where extends Record<string, any>> = HasAnyKeys<Where> extends true ? SyncedKeelPropsManyWhere<TRemote, TLocal, AOption, Where> : SyncedKeelPropsManyNoWhere<TRemote, TLocal, AOption>;
|
|
95
|
+
interface SyncedKeelPropsSingle<TRemote extends {
|
|
96
|
+
id: string;
|
|
97
|
+
}, TLocal> extends Omit<SyncedCrudPropsSingle<TRemote, TLocal>, 'get'> {
|
|
81
98
|
get?: (params: KeelGetParams) => Promise<APIResult<TRemote>>;
|
|
82
99
|
first?: never;
|
|
83
100
|
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,14 +39,14 @@ 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'> {
|
|
46
46
|
client: {
|
|
47
47
|
auth: {
|
|
48
|
-
refresh: () => Promise<boolean
|
|
49
|
-
isAuthenticated: () => Promise<boolean
|
|
48
|
+
refresh: () => Promise<APIResult<boolean>>;
|
|
49
|
+
isAuthenticated: () => Promise<APIResult<boolean>>;
|
|
50
50
|
};
|
|
51
51
|
api: {
|
|
52
52
|
queries: Record<string, (i: any) => Promise<any>>;
|
|
@@ -55,20 +55,33 @@ 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;
|
|
65
|
+
refreshAuth?: () => void | Promise<void>;
|
|
59
66
|
}
|
|
60
|
-
interface SyncedKeelPropsManyBase<TRemote
|
|
67
|
+
interface SyncedKeelPropsManyBase<TRemote extends {
|
|
68
|
+
id: string;
|
|
69
|
+
}, TLocal, AOption extends CrudAsOption> extends Omit<SyncedCrudPropsMany<TRemote, TLocal, AOption>, 'list'> {
|
|
61
70
|
first?: number;
|
|
62
71
|
get?: never;
|
|
63
72
|
}
|
|
64
|
-
interface SyncedKeelPropsManyWhere<TRemote
|
|
73
|
+
interface SyncedKeelPropsManyWhere<TRemote extends {
|
|
74
|
+
id: string;
|
|
75
|
+
}, TLocal, AOption extends CrudAsOption, Where extends Record<string, any>> extends SyncedKeelPropsManyBase<TRemote, TLocal, AOption> {
|
|
65
76
|
list?: (params: KeelListParams<NoInfer<Where>>) => Promise<CrudResult<APIResult<{
|
|
66
77
|
results: TRemote[];
|
|
67
78
|
pageInfo: any;
|
|
68
79
|
}>>>;
|
|
69
80
|
where?: Where | (() => Where);
|
|
70
81
|
}
|
|
71
|
-
interface SyncedKeelPropsManyNoWhere<TRemote
|
|
82
|
+
interface SyncedKeelPropsManyNoWhere<TRemote extends {
|
|
83
|
+
id: string;
|
|
84
|
+
}, TLocal, AOption extends CrudAsOption> extends SyncedKeelPropsManyBase<TRemote, TLocal, AOption> {
|
|
72
85
|
list?: (params: KeelListParams<{}>) => Promise<CrudResult<APIResult<{
|
|
73
86
|
results: TRemote[];
|
|
74
87
|
pageInfo: any;
|
|
@@ -76,8 +89,12 @@ interface SyncedKeelPropsManyNoWhere<TRemote, TLocal, AOption extends CrudAsOpti
|
|
|
76
89
|
where?: never | {};
|
|
77
90
|
}
|
|
78
91
|
type HasAnyKeys<T> = keyof T extends never ? false : true;
|
|
79
|
-
type SyncedKeelPropsMany<TRemote
|
|
80
|
-
|
|
92
|
+
type SyncedKeelPropsMany<TRemote extends {
|
|
93
|
+
id: string;
|
|
94
|
+
}, TLocal, AOption extends CrudAsOption, Where extends Record<string, any>> = HasAnyKeys<Where> extends true ? SyncedKeelPropsManyWhere<TRemote, TLocal, AOption, Where> : SyncedKeelPropsManyNoWhere<TRemote, TLocal, AOption>;
|
|
95
|
+
interface SyncedKeelPropsSingle<TRemote extends {
|
|
96
|
+
id: string;
|
|
97
|
+
}, TLocal> extends Omit<SyncedCrudPropsSingle<TRemote, TLocal>, 'get'> {
|
|
81
98
|
get?: (params: KeelGetParams) => Promise<APIResult<TRemote>>;
|
|
82
99
|
first?: never;
|
|
83
100
|
where?: never;
|
package/sync-plugins/keel.js
CHANGED
|
@@ -20,6 +20,9 @@ var modifiedClients = /* @__PURE__ */ new WeakSet();
|
|
|
20
20
|
var isEnabled$ = state.observable(true);
|
|
21
21
|
async function ensureAuthToken() {
|
|
22
22
|
await state.when(isEnabled$.get());
|
|
23
|
+
if (keelConfig.refreshAuth) {
|
|
24
|
+
await keelConfig.refreshAuth();
|
|
25
|
+
}
|
|
23
26
|
let isAuthed = await keelConfig.client.auth.isAuthenticated();
|
|
24
27
|
if (!isAuthed) {
|
|
25
28
|
isAuthed = await keelConfig.client.auth.refresh();
|
|
@@ -65,9 +68,9 @@ function configureSyncedKeel(config) {
|
|
|
65
68
|
const oldFn = queries[key];
|
|
66
69
|
queries[key] = (i) => {
|
|
67
70
|
const realtimeKey = [key, ...Object.values(i.where || {})].filter((value) => value && typeof value !== "object").join("/");
|
|
68
|
-
const subscribe = (
|
|
71
|
+
const subscribe = (params) => {
|
|
69
72
|
if (realtimeKey) {
|
|
70
|
-
return realtimePlugin.subscribe(realtimeKey,
|
|
73
|
+
return realtimePlugin.subscribe(realtimeKey, params);
|
|
71
74
|
}
|
|
72
75
|
};
|
|
73
76
|
return oldFn(i).then((ret) => {
|
|
@@ -128,7 +131,9 @@ function syncedKeel(props) {
|
|
|
128
131
|
first,
|
|
129
132
|
where: whereParam,
|
|
130
133
|
waitFor,
|
|
134
|
+
waitForSet,
|
|
131
135
|
fieldDeleted,
|
|
136
|
+
mode,
|
|
132
137
|
...rest
|
|
133
138
|
} = props;
|
|
134
139
|
const { changesSince } = props;
|
|
@@ -137,8 +142,15 @@ function syncedKeel(props) {
|
|
|
137
142
|
const subscribeFnKey$ = state.observable("");
|
|
138
143
|
const fieldCreatedAt = "createdAt";
|
|
139
144
|
const fieldUpdatedAt = "updatedAt";
|
|
145
|
+
const setupSubscribe = (doSubscribe, subscribeKey, lastSync) => {
|
|
146
|
+
subscribeFn = doSubscribe;
|
|
147
|
+
subscribeFnKey$.set(subscribeKey);
|
|
148
|
+
if (realtimePlugin && lastSync) {
|
|
149
|
+
realtimePlugin.setLatestChange(subscribeKey, new Date(lastSync));
|
|
150
|
+
}
|
|
151
|
+
};
|
|
140
152
|
const list = listParam ? async (listParams) => {
|
|
141
|
-
const { lastSync
|
|
153
|
+
const { lastSync } = listParams;
|
|
142
154
|
const queryBySync = !!lastSync && changesSince === "last-sync";
|
|
143
155
|
const where = Object.assign(
|
|
144
156
|
queryBySync ? { updatedAt: { after: new Date(lastSync + 1) } } : {},
|
|
@@ -147,8 +159,7 @@ function syncedKeel(props) {
|
|
|
147
159
|
const params = { where, first };
|
|
148
160
|
const { results, subscribe: subscribe2, subscribeKey } = await getAllPages(listParam, params);
|
|
149
161
|
if (subscribe2) {
|
|
150
|
-
|
|
151
|
-
subscribeFnKey$.set(subscribeKey);
|
|
162
|
+
setupSubscribe(() => subscribe2(listParams), subscribeKey, lastSync);
|
|
152
163
|
}
|
|
153
164
|
return results;
|
|
154
165
|
} : void 0;
|
|
@@ -156,8 +167,7 @@ function syncedKeel(props) {
|
|
|
156
167
|
const { refresh } = getParams;
|
|
157
168
|
const { data, error, subscribe: subscribe2, subscribeKey } = await getParam({ refresh });
|
|
158
169
|
if (subscribe2) {
|
|
159
|
-
|
|
160
|
-
subscribeFnKey$.set(subscribeKey);
|
|
170
|
+
setupSubscribe(() => subscribe2(getParams), subscribeKey);
|
|
161
171
|
}
|
|
162
172
|
if (error) {
|
|
163
173
|
throw new Error(error.message);
|
|
@@ -176,32 +186,40 @@ function syncedKeel(props) {
|
|
|
176
186
|
}
|
|
177
187
|
}
|
|
178
188
|
};
|
|
179
|
-
const handleSetError = async (error, params,
|
|
189
|
+
const handleSetError = async (error, params, input, fn, from) => {
|
|
180
190
|
var _a, _b, _c;
|
|
181
|
-
const { retryNum,
|
|
182
|
-
if (
|
|
191
|
+
const { retryNum, update: update2 } = params;
|
|
192
|
+
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
193
|
if (__DEV__) {
|
|
184
194
|
console.log("Creating duplicate data already saved, just ignore.");
|
|
185
195
|
}
|
|
196
|
+
params.cancelRetry = true;
|
|
186
197
|
update2({
|
|
187
198
|
value: {},
|
|
188
199
|
mode: "assign"
|
|
189
200
|
});
|
|
201
|
+
} else if (from === "delete") {
|
|
202
|
+
if (error.message === "record not found") {
|
|
203
|
+
if (__DEV__) {
|
|
204
|
+
console.log("Deleting non-existing data, just ignore.");
|
|
205
|
+
}
|
|
206
|
+
params.cancelRetry = true;
|
|
207
|
+
}
|
|
190
208
|
} else if (error.type === "bad_request") {
|
|
191
|
-
(_c = keelConfig.onError) == null ? void 0 : _c.call(keelConfig, error);
|
|
209
|
+
(_c = keelConfig.onError) == null ? void 0 : _c.call(keelConfig, { error, params, input, type: from, action: fn.name || fn.toString() });
|
|
192
210
|
if (retryNum > 4) {
|
|
193
|
-
cancelRetry
|
|
211
|
+
params.cancelRetry = true;
|
|
194
212
|
}
|
|
195
|
-
throw new Error(error.message);
|
|
213
|
+
throw new Error(error.message, { cause: { input } });
|
|
196
214
|
} else {
|
|
197
215
|
await handleApiError(error);
|
|
198
|
-
throw new Error(error.message);
|
|
216
|
+
throw new Error(error.message, { cause: { input } });
|
|
199
217
|
}
|
|
200
218
|
};
|
|
201
219
|
const create = createParam ? async (input, params) => {
|
|
202
220
|
const { data, error } = await createParam(convertObjectToCreate(input));
|
|
203
221
|
if (error) {
|
|
204
|
-
handleSetError(error, params,
|
|
222
|
+
await handleSetError(error, params, input, createParam, "create");
|
|
205
223
|
}
|
|
206
224
|
return data;
|
|
207
225
|
} : void 0;
|
|
@@ -214,15 +232,15 @@ function syncedKeel(props) {
|
|
|
214
232
|
if (!state.isEmpty(values)) {
|
|
215
233
|
const { data, error } = await updateParam({ where: { id }, values });
|
|
216
234
|
if (error) {
|
|
217
|
-
handleSetError(error, params,
|
|
235
|
+
await handleSetError(error, params, input, updateParam, "update");
|
|
218
236
|
}
|
|
219
237
|
return data;
|
|
220
238
|
}
|
|
221
239
|
} : void 0;
|
|
222
|
-
const deleteFn = deleteParam ? async (
|
|
223
|
-
const { data, error } = await deleteParam({ id });
|
|
240
|
+
const deleteFn = deleteParam ? async (value, params) => {
|
|
241
|
+
const { data, error } = await deleteParam({ id: value.id });
|
|
224
242
|
if (error) {
|
|
225
|
-
handleSetError(error, params,
|
|
243
|
+
await handleSetError(error, params, value, deleteParam, "delete");
|
|
226
244
|
}
|
|
227
245
|
return data;
|
|
228
246
|
} : void 0;
|
|
@@ -238,16 +256,17 @@ function syncedKeel(props) {
|
|
|
238
256
|
return crud.syncedCrud({
|
|
239
257
|
...rest,
|
|
240
258
|
as: asType,
|
|
259
|
+
mode: mode || "merge",
|
|
241
260
|
list,
|
|
242
261
|
create,
|
|
243
262
|
update,
|
|
244
263
|
delete: deleteFn,
|
|
245
264
|
waitFor: () => isEnabled$.get() && (waitFor ? state.computeSelector(waitFor) : true),
|
|
265
|
+
waitForSet: (params) => isEnabled$.get() && (waitForSet ? state.isFunction(waitForSet) ? waitForSet(params) : waitForSet : true),
|
|
246
266
|
onSaved,
|
|
247
|
-
onSavedUpdate: "createdUpdatedAt",
|
|
248
267
|
fieldCreatedAt,
|
|
249
268
|
fieldUpdatedAt,
|
|
250
|
-
fieldDeleted
|
|
269
|
+
fieldDeleted,
|
|
251
270
|
changesSince,
|
|
252
271
|
updatePartial: true,
|
|
253
272
|
subscribe,
|