@legendapp/state 3.0.0-alpha.3 → 3.0.0-alpha.30

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