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

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/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 +2066 -0
  17. package/config.mjs +2064 -0
  18. package/helpers/trackHistory.js +2 -2
  19. package/helpers/trackHistory.mjs +2 -2
  20. package/index.d.mts +25 -302
  21. package/index.d.ts +25 -302
  22. package/index.js +293 -322
  23. package/index.mjs +294 -321
  24. package/observableInterfaces-CZR3_8mM.d.mts +283 -0
  25. package/observableInterfaces-CZR3_8mM.d.ts +283 -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/crud.d.mts +21 -24
  44. package/sync-plugins/crud.d.ts +21 -24
  45. package/sync-plugins/crud.js +240 -139
  46. package/sync-plugins/crud.mjs +242 -141
  47. package/sync-plugins/fetch.js +12 -8
  48. package/sync-plugins/fetch.mjs +13 -9
  49. package/sync-plugins/firebase.d.mts +27 -0
  50. package/sync-plugins/firebase.d.ts +27 -0
  51. package/sync-plugins/firebase.js +373 -0
  52. package/sync-plugins/firebase.mjs +368 -0
  53. package/sync-plugins/keel.d.mts +27 -10
  54. package/sync-plugins/keel.d.ts +27 -10
  55. package/sync-plugins/keel.js +51 -32
  56. package/sync-plugins/keel.mjs +52 -33
  57. package/sync-plugins/supabase.d.mts +12 -7
  58. package/sync-plugins/supabase.d.ts +12 -7
  59. package/sync-plugins/supabase.js +24 -13
  60. package/sync-plugins/supabase.mjs +25 -14
  61. package/sync-plugins/tanstack-query.d.mts +2 -2
  62. package/sync-plugins/tanstack-query.d.ts +2 -2
  63. package/sync-plugins/tanstack-query.js +3 -2
  64. package/sync-plugins/tanstack-query.mjs +3 -2
  65. package/sync-plugins/tanstack-react-query.d.mts +1 -1
  66. package/sync-plugins/tanstack-react-query.d.ts +1 -1
  67. package/sync.d.mts +71 -197
  68. package/sync.d.ts +71 -197
  69. package/sync.js +465 -284
  70. package/sync.mjs +471 -290
  71. package/types/babel.d.ts +12 -1
  72. package/.DS_Store +0 -0
  73. /package/config/{enable_GetSet.d.mts → enable$GetSet.d.mts} +0 -0
  74. /package/config/{enable_GetSet.d.ts → enable$GetSet.d.ts} +0 -0
@@ -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, isObservable, 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,165 +31,230 @@ 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 isObs = isObservable(result);
64
+ const value = isObs ? result.peek() : result;
65
+ if (value) {
66
+ result = !isObs && (fieldDeleted && result[fieldDeleted] || fieldDeletedList && result[fieldDeletedList] || result[symbolDelete]) ? internal.symbolDelete : result;
67
+ if (asArray) {
68
+ out.push(result);
69
+ } else if (asMap) {
70
+ out.set(value[fieldId], result);
71
+ } else {
72
+ out[value[fieldId]] = result;
73
+ }
74
+ }
75
+ }
76
+ return out;
77
+ };
78
+ const transformRows = (data) => {
79
+ return Promise.all(
80
+ data.map(
81
+ (value) => (
82
+ // Skip transforming any children with symbolDelete or fieldDeleted because they'll get deleted by resultsToOutType
83
+ value[symbolDelete] || fieldDeleted && value[fieldDeleted] || fieldDeletedList && value[fieldDeletedList] ? value : transform.load(value, "get")
84
+ )
85
+ )
86
+ );
87
+ };
88
+ const get = getFn || listFn ? (getParams) => {
60
89
  const { updateLastSync, lastSync, value } = getParams;
61
90
  if (listFn) {
62
91
  const isLastSyncMode = changesSince === "last-sync";
63
92
  if (isLastSyncMode && lastSync) {
64
93
  getParams.mode = modeParam || (asType === "array" ? "append" : asType === "value" ? "set" : "assign");
65
94
  }
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));
95
+ const listPromise = listFn(getParams);
96
+ const toOut = (transformed) => {
97
+ var _a;
98
+ if (asType === "value") {
99
+ return transformed.length > 0 ? transformed[0] : (_a = (isLastSyncMode && lastSync || fieldDeleted) && value) != null ? _a : null;
100
+ } else {
101
+ return resultsToOutType(transformed);
72
102
  }
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;
103
+ };
104
+ const processResults = (data) => {
105
+ data || (data = []);
106
+ if (fieldUpdatedAt) {
107
+ const newLastSync = computeLastSync(data, fieldUpdatedAt, fieldCreatedAt);
108
+ if (newLastSync && newLastSync !== lastSync) {
109
+ updateLastSync(newLastSync);
97
110
  }
98
111
  }
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
- }
112
+ let transformed = data;
109
113
  if (transform == null ? void 0 : transform.load) {
110
- transformed = await transform.load(data, "get");
114
+ transformed = transformRows(data);
111
115
  }
112
- }
113
- return transformed;
116
+ return isPromise(transformed) ? transformed.then(toOut) : toOut(transformed);
117
+ };
118
+ return isPromise(listPromise) ? listPromise.then(processResults) : processResults(listPromise);
119
+ } else if (getFn) {
120
+ const dataPromise = getFn(getParams);
121
+ const processData = (data) => {
122
+ let transformed = data;
123
+ if (data) {
124
+ const newLastSync = data[fieldUpdatedAt] || data[fieldCreatedAt];
125
+ if (newLastSync && newLastSync !== lastSync) {
126
+ updateLastSync(newLastSync);
127
+ }
128
+ if (transform == null ? void 0 : transform.load) {
129
+ transformed = transform.load(data, "get");
130
+ }
131
+ }
132
+ return transformed;
133
+ };
134
+ return isPromise(dataPromise) ? dataPromise.then(processData) : processData(dataPromise);
114
135
  }
115
136
  } : void 0;
116
137
  const set = createFn || updateFn || deleteFn ? async (params) => {
117
- const { value, changes, update, retryAsCreate, valuePrevious, node } = params;
138
+ const { value, changes, update, retryAsCreate, node } = params;
118
139
  const creates = /* @__PURE__ */ new Map();
119
140
  const updates = /* @__PURE__ */ new Map();
120
141
  const deletes = /* @__PURE__ */ new Set();
121
- changes.forEach(({ path, prevAtPath, valueAtPath }) => {
142
+ const getUpdateValue = (itemValue, prev) => {
143
+ return updatePartial ? Object.assign(
144
+ diffObjects(
145
+ prev,
146
+ itemValue,
147
+ /*deep*/
148
+ true
149
+ ),
150
+ itemValue[fieldId] ? { [fieldId]: itemValue[fieldId] } : {}
151
+ ) : itemValue;
152
+ };
153
+ changes.forEach((change) => {
154
+ const { path, prevAtPath, valueAtPath, pathTypes } = change;
122
155
  if (asType === "value") {
123
156
  if (value) {
124
- let id = value == null ? void 0 : value.id;
125
- const isCreate = fieldCreatedAt ? !value[fieldCreatedAt] : !prevAtPath;
157
+ let id = value == null ? void 0 : value[fieldId];
158
+ let isCreate = fieldCreatedAt ? !value[fieldCreatedAt] : !prevAtPath;
126
159
  if (!id && generateId) {
127
- id = ensureId(value, generateId);
160
+ id = ensureId(value, fieldId, generateId);
128
161
  }
129
162
  if (id) {
163
+ if (pendingCreates.has(id)) {
164
+ isCreate = false;
165
+ }
130
166
  if (isCreate || retryAsCreate) {
131
- creates.set(id, value);
167
+ if (createFn) {
168
+ creates.set(id, value);
169
+ } else {
170
+ console.log("[legend-state] missing create function");
171
+ }
132
172
  } else if (path.length === 0) {
133
173
  if (valueAtPath) {
134
- updates.set(id, valueAtPath);
174
+ updates.set(id, getUpdateValue(valueAtPath, prevAtPath));
135
175
  } else if (prevAtPath) {
136
- deletes.add(prevAtPath == null ? void 0 : prevAtPath.id);
176
+ deletes.add(prevAtPath);
137
177
  }
138
- } else {
139
- updates.set(id, Object.assign(updates.get(id) || { id }, value));
178
+ } else if (!updates.has(id)) {
179
+ const previous = applyChanges(
180
+ clone(value),
181
+ changes,
182
+ /*applyPrevious*/
183
+ true
184
+ );
185
+ updates.set(id, getUpdateValue(value, previous));
140
186
  }
141
187
  } else {
142
188
  console.error("[legend-state]: added synced item without an id");
143
189
  }
144
190
  } else if (path.length === 0) {
145
- const id = prevAtPath == null ? void 0 : prevAtPath.id;
146
- if (id) {
147
- deletes.add(id);
148
- }
191
+ deletes.add(prevAtPath);
149
192
  }
150
193
  } else {
151
- let itemsChanged = void 0;
194
+ let itemsChanged = [];
152
195
  if (path.length === 0) {
153
- itemsChanged = (asMap ? Array.from(valueAtPath.entries()) : Object.entries(valueAtPath)).filter(([key, value2]) => {
196
+ const changed = asMap ? Array.from(valueAtPath.entries()) : Object.entries(valueAtPath);
197
+ for (let i = 0; i < changed.length; i++) {
198
+ const [key, value2] = changed[i];
154
199
  const prev = asMap ? prevAtPath.get(key) : prevAtPath[key];
155
- const isDiff = !prevAtPath || !deepEqual(value2, prev);
156
- return isDiff;
157
- });
200
+ if (isNullOrUndefined(value2) && !isNullOrUndefined(prev)) {
201
+ deletes.add(prev);
202
+ return false;
203
+ } else {
204
+ const isDiff = !prevAtPath || !deepEqual(value2, prev);
205
+ if (isDiff) {
206
+ itemsChanged.push([getUpdateValue(value2, prev), prev]);
207
+ }
208
+ }
209
+ }
158
210
  } else {
159
211
  const itemKey = path[0];
160
212
  const itemValue = asMap ? value.get(itemKey) : value[itemKey];
161
213
  if (!itemValue) {
162
214
  if (path.length === 1 && prevAtPath) {
163
- deletes.add(itemKey);
215
+ deletes.add(prevAtPath);
164
216
  }
165
217
  } else {
166
- itemsChanged = [[itemKey, itemValue]];
218
+ const previous = setAtPath(
219
+ clone(itemValue),
220
+ path.slice(1),
221
+ pathTypes.slice(1),
222
+ prevAtPath
223
+ );
224
+ itemsChanged = [[getUpdateValue(itemValue, previous), previous]];
167
225
  }
168
226
  }
169
- itemsChanged == null ? void 0 : itemsChanged.forEach(([itemKey, item]) => {
170
- if (isNullOrUndefined(item)) {
171
- deletes.add(itemKey);
227
+ itemsChanged == null ? void 0 : itemsChanged.forEach(([item, prev]) => {
228
+ const isCreate = !pendingCreates.has(item.id) && (fieldCreatedAt ? !item[fieldCreatedAt] && !(prev == null ? void 0 : prev[fieldCreatedAt]) : fieldUpdatedAt ? !item[fieldUpdatedAt] && !(prev == null ? void 0 : prev[fieldCreatedAt]) : isNullOrUndefined(prev));
229
+ if (isCreate) {
230
+ if (generateId) {
231
+ ensureId(item, fieldId, generateId);
232
+ }
233
+ if (!item.id) {
234
+ console.error("[legend-state]: added item without an id");
235
+ }
236
+ if (createFn) {
237
+ pendingCreates.add(item.id);
238
+ creates.set(item.id, item);
239
+ } else {
240
+ console.log("[legend-state] missing create function");
241
+ }
172
242
  } 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
- }
243
+ if (updateFn) {
244
+ updates.set(
245
+ item.id,
246
+ updates.has(item.id) ? Object.assign(updates.get(item.id), item) : item
247
+ );
187
248
  } else {
188
- if (updateFn) {
189
- updates.set(item.id, item);
190
- } else {
191
- console.log("[legend-state] missing update function");
192
- }
249
+ console.log("[legend-state] missing update function");
193
250
  }
194
251
  }
195
252
  });
196
253
  }
197
254
  });
198
255
  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;
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
260
  const currentValue = isChild ? currentPeeked == null ? void 0 : currentPeeked[itemKey] : currentPeeked;
@@ -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.log("[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 };