@legendapp/state 3.0.0-alpha.8 → 3.0.0-beta.0

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 (86) hide show
  1. package/.DS_Store +0 -0
  2. package/config/configureLegendState.d.mts +13 -0
  3. package/config/configureLegendState.d.ts +13 -0
  4. package/config/configureLegendState.js +45 -0
  5. package/config/configureLegendState.mjs +43 -0
  6. package/config/enable$GetSet.js +2 -1
  7. package/config/enable$GetSet.mjs +2 -1
  8. package/config/enableReactTracking.js +2 -1
  9. package/config/enableReactTracking.mjs +2 -1
  10. package/config/enableReactUse.js +2 -1
  11. package/config/enableReactUse.mjs +2 -1
  12. package/config/enable_PeekAssign.js +2 -1
  13. package/config/enable_PeekAssign.mjs +2 -1
  14. package/helpers/trackHistory.js +2 -2
  15. package/helpers/trackHistory.mjs +2 -2
  16. package/index.d.mts +104 -80
  17. package/index.d.ts +104 -80
  18. package/index.js +328 -318
  19. package/index.mjs +325 -316
  20. package/package.json +36 -1
  21. package/persist-plugins/async-storage.d.mts +6 -3
  22. package/persist-plugins/async-storage.d.ts +6 -3
  23. package/persist-plugins/async-storage.js +8 -4
  24. package/persist-plugins/async-storage.mjs +8 -5
  25. package/persist-plugins/indexeddb.d.mts +6 -4
  26. package/persist-plugins/indexeddb.d.ts +6 -4
  27. package/persist-plugins/indexeddb.js +35 -15
  28. package/persist-plugins/indexeddb.mjs +35 -16
  29. package/persist-plugins/mmkv.d.mts +5 -1
  30. package/persist-plugins/mmkv.d.ts +5 -1
  31. package/persist-plugins/mmkv.js +10 -5
  32. package/persist-plugins/mmkv.mjs +10 -6
  33. package/react-reactive/enableReactComponents.d.mts +9 -0
  34. package/react-reactive/enableReactComponents.d.ts +9 -0
  35. package/react-reactive/enableReactComponents.js +19 -0
  36. package/react-reactive/enableReactComponents.mjs +17 -0
  37. package/react-reactive/enableReactNativeComponents.d.mts +22 -0
  38. package/react-reactive/enableReactNativeComponents.d.ts +22 -0
  39. package/react-reactive/enableReactNativeComponents.js +53 -0
  40. package/react-reactive/enableReactNativeComponents.mjs +51 -0
  41. package/react-reactive/enableReactive.d.mts +5 -0
  42. package/react-reactive/enableReactive.d.ts +5 -0
  43. package/react-reactive/enableReactive.js +24 -0
  44. package/react-reactive/enableReactive.mjs +22 -0
  45. package/react-reactive/enableReactive.native.d.mts +5 -0
  46. package/react-reactive/enableReactive.native.d.ts +5 -0
  47. package/react-reactive/enableReactive.native.js +58 -0
  48. package/react-reactive/enableReactive.native.mjs +56 -0
  49. package/react-reactive/enableReactive.web.d.mts +5 -0
  50. package/react-reactive/enableReactive.web.d.ts +5 -0
  51. package/react-reactive/enableReactive.web.js +58 -0
  52. package/react-reactive/enableReactive.web.mjs +56 -0
  53. package/react.d.mts +39 -34
  54. package/react.d.ts +39 -34
  55. package/react.js +39 -17
  56. package/react.mjs +39 -17
  57. package/sync-plugins/crud.d.mts +21 -24
  58. package/sync-plugins/crud.d.ts +21 -24
  59. package/sync-plugins/crud.js +241 -140
  60. package/sync-plugins/crud.mjs +243 -142
  61. package/sync-plugins/fetch.js +12 -8
  62. package/sync-plugins/fetch.mjs +13 -9
  63. package/sync-plugins/firebase.d.mts +27 -0
  64. package/sync-plugins/firebase.d.ts +27 -0
  65. package/sync-plugins/firebase.js +373 -0
  66. package/sync-plugins/firebase.mjs +368 -0
  67. package/sync-plugins/keel.d.mts +43 -26
  68. package/sync-plugins/keel.d.ts +43 -26
  69. package/sync-plugins/keel.js +145 -100
  70. package/sync-plugins/keel.mjs +147 -100
  71. package/sync-plugins/supabase.d.mts +19 -9
  72. package/sync-plugins/supabase.d.ts +19 -9
  73. package/sync-plugins/supabase.js +52 -22
  74. package/sync-plugins/supabase.mjs +53 -23
  75. package/sync-plugins/tanstack-query.d.mts +2 -2
  76. package/sync-plugins/tanstack-query.d.ts +2 -2
  77. package/sync-plugins/tanstack-query.js +22 -5
  78. package/sync-plugins/tanstack-query.mjs +22 -5
  79. package/sync-plugins/tanstack-react-query.d.mts +1 -1
  80. package/sync-plugins/tanstack-react-query.d.ts +1 -1
  81. package/sync-plugins/tanstack-react-query.js +8 -1
  82. package/sync-plugins/tanstack-react-query.mjs +8 -1
  83. package/sync.d.mts +74 -200
  84. package/sync.d.ts +74 -200
  85. package/sync.js +492 -293
  86. package/sync.mjs +498 -299
@@ -1,35 +1,27 @@
1
- import { internal, isNullOrUndefined, getNodeValue } from '@legendapp/state';
2
- import { synced, deepEqual, diffObjects } from '@legendapp/state/sync';
1
+ import { isPromise, applyChanges, isNullOrUndefined, setAtPath, symbolDelete, isArray, internal, getNodeValue } from '@legendapp/state';
2
+ import { synced, deepEqual, internal as internal$1, diffObjects } from '@legendapp/state/sync';
3
3
 
4
4
  // src/sync-plugins/crud.ts
5
5
  var { clone } = internal;
6
+ var { waitForSet } = internal$1;
6
7
  function transformOut(data, transform) {
7
8
  return transform ? transform(clone(data)) : data;
8
9
  }
9
- function ensureId(obj, generateId) {
10
- if (!obj.id) {
11
- obj.id = generateId();
10
+ function ensureId(obj, fieldId, generateId) {
11
+ if (!obj[fieldId]) {
12
+ obj[fieldId] = generateId();
12
13
  }
13
- return obj.id;
14
+ return obj[fieldId];
14
15
  }
15
- function onSavedCreatedUpdatedAt(mode, { saved, currentValue, isCreate, props }) {
16
- const savedOut = {};
17
- if (isCreate) {
18
- Object.keys(saved).forEach((key) => {
19
- if (isNullOrUndefined(currentValue[key])) {
20
- savedOut[key] = saved[key];
21
- }
22
- });
23
- } else if (mode === "createdUpdatedAt") {
24
- Object.keys(saved).forEach((key) => {
25
- const k = key;
26
- const keyLower = key.toLowerCase();
27
- if ((key === props.fieldCreatedAt || key === props.fieldUpdatedAt || keyLower.endsWith("createdat") || keyLower.endsWith("updatedat") || keyLower.endsWith("created_at") || keyLower.endsWith("updated_at")) && saved[k] instanceof Date) {
28
- savedOut[k] = saved[k];
29
- }
30
- });
16
+ function computeLastSync(data, fieldUpdatedAt, fieldCreatedAt) {
17
+ let newLastSync = 0;
18
+ for (let i = 0; i < data.length; i++) {
19
+ const updated = (fieldUpdatedAt ? data[i][fieldUpdatedAt] : 0) || (fieldCreatedAt ? data[i][fieldCreatedAt] : 0);
20
+ if (updated) {
21
+ newLastSync = Math.max(newLastSync, +new Date(updated));
22
+ }
31
23
  }
32
- return savedOut;
24
+ return newLastSync;
33
25
  }
34
26
  function syncedCrud(props) {
35
27
  const {
@@ -39,168 +31,233 @@ function syncedCrud(props) {
39
31
  update: updateFn,
40
32
  delete: deleteFn,
41
33
  transform,
34
+ fieldId: fieldIdProp,
42
35
  fieldCreatedAt,
43
36
  fieldUpdatedAt,
44
37
  fieldDeleted,
38
+ fieldDeletedList,
45
39
  updatePartial,
40
+ subscribe: subscribeProp,
46
41
  onSaved,
47
- onSavedUpdate,
48
42
  mode: modeParam,
49
43
  changesSince,
50
44
  generateId,
45
+ waitForSet: waitForSetParam,
51
46
  ...rest
52
47
  } = props;
48
+ const fieldId = fieldIdProp || "id";
49
+ const pendingCreates = /* @__PURE__ */ new Set();
53
50
  let asType = props.as;
54
51
  if (!asType) {
55
52
  asType = getFn ? "value" : "object";
56
53
  }
57
54
  const asMap = asType === "Map";
58
55
  const asArray = asType === "array";
59
- const get = getFn || listFn ? async (getParams) => {
56
+ const resultsToOutType = (results) => {
57
+ if (asType === "value") {
58
+ return results[0];
59
+ }
60
+ const out = asType === "array" ? [] : asMap ? /* @__PURE__ */ new Map() : {};
61
+ for (let i = 0; i < results.length; i++) {
62
+ let result = results[i];
63
+ const value = result;
64
+ if (value) {
65
+ result = fieldDeleted && result[fieldDeleted] || fieldDeletedList && result[fieldDeletedList] || result[symbolDelete] ? internal.symbolDelete : result;
66
+ if (asArray) {
67
+ out.push(result);
68
+ } else if (asMap) {
69
+ out.set(value[fieldId], result);
70
+ } else {
71
+ out[value[fieldId]] = result;
72
+ }
73
+ }
74
+ }
75
+ return out;
76
+ };
77
+ const transformRows = (data) => {
78
+ return Promise.all(
79
+ data.map(
80
+ (value) => (
81
+ // Skip transforming any children with symbolDelete or fieldDeleted because they'll get deleted by resultsToOutType
82
+ value[symbolDelete] || fieldDeleted && value[fieldDeleted] || fieldDeletedList && value[fieldDeletedList] ? value : transform.load(value, "get")
83
+ )
84
+ )
85
+ );
86
+ };
87
+ const get = getFn || listFn ? (getParams) => {
60
88
  const { updateLastSync, lastSync, value } = getParams;
61
89
  if (listFn) {
62
90
  const isLastSyncMode = changesSince === "last-sync";
63
91
  if (isLastSyncMode && lastSync) {
64
92
  getParams.mode = modeParam || (asType === "array" ? "append" : asType === "value" ? "set" : "assign");
65
93
  }
66
- const data = await listFn(getParams) || [];
67
- let newLastSync = 0;
68
- for (let i = 0; i < data.length; i++) {
69
- const updated = data[i][fieldUpdatedAt] || data[i][fieldCreatedAt];
70
- if (updated) {
71
- newLastSync = Math.max(newLastSync, +new Date(updated));
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);
72
101
  }
73
- }
74
- if (newLastSync && newLastSync !== lastSync) {
75
- updateLastSync(newLastSync);
76
- }
77
- let transformed = data;
78
- if (transform == null ? void 0 : transform.load) {
79
- transformed = await Promise.all(data.map((value2) => transform.load(value2, "get")));
80
- }
81
- if (asType === "value") {
82
- return transformed.length > 0 ? transformed[0] : isLastSyncMode && lastSync && value || null;
83
- } else {
84
- const results = transformed.map(
85
- (result) => result[fieldDeleted] || result.__deleted ? internal.symbolDelete : result
86
- );
87
- const out = asType === "array" ? [] : asMap ? /* @__PURE__ */ new Map() : {};
88
- for (let i = 0; i < results.length; i++) {
89
- let result = results[i];
90
- result = result[fieldDeleted] || result.__deleted ? internal.symbolDelete : result;
91
- if (asArray) {
92
- out.push(result);
93
- } else if (asMap) {
94
- out.set(result.id, result);
95
- } else {
96
- out[result.id] = result;
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);
97
109
  }
98
110
  }
99
- return out;
100
- }
101
- } else if (getFn) {
102
- const data = await getFn(getParams);
103
- let transformed = data;
104
- if (data) {
105
- const newLastSync = data[fieldUpdatedAt] || data[fieldCreatedAt];
106
- if (newLastSync && newLastSync !== lastSync) {
107
- updateLastSync(newLastSync);
108
- }
111
+ let transformed = data;
109
112
  if (transform == null ? void 0 : transform.load) {
110
- transformed = await transform.load(data, "get");
113
+ transformed = transformRows(data);
111
114
  }
112
- }
113
- return transformed;
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);
126
+ }
127
+ if (transform == null ? void 0 : transform.load) {
128
+ transformed = transform.load(data, "get");
129
+ }
130
+ }
131
+ return transformed;
132
+ };
133
+ return isPromise(dataPromise) ? dataPromise.then(processData) : processData(dataPromise);
114
134
  }
115
135
  } : void 0;
116
136
  const set = createFn || updateFn || deleteFn ? async (params) => {
117
- const { value, changes, update, retryAsCreate, valuePrevious, node } = params;
137
+ const { value, changes, update, retryAsCreate, node } = params;
118
138
  const creates = /* @__PURE__ */ new Map();
119
139
  const updates = /* @__PURE__ */ new Map();
120
140
  const deletes = /* @__PURE__ */ new Set();
121
- changes.forEach(({ path, prevAtPath, valueAtPath }) => {
141
+ const getUpdateValue = (itemValue, prev) => {
142
+ return updatePartial ? Object.assign(
143
+ diffObjects(
144
+ prev,
145
+ itemValue,
146
+ /*deep*/
147
+ true
148
+ ),
149
+ itemValue[fieldId] ? { [fieldId]: itemValue[fieldId] } : {}
150
+ ) : itemValue;
151
+ };
152
+ changes.forEach((change) => {
153
+ const { path, prevAtPath, valueAtPath, pathTypes } = change;
122
154
  if (asType === "value") {
123
155
  if (value) {
124
- let id = value == null ? void 0 : value.id;
125
- const isCreate = fieldCreatedAt ? !value[fieldCreatedAt] : !prevAtPath;
156
+ let id = value == null ? void 0 : value[fieldId];
157
+ let isCreate = fieldCreatedAt ? !value[fieldCreatedAt] : !prevAtPath;
126
158
  if (!id && generateId) {
127
- id = ensureId(value, generateId);
159
+ id = ensureId(value, fieldId, generateId);
128
160
  }
129
161
  if (id) {
162
+ if (pendingCreates.has(id)) {
163
+ isCreate = false;
164
+ }
130
165
  if (isCreate || retryAsCreate) {
131
- creates.set(id, value);
166
+ if (createFn) {
167
+ creates.set(id, value);
168
+ } else {
169
+ console.warn("[legend-state] missing create function");
170
+ }
132
171
  } else if (path.length === 0) {
133
172
  if (valueAtPath) {
134
- updates.set(id, valueAtPath);
173
+ updates.set(id, getUpdateValue(valueAtPath, prevAtPath));
135
174
  } else if (prevAtPath) {
136
- deletes.add(prevAtPath == null ? void 0 : prevAtPath.id);
175
+ deletes.add(prevAtPath);
137
176
  }
138
- } else {
139
- updates.set(id, Object.assign(updates.get(id) || { id }, value));
177
+ } else if (!updates.has(id)) {
178
+ const previous = applyChanges(
179
+ clone(value),
180
+ changes,
181
+ /*applyPrevious*/
182
+ true
183
+ );
184
+ updates.set(id, getUpdateValue(value, previous));
140
185
  }
141
186
  } else {
142
187
  console.error("[legend-state]: added synced item without an id");
143
188
  }
144
189
  } else if (path.length === 0) {
145
- const id = prevAtPath == null ? void 0 : prevAtPath.id;
146
- if (id) {
147
- deletes.add(id);
148
- }
190
+ deletes.add(prevAtPath);
149
191
  }
150
192
  } else {
151
- let itemsChanged = void 0;
193
+ let itemsChanged = [];
152
194
  if (path.length === 0) {
153
- itemsChanged = (asMap ? Array.from(valueAtPath.entries()) : Object.entries(valueAtPath)).filter(([key, value2]) => {
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];
154
198
  const prev = asMap ? prevAtPath.get(key) : prevAtPath[key];
155
- const isDiff = !prevAtPath || !deepEqual(value2, prev);
156
- return isDiff;
157
- });
199
+ if (isNullOrUndefined(value2) && !isNullOrUndefined(prev)) {
200
+ deletes.add(prev);
201
+ return false;
202
+ } else {
203
+ const isDiff = !prevAtPath || !deepEqual(value2, prev);
204
+ if (isDiff) {
205
+ itemsChanged.push([getUpdateValue(value2, prev), prev]);
206
+ }
207
+ }
208
+ }
158
209
  } else {
159
210
  const itemKey = path[0];
160
211
  const itemValue = asMap ? value.get(itemKey) : value[itemKey];
161
212
  if (!itemValue) {
162
213
  if (path.length === 1 && prevAtPath) {
163
- deletes.add(itemKey);
214
+ deletes.add(prevAtPath);
164
215
  }
165
216
  } else {
166
- itemsChanged = [[itemKey, itemValue]];
217
+ const previous = setAtPath(
218
+ clone(itemValue),
219
+ path.slice(1),
220
+ pathTypes.slice(1),
221
+ prevAtPath
222
+ );
223
+ itemsChanged = [[getUpdateValue(itemValue, previous), previous]];
167
224
  }
168
225
  }
169
- itemsChanged == null ? void 0 : itemsChanged.forEach(([itemKey, item]) => {
170
- if (isNullOrUndefined(item)) {
171
- deletes.add(itemKey);
226
+ itemsChanged == null ? void 0 : itemsChanged.forEach(([item, prev]) => {
227
+ 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
+ if (isCreate) {
229
+ if (generateId) {
230
+ ensureId(item, fieldId, generateId);
231
+ }
232
+ if (!item[fieldId]) {
233
+ console.error("[legend-state]: added item without an id");
234
+ }
235
+ if (createFn) {
236
+ pendingCreates.add(item[fieldId]);
237
+ creates.set(item[fieldId], item);
238
+ } else {
239
+ console.warn("[legend-state] missing create function");
240
+ }
172
241
  } else {
173
- const prev = asMap ? valuePrevious.get(itemKey) : valuePrevious[itemKey];
174
- const isCreate = fieldCreatedAt ? !item[fieldCreatedAt] : fieldUpdatedAt ? !item[fieldUpdatedAt] : isNullOrUndefined(prev);
175
- if (isCreate) {
176
- if (generateId) {
177
- ensureId(item, generateId);
178
- }
179
- if (!item.id) {
180
- console.error("[legend-state]: added item without an id");
181
- }
182
- if (createFn) {
183
- creates.set(item.id, item);
184
- } else {
185
- console.log("[legend-state] missing create function");
186
- }
242
+ if (updateFn) {
243
+ updates.set(
244
+ item[fieldId],
245
+ updates.has(item[fieldId]) ? Object.assign(updates.get(item[fieldId]), item) : item
246
+ );
187
247
  } else {
188
- if (updateFn) {
189
- updates.set(item.id, item);
190
- } else {
191
- console.log("[legend-state] missing update function");
192
- }
248
+ console.warn("[legend-state] missing update function");
193
249
  }
194
250
  }
195
251
  });
196
252
  }
197
253
  });
198
254
  const saveResult = async (itemKey, input, data, isCreate) => {
199
- if (data && (onSaved || onSavedUpdate)) {
200
- const saved = (transform == null ? void 0 : transform.load) ? transform.load(data, "set") : data;
255
+ var _a;
256
+ if (data) {
257
+ const saved = (transform == null ? void 0 : transform.load) ? await transform.load(data, "set") : data;
201
258
  const isChild = itemKey !== "undefined" && asType !== "value";
202
259
  const currentPeeked = getNodeValue(node);
203
- const currentValue = isChild ? currentPeeked == null ? void 0 : currentPeeked[itemKey] : currentPeeked;
260
+ const currentValue = isChild ? (_a = asType === "array" && isArray(currentPeeked) ? currentPeeked.find((v) => v[fieldId] === itemKey) : void 0) != null ? _a : currentPeeked[itemKey] : currentPeeked;
204
261
  const dataOnSaved = {
205
262
  saved,
206
263
  input,
@@ -208,17 +265,26 @@ function syncedCrud(props) {
208
265
  isCreate,
209
266
  props
210
267
  };
211
- let savedOut = void 0;
212
- if (onSavedUpdate) {
213
- savedOut = onSavedCreatedUpdatedAt(onSavedUpdate, dataOnSaved);
214
- }
215
- if (onSaved) {
216
- const ret = onSaved(dataOnSaved);
217
- if (ret) {
218
- savedOut = ret;
268
+ let savedOut = saved;
269
+ if (savedOut && !isNullOrUndefined(currentValue)) {
270
+ savedOut = clone(savedOut);
271
+ Object.keys(savedOut).forEach((key) => {
272
+ const i = input[key];
273
+ const c = currentValue[key];
274
+ if (
275
+ // value is already the new value, can ignore
276
+ savedOut[key] === c || // user has changed local value
277
+ key !== "id" && i !== c
278
+ ) {
279
+ delete savedOut[key];
280
+ }
281
+ });
282
+ if (onSaved) {
283
+ const ret = onSaved(dataOnSaved);
284
+ if (ret) {
285
+ savedOut = ret;
286
+ }
219
287
  }
220
- }
221
- if (savedOut) {
222
288
  const createdAt = fieldCreatedAt ? savedOut[fieldCreatedAt] : void 0;
223
289
  const updatedAt = fieldUpdatedAt ? savedOut[fieldUpdatedAt] : void 0;
224
290
  const value2 = itemKey !== "undefined" && asType !== "value" ? { [itemKey]: savedOut } : savedOut;
@@ -231,38 +297,73 @@ function syncedCrud(props) {
231
297
  }
232
298
  };
233
299
  return Promise.all([
234
- ...Array.from(creates).map(([itemKey, itemValue]) => {
235
- const createObj = transformOut(itemValue, transform == null ? void 0 : transform.save);
236
- return createFn(createObj, params).then(
237
- (result) => saveResult(itemKey, createObj, result, true)
238
- );
300
+ ...Array.from(creates).map(async ([itemKey, itemValue]) => {
301
+ if (waitForSetParam) {
302
+ await waitForSet(waitForSetParam, changes, itemValue, { type: "create" });
303
+ }
304
+ 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(() => {
308
+ pendingCreates.delete(itemKey);
309
+ });
239
310
  }),
240
- ...Array.from(updates).map(([itemKey, itemValue]) => {
241
- const toSave = updatePartial ? Object.assign(
242
- diffObjects(asType === "value" ? valuePrevious : valuePrevious[itemKey], itemValue),
243
- { id: itemValue.id }
244
- ) : itemValue;
245
- const changed = transformOut(toSave, transform == null ? void 0 : transform.save);
311
+ ...Array.from(updates).map(async ([itemKey, itemValue]) => {
312
+ if (waitForSetParam) {
313
+ await waitForSet(waitForSetParam, changes, itemValue, { type: "update" });
314
+ }
315
+ const toSave = itemValue;
316
+ const changed = await transformOut(toSave, transform == null ? void 0 : transform.save);
246
317
  if (Object.keys(changed).length > 0) {
247
318
  return updateFn(changed, params).then(
248
319
  (result) => result && saveResult(itemKey, changed, result, false)
249
320
  );
250
321
  }
251
322
  }),
252
- ...Array.from(deletes).map((id) => {
253
- if (deleteFn) {
254
- deleteFn({ id }, params);
255
- } else if (fieldDeleted && updateFn) {
256
- updateFn({ id, [fieldDeleted]: true }, params);
257
- } else {
258
- console.log("[legend-state] missing delete function");
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
+ }
259
340
  }
260
341
  })
261
342
  ]);
262
343
  } : void 0;
344
+ const subscribe = subscribeProp ? (params) => subscribeProp({
345
+ ...params,
346
+ update: async (paramsUpdate) => {
347
+ const paramsForUpdate = paramsUpdate;
348
+ const rows = paramsUpdate.value;
349
+ if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") {
350
+ if (!isArray(rows)) {
351
+ console.error("[legend-state] subscribe:update expects an array of changed items");
352
+ }
353
+ }
354
+ const newLastSync = computeLastSync(rows, fieldUpdatedAt, fieldCreatedAt);
355
+ if (newLastSync) {
356
+ paramsForUpdate.lastSync = newLastSync;
357
+ }
358
+ const rowsTransformed = (transform == null ? void 0 : transform.load) ? await transformRows(rows) : rows;
359
+ paramsForUpdate.value = resultsToOutType(rowsTransformed);
360
+ params.update(paramsForUpdate);
361
+ }
362
+ }) : void 0;
263
363
  return synced({
264
364
  set,
265
365
  get,
366
+ subscribe,
266
367
  mode: modeParam,
267
368
  ...rest
268
369
  });
@@ -18,15 +18,19 @@ function syncedFetch(props) {
18
18
  } = props;
19
19
  const get = async () => {
20
20
  const url = state.computeSelector(getParam);
21
- const response = await fetch(url, getInit);
22
- if (!response.ok) {
23
- throw new Error(response.statusText);
24
- }
25
- let value = await response[valueType || "json"]();
26
- if (transform == null ? void 0 : transform.load) {
27
- value = transform == null ? void 0 : transform.load(value, "get");
21
+ if (url && state.isString(url)) {
22
+ const response = await fetch(url, getInit);
23
+ if (!response.ok) {
24
+ throw new Error(response.statusText);
25
+ }
26
+ let value = await response[valueType || "json"]();
27
+ if (transform == null ? void 0 : transform.load) {
28
+ value = transform == null ? void 0 : transform.load(value, "get");
29
+ }
30
+ return value;
31
+ } else {
32
+ return null;
28
33
  }
29
- return value;
30
34
  };
31
35
  let set = void 0;
32
36
  if (setParam) {
@@ -1,4 +1,4 @@
1
- import { computeSelector, getNodeValue } from '@legendapp/state';
1
+ import { computeSelector, isString, getNodeValue } from '@legendapp/state';
2
2
  import { synced } from '@legendapp/state/sync';
3
3
 
4
4
  // src/sync-plugins/fetch.ts
@@ -16,15 +16,19 @@ function syncedFetch(props) {
16
16
  } = props;
17
17
  const get = async () => {
18
18
  const url = computeSelector(getParam);
19
- const response = await fetch(url, getInit);
20
- if (!response.ok) {
21
- throw new Error(response.statusText);
22
- }
23
- let value = await response[valueType || "json"]();
24
- if (transform == null ? void 0 : transform.load) {
25
- value = transform == null ? void 0 : transform.load(value, "get");
19
+ if (url && isString(url)) {
20
+ const response = await fetch(url, getInit);
21
+ if (!response.ok) {
22
+ throw new Error(response.statusText);
23
+ }
24
+ let value = await response[valueType || "json"]();
25
+ if (transform == null ? void 0 : transform.load) {
26
+ value = transform == null ? void 0 : transform.load(value, "get");
27
+ }
28
+ return value;
29
+ } else {
30
+ return null;
26
31
  }
27
- return value;
28
32
  };
29
33
  let set = void 0;
30
34
  if (setParam) {
@@ -0,0 +1,27 @@
1
+ import { FieldTransforms } from '@legendapp/state/sync';
2
+ export { FieldTransforms } from '@legendapp/state/sync';
3
+ import { CrudAsOption, SyncedCrudPropsMany, SyncedCrudPropsBase, SyncedCrudReturnType } from '@legendapp/state/sync-plugins/crud';
4
+ import { DatabaseReference, Query } from 'firebase/database';
5
+
6
+ declare function transformObjectFields(dataIn: Record<string, any>, map: Record<string, any>): any;
7
+ declare function invertFieldMap(obj: Record<string, any>): any;
8
+
9
+ interface SyncedFirebaseProps<TRemote extends object, TLocal, TAs extends CrudAsOption = 'value'> extends Omit<SyncedCrudPropsMany<TRemote, TLocal, TAs>, 'list' | 'retry'>, SyncedCrudPropsBase<TRemote, TLocal> {
10
+ refPath: (uid: string | undefined) => string;
11
+ query?: (ref: DatabaseReference) => DatabaseReference | Query;
12
+ fieldId?: string;
13
+ fieldTransforms?: FieldTransforms<TRemote>;
14
+ realtime?: boolean;
15
+ requireAuth?: boolean;
16
+ readonly?: boolean;
17
+ }
18
+ interface SyncedFirebaseConfiguration {
19
+ realtime?: boolean;
20
+ requireAuth?: boolean;
21
+ readonly?: boolean;
22
+ enabled?: boolean;
23
+ }
24
+ declare function configureSyncedFirebase(config: SyncedFirebaseConfiguration): void;
25
+ declare function syncedFirebase<TRemote extends object, TLocal = TRemote, TAs extends CrudAsOption = 'object'>(props: SyncedFirebaseProps<TRemote, TLocal, TAs>): SyncedCrudReturnType<TLocal, TAs>;
26
+
27
+ export { type SyncedFirebaseProps, configureSyncedFirebase, invertFieldMap, syncedFirebase, transformObjectFields };
@@ -0,0 +1,27 @@
1
+ import { FieldTransforms } from '@legendapp/state/sync';
2
+ export { FieldTransforms } from '@legendapp/state/sync';
3
+ import { CrudAsOption, SyncedCrudPropsMany, SyncedCrudPropsBase, SyncedCrudReturnType } from '@legendapp/state/sync-plugins/crud';
4
+ import { DatabaseReference, Query } from 'firebase/database';
5
+
6
+ declare function transformObjectFields(dataIn: Record<string, any>, map: Record<string, any>): any;
7
+ declare function invertFieldMap(obj: Record<string, any>): any;
8
+
9
+ interface SyncedFirebaseProps<TRemote extends object, TLocal, TAs extends CrudAsOption = 'value'> extends Omit<SyncedCrudPropsMany<TRemote, TLocal, TAs>, 'list' | 'retry'>, SyncedCrudPropsBase<TRemote, TLocal> {
10
+ refPath: (uid: string | undefined) => string;
11
+ query?: (ref: DatabaseReference) => DatabaseReference | Query;
12
+ fieldId?: string;
13
+ fieldTransforms?: FieldTransforms<TRemote>;
14
+ realtime?: boolean;
15
+ requireAuth?: boolean;
16
+ readonly?: boolean;
17
+ }
18
+ interface SyncedFirebaseConfiguration {
19
+ realtime?: boolean;
20
+ requireAuth?: boolean;
21
+ readonly?: boolean;
22
+ enabled?: boolean;
23
+ }
24
+ declare function configureSyncedFirebase(config: SyncedFirebaseConfiguration): void;
25
+ declare function syncedFirebase<TRemote extends object, TLocal = TRemote, TAs extends CrudAsOption = 'object'>(props: SyncedFirebaseProps<TRemote, TLocal, TAs>): SyncedCrudReturnType<TLocal, TAs>;
26
+
27
+ export { type SyncedFirebaseProps, configureSyncedFirebase, invertFieldMap, syncedFirebase, transformObjectFields };