@legendapp/state 3.0.0-alpha.9 → 3.0.0-beta.1

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 +103 -79
  17. package/index.d.ts +103 -79
  18. package/index.js +326 -316
  19. package/index.mjs +323 -314
  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 -23
  58. package/sync-plugins/crud.d.ts +21 -23
  59. package/sync-plugins/crud.js +224 -112
  60. package/sync-plugins/crud.mjs +226 -114
  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 -99
  70. package/sync-plugins/keel.mjs +147 -99
  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 -21
  74. package/sync-plugins/supabase.mjs +53 -22
  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 +498 -281
  86. package/sync.mjs +503 -286
@@ -1,16 +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];
15
+ }
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
+ }
23
+ }
24
+ return newLastSync;
14
25
  }
15
26
  function syncedCrud(props) {
16
27
  const {
@@ -20,167 +31,233 @@ function syncedCrud(props) {
20
31
  update: updateFn,
21
32
  delete: deleteFn,
22
33
  transform,
34
+ fieldId: fieldIdProp,
23
35
  fieldCreatedAt,
24
36
  fieldUpdatedAt,
25
37
  fieldDeleted,
38
+ fieldDeletedList,
26
39
  updatePartial,
40
+ subscribe: subscribeProp,
27
41
  onSaved,
28
42
  mode: modeParam,
29
43
  changesSince,
30
44
  generateId,
45
+ waitForSet: waitForSetParam,
31
46
  ...rest
32
47
  } = props;
48
+ const fieldId = fieldIdProp || "id";
49
+ const pendingCreates = /* @__PURE__ */ new Set();
33
50
  let asType = props.as;
34
51
  if (!asType) {
35
52
  asType = getFn ? "value" : "object";
36
53
  }
37
54
  const asMap = asType === "Map";
38
55
  const asArray = asType === "array";
39
- 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) => {
40
88
  const { updateLastSync, lastSync, value } = getParams;
41
89
  if (listFn) {
42
90
  const isLastSyncMode = changesSince === "last-sync";
43
91
  if (isLastSyncMode && lastSync) {
44
92
  getParams.mode = modeParam || (asType === "array" ? "append" : asType === "value" ? "set" : "assign");
45
93
  }
46
- const data = await listFn(getParams) || [];
47
- let newLastSync = 0;
48
- for (let i = 0; i < data.length; i++) {
49
- const updated = data[i][fieldUpdatedAt] || data[i][fieldCreatedAt];
50
- if (updated) {
51
- 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);
52
101
  }
53
- }
54
- if (newLastSync && newLastSync !== lastSync) {
55
- updateLastSync(newLastSync);
56
- }
57
- let transformed = data;
58
- if (transform == null ? void 0 : transform.load) {
59
- transformed = await Promise.all(data.map((value2) => transform.load(value2, "get")));
60
- }
61
- if (asType === "value") {
62
- return transformed.length > 0 ? transformed[0] : isLastSyncMode && lastSync && value || null;
63
- } else {
64
- const results = transformed.map(
65
- (result) => result[fieldDeleted] || result.__deleted ? internal.symbolDelete : result
66
- );
67
- const out = asType === "array" ? [] : asMap ? /* @__PURE__ */ new Map() : {};
68
- for (let i = 0; i < results.length; i++) {
69
- let result = results[i];
70
- result = result[fieldDeleted] || result.__deleted ? internal.symbolDelete : result;
71
- if (asArray) {
72
- out.push(result);
73
- } else if (asMap) {
74
- out.set(result.id, result);
75
- } else {
76
- 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);
77
109
  }
78
110
  }
79
- return out;
80
- }
81
- } else if (getFn) {
82
- const data = await getFn(getParams);
83
- let transformed = data;
84
- if (data) {
85
- const newLastSync = data[fieldUpdatedAt] || data[fieldCreatedAt];
86
- if (newLastSync && newLastSync !== lastSync) {
87
- updateLastSync(newLastSync);
88
- }
111
+ let transformed = data;
89
112
  if (transform == null ? void 0 : transform.load) {
90
- transformed = await transform.load(data, "get");
113
+ transformed = transformRows(data);
91
114
  }
92
- }
93
- 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);
94
134
  }
95
135
  } : void 0;
96
136
  const set = createFn || updateFn || deleteFn ? async (params) => {
97
- const { value, changes, update, retryAsCreate, valuePrevious, node } = params;
137
+ const { value, changes, update, retryAsCreate, node } = params;
98
138
  const creates = /* @__PURE__ */ new Map();
99
139
  const updates = /* @__PURE__ */ new Map();
100
140
  const deletes = /* @__PURE__ */ new Set();
101
- 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;
102
154
  if (asType === "value") {
103
155
  if (value) {
104
- let id = value == null ? void 0 : value.id;
105
- const isCreate = fieldCreatedAt ? !value[fieldCreatedAt] : !prevAtPath;
156
+ let id = value == null ? void 0 : value[fieldId];
157
+ let isCreate = fieldCreatedAt ? !value[fieldCreatedAt] : !prevAtPath;
106
158
  if (!id && generateId) {
107
- id = ensureId(value, generateId);
159
+ id = ensureId(value, fieldId, generateId);
108
160
  }
109
161
  if (id) {
162
+ if (pendingCreates.has(id)) {
163
+ isCreate = false;
164
+ }
110
165
  if (isCreate || retryAsCreate) {
111
- creates.set(id, value);
166
+ if (createFn) {
167
+ creates.set(id, value);
168
+ } else {
169
+ console.warn("[legend-state] missing create function");
170
+ }
112
171
  } else if (path.length === 0) {
113
172
  if (valueAtPath) {
114
- updates.set(id, valueAtPath);
173
+ updates.set(id, getUpdateValue(valueAtPath, prevAtPath));
115
174
  } else if (prevAtPath) {
116
- deletes.add(prevAtPath == null ? void 0 : prevAtPath.id);
175
+ deletes.add(prevAtPath);
117
176
  }
118
- } else {
119
- 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));
120
185
  }
121
186
  } else {
122
187
  console.error("[legend-state]: added synced item without an id");
123
188
  }
124
189
  } else if (path.length === 0) {
125
- const id = prevAtPath == null ? void 0 : prevAtPath.id;
126
- if (id) {
127
- deletes.add(id);
128
- }
190
+ deletes.add(prevAtPath);
129
191
  }
130
192
  } else {
131
- let itemsChanged = void 0;
193
+ let itemsChanged = [];
132
194
  if (path.length === 0) {
133
- 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];
134
198
  const prev = asMap ? prevAtPath.get(key) : prevAtPath[key];
135
- const isDiff = !prevAtPath || !deepEqual(value2, prev);
136
- return isDiff;
137
- });
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
+ }
138
209
  } else {
139
210
  const itemKey = path[0];
140
211
  const itemValue = asMap ? value.get(itemKey) : value[itemKey];
141
212
  if (!itemValue) {
142
213
  if (path.length === 1 && prevAtPath) {
143
- deletes.add(itemKey);
214
+ deletes.add(prevAtPath);
144
215
  }
145
216
  } else {
146
- 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]];
147
224
  }
148
225
  }
149
- itemsChanged == null ? void 0 : itemsChanged.forEach(([itemKey, item]) => {
150
- if (isNullOrUndefined(item)) {
151
- 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
+ }
152
241
  } else {
153
- const prev = asMap ? valuePrevious.get(itemKey) : valuePrevious[itemKey];
154
- const isCreate = fieldCreatedAt ? !item[fieldCreatedAt] : fieldUpdatedAt ? !item[fieldUpdatedAt] : isNullOrUndefined(prev);
155
- if (isCreate) {
156
- if (generateId) {
157
- ensureId(item, generateId);
158
- }
159
- if (!item.id) {
160
- console.error("[legend-state]: added item without an id");
161
- }
162
- if (createFn) {
163
- creates.set(item.id, item);
164
- } else {
165
- console.log("[legend-state] missing create function");
166
- }
242
+ if (updateFn) {
243
+ updates.set(
244
+ item[fieldId],
245
+ updates.has(item[fieldId]) ? Object.assign(updates.get(item[fieldId]), item) : item
246
+ );
167
247
  } else {
168
- if (updateFn) {
169
- updates.set(item.id, item);
170
- } else {
171
- console.log("[legend-state] missing update function");
172
- }
248
+ console.warn("[legend-state] missing update function");
173
249
  }
174
250
  }
175
251
  });
176
252
  }
177
253
  });
178
254
  const saveResult = async (itemKey, input, data, isCreate) => {
255
+ var _a;
179
256
  if (data) {
180
- const saved = (transform == null ? void 0 : transform.load) ? transform.load(data, "set") : data;
257
+ const saved = (transform == null ? void 0 : transform.load) ? await transform.load(data, "set") : data;
181
258
  const isChild = itemKey !== "undefined" && asType !== "value";
182
259
  const currentPeeked = getNodeValue(node);
183
- 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;
184
261
  const dataOnSaved = {
185
262
  saved,
186
263
  input,
@@ -189,7 +266,7 @@ function syncedCrud(props) {
189
266
  props
190
267
  };
191
268
  let savedOut = saved;
192
- if (savedOut) {
269
+ if (savedOut && !isNullOrUndefined(currentValue)) {
193
270
  savedOut = clone(savedOut);
194
271
  Object.keys(savedOut).forEach((key) => {
195
272
  const i = input[key];
@@ -220,38 +297,73 @@ function syncedCrud(props) {
220
297
  }
221
298
  };
222
299
  return Promise.all([
223
- ...Array.from(creates).map(([itemKey, itemValue]) => {
224
- const createObj = transformOut(itemValue, transform == null ? void 0 : transform.save);
225
- return createFn(createObj, params).then(
226
- (result) => saveResult(itemKey, createObj, result, true)
227
- );
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
+ });
228
310
  }),
229
- ...Array.from(updates).map(([itemKey, itemValue]) => {
230
- const toSave = updatePartial ? Object.assign(
231
- diffObjects(asType === "value" ? valuePrevious : valuePrevious[itemKey], itemValue),
232
- { id: itemValue.id }
233
- ) : itemValue;
234
- 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);
235
317
  if (Object.keys(changed).length > 0) {
236
318
  return updateFn(changed, params).then(
237
319
  (result) => result && saveResult(itemKey, changed, result, false)
238
320
  );
239
321
  }
240
322
  }),
241
- ...Array.from(deletes).map((id) => {
242
- if (deleteFn) {
243
- deleteFn({ id }, params);
244
- } else if (fieldDeleted && updateFn) {
245
- updateFn({ id, [fieldDeleted]: true }, params);
246
- } else {
247
- 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
+ }
248
340
  }
249
341
  })
250
342
  ]);
251
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;
252
363
  return synced({
253
364
  set,
254
365
  get,
366
+ subscribe,
255
367
  mode: modeParam,
256
368
  ...rest
257
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 };