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