@legendapp/state 3.0.0-beta.13 → 3.0.0-beta.14
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/.DS_Store +0 -0
- package/index.d.mts +5 -1
- package/index.d.ts +5 -1
- package/index.js +6 -4
- package/index.mjs +6 -4
- package/package.json +1 -1
- package/react.d.mts +3 -3
- package/react.d.ts +3 -3
- package/sync-plugins/crud.d.mts +8 -3
- package/sync-plugins/crud.d.ts +8 -3
- package/sync-plugins/crud.js +144 -80
- package/sync-plugins/crud.mjs +144 -80
- package/sync-plugins/firebase.d.mts +7 -3
- package/sync-plugins/firebase.d.ts +7 -3
- package/sync-plugins/firebase.js +4 -2
- package/sync-plugins/firebase.mjs +4 -2
- package/sync-plugins/keel.d.mts +5 -9
- package/sync-plugins/keel.d.ts +5 -9
- package/sync-plugins/keel.js +48 -30
- package/sync-plugins/keel.mjs +48 -30
- package/sync-plugins/supabase.d.mts +7 -3
- package/sync-plugins/supabase.d.ts +7 -3
- package/sync-plugins/supabase.js +55 -18
- package/sync-plugins/supabase.mjs +56 -19
- package/sync.d.mts +16 -8
- package/sync.d.ts +16 -8
- package/sync.js +95 -67
- package/sync.mjs +94 -67
package/sync-plugins/crud.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { synced, deepEqual, internal as internal$1, diffObjects } from '@legenda
|
|
|
3
3
|
|
|
4
4
|
// src/sync-plugins/crud.ts
|
|
5
5
|
var { clone } = internal;
|
|
6
|
-
var { waitForSet } = internal$1;
|
|
6
|
+
var { waitForSet, runWithRetry } = internal$1;
|
|
7
7
|
function transformOut(data, transform) {
|
|
8
8
|
return transform ? transform(clone(data)) : data;
|
|
9
9
|
}
|
|
@@ -23,6 +23,39 @@ function computeLastSync(data, fieldUpdatedAt, fieldCreatedAt) {
|
|
|
23
23
|
}
|
|
24
24
|
return newLastSync;
|
|
25
25
|
}
|
|
26
|
+
var queuedRetries = {
|
|
27
|
+
create: /* @__PURE__ */ new Map(),
|
|
28
|
+
update: /* @__PURE__ */ new Map(),
|
|
29
|
+
delete: /* @__PURE__ */ new Map()
|
|
30
|
+
};
|
|
31
|
+
function retrySet(params, retry, action, itemKey, itemValue, actionFn, saveResult) {
|
|
32
|
+
if (action === "delete") {
|
|
33
|
+
if (queuedRetries.create.has(itemKey)) {
|
|
34
|
+
queuedRetries.create.delete(itemKey);
|
|
35
|
+
}
|
|
36
|
+
if (queuedRetries.update.has(itemKey)) {
|
|
37
|
+
queuedRetries.update.delete(itemKey);
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
if (queuedRetries.delete.has(itemKey)) {
|
|
41
|
+
queuedRetries.delete.delete(itemKey);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const queuedRetry = queuedRetries[action].get(itemKey);
|
|
45
|
+
if (queuedRetry) {
|
|
46
|
+
itemValue = Object.assign(queuedRetry, itemValue);
|
|
47
|
+
}
|
|
48
|
+
queuedRetries[action].set(itemKey, itemValue);
|
|
49
|
+
return runWithRetry(
|
|
50
|
+
params,
|
|
51
|
+
retry,
|
|
52
|
+
"create_" + itemKey,
|
|
53
|
+
() => actionFn(itemValue, params).then((result) => {
|
|
54
|
+
queuedRetries[action].delete(itemKey);
|
|
55
|
+
return saveResult(itemKey, itemValue, result, true);
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
}
|
|
26
59
|
function syncedCrud(props) {
|
|
27
60
|
const {
|
|
28
61
|
get: getFn,
|
|
@@ -43,6 +76,7 @@ function syncedCrud(props) {
|
|
|
43
76
|
changesSince,
|
|
44
77
|
generateId,
|
|
45
78
|
waitForSet: waitForSetParam,
|
|
79
|
+
retry,
|
|
46
80
|
...rest
|
|
47
81
|
} = props;
|
|
48
82
|
const fieldId = fieldIdProp || "id";
|
|
@@ -75,63 +109,68 @@ function syncedCrud(props) {
|
|
|
75
109
|
return out;
|
|
76
110
|
};
|
|
77
111
|
const transformRows = (data) => {
|
|
78
|
-
return Promise.all(
|
|
112
|
+
return data.length ? Promise.all(
|
|
79
113
|
data.map(
|
|
80
114
|
(value) => (
|
|
81
115
|
// Skip transforming any children with symbolDelete or fieldDeleted because they'll get deleted by resultsToOutType
|
|
82
116
|
value[symbolDelete] || fieldDeleted && value[fieldDeleted] || fieldDeletedList && value[fieldDeletedList] ? value : transform.load(value, "get")
|
|
83
117
|
)
|
|
84
118
|
)
|
|
85
|
-
);
|
|
119
|
+
) : [];
|
|
86
120
|
};
|
|
87
121
|
const get = getFn || listFn ? (getParams) => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const listPromise = listFn(getParams);
|
|
95
|
-
const toOut = (transformed) => {
|
|
96
|
-
var _a;
|
|
97
|
-
if (asType === "value") {
|
|
98
|
-
return transformed.length > 0 ? transformed[0] : (_a = (isLastSyncMode && lastSync || fieldDeleted) && value) != null ? _a : null;
|
|
99
|
-
} else {
|
|
100
|
-
return resultsToOutType(transformed);
|
|
122
|
+
return runWithRetry(getParams, retry, getFn || listFn, () => {
|
|
123
|
+
const { updateLastSync, lastSync, value } = getParams;
|
|
124
|
+
if (listFn) {
|
|
125
|
+
const isLastSyncMode = changesSince === "last-sync";
|
|
126
|
+
if (isLastSyncMode && lastSync) {
|
|
127
|
+
getParams.mode = modeParam || (asType === "array" ? "append" : asType === "value" ? "set" : "assign");
|
|
101
128
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
129
|
+
const listPromise = listFn(getParams);
|
|
130
|
+
const toOut = (transformed) => {
|
|
131
|
+
if (asType === "value") {
|
|
132
|
+
if (transformed.length > 0) {
|
|
133
|
+
return transformed[0];
|
|
134
|
+
} else {
|
|
135
|
+
return value ? void 0 : null;
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
return resultsToOutType(transformed);
|
|
109
139
|
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
} else if (getFn) {
|
|
119
|
-
const dataPromise = getFn(getParams);
|
|
120
|
-
const processData = (data) => {
|
|
121
|
-
let transformed = data;
|
|
122
|
-
if (data) {
|
|
123
|
-
const newLastSync = data[fieldUpdatedAt] || data[fieldCreatedAt];
|
|
124
|
-
if (newLastSync && newLastSync !== lastSync) {
|
|
125
|
-
updateLastSync(newLastSync);
|
|
140
|
+
};
|
|
141
|
+
const processResults = (data) => {
|
|
142
|
+
data || (data = []);
|
|
143
|
+
if (fieldUpdatedAt) {
|
|
144
|
+
const newLastSync = computeLastSync(data, fieldUpdatedAt, fieldCreatedAt);
|
|
145
|
+
if (newLastSync && newLastSync !== lastSync) {
|
|
146
|
+
updateLastSync(newLastSync);
|
|
147
|
+
}
|
|
126
148
|
}
|
|
149
|
+
let transformed = data;
|
|
127
150
|
if (transform == null ? void 0 : transform.load) {
|
|
128
|
-
transformed =
|
|
151
|
+
transformed = transformRows(data);
|
|
129
152
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
153
|
+
return isPromise(transformed) ? transformed.then(toOut) : toOut(transformed);
|
|
154
|
+
};
|
|
155
|
+
return isPromise(listPromise) ? listPromise.then(processResults) : processResults(listPromise);
|
|
156
|
+
} else if (getFn) {
|
|
157
|
+
const dataPromise = getFn(getParams);
|
|
158
|
+
const processData = (data) => {
|
|
159
|
+
let transformed = data;
|
|
160
|
+
if (data) {
|
|
161
|
+
const newLastSync = data[fieldUpdatedAt] || data[fieldCreatedAt];
|
|
162
|
+
if (newLastSync && newLastSync !== lastSync) {
|
|
163
|
+
updateLastSync(newLastSync);
|
|
164
|
+
}
|
|
165
|
+
if (transform == null ? void 0 : transform.load) {
|
|
166
|
+
transformed = transform.load(data, "get");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return transformed;
|
|
170
|
+
};
|
|
171
|
+
return isPromise(dataPromise) ? dataPromise.then(processData) : processData(dataPromise);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
135
174
|
} : void 0;
|
|
136
175
|
const set = createFn || updateFn || deleteFn ? async (params) => {
|
|
137
176
|
const { value, changes, update, retryAsCreate, node } = params;
|
|
@@ -283,59 +322,84 @@ function syncedCrud(props) {
|
|
|
283
322
|
delete saved[key];
|
|
284
323
|
}
|
|
285
324
|
});
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
325
|
+
let value2;
|
|
326
|
+
if (asType === "array") {
|
|
327
|
+
const index = currentPeeked.findIndex(
|
|
328
|
+
(cur) => cur[fieldId] === itemKey
|
|
329
|
+
);
|
|
330
|
+
if (index < 0) {
|
|
331
|
+
console.warn("[legend-state] Item saved that does not exist in array", saved);
|
|
332
|
+
} else {
|
|
333
|
+
value2 = { [index < 0 ? 0 : index]: saved };
|
|
334
|
+
}
|
|
335
|
+
} else {
|
|
336
|
+
value2 = itemKey !== "undefined" && asType !== "value" ? { [itemKey]: saved } : saved;
|
|
337
|
+
}
|
|
338
|
+
if (value2 !== void 0) {
|
|
339
|
+
update({
|
|
340
|
+
value: value2,
|
|
341
|
+
mode: "merge"
|
|
342
|
+
});
|
|
343
|
+
}
|
|
294
344
|
}
|
|
295
345
|
}
|
|
296
346
|
};
|
|
297
347
|
return Promise.all([
|
|
348
|
+
// Handle creates
|
|
298
349
|
...Array.from(creates).map(async ([itemKey, itemValue]) => {
|
|
299
350
|
if (waitForSetParam) {
|
|
300
351
|
await waitForSet(waitForSetParam, changes, itemValue, { type: "create" });
|
|
301
352
|
}
|
|
302
353
|
const createObj = await transformOut(itemValue, transform == null ? void 0 : transform.save);
|
|
303
|
-
return
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
354
|
+
return retrySet(params, retry, "create", itemKey, createObj, createFn, saveResult).then(
|
|
355
|
+
() => {
|
|
356
|
+
pendingCreates.delete(itemKey);
|
|
357
|
+
}
|
|
358
|
+
);
|
|
308
359
|
}),
|
|
360
|
+
// Handle updates
|
|
309
361
|
...Array.from(updates).map(async ([itemKey, itemValue]) => {
|
|
310
362
|
if (waitForSetParam) {
|
|
311
363
|
await waitForSet(waitForSetParam, changes, itemValue, { type: "update" });
|
|
312
364
|
}
|
|
313
|
-
const
|
|
314
|
-
const changed = await transformOut(toSave, transform == null ? void 0 : transform.save);
|
|
365
|
+
const changed = await transformOut(itemValue, transform == null ? void 0 : transform.save);
|
|
315
366
|
if (Object.keys(changed).length > 0) {
|
|
316
|
-
return
|
|
317
|
-
(result) => result && saveResult(itemKey, changed, result, false)
|
|
318
|
-
);
|
|
367
|
+
return retrySet(params, retry, "update", itemKey, changed, updateFn, saveResult);
|
|
319
368
|
}
|
|
320
369
|
}),
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
370
|
+
// Handle deletes
|
|
371
|
+
...Array.from(deletes).filter((val) => val !== symbolDelete).map(async (valuePrevious) => {
|
|
372
|
+
if (waitForSetParam) {
|
|
373
|
+
await waitForSet(waitForSetParam, changes, valuePrevious, { type: "delete" });
|
|
374
|
+
}
|
|
375
|
+
const valueId = valuePrevious[fieldId];
|
|
376
|
+
if (!valueId) {
|
|
377
|
+
console.error("[legend-state]: deleting item without an id");
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (deleteFn) {
|
|
381
|
+
return retrySet(
|
|
382
|
+
params,
|
|
383
|
+
retry,
|
|
384
|
+
"delete",
|
|
385
|
+
valueId,
|
|
386
|
+
valuePrevious,
|
|
387
|
+
deleteFn,
|
|
388
|
+
saveResult
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
if (fieldDeleted && updateFn) {
|
|
392
|
+
return retrySet(
|
|
393
|
+
params,
|
|
394
|
+
retry,
|
|
395
|
+
"delete",
|
|
396
|
+
valueId,
|
|
397
|
+
{ [fieldId]: valueId, [fieldDeleted]: true },
|
|
398
|
+
updateFn,
|
|
399
|
+
saveResult
|
|
400
|
+
);
|
|
338
401
|
}
|
|
402
|
+
console.warn("[legend-state] missing delete function");
|
|
339
403
|
})
|
|
340
404
|
]);
|
|
341
405
|
} : void 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FieldTransforms } from '@legendapp/state/sync';
|
|
1
|
+
import { FieldTransforms, SyncedErrorParams } from '@legendapp/state/sync';
|
|
2
2
|
export { FieldTransforms } from '@legendapp/state/sync';
|
|
3
3
|
import { CrudAsOption, SyncedCrudPropsMany, SyncedCrudPropsBase, SyncedCrudReturnType } from '@legendapp/state/sync-plugins/crud';
|
|
4
4
|
import { DatabaseReference, Query } from 'firebase/database';
|
|
@@ -6,11 +6,12 @@ import { DatabaseReference, Query } from 'firebase/database';
|
|
|
6
6
|
declare function transformObjectFields(dataIn: Record<string, any>, map: Record<string, any>): any;
|
|
7
7
|
declare function invertFieldMap(obj: Record<string, any>): any;
|
|
8
8
|
|
|
9
|
-
interface SyncedFirebaseProps<TRemote extends object, TLocal, TAs extends CrudAsOption = 'value'> extends Omit<SyncedCrudPropsMany<TRemote, TLocal, TAs>, 'list' | 'retry'>, SyncedCrudPropsBase<TRemote, TLocal> {
|
|
9
|
+
interface SyncedFirebaseProps<TRemote extends object, TLocal, TAs extends CrudAsOption = 'value'> extends Omit<SyncedCrudPropsMany<TRemote, TLocal, TAs>, 'list' | 'retry'>, Omit<SyncedCrudPropsBase<TRemote, TLocal>, 'onError'> {
|
|
10
10
|
refPath: (uid: string | undefined) => string;
|
|
11
11
|
query?: (ref: DatabaseReference) => DatabaseReference | Query;
|
|
12
12
|
fieldId?: string;
|
|
13
13
|
fieldTransforms?: FieldTransforms<TRemote>;
|
|
14
|
+
onError?: (error: Error, params: FirebaseErrorParams) => void;
|
|
14
15
|
realtime?: boolean;
|
|
15
16
|
requireAuth?: boolean;
|
|
16
17
|
readonly?: boolean;
|
|
@@ -22,6 +23,9 @@ interface SyncedFirebaseConfiguration {
|
|
|
22
23
|
enabled?: boolean;
|
|
23
24
|
}
|
|
24
25
|
declare function configureSyncedFirebase(config: SyncedFirebaseConfiguration): void;
|
|
26
|
+
interface FirebaseErrorParams extends Omit<SyncedErrorParams, 'source'> {
|
|
27
|
+
source: 'list' | 'get' | 'create' | 'update' | 'delete';
|
|
28
|
+
}
|
|
25
29
|
declare function syncedFirebase<TRemote extends object, TLocal = TRemote, TAs extends CrudAsOption = 'object'>(props: SyncedFirebaseProps<TRemote, TLocal, TAs>): SyncedCrudReturnType<TLocal, TAs>;
|
|
26
30
|
|
|
27
|
-
export { type SyncedFirebaseProps, configureSyncedFirebase, invertFieldMap, syncedFirebase, transformObjectFields };
|
|
31
|
+
export { type FirebaseErrorParams, type SyncedFirebaseProps, configureSyncedFirebase, invertFieldMap, syncedFirebase, transformObjectFields };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FieldTransforms } from '@legendapp/state/sync';
|
|
1
|
+
import { FieldTransforms, SyncedErrorParams } from '@legendapp/state/sync';
|
|
2
2
|
export { FieldTransforms } from '@legendapp/state/sync';
|
|
3
3
|
import { CrudAsOption, SyncedCrudPropsMany, SyncedCrudPropsBase, SyncedCrudReturnType } from '@legendapp/state/sync-plugins/crud';
|
|
4
4
|
import { DatabaseReference, Query } from 'firebase/database';
|
|
@@ -6,11 +6,12 @@ import { DatabaseReference, Query } from 'firebase/database';
|
|
|
6
6
|
declare function transformObjectFields(dataIn: Record<string, any>, map: Record<string, any>): any;
|
|
7
7
|
declare function invertFieldMap(obj: Record<string, any>): any;
|
|
8
8
|
|
|
9
|
-
interface SyncedFirebaseProps<TRemote extends object, TLocal, TAs extends CrudAsOption = 'value'> extends Omit<SyncedCrudPropsMany<TRemote, TLocal, TAs>, 'list' | 'retry'>, SyncedCrudPropsBase<TRemote, TLocal> {
|
|
9
|
+
interface SyncedFirebaseProps<TRemote extends object, TLocal, TAs extends CrudAsOption = 'value'> extends Omit<SyncedCrudPropsMany<TRemote, TLocal, TAs>, 'list' | 'retry'>, Omit<SyncedCrudPropsBase<TRemote, TLocal>, 'onError'> {
|
|
10
10
|
refPath: (uid: string | undefined) => string;
|
|
11
11
|
query?: (ref: DatabaseReference) => DatabaseReference | Query;
|
|
12
12
|
fieldId?: string;
|
|
13
13
|
fieldTransforms?: FieldTransforms<TRemote>;
|
|
14
|
+
onError?: (error: Error, params: FirebaseErrorParams) => void;
|
|
14
15
|
realtime?: boolean;
|
|
15
16
|
requireAuth?: boolean;
|
|
16
17
|
readonly?: boolean;
|
|
@@ -22,6 +23,9 @@ interface SyncedFirebaseConfiguration {
|
|
|
22
23
|
enabled?: boolean;
|
|
23
24
|
}
|
|
24
25
|
declare function configureSyncedFirebase(config: SyncedFirebaseConfiguration): void;
|
|
26
|
+
interface FirebaseErrorParams extends Omit<SyncedErrorParams, 'source'> {
|
|
27
|
+
source: 'list' | 'get' | 'create' | 'update' | 'delete';
|
|
28
|
+
}
|
|
25
29
|
declare function syncedFirebase<TRemote extends object, TLocal = TRemote, TAs extends CrudAsOption = 'object'>(props: SyncedFirebaseProps<TRemote, TLocal, TAs>): SyncedCrudReturnType<TLocal, TAs>;
|
|
26
30
|
|
|
27
|
-
export { type SyncedFirebaseProps, configureSyncedFirebase, invertFieldMap, syncedFirebase, transformObjectFields };
|
|
31
|
+
export { type FirebaseErrorParams, type SyncedFirebaseProps, configureSyncedFirebase, invertFieldMap, syncedFirebase, transformObjectFields };
|
package/sync-plugins/firebase.js
CHANGED
|
@@ -185,7 +185,8 @@ function syncedFirebase(props) {
|
|
|
185
185
|
}
|
|
186
186
|
return ref;
|
|
187
187
|
};
|
|
188
|
-
const list = async (
|
|
188
|
+
const list = async (getParams) => {
|
|
189
|
+
const { lastSync, onError } = getParams;
|
|
189
190
|
const ref = computeRef(lastSync);
|
|
190
191
|
return new Promise((resolve) => {
|
|
191
192
|
fns.once(
|
|
@@ -204,7 +205,7 @@ function syncedFirebase(props) {
|
|
|
204
205
|
didList = true;
|
|
205
206
|
resolve(values);
|
|
206
207
|
},
|
|
207
|
-
onError
|
|
208
|
+
(error) => onError(error, { source: "list", type: "get", retry: getParams })
|
|
208
209
|
);
|
|
209
210
|
});
|
|
210
211
|
};
|
|
@@ -354,6 +355,7 @@ function syncedFirebase(props) {
|
|
|
354
355
|
}
|
|
355
356
|
return crud.syncedCrud({
|
|
356
357
|
...rest,
|
|
358
|
+
// Workaround for type errors
|
|
357
359
|
list,
|
|
358
360
|
subscribe,
|
|
359
361
|
create,
|
|
@@ -183,7 +183,8 @@ function syncedFirebase(props) {
|
|
|
183
183
|
}
|
|
184
184
|
return ref;
|
|
185
185
|
};
|
|
186
|
-
const list = async (
|
|
186
|
+
const list = async (getParams) => {
|
|
187
|
+
const { lastSync, onError } = getParams;
|
|
187
188
|
const ref = computeRef(lastSync);
|
|
188
189
|
return new Promise((resolve) => {
|
|
189
190
|
fns.once(
|
|
@@ -202,7 +203,7 @@ function syncedFirebase(props) {
|
|
|
202
203
|
didList = true;
|
|
203
204
|
resolve(values);
|
|
204
205
|
},
|
|
205
|
-
onError
|
|
206
|
+
(error) => onError(error, { source: "list", type: "get", retry: getParams })
|
|
206
207
|
);
|
|
207
208
|
});
|
|
208
209
|
};
|
|
@@ -352,6 +353,7 @@ function syncedFirebase(props) {
|
|
|
352
353
|
}
|
|
353
354
|
return syncedCrud({
|
|
354
355
|
...rest,
|
|
356
|
+
// Workaround for type errors
|
|
355
357
|
list,
|
|
356
358
|
subscribe,
|
|
357
359
|
create,
|
package/sync-plugins/keel.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SyncedGetSetSubscribeBaseParams
|
|
2
|
-
import { SyncedCrudPropsBase, SyncedCrudReturnType, CrudAsOption, SyncedCrudPropsSingle, CrudResult, SyncedCrudPropsMany } from '@legendapp/state/sync-plugins/crud';
|
|
1
|
+
import { SyncedGetSetSubscribeBaseParams } from '@legendapp/state/sync';
|
|
2
|
+
import { CrudErrorParams, SyncedCrudPropsBase, SyncedCrudReturnType, CrudAsOption, SyncedCrudPropsSingle, CrudResult, SyncedCrudPropsMany } from '@legendapp/state/sync-plugins/crud';
|
|
3
3
|
|
|
4
4
|
interface KeelObjectBase {
|
|
5
5
|
id: string;
|
|
@@ -88,12 +88,8 @@ interface SyncedKeelPropsSingle<TRemote extends {
|
|
|
88
88
|
list?: never;
|
|
89
89
|
as?: never;
|
|
90
90
|
}
|
|
91
|
-
interface
|
|
92
|
-
type: 'create' | 'update' | 'delete';
|
|
93
|
-
params: SyncedSetParams<any>;
|
|
94
|
-
input: any;
|
|
91
|
+
interface KeelErrorParams extends CrudErrorParams {
|
|
95
92
|
action: string;
|
|
96
|
-
error: APIResult<any>['error'];
|
|
97
93
|
}
|
|
98
94
|
interface SyncedKeelPropsBase<TRemote extends {
|
|
99
95
|
id: string;
|
|
@@ -113,7 +109,7 @@ interface SyncedKeelPropsBase<TRemote extends {
|
|
|
113
109
|
};
|
|
114
110
|
refreshAuth?: () => void | Promise<void>;
|
|
115
111
|
requireAuth?: boolean;
|
|
116
|
-
onError?: (error: Error,
|
|
112
|
+
onError?: (error: Error, params: KeelErrorParams) => void;
|
|
117
113
|
}
|
|
118
114
|
declare function syncedKeel<TRemote extends {
|
|
119
115
|
id: string;
|
|
@@ -122,4 +118,4 @@ declare function syncedKeel<TRemote extends {
|
|
|
122
118
|
id: string;
|
|
123
119
|
}, TLocal = TRemote, TOption extends CrudAsOption = 'object', Where extends Record<string, any> = {}>(props: SyncedKeelPropsBase<TRemote, TLocal> & SyncedKeelPropsMany<TRemote, TLocal, TOption, Where>): SyncedCrudReturnType<TLocal, Exclude<TOption, 'value'>>;
|
|
124
120
|
|
|
125
|
-
export { type KeelClient, type KeelGetParams, type KeelKey, KeelKeys, type KeelListParams, type KeelObjectBase, type KeelRealtimePlugin, type OmitKeelBuiltins, type SyncedKeelPropsBase, generateKeelId, syncedKeel };
|
|
121
|
+
export { type KeelClient, type KeelErrorParams, type KeelGetParams, type KeelKey, KeelKeys, type KeelListParams, type KeelObjectBase, type KeelRealtimePlugin, type OmitKeelBuiltins, type SyncedKeelPropsBase, generateKeelId, syncedKeel };
|
package/sync-plugins/keel.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SyncedGetSetSubscribeBaseParams
|
|
2
|
-
import { SyncedCrudPropsBase, SyncedCrudReturnType, CrudAsOption, SyncedCrudPropsSingle, CrudResult, SyncedCrudPropsMany } from '@legendapp/state/sync-plugins/crud';
|
|
1
|
+
import { SyncedGetSetSubscribeBaseParams } from '@legendapp/state/sync';
|
|
2
|
+
import { CrudErrorParams, SyncedCrudPropsBase, SyncedCrudReturnType, CrudAsOption, SyncedCrudPropsSingle, CrudResult, SyncedCrudPropsMany } from '@legendapp/state/sync-plugins/crud';
|
|
3
3
|
|
|
4
4
|
interface KeelObjectBase {
|
|
5
5
|
id: string;
|
|
@@ -88,12 +88,8 @@ interface SyncedKeelPropsSingle<TRemote extends {
|
|
|
88
88
|
list?: never;
|
|
89
89
|
as?: never;
|
|
90
90
|
}
|
|
91
|
-
interface
|
|
92
|
-
type: 'create' | 'update' | 'delete';
|
|
93
|
-
params: SyncedSetParams<any>;
|
|
94
|
-
input: any;
|
|
91
|
+
interface KeelErrorParams extends CrudErrorParams {
|
|
95
92
|
action: string;
|
|
96
|
-
error: APIResult<any>['error'];
|
|
97
93
|
}
|
|
98
94
|
interface SyncedKeelPropsBase<TRemote extends {
|
|
99
95
|
id: string;
|
|
@@ -113,7 +109,7 @@ interface SyncedKeelPropsBase<TRemote extends {
|
|
|
113
109
|
};
|
|
114
110
|
refreshAuth?: () => void | Promise<void>;
|
|
115
111
|
requireAuth?: boolean;
|
|
116
|
-
onError?: (error: Error,
|
|
112
|
+
onError?: (error: Error, params: KeelErrorParams) => void;
|
|
117
113
|
}
|
|
118
114
|
declare function syncedKeel<TRemote extends {
|
|
119
115
|
id: string;
|
|
@@ -122,4 +118,4 @@ declare function syncedKeel<TRemote extends {
|
|
|
122
118
|
id: string;
|
|
123
119
|
}, TLocal = TRemote, TOption extends CrudAsOption = 'object', Where extends Record<string, any> = {}>(props: SyncedKeelPropsBase<TRemote, TLocal> & SyncedKeelPropsMany<TRemote, TLocal, TOption, Where>): SyncedCrudReturnType<TLocal, Exclude<TOption, 'value'>>;
|
|
124
120
|
|
|
125
|
-
export { type KeelClient, type KeelGetParams, type KeelKey, KeelKeys, type KeelListParams, type KeelObjectBase, type KeelRealtimePlugin, type OmitKeelBuiltins, type SyncedKeelPropsBase, generateKeelId, syncedKeel };
|
|
121
|
+
export { type KeelClient, type KeelErrorParams, type KeelGetParams, type KeelKey, KeelKeys, type KeelListParams, type KeelObjectBase, type KeelRealtimePlugin, type OmitKeelBuiltins, type SyncedKeelPropsBase, generateKeelId, syncedKeel };
|
package/sync-plugins/keel.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var state = require('@legendapp/state');
|
|
4
|
+
var sync = require('@legendapp/state/sync');
|
|
4
5
|
var crud = require('@legendapp/state/sync-plugins/crud');
|
|
5
6
|
var ksuid = require('ksuid');
|
|
6
7
|
|
|
@@ -52,12 +53,14 @@ async function ensureAuthToken(props, force) {
|
|
|
52
53
|
}
|
|
53
54
|
return isAuthed;
|
|
54
55
|
}
|
|
55
|
-
async function handleApiError(props, error
|
|
56
|
+
async function handleApiError(props, error) {
|
|
56
57
|
if (error.type === "unauthorized" || error.type === "forbidden") {
|
|
57
58
|
console.warn("Keel token expired, refreshing...");
|
|
58
59
|
isAuthed$.set(false);
|
|
59
60
|
await ensureAuthToken(props);
|
|
61
|
+
return true;
|
|
60
62
|
}
|
|
63
|
+
return false;
|
|
61
64
|
}
|
|
62
65
|
function convertObjectToCreate(item) {
|
|
63
66
|
const cloned = {};
|
|
@@ -97,7 +100,7 @@ function setupRealtime(props) {
|
|
|
97
100
|
}
|
|
98
101
|
}
|
|
99
102
|
var NumPerPage = 200;
|
|
100
|
-
async function getAllPages(props, listFn, params) {
|
|
103
|
+
async function getAllPages(props, listFn, params, listParams, onError) {
|
|
101
104
|
const allData = [];
|
|
102
105
|
let pageInfo = void 0;
|
|
103
106
|
const { first: firstParam } = params;
|
|
@@ -113,8 +116,17 @@ async function getAllPages(props, listFn, params) {
|
|
|
113
116
|
if (ret) {
|
|
114
117
|
const { data, error } = ret;
|
|
115
118
|
if (error) {
|
|
116
|
-
await handleApiError(props, error);
|
|
117
|
-
|
|
119
|
+
const handled = await handleApiError(props, error);
|
|
120
|
+
if (!handled) {
|
|
121
|
+
const err = new Error(error.message, { cause: { error } });
|
|
122
|
+
onError(err, {
|
|
123
|
+
getParams: listParams,
|
|
124
|
+
type: "get",
|
|
125
|
+
source: "list",
|
|
126
|
+
action: listFn.name || listFn.toString(),
|
|
127
|
+
retry: listParams
|
|
128
|
+
});
|
|
129
|
+
}
|
|
118
130
|
} else if (data) {
|
|
119
131
|
pageInfo = data.pageInfo;
|
|
120
132
|
allData.push(...data.results);
|
|
@@ -138,7 +150,6 @@ function syncedKeel(props) {
|
|
|
138
150
|
fieldDeleted,
|
|
139
151
|
realtime,
|
|
140
152
|
mode,
|
|
141
|
-
onError,
|
|
142
153
|
requireAuth = true,
|
|
143
154
|
...rest
|
|
144
155
|
} = props;
|
|
@@ -158,7 +169,7 @@ function syncedKeel(props) {
|
|
|
158
169
|
}
|
|
159
170
|
} : void 0;
|
|
160
171
|
const list = listParam ? async (listParams) => {
|
|
161
|
-
const { lastSync } = listParams;
|
|
172
|
+
const { lastSync, onError } = listParams;
|
|
162
173
|
const queryBySync = !!lastSync && changesSince === "last-sync";
|
|
163
174
|
const where = Object.assign(
|
|
164
175
|
queryBySync ? { updatedAt: { after: new Date(lastSync + 1) } } : {},
|
|
@@ -166,14 +177,14 @@ function syncedKeel(props) {
|
|
|
166
177
|
);
|
|
167
178
|
const params = { where, first };
|
|
168
179
|
realtimeState.current = {};
|
|
169
|
-
const promise = getAllPages(props, listParam, params);
|
|
180
|
+
const promise = getAllPages(props, listParam, params, listParams, onError);
|
|
170
181
|
if (realtime) {
|
|
171
182
|
setupSubscribe(listParams);
|
|
172
183
|
}
|
|
173
184
|
return promise;
|
|
174
185
|
} : void 0;
|
|
175
186
|
const get = getParam ? async (getParams) => {
|
|
176
|
-
const { refresh } = getParams;
|
|
187
|
+
const { refresh, onError } = getParams;
|
|
177
188
|
realtimeState.current = {};
|
|
178
189
|
const promise = getParam({ refresh });
|
|
179
190
|
if (realtime) {
|
|
@@ -181,7 +192,17 @@ function syncedKeel(props) {
|
|
|
181
192
|
}
|
|
182
193
|
const { data, error } = await promise;
|
|
183
194
|
if (error) {
|
|
184
|
-
|
|
195
|
+
const handled = await handleApiError(props, error);
|
|
196
|
+
if (!handled) {
|
|
197
|
+
const err = new Error(error.message, { cause: { error } });
|
|
198
|
+
onError(err, {
|
|
199
|
+
getParams,
|
|
200
|
+
type: "get",
|
|
201
|
+
source: "get",
|
|
202
|
+
action: getParam.name || getParam.toString(),
|
|
203
|
+
retry: getParams
|
|
204
|
+
});
|
|
205
|
+
}
|
|
185
206
|
} else {
|
|
186
207
|
return data;
|
|
187
208
|
}
|
|
@@ -198,7 +219,7 @@ function syncedKeel(props) {
|
|
|
198
219
|
};
|
|
199
220
|
const handleSetError = async (error, params, input, fn, from) => {
|
|
200
221
|
var _a, _b;
|
|
201
|
-
const {
|
|
222
|
+
const { update: update2, onError } = params;
|
|
202
223
|
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"))) {
|
|
203
224
|
if (__DEV__) {
|
|
204
225
|
console.log("Creating duplicate data already saved, just ignore.");
|
|
@@ -208,28 +229,25 @@ function syncedKeel(props) {
|
|
|
208
229
|
value: {},
|
|
209
230
|
mode: "assign"
|
|
210
231
|
});
|
|
211
|
-
} else if (from === "delete") {
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
console.log("Deleting non-existing data, just ignore.");
|
|
215
|
-
}
|
|
216
|
-
params.cancelRetry = true;
|
|
217
|
-
}
|
|
218
|
-
} else if (error.type === "bad_request") {
|
|
219
|
-
onError == null ? void 0 : onError(new Error(error.message), params, {
|
|
220
|
-
error,
|
|
221
|
-
params,
|
|
222
|
-
input,
|
|
223
|
-
type: from,
|
|
224
|
-
action: fn.name || fn.toString()
|
|
225
|
-
});
|
|
226
|
-
if (retryNum > 4) {
|
|
227
|
-
params.cancelRetry = true;
|
|
232
|
+
} else if (from === "delete" && error.message === "record not found") {
|
|
233
|
+
if (__DEV__) {
|
|
234
|
+
console.log("Deleting non-existing data, just ignore.");
|
|
228
235
|
}
|
|
229
|
-
|
|
236
|
+
params.cancelRetry = true;
|
|
230
237
|
} else {
|
|
231
|
-
await handleApiError(props, error);
|
|
232
|
-
|
|
238
|
+
const handled = await handleApiError(props, error);
|
|
239
|
+
if (!handled) {
|
|
240
|
+
const err = new Error(error.message, { cause: { error } });
|
|
241
|
+
onError(err, {
|
|
242
|
+
setParams: params,
|
|
243
|
+
input,
|
|
244
|
+
type: "set",
|
|
245
|
+
source: from,
|
|
246
|
+
action: fn.name || fn.toString(),
|
|
247
|
+
retry: params,
|
|
248
|
+
revert: sync.createRevertChanges(params.value$, params.changes)
|
|
249
|
+
});
|
|
250
|
+
}
|
|
233
251
|
}
|
|
234
252
|
};
|
|
235
253
|
const create = createParam ? async (input, params) => {
|