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