@legendapp/state 3.0.0-beta.4 → 3.0.0-beta.41

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.
Files changed (79) hide show
  1. package/.DS_Store +0 -0
  2. package/README.md +2 -2
  3. package/config/enableReactComponents.js +3 -1
  4. package/config/enableReactComponents.mjs +3 -1
  5. package/config/enableReactTracking.d.mts +2 -1
  6. package/config/enableReactTracking.d.ts +2 -1
  7. package/config/enableReactTracking.js +32 -13
  8. package/config/enableReactTracking.mjs +32 -13
  9. package/index.d.mts +46 -8
  10. package/index.d.ts +46 -8
  11. package/index.js +267 -75
  12. package/index.mjs +267 -75
  13. package/package.json +35 -1
  14. package/persist-plugins/async-storage.js +17 -9
  15. package/persist-plugins/async-storage.mjs +17 -9
  16. package/persist-plugins/expo-sqlite.d.mts +19 -0
  17. package/persist-plugins/expo-sqlite.d.ts +19 -0
  18. package/persist-plugins/expo-sqlite.js +72 -0
  19. package/persist-plugins/expo-sqlite.mjs +69 -0
  20. package/persist-plugins/indexeddb.js +13 -3
  21. package/persist-plugins/indexeddb.mjs +13 -3
  22. package/react-native.d.mts +4 -0
  23. package/react-native.d.ts +4 -0
  24. package/react-native.js +53 -0
  25. package/react-native.mjs +40 -0
  26. package/react-reactive/Components.d.mts +19 -0
  27. package/react-reactive/Components.d.ts +19 -0
  28. package/react-reactive/Components.js +53 -0
  29. package/react-reactive/Components.mjs +40 -0
  30. package/react-reactive/enableReactComponents.d.mts +3 -2
  31. package/react-reactive/enableReactComponents.d.ts +3 -2
  32. package/react-reactive/enableReactComponents.js +10 -3
  33. package/react-reactive/enableReactComponents.mjs +10 -3
  34. package/react-reactive/enableReactNativeComponents.d.mts +3 -20
  35. package/react-reactive/enableReactNativeComponents.d.ts +3 -20
  36. package/react-reactive/enableReactNativeComponents.js +8 -3
  37. package/react-reactive/enableReactNativeComponents.mjs +8 -3
  38. package/react-reactive/enableReactive.js +10 -3
  39. package/react-reactive/enableReactive.mjs +10 -3
  40. package/react-reactive/enableReactive.native.js +8 -3
  41. package/react-reactive/enableReactive.native.mjs +8 -3
  42. package/react-reactive/enableReactive.web.js +8 -3
  43. package/react-reactive/enableReactive.web.mjs +8 -3
  44. package/react-web.d.mts +7 -0
  45. package/react-web.d.ts +7 -0
  46. package/react-web.js +39 -0
  47. package/react-web.mjs +37 -0
  48. package/react.d.mts +59 -26
  49. package/react.d.ts +59 -26
  50. package/react.js +136 -87
  51. package/react.mjs +135 -89
  52. package/sync-plugins/crud.d.mts +24 -9
  53. package/sync-plugins/crud.d.ts +24 -9
  54. package/sync-plugins/crud.js +267 -123
  55. package/sync-plugins/crud.mjs +268 -124
  56. package/sync-plugins/firebase.d.mts +7 -3
  57. package/sync-plugins/firebase.d.ts +7 -3
  58. package/sync-plugins/firebase.js +214 -64
  59. package/sync-plugins/firebase.mjs +215 -65
  60. package/sync-plugins/keel.d.mts +12 -13
  61. package/sync-plugins/keel.d.ts +12 -13
  62. package/sync-plugins/keel.js +60 -52
  63. package/sync-plugins/keel.mjs +61 -48
  64. package/sync-plugins/supabase.d.mts +10 -5
  65. package/sync-plugins/supabase.d.ts +10 -5
  66. package/sync-plugins/supabase.js +90 -33
  67. package/sync-plugins/supabase.mjs +91 -34
  68. package/sync-plugins/tanstack-query.d.mts +3 -3
  69. package/sync-plugins/tanstack-query.d.ts +3 -3
  70. package/sync-plugins/tanstack-query.js +1 -1
  71. package/sync-plugins/tanstack-query.mjs +1 -1
  72. package/sync.d.mts +17 -8
  73. package/sync.d.ts +17 -8
  74. package/sync.js +448 -307
  75. package/sync.mjs +446 -307
  76. package/trace.js +5 -6
  77. package/trace.mjs +5 -6
  78. package/types/reactive-native.d.ts +19 -0
  79. package/types/reactive-web.d.ts +7 -0
@@ -1,17 +1,19 @@
1
- import { isPromise, applyChanges, isNullOrUndefined, setAtPath, symbolDelete, isArray, internal, getNodeValue } from '@legendapp/state';
1
+ import { isPromise, isNullOrUndefined, applyChanges, setAtPath, symbolDelete, isArray, internal, getNodeValue } from '@legendapp/state';
2
2
  import { synced, deepEqual, internal as internal$1, diffObjects } from '@legendapp/state/sync';
3
3
 
4
4
  // src/sync-plugins/crud.ts
5
- var { clone } = internal;
6
- var { waitForSet } = internal$1;
5
+ var { clone, getKeys, setNodeValue, getChildNode } = internal;
6
+ var { waitForSet, runWithRetry } = internal$1;
7
7
  function transformOut(data, transform) {
8
8
  return transform ? transform(clone(data)) : data;
9
9
  }
10
- function ensureId(obj, fieldId, generateId) {
10
+ function ensureId(obj, fieldId, generateId, node) {
11
11
  if (!obj[fieldId]) {
12
12
  obj[fieldId] = generateId();
13
+ if (node) {
14
+ setNodeValue(node, obj);
15
+ }
13
16
  }
14
- return obj[fieldId];
15
17
  }
16
18
  function computeLastSync(data, fieldUpdatedAt, fieldCreatedAt) {
17
19
  let newLastSync = 0;
@@ -23,6 +25,47 @@ function computeLastSync(data, fieldUpdatedAt, fieldCreatedAt) {
23
25
  }
24
26
  return newLastSync;
25
27
  }
28
+ function arrayToRecord(arr, keyField) {
29
+ const record = {};
30
+ if (arr == null ? void 0 : arr.length) {
31
+ for (let i = 0; i < arr.length; i++) {
32
+ const v = arr[i];
33
+ const key = v[keyField];
34
+ record[key] = v;
35
+ }
36
+ }
37
+ return record;
38
+ }
39
+ function retrySet(params, retry, action, itemKey, itemValue, change, queuedRetries, itemValueFull, actionFn, saveResult) {
40
+ if (action === "delete") {
41
+ if (queuedRetries.create.has(itemKey)) {
42
+ queuedRetries.create.delete(itemKey);
43
+ }
44
+ if (queuedRetries.update.has(itemKey)) {
45
+ queuedRetries.update.delete(itemKey);
46
+ }
47
+ } else {
48
+ if (queuedRetries.delete.has(itemKey)) {
49
+ queuedRetries.delete.delete(itemKey);
50
+ }
51
+ }
52
+ const queuedRetry = queuedRetries[action].get(itemKey);
53
+ if (queuedRetry) {
54
+ itemValue = Object.assign(queuedRetry, itemValue);
55
+ }
56
+ queuedRetries[action].set(itemKey, itemValue);
57
+ const clonedValue = clone(itemValueFull);
58
+ const paramsWithChanges = { ...params, changes: [change] };
59
+ return runWithRetry(
60
+ paramsWithChanges,
61
+ retry,
62
+ "create_" + itemKey,
63
+ () => actionFn(itemValue, paramsWithChanges).then((result) => {
64
+ queuedRetries[action].delete(itemKey);
65
+ return saveResult(itemKey, clonedValue, result, true, change);
66
+ })
67
+ );
68
+ }
26
69
  function syncedCrud(props) {
27
70
  const {
28
71
  get: getFn,
@@ -43,10 +86,16 @@ function syncedCrud(props) {
43
86
  changesSince,
44
87
  generateId,
45
88
  waitForSet: waitForSetParam,
89
+ retry,
46
90
  ...rest
47
91
  } = props;
48
92
  const fieldId = fieldIdProp || "id";
49
93
  const pendingCreates = /* @__PURE__ */ new Set();
94
+ const queuedRetries = {
95
+ create: /* @__PURE__ */ new Map(),
96
+ update: /* @__PURE__ */ new Map(),
97
+ delete: /* @__PURE__ */ new Map()
98
+ };
50
99
  let asType = props.as;
51
100
  if (!asType) {
52
101
  asType = getFn ? "value" : "object";
@@ -75,69 +124,76 @@ function syncedCrud(props) {
75
124
  return out;
76
125
  };
77
126
  const transformRows = (data) => {
78
- return Promise.all(
127
+ return data.length ? Promise.all(
79
128
  data.map(
80
129
  (value) => (
81
130
  // Skip transforming any children with symbolDelete or fieldDeleted because they'll get deleted by resultsToOutType
82
131
  value[symbolDelete] || fieldDeleted && value[fieldDeleted] || fieldDeletedList && value[fieldDeletedList] ? value : transform.load(value, "get")
83
132
  )
84
133
  )
85
- );
134
+ ) : [];
86
135
  };
87
136
  const get = getFn || listFn ? (getParams) => {
88
- const { updateLastSync, lastSync, value } = getParams;
89
- if (listFn) {
90
- const isLastSyncMode = changesSince === "last-sync";
91
- if (isLastSyncMode && lastSync) {
92
- getParams.mode = modeParam || (asType === "array" ? "append" : asType === "value" ? "set" : "assign");
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);
137
+ return runWithRetry(getParams, retry, getFn || listFn, () => {
138
+ const { updateLastSync, lastSync, value } = getParams;
139
+ if (listFn) {
140
+ const isLastSyncMode = changesSince === "last-sync";
141
+ if (isLastSyncMode && lastSync) {
142
+ getParams.mode = modeParam || (asType === "array" ? "append" : asType === "value" ? "set" : "assign");
101
143
  }
102
- };
103
- const processResults = (data) => {
104
- data || (data = []);
105
- if (fieldUpdatedAt) {
106
- const newLastSync = computeLastSync(data, fieldUpdatedAt, fieldCreatedAt);
107
- if (newLastSync && newLastSync !== lastSync) {
108
- updateLastSync(newLastSync);
144
+ const listPromise = listFn(getParams);
145
+ const toOut = (transformed) => {
146
+ if (asType === "value") {
147
+ if (transformed.length > 0) {
148
+ return transformed[0];
149
+ } else {
150
+ return value ? void 0 : null;
151
+ }
152
+ } else {
153
+ return resultsToOutType(transformed);
109
154
  }
110
- }
111
- let transformed = data;
112
- if (transform == null ? void 0 : transform.load) {
113
- transformed = transformRows(data);
114
- }
115
- return isPromise(transformed) ? transformed.then(toOut) : toOut(transformed);
116
- };
117
- return isPromise(listPromise) ? listPromise.then(processResults) : processResults(listPromise);
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);
155
+ };
156
+ const processResults = (data) => {
157
+ data || (data = []);
158
+ if (fieldUpdatedAt) {
159
+ const newLastSync = computeLastSync(data, fieldUpdatedAt, fieldCreatedAt);
160
+ if (newLastSync && newLastSync !== lastSync) {
161
+ updateLastSync(newLastSync);
162
+ }
126
163
  }
164
+ let transformed = data;
127
165
  if (transform == null ? void 0 : transform.load) {
128
- transformed = transform.load(data, "get");
166
+ transformed = transformRows(data);
129
167
  }
130
- }
131
- return transformed;
132
- };
133
- return isPromise(dataPromise) ? dataPromise.then(processData) : processData(dataPromise);
134
- }
168
+ return isPromise(transformed) ? transformed.then(toOut) : toOut(transformed);
169
+ };
170
+ return isPromise(listPromise) ? listPromise.then(processResults) : processResults(listPromise);
171
+ } else if (getFn) {
172
+ const dataPromise = getFn(getParams);
173
+ const processData = (data) => {
174
+ let transformed = data;
175
+ if (data) {
176
+ const newLastSync = data[fieldUpdatedAt] || data[fieldCreatedAt];
177
+ if (newLastSync && newLastSync !== lastSync) {
178
+ updateLastSync(newLastSync);
179
+ }
180
+ if (transform == null ? void 0 : transform.load) {
181
+ transformed = transform.load(data, "get");
182
+ }
183
+ }
184
+ return transformed;
185
+ };
186
+ return isPromise(dataPromise) ? dataPromise.then(processData) : processData(dataPromise);
187
+ }
188
+ });
135
189
  } : void 0;
136
190
  const set = createFn || updateFn || deleteFn ? async (params) => {
137
191
  const { value, changes, update, retryAsCreate, node } = params;
138
192
  const creates = /* @__PURE__ */ new Map();
139
193
  const updates = /* @__PURE__ */ new Map();
194
+ const updateFullValues = /* @__PURE__ */ new Map();
140
195
  const deletes = /* @__PURE__ */ new Set();
196
+ const changesById = /* @__PURE__ */ new Map();
141
197
  const getUpdateValue = (itemValue, prev) => {
142
198
  return updatePartial ? Object.assign(
143
199
  diffObjects(
@@ -146,19 +202,21 @@ function syncedCrud(props) {
146
202
  /*deep*/
147
203
  true
148
204
  ),
149
- itemValue[fieldId] ? { [fieldId]: itemValue[fieldId] } : {}
205
+ !isNullOrUndefined(itemValue[fieldId]) ? { [fieldId]: itemValue[fieldId] } : {}
150
206
  ) : itemValue;
151
207
  };
152
208
  changes.forEach((change) => {
209
+ var _a, _b;
153
210
  const { path, prevAtPath, valueAtPath, pathTypes } = change;
154
211
  if (asType === "value") {
155
212
  if (value) {
156
- let id = value == null ? void 0 : value[fieldId];
157
213
  let isCreate = fieldCreatedAt ? !value[fieldCreatedAt] : !prevAtPath;
158
- if (!id && generateId) {
159
- id = ensureId(value, fieldId, generateId);
214
+ if (isNullOrUndefined(value[fieldId]) && generateId) {
215
+ ensureId(value, fieldId, generateId, node);
160
216
  }
161
- if (id) {
217
+ const id = value[fieldId];
218
+ if (!isNullOrUndefined(id)) {
219
+ changesById.set(id, change);
162
220
  if (pendingCreates.has(id)) {
163
221
  isCreate = false;
164
222
  }
@@ -171,6 +229,7 @@ function syncedCrud(props) {
171
229
  } else if (path.length === 0) {
172
230
  if (valueAtPath) {
173
231
  updates.set(id, getUpdateValue(valueAtPath, prevAtPath));
232
+ updateFullValues.set(id, valueAtPath);
174
233
  } else if (prevAtPath) {
175
234
  deletes.add(prevAtPath);
176
235
  }
@@ -182,27 +241,50 @@ function syncedCrud(props) {
182
241
  true
183
242
  );
184
243
  updates.set(id, getUpdateValue(value, previous));
244
+ updateFullValues.set(id, value);
185
245
  }
186
246
  } else {
187
247
  console.error("[legend-state]: added synced item without an id");
188
248
  }
189
249
  } else if (path.length === 0) {
190
250
  deletes.add(prevAtPath);
251
+ changesById.set(prevAtPath[fieldId], change);
191
252
  }
192
253
  } else {
193
254
  let itemsChanged = [];
194
255
  if (path.length === 0) {
195
- const changed = asMap ? Array.from(valueAtPath.entries()) : Object.entries(valueAtPath);
196
- for (let i = 0; i < changed.length; i++) {
197
- const [key, value2] = changed[i];
198
- const prev = asMap ? prevAtPath.get(key) : prevAtPath[key];
256
+ if (asArray && generateId) {
257
+ for (let i = 0; i < valueAtPath.length; i++) {
258
+ const value2 = valueAtPath[i];
259
+ if (value2 && !value2[fieldId]) {
260
+ ensureId(value2, fieldId, generateId, getChildNode(node, i + ""));
261
+ }
262
+ }
263
+ }
264
+ const valueAsObject = asArray ? arrayToRecord(valueAtPath, fieldId) : valueAtPath;
265
+ const prevAsObject = asArray ? arrayToRecord(prevAtPath, fieldId) : prevAtPath;
266
+ const keys = getKeys(valueAsObject, false, asMap, false);
267
+ const keysPrev = getKeys(prevAsObject, false, asMap, false);
268
+ const keysSet = new Set(keys);
269
+ const length = ((_a = keys || valueAsObject) == null ? void 0 : _a.length) || 0;
270
+ const lengthPrev = ((_b = keysPrev || prevAsObject) == null ? void 0 : _b.length) || 0;
271
+ for (let i = 0; i < lengthPrev; i++) {
272
+ const key = keysPrev[i];
273
+ if (!keysSet.has(key)) {
274
+ deletes.add(prevAsObject[key]);
275
+ }
276
+ }
277
+ for (let i = 0; i < length; i++) {
278
+ const key = keys[i];
279
+ const value2 = asMap ? valueAsObject.get(key) : valueAsObject[key];
280
+ const prev = prevAsObject ? asMap ? prevAsObject.get(key) : prevAsObject[key] : void 0;
199
281
  if (isNullOrUndefined(value2) && !isNullOrUndefined(prev)) {
200
282
  deletes.add(prev);
201
283
  return false;
202
284
  } else {
203
- const isDiff = !prevAtPath || !deepEqual(value2, prev);
285
+ const isDiff = !prevAsObject || !deepEqual(value2, prev);
204
286
  if (isDiff) {
205
- itemsChanged.push([getUpdateValue(value2, prev), prev]);
287
+ itemsChanged.push([getUpdateValue(value2, prev), prev, value2]);
206
288
  }
207
289
  }
208
290
  }
@@ -212,37 +294,43 @@ function syncedCrud(props) {
212
294
  if (!itemValue) {
213
295
  if (path.length === 1 && prevAtPath) {
214
296
  deletes.add(prevAtPath);
297
+ changesById.set(prevAtPath[fieldId], change);
215
298
  }
216
299
  } else {
300
+ if (generateId) {
301
+ ensureId(itemValue, fieldId, generateId, getChildNode(node, itemKey + ""));
302
+ }
217
303
  const previous = setAtPath(
218
304
  clone(itemValue),
219
305
  path.slice(1),
220
306
  pathTypes.slice(1),
221
307
  prevAtPath
222
308
  );
223
- itemsChanged = [[getUpdateValue(itemValue, previous), previous]];
309
+ itemsChanged = [[getUpdateValue(itemValue, previous), previous, itemValue]];
224
310
  }
225
311
  }
226
- itemsChanged == null ? void 0 : itemsChanged.forEach(([item, prev]) => {
312
+ itemsChanged == null ? void 0 : itemsChanged.forEach(([item, prev, fullValue]) => {
227
313
  const isCreate = !pendingCreates.has(item[fieldId]) && (fieldCreatedAt ? !item[fieldCreatedAt] && !(prev == null ? void 0 : prev[fieldCreatedAt]) : fieldUpdatedAt ? !item[fieldUpdatedAt] && !(prev == null ? void 0 : prev[fieldCreatedAt]) : isNullOrUndefined(prev));
228
314
  if (isCreate) {
229
- if (generateId) {
230
- ensureId(item, fieldId, generateId);
231
- }
232
315
  if (!item[fieldId]) {
233
316
  console.error("[legend-state]: added item without an id");
234
317
  }
235
318
  if (createFn) {
236
- pendingCreates.add(item[fieldId]);
237
- creates.set(item[fieldId], item);
319
+ const id = item[fieldId];
320
+ changesById.set(id, change);
321
+ pendingCreates.add(id);
322
+ creates.set(id, item);
238
323
  } else {
239
324
  console.warn("[legend-state] missing create function");
240
325
  }
241
326
  } else {
242
327
  if (updateFn) {
243
- updates.set(
244
- item[fieldId],
245
- updates.has(item[fieldId]) ? Object.assign(updates.get(item[fieldId]), item) : item
328
+ const id = item[fieldId];
329
+ changesById.set(id, change);
330
+ updates.set(id, updates.has(id) ? Object.assign(updates.get(id), item) : item);
331
+ updateFullValues.set(
332
+ id,
333
+ updateFullValues.has(id) ? Object.assign(updateFullValues.get(id), fullValue) : fullValue
246
334
  );
247
335
  } else {
248
336
  console.warn("[legend-state] missing update function");
@@ -251,93 +339,149 @@ function syncedCrud(props) {
251
339
  });
252
340
  }
253
341
  });
254
- const saveResult = async (itemKey, input, data, isCreate) => {
342
+ const saveResult = async (itemKey, input, data, isCreate, change) => {
255
343
  var _a;
256
344
  if (data) {
257
- const saved = (transform == null ? void 0 : transform.load) ? await transform.load(data, "set") : data;
345
+ let saved = (transform == null ? void 0 : transform.load) ? await transform.load(data, "set") : data;
258
346
  const isChild = itemKey !== "undefined" && asType !== "value";
259
347
  const currentPeeked = getNodeValue(node);
260
- const currentValue = isChild ? (_a = asType === "array" && isArray(currentPeeked) ? currentPeeked.find((v) => v[fieldId] === itemKey) : void 0) != null ? _a : currentPeeked[itemKey] : currentPeeked;
261
- const dataOnSaved = {
262
- saved,
263
- input,
264
- currentValue,
265
- isCreate,
266
- props
267
- };
268
- let savedOut = saved;
269
- if (savedOut && !isNullOrUndefined(currentValue)) {
270
- savedOut = clone(savedOut);
271
- Object.keys(savedOut).forEach((key) => {
348
+ const currentValue = isChild ? (_a = asType === "array" && isArray(currentPeeked) ? currentPeeked.find((v) => v[fieldId] === itemKey) : void 0) != null ? _a : asType === "Map" ? currentPeeked.get(itemKey) : currentPeeked[itemKey] : currentPeeked;
349
+ if (saved && !isNullOrUndefined(currentValue)) {
350
+ if (onSaved) {
351
+ const ret = onSaved({
352
+ saved,
353
+ input,
354
+ currentValue,
355
+ isCreate,
356
+ props
357
+ });
358
+ if (ret) {
359
+ saved = ret;
360
+ }
361
+ }
362
+ saved = clone(saved);
363
+ Object.keys(saved).forEach((key) => {
272
364
  const i = input[key];
273
365
  const c = currentValue[key];
274
366
  if (
275
367
  // value is already the new value, can ignore
276
- savedOut[key] === c || // user has changed local value
277
- key !== "id" && i !== c
368
+ saved[key] === c || // user has changed local value
369
+ key !== fieldId && i !== void 0 && i !== c
278
370
  ) {
279
- delete savedOut[key];
371
+ delete saved[key];
280
372
  }
281
373
  });
282
- if (onSaved) {
283
- const ret = onSaved(dataOnSaved);
284
- if (ret) {
285
- savedOut = ret;
374
+ let value2;
375
+ if (asType === "array") {
376
+ const index = currentPeeked.findIndex(
377
+ (cur) => cur[fieldId] === itemKey
378
+ );
379
+ if (index < 0) {
380
+ console.warn("[legend-state] Item saved that does not exist in array", saved);
381
+ } else {
382
+ value2 = { [index < 0 ? 0 : index]: saved };
286
383
  }
384
+ } else {
385
+ value2 = itemKey !== "undefined" && asType !== "value" ? { [itemKey]: saved } : saved;
386
+ }
387
+ if (value2 !== void 0) {
388
+ update({
389
+ value: value2,
390
+ mode: "merge",
391
+ changes: [change]
392
+ });
287
393
  }
288
- const createdAt = fieldCreatedAt ? savedOut[fieldCreatedAt] : void 0;
289
- const updatedAt = fieldUpdatedAt ? savedOut[fieldUpdatedAt] : void 0;
290
- const value2 = itemKey !== "undefined" && asType !== "value" ? { [itemKey]: savedOut } : savedOut;
291
- update({
292
- value: value2,
293
- lastSync: updatedAt || createdAt ? +new Date(updatedAt || createdAt) : void 0,
294
- mode: "merge"
295
- });
296
394
  }
297
395
  }
298
396
  };
299
397
  return Promise.all([
398
+ // Handle creates
300
399
  ...Array.from(creates).map(async ([itemKey, itemValue]) => {
301
400
  if (waitForSetParam) {
302
401
  await waitForSet(waitForSetParam, changes, itemValue, { type: "create" });
303
402
  }
304
403
  const createObj = await transformOut(itemValue, transform == null ? void 0 : transform.save);
305
- return createFn(createObj, params).then((result) => {
306
- return saveResult(itemKey, createObj, result, true);
307
- }).finally(() => {
404
+ return retrySet(
405
+ params,
406
+ retry,
407
+ "create",
408
+ itemKey,
409
+ createObj,
410
+ changesById.get(itemKey),
411
+ queuedRetries,
412
+ createObj,
413
+ createFn,
414
+ saveResult
415
+ ).then(() => {
308
416
  pendingCreates.delete(itemKey);
309
417
  });
310
418
  }),
419
+ // Handle updates
311
420
  ...Array.from(updates).map(async ([itemKey, itemValue]) => {
421
+ const fullValue = updateFullValues.get(itemKey);
312
422
  if (waitForSetParam) {
313
- await waitForSet(waitForSetParam, changes, itemValue, { type: "update" });
423
+ await waitForSet(waitForSetParam, changes, fullValue, { type: "update" });
314
424
  }
315
- const toSave = itemValue;
316
- const changed = await transformOut(toSave, transform == null ? void 0 : transform.save);
425
+ const changed = await transformOut(itemValue, transform == null ? void 0 : transform.save);
426
+ const fullValueTransformed = await transformOut(
427
+ fullValue,
428
+ transform == null ? void 0 : transform.save
429
+ );
317
430
  if (Object.keys(changed).length > 0) {
318
- return updateFn(changed, params).then(
319
- (result) => result && saveResult(itemKey, changed, result, false)
431
+ return retrySet(
432
+ params,
433
+ retry,
434
+ "update",
435
+ itemKey,
436
+ changed,
437
+ changesById.get(itemKey),
438
+ queuedRetries,
439
+ fullValueTransformed,
440
+ updateFn,
441
+ saveResult
320
442
  );
321
443
  }
322
444
  }),
323
- ...Array.from(deletes).map(async (valuePrevious) => {
324
- if (valuePrevious !== symbolDelete) {
325
- if (waitForSetParam) {
326
- await waitForSet(waitForSetParam, changes, valuePrevious, { type: "delete" });
327
- }
328
- if (deleteFn) {
329
- deleteFn(valuePrevious, params);
330
- } else if (fieldDeleted && updateFn) {
331
- const valueId = valuePrevious[fieldId];
332
- if (valueId) {
333
- updateFn({ ...{ [fieldId]: valueId }, [fieldDeleted]: true }, params);
334
- } else {
335
- console.error("[legend-state]: deleting item without an id");
336
- }
337
- } else {
338
- console.warn("[legend-state] missing delete function");
339
- }
445
+ // Handle deletes
446
+ ...Array.from(deletes).filter((val) => val !== symbolDelete).map(async (valuePrevious) => {
447
+ if (waitForSetParam) {
448
+ await waitForSet(waitForSetParam, changes, valuePrevious, { type: "delete" });
449
+ }
450
+ const itemKey = valuePrevious[fieldId];
451
+ if (!itemKey) {
452
+ console.error("[legend-state]: deleting item without an id");
453
+ return;
454
+ }
455
+ if (deleteFn) {
456
+ return retrySet(
457
+ params,
458
+ retry,
459
+ "delete",
460
+ itemKey,
461
+ valuePrevious,
462
+ changesById.get(itemKey),
463
+ queuedRetries,
464
+ valuePrevious,
465
+ deleteFn,
466
+ saveResult
467
+ );
468
+ }
469
+ if (fieldDeleted && updateFn) {
470
+ const value2 = { [fieldId]: itemKey, [fieldDeleted]: true };
471
+ return retrySet(
472
+ params,
473
+ retry,
474
+ "delete",
475
+ itemKey,
476
+ value2,
477
+ changesById.get(itemKey),
478
+ queuedRetries,
479
+ value2,
480
+ updateFn,
481
+ saveResult
482
+ );
340
483
  }
484
+ console.warn("[legend-state] missing delete function");
341
485
  })
342
486
  ]);
343
487
  } : 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 };