@legendapp/state 3.0.0-beta.47 → 3.0.0-beta.48

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.
package/react.js CHANGED
@@ -13,8 +13,17 @@ var React__default = /*#__PURE__*/_interopDefault(React);
13
13
 
14
14
  // src/react/react-globals.ts
15
15
  var reactGlobals = {
16
- inObserver: false
16
+ inObserver: false,
17
+ renderDepth: 0
17
18
  };
19
+ function runInRender(fn) {
20
+ reactGlobals.renderDepth++;
21
+ try {
22
+ return fn();
23
+ } finally {
24
+ reactGlobals.renderDepth--;
25
+ }
26
+ }
18
27
  var pauseContext = void 0;
19
28
  var getPauseContext = () => {
20
29
  return pauseContext || (pauseContext = React.createContext(null));
@@ -36,6 +45,22 @@ function createSelectorFunctions(options, isPaused$) {
36
45
  let _selector;
37
46
  let prev;
38
47
  let pendingUpdate = void 0;
48
+ let notifyQueued = false;
49
+ const scheduleNotify = () => {
50
+ if (notify) {
51
+ if (reactGlobals.renderDepth > 0) {
52
+ if (!notifyQueued) {
53
+ notifyQueued = true;
54
+ queueMicrotask(() => {
55
+ notifyQueued = false;
56
+ notify == null ? void 0 : notify();
57
+ });
58
+ }
59
+ } else {
60
+ notify();
61
+ }
62
+ }
63
+ };
39
64
  const run = () => {
40
65
  dispose == null ? void 0 : dispose();
41
66
  const {
@@ -79,7 +104,7 @@ function createSelectorFunctions(options, isPaused$) {
79
104
  }
80
105
  if (changed) {
81
106
  version++;
82
- notify == null ? void 0 : notify();
107
+ scheduleNotify();
83
108
  }
84
109
  }
85
110
  };
@@ -91,6 +116,7 @@ function createSelectorFunctions(options, isPaused$) {
91
116
  }
92
117
  return () => {
93
118
  dispose == null ? void 0 : dispose();
119
+ notify = void 0;
94
120
  dispose = void 0;
95
121
  };
96
122
  },
@@ -123,7 +149,7 @@ function useSelector(selector, options) {
123
149
  const isPaused$ = React.useContext(getPauseContext());
124
150
  const selectorFn = React.useMemo(() => createSelectorFunctions(options, isPaused$), []);
125
151
  const { subscribe, getVersion, run } = selectorFn;
126
- value = run(selector);
152
+ value = runInRender(() => run(selector));
127
153
  index_js.useSyncExternalStore(subscribe, getVersion, getVersion);
128
154
  } catch (err) {
129
155
  if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && ((_a = err == null ? void 0 : err.message) == null ? void 0 : _a.includes("Rendered more"))) {
@@ -175,50 +201,64 @@ function createReactiveComponent(component, observe3, reactive2, keysReactive, b
175
201
  apply(target, thisArg, argArray) {
176
202
  if (reactive2) {
177
203
  const props = argArray[0];
178
- const propsOut = {};
179
- const keys = Object.keys(props);
180
- for (let i = 0; i < keys.length; i++) {
181
- const key = keys[i];
182
- const p = props[key];
183
- const isReactiveKey = keysReactiveSet && keysReactiveSet.has(key);
184
- if (key === "children" && (state.isFunction(p) || state.isObservable(p))) {
185
- propsOut[key] = useSelector(p, { skipCheck: true });
186
- } else if (isReactiveKey || key.startsWith("$") || key.endsWith("$")) {
187
- if (process.env.NODE_ENV === "development" && !didWarnProps && key.endsWith("$")) {
188
- didWarnProps = true;
189
- console.warn(
190
- `[legend-state] Reactive props were changed to start with $ instead of end with $ in version 2.0. So please change ${key} to $${key.replace(
191
- "$",
192
- ""
193
- )}. See https://legendapp.com/open-source/state/migrating for more details.`
194
- );
195
- }
196
- const k = isReactiveKey ? key : key.endsWith("$") ? key.slice(0, -1) : key.slice(1);
197
- const bind = bindKeys == null ? void 0 : bindKeys[k];
198
- const shouldBind = bind && state.isObservable(p);
199
- propsOut[k] = shouldBind && (bind == null ? void 0 : bind.selector) ? bind.selector(propsOut, p) : useSelector(p);
200
- if (shouldBind) {
201
- if (bind.defaultValue !== void 0 && propsOut[k] === void 0) {
202
- propsOut[k] = bind.defaultValue;
203
- }
204
- if (bind.handler && bind.getValue) {
205
- const handlerFn = (e) => {
206
- var _a;
207
- p.set(bind.getValue(e));
208
- (_a = props[bind.handler]) == null ? void 0 : _a.call(props, e);
209
- };
210
- propsOut[bind.handler] = // If in development mode, don't memoize the handler. fix fast refresh bug
211
- process.env.NODE_ENV === "development" ? handlerFn : React.useCallback(handlerFn, [props[bind.handler], bindKeys]);
204
+ const bindHandlersRef = React.useRef({});
205
+ argArray[0] = useSelector(
206
+ () => {
207
+ const propsOut = {};
208
+ const keys = Object.keys(props);
209
+ for (let i = 0; i < keys.length; i++) {
210
+ const key = keys[i];
211
+ const p = props[key];
212
+ const isReactiveKey = keysReactiveSet && keysReactiveSet.has(key);
213
+ if (key === "children" && (state.isFunction(p) || state.isObservable(p))) {
214
+ propsOut[key] = state.computeSelector(p);
215
+ } else if (isReactiveKey || key.startsWith("$") || key.endsWith("$")) {
216
+ if (process.env.NODE_ENV === "development" && !didWarnProps && key.endsWith("$")) {
217
+ didWarnProps = true;
218
+ console.warn(
219
+ `[legend-state] Reactive props were changed to start with $ instead of end with $ in version 2.0. So please change ${key} to $${key.replace(
220
+ "$",
221
+ ""
222
+ )}. See https://legendapp.com/open-source/state/migrating for more details.`
223
+ );
224
+ }
225
+ const k = isReactiveKey ? key : key.endsWith("$") ? key.slice(0, -1) : key.slice(1);
226
+ const bind = bindKeys == null ? void 0 : bindKeys[k];
227
+ const shouldBind = bind && state.isObservable(p);
228
+ propsOut[k] = shouldBind && (bind == null ? void 0 : bind.selector) ? bind.selector(propsOut, p) : state.computeSelector(p);
229
+ if (shouldBind) {
230
+ if (bind.defaultValue !== void 0 && propsOut[k] === void 0) {
231
+ propsOut[k] = bind.defaultValue;
232
+ }
233
+ if (bind.handler && bind.getValue) {
234
+ const handlerKey = bind.handler;
235
+ const originalHandler = props[handlerKey];
236
+ let cached = bindHandlersRef.current[k];
237
+ if (!cached || cached.bind !== bind || cached.observable !== p || cached.originalHandler !== originalHandler) {
238
+ cached = bindHandlersRef.current[k] = {
239
+ bind,
240
+ observable: p,
241
+ originalHandler,
242
+ handler: (event) => {
243
+ p.set(bind.getValue(event));
244
+ originalHandler == null ? void 0 : originalHandler(event);
245
+ }
246
+ };
247
+ }
248
+ propsOut[handlerKey] = cached.handler;
249
+ }
250
+ }
251
+ if (!isReactiveKey) {
252
+ delete propsOut[key];
253
+ }
254
+ } else if (propsOut[key] === void 0) {
255
+ propsOut[key] = p;
212
256
  }
213
257
  }
214
- if (!isReactiveKey) {
215
- delete propsOut[key];
216
- }
217
- } else if (propsOut[key] === void 0) {
218
- propsOut[key] = p;
219
- }
220
- }
221
- argArray[0] = propsOut;
258
+ return propsOut;
259
+ },
260
+ { skipCheck: true }
261
+ );
222
262
  }
223
263
  if (observe3) {
224
264
  return useSelector(
@@ -477,7 +517,7 @@ function useObservable(initialValue, deps) {
477
517
  ref.current.obs$ = state.observable(value);
478
518
  }
479
519
  if (depsObs$) {
480
- depsObs$.set(deps);
520
+ runInRender(() => depsObs$.set(deps));
481
521
  }
482
522
  useUnmount(() => {
483
523
  const obs = ref.current.obs$;
@@ -546,21 +586,23 @@ function useObserve(selector, reactionOrOptionsOrDeps, options, deps) {
546
586
  ref.current.selector = selector;
547
587
  ref.current.reaction = reaction;
548
588
  if (depsObs$) {
549
- depsObs$.set(deps);
589
+ runInRender(() => depsObs$.set(deps));
550
590
  }
551
591
  if (!ref.current.dispose) {
552
- ref.current.dispose = state.observe(
553
- (e) => {
554
- var _a;
555
- depsObs$ == null ? void 0 : depsObs$.get();
556
- const selector2 = (_a = ref.current) == null ? void 0 : _a.selector;
557
- return state.computeSelector(selector2, void 0, e);
558
- },
559
- (e) => {
560
- var _a, _b;
561
- return (_b = (_a = ref.current).reaction) == null ? void 0 : _b.call(_a, e);
562
- },
563
- options
592
+ ref.current.dispose = runInRender(
593
+ () => state.observe(
594
+ (e) => {
595
+ var _a;
596
+ depsObs$ == null ? void 0 : depsObs$.get();
597
+ const selector2 = (_a = ref.current) == null ? void 0 : _a.selector;
598
+ return state.computeSelector(selector2, void 0, e);
599
+ },
600
+ (e) => {
601
+ var _a, _b;
602
+ return (_b = (_a = ref.current).reaction) == null ? void 0 : _b.call(_a, e);
603
+ },
604
+ options
605
+ )
564
606
  );
565
607
  }
566
608
  useUnmountOnce(() => {
@@ -589,7 +631,7 @@ function useObserveEffect(selector, reactionOrOptionsOrDeps, options, deps) {
589
631
  ref.current.selector = selector;
590
632
  ref.current.reaction = reaction;
591
633
  if (depsObs$) {
592
- depsObs$.set(deps);
634
+ runInRender(() => depsObs$.set(deps));
593
635
  }
594
636
  useMountOnce(
595
637
  () => state.observe(
package/react.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { isFunction, isEmpty, observable, computeSelector, isArray, isMap, isObservableValueReady, isPromise, getNode as getNode$1, linked, observe, when, whenReady, internal, trackSelector, isPrimitive, isObservable } from '@legendapp/state';
2
- import React, { memo, forwardRef, createElement, useState, useContext, useMemo, useRef, useEffect, createContext, useCallback } from 'react';
2
+ import React, { memo, forwardRef, createElement, useState, useContext, useMemo, useRef, useEffect, createContext } from 'react';
3
3
  import { useSyncExternalStore } from 'use-sync-external-store/shim/index.js';
4
4
  import { enableReactive } from '@legendapp/state/react-reactive/enableReactive';
5
5
 
@@ -7,8 +7,17 @@ import { enableReactive } from '@legendapp/state/react-reactive/enableReactive';
7
7
 
8
8
  // src/react/react-globals.ts
9
9
  var reactGlobals = {
10
- inObserver: false
10
+ inObserver: false,
11
+ renderDepth: 0
11
12
  };
13
+ function runInRender(fn) {
14
+ reactGlobals.renderDepth++;
15
+ try {
16
+ return fn();
17
+ } finally {
18
+ reactGlobals.renderDepth--;
19
+ }
20
+ }
12
21
  var pauseContext = void 0;
13
22
  var getPauseContext = () => {
14
23
  return pauseContext || (pauseContext = createContext(null));
@@ -30,6 +39,22 @@ function createSelectorFunctions(options, isPaused$) {
30
39
  let _selector;
31
40
  let prev;
32
41
  let pendingUpdate = void 0;
42
+ let notifyQueued = false;
43
+ const scheduleNotify = () => {
44
+ if (notify) {
45
+ if (reactGlobals.renderDepth > 0) {
46
+ if (!notifyQueued) {
47
+ notifyQueued = true;
48
+ queueMicrotask(() => {
49
+ notifyQueued = false;
50
+ notify == null ? void 0 : notify();
51
+ });
52
+ }
53
+ } else {
54
+ notify();
55
+ }
56
+ }
57
+ };
33
58
  const run = () => {
34
59
  dispose == null ? void 0 : dispose();
35
60
  const {
@@ -73,7 +98,7 @@ function createSelectorFunctions(options, isPaused$) {
73
98
  }
74
99
  if (changed) {
75
100
  version++;
76
- notify == null ? void 0 : notify();
101
+ scheduleNotify();
77
102
  }
78
103
  }
79
104
  };
@@ -85,6 +110,7 @@ function createSelectorFunctions(options, isPaused$) {
85
110
  }
86
111
  return () => {
87
112
  dispose == null ? void 0 : dispose();
113
+ notify = void 0;
88
114
  dispose = void 0;
89
115
  };
90
116
  },
@@ -117,7 +143,7 @@ function useSelector(selector, options) {
117
143
  const isPaused$ = useContext(getPauseContext());
118
144
  const selectorFn = useMemo(() => createSelectorFunctions(options, isPaused$), []);
119
145
  const { subscribe, getVersion, run } = selectorFn;
120
- value = run(selector);
146
+ value = runInRender(() => run(selector));
121
147
  useSyncExternalStore(subscribe, getVersion, getVersion);
122
148
  } catch (err) {
123
149
  if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && ((_a = err == null ? void 0 : err.message) == null ? void 0 : _a.includes("Rendered more"))) {
@@ -169,50 +195,64 @@ function createReactiveComponent(component, observe3, reactive2, keysReactive, b
169
195
  apply(target, thisArg, argArray) {
170
196
  if (reactive2) {
171
197
  const props = argArray[0];
172
- const propsOut = {};
173
- const keys = Object.keys(props);
174
- for (let i = 0; i < keys.length; i++) {
175
- const key = keys[i];
176
- const p = props[key];
177
- const isReactiveKey = keysReactiveSet && keysReactiveSet.has(key);
178
- if (key === "children" && (isFunction(p) || isObservable(p))) {
179
- propsOut[key] = useSelector(p, { skipCheck: true });
180
- } else if (isReactiveKey || key.startsWith("$") || key.endsWith("$")) {
181
- if (process.env.NODE_ENV === "development" && !didWarnProps && key.endsWith("$")) {
182
- didWarnProps = true;
183
- console.warn(
184
- `[legend-state] Reactive props were changed to start with $ instead of end with $ in version 2.0. So please change ${key} to $${key.replace(
185
- "$",
186
- ""
187
- )}. See https://legendapp.com/open-source/state/migrating for more details.`
188
- );
189
- }
190
- const k = isReactiveKey ? key : key.endsWith("$") ? key.slice(0, -1) : key.slice(1);
191
- const bind = bindKeys == null ? void 0 : bindKeys[k];
192
- const shouldBind = bind && isObservable(p);
193
- propsOut[k] = shouldBind && (bind == null ? void 0 : bind.selector) ? bind.selector(propsOut, p) : useSelector(p);
194
- if (shouldBind) {
195
- if (bind.defaultValue !== void 0 && propsOut[k] === void 0) {
196
- propsOut[k] = bind.defaultValue;
197
- }
198
- if (bind.handler && bind.getValue) {
199
- const handlerFn = (e) => {
200
- var _a;
201
- p.set(bind.getValue(e));
202
- (_a = props[bind.handler]) == null ? void 0 : _a.call(props, e);
203
- };
204
- propsOut[bind.handler] = // If in development mode, don't memoize the handler. fix fast refresh bug
205
- process.env.NODE_ENV === "development" ? handlerFn : useCallback(handlerFn, [props[bind.handler], bindKeys]);
198
+ const bindHandlersRef = useRef({});
199
+ argArray[0] = useSelector(
200
+ () => {
201
+ const propsOut = {};
202
+ const keys = Object.keys(props);
203
+ for (let i = 0; i < keys.length; i++) {
204
+ const key = keys[i];
205
+ const p = props[key];
206
+ const isReactiveKey = keysReactiveSet && keysReactiveSet.has(key);
207
+ if (key === "children" && (isFunction(p) || isObservable(p))) {
208
+ propsOut[key] = computeSelector(p);
209
+ } else if (isReactiveKey || key.startsWith("$") || key.endsWith("$")) {
210
+ if (process.env.NODE_ENV === "development" && !didWarnProps && key.endsWith("$")) {
211
+ didWarnProps = true;
212
+ console.warn(
213
+ `[legend-state] Reactive props were changed to start with $ instead of end with $ in version 2.0. So please change ${key} to $${key.replace(
214
+ "$",
215
+ ""
216
+ )}. See https://legendapp.com/open-source/state/migrating for more details.`
217
+ );
218
+ }
219
+ const k = isReactiveKey ? key : key.endsWith("$") ? key.slice(0, -1) : key.slice(1);
220
+ const bind = bindKeys == null ? void 0 : bindKeys[k];
221
+ const shouldBind = bind && isObservable(p);
222
+ propsOut[k] = shouldBind && (bind == null ? void 0 : bind.selector) ? bind.selector(propsOut, p) : computeSelector(p);
223
+ if (shouldBind) {
224
+ if (bind.defaultValue !== void 0 && propsOut[k] === void 0) {
225
+ propsOut[k] = bind.defaultValue;
226
+ }
227
+ if (bind.handler && bind.getValue) {
228
+ const handlerKey = bind.handler;
229
+ const originalHandler = props[handlerKey];
230
+ let cached = bindHandlersRef.current[k];
231
+ if (!cached || cached.bind !== bind || cached.observable !== p || cached.originalHandler !== originalHandler) {
232
+ cached = bindHandlersRef.current[k] = {
233
+ bind,
234
+ observable: p,
235
+ originalHandler,
236
+ handler: (event) => {
237
+ p.set(bind.getValue(event));
238
+ originalHandler == null ? void 0 : originalHandler(event);
239
+ }
240
+ };
241
+ }
242
+ propsOut[handlerKey] = cached.handler;
243
+ }
244
+ }
245
+ if (!isReactiveKey) {
246
+ delete propsOut[key];
247
+ }
248
+ } else if (propsOut[key] === void 0) {
249
+ propsOut[key] = p;
206
250
  }
207
251
  }
208
- if (!isReactiveKey) {
209
- delete propsOut[key];
210
- }
211
- } else if (propsOut[key] === void 0) {
212
- propsOut[key] = p;
213
- }
214
- }
215
- argArray[0] = propsOut;
252
+ return propsOut;
253
+ },
254
+ { skipCheck: true }
255
+ );
216
256
  }
217
257
  if (observe3) {
218
258
  return useSelector(
@@ -471,7 +511,7 @@ function useObservable(initialValue, deps) {
471
511
  ref.current.obs$ = observable(value);
472
512
  }
473
513
  if (depsObs$) {
474
- depsObs$.set(deps);
514
+ runInRender(() => depsObs$.set(deps));
475
515
  }
476
516
  useUnmount(() => {
477
517
  const obs = ref.current.obs$;
@@ -540,21 +580,23 @@ function useObserve(selector, reactionOrOptionsOrDeps, options, deps) {
540
580
  ref.current.selector = selector;
541
581
  ref.current.reaction = reaction;
542
582
  if (depsObs$) {
543
- depsObs$.set(deps);
583
+ runInRender(() => depsObs$.set(deps));
544
584
  }
545
585
  if (!ref.current.dispose) {
546
- ref.current.dispose = observe(
547
- (e) => {
548
- var _a;
549
- depsObs$ == null ? void 0 : depsObs$.get();
550
- const selector2 = (_a = ref.current) == null ? void 0 : _a.selector;
551
- return computeSelector(selector2, void 0, e);
552
- },
553
- (e) => {
554
- var _a, _b;
555
- return (_b = (_a = ref.current).reaction) == null ? void 0 : _b.call(_a, e);
556
- },
557
- options
586
+ ref.current.dispose = runInRender(
587
+ () => observe(
588
+ (e) => {
589
+ var _a;
590
+ depsObs$ == null ? void 0 : depsObs$.get();
591
+ const selector2 = (_a = ref.current) == null ? void 0 : _a.selector;
592
+ return computeSelector(selector2, void 0, e);
593
+ },
594
+ (e) => {
595
+ var _a, _b;
596
+ return (_b = (_a = ref.current).reaction) == null ? void 0 : _b.call(_a, e);
597
+ },
598
+ options
599
+ )
558
600
  );
559
601
  }
560
602
  useUnmountOnce(() => {
@@ -583,7 +625,7 @@ function useObserveEffect(selector, reactionOrOptionsOrDeps, options, deps) {
583
625
  ref.current.selector = selector;
584
626
  ref.current.reaction = reaction;
585
627
  if (depsObs$) {
586
- depsObs$.set(deps);
628
+ runInRender(() => depsObs$.set(deps));
587
629
  }
588
630
  useMountOnce(
589
631
  () => observe(
@@ -9,6 +9,11 @@ var { waitForSet, runWithRetry } = sync.internal;
9
9
  function transformOut(data, transform) {
10
10
  return transform ? transform(clone(data)) : data;
11
11
  }
12
+ function warnMissingId(value) {
13
+ if (process.env.NODE_ENV === "development") {
14
+ console.warn("[legend-state] syncedCrud received an item without an id", value);
15
+ }
16
+ }
12
17
  function ensureId(obj, fieldId, generateId, node) {
13
18
  if (!obj[fieldId]) {
14
19
  obj[fieldId] = generateId();
@@ -43,7 +48,11 @@ function arrayToRecord(arr, keyField) {
43
48
  for (let i = 0; i < arr.length; i++) {
44
49
  const v = arr[i];
45
50
  const key = v[keyField];
46
- record[key] = v;
51
+ if (state.isNullOrUndefined(key)) {
52
+ warnMissingId(v);
53
+ } else {
54
+ record[key] = v;
55
+ }
47
56
  }
48
57
  }
49
58
  return record;
@@ -118,7 +127,10 @@ function retrySet(params, retry, action, itemKey, itemValue, change, queuedRetri
118
127
  };
119
128
  return state.isPromise(refreshed) ? refreshed.then(runWithRefreshedValue) : runWithRefreshedValue(refreshed);
120
129
  };
121
- return runWithRetry(paramsWithChanges, retry, action + "_" + itemKey, runAttempt);
130
+ return runWithRetry(paramsWithChanges, retry, action + "_" + itemKey, runAttempt).catch((error) => {
131
+ params.update({ value: {}, failedChanges: [change] });
132
+ throw error;
133
+ });
122
134
  }
123
135
  function syncedCrud(props) {
124
136
  const {
@@ -210,9 +222,19 @@ function syncedCrud(props) {
210
222
  if (asArray) {
211
223
  out.push(result);
212
224
  } else if (asMap) {
213
- out.set(value[fieldId], result);
225
+ const key = value[fieldId];
226
+ if (state.isNullOrUndefined(key)) {
227
+ warnMissingId(value);
228
+ } else {
229
+ out.set(key, result);
230
+ }
214
231
  } else {
215
- out[value[fieldId]] = result;
232
+ const key = value[fieldId];
233
+ if (state.isNullOrUndefined(key)) {
234
+ warnMissingId(value);
235
+ } else {
236
+ out[key] = result;
237
+ }
216
238
  }
217
239
  }
218
240
  }
@@ -320,6 +342,10 @@ function syncedCrud(props) {
320
342
  return !!(pendingCreate == null ? void 0 : pendingCreate.hasFailed) && !pendingCreate.promise;
321
343
  };
322
344
  const addCreate = (id, itemValue, change) => {
345
+ const existingPendingCreate = pendingCreates.get(id);
346
+ if ((existingPendingCreate == null ? void 0 : existingPendingCreate.hasFailed) && !existingPendingCreate.promise && change.path.length === 0) {
347
+ existingPendingCreate.change = change;
348
+ }
323
349
  const pendingCreate = beginPendingCreate(id, change);
324
350
  changesById.set(id, pendingCreate.change);
325
351
  creates.set(id, itemValue);
@@ -411,7 +437,8 @@ function syncedCrud(props) {
411
437
  return false;
412
438
  } else {
413
439
  const isDiff = !prevAsObject || !sync.deepEqual(value2, prev);
414
- if (isDiff) {
440
+ const isFailedCreateRetry = isFailedCreateReadyToRetry(value2 == null ? void 0 : value2[fieldId]);
441
+ if (isDiff || isFailedCreateRetry) {
415
442
  itemsChanged.push([getUpdateValue(value2, prev), prev, value2]);
416
443
  }
417
444
  }
@@ -7,6 +7,11 @@ var { waitForSet, runWithRetry } = internal$1;
7
7
  function transformOut(data, transform) {
8
8
  return transform ? transform(clone(data)) : data;
9
9
  }
10
+ function warnMissingId(value) {
11
+ if (process.env.NODE_ENV === "development") {
12
+ console.warn("[legend-state] syncedCrud received an item without an id", value);
13
+ }
14
+ }
10
15
  function ensureId(obj, fieldId, generateId, node) {
11
16
  if (!obj[fieldId]) {
12
17
  obj[fieldId] = generateId();
@@ -41,7 +46,11 @@ function arrayToRecord(arr, keyField) {
41
46
  for (let i = 0; i < arr.length; i++) {
42
47
  const v = arr[i];
43
48
  const key = v[keyField];
44
- record[key] = v;
49
+ if (isNullOrUndefined(key)) {
50
+ warnMissingId(v);
51
+ } else {
52
+ record[key] = v;
53
+ }
45
54
  }
46
55
  }
47
56
  return record;
@@ -116,7 +125,10 @@ function retrySet(params, retry, action, itemKey, itemValue, change, queuedRetri
116
125
  };
117
126
  return isPromise(refreshed) ? refreshed.then(runWithRefreshedValue) : runWithRefreshedValue(refreshed);
118
127
  };
119
- return runWithRetry(paramsWithChanges, retry, action + "_" + itemKey, runAttempt);
128
+ return runWithRetry(paramsWithChanges, retry, action + "_" + itemKey, runAttempt).catch((error) => {
129
+ params.update({ value: {}, failedChanges: [change] });
130
+ throw error;
131
+ });
120
132
  }
121
133
  function syncedCrud(props) {
122
134
  const {
@@ -208,9 +220,19 @@ function syncedCrud(props) {
208
220
  if (asArray) {
209
221
  out.push(result);
210
222
  } else if (asMap) {
211
- out.set(value[fieldId], result);
223
+ const key = value[fieldId];
224
+ if (isNullOrUndefined(key)) {
225
+ warnMissingId(value);
226
+ } else {
227
+ out.set(key, result);
228
+ }
212
229
  } else {
213
- out[value[fieldId]] = result;
230
+ const key = value[fieldId];
231
+ if (isNullOrUndefined(key)) {
232
+ warnMissingId(value);
233
+ } else {
234
+ out[key] = result;
235
+ }
214
236
  }
215
237
  }
216
238
  }
@@ -318,6 +340,10 @@ function syncedCrud(props) {
318
340
  return !!(pendingCreate == null ? void 0 : pendingCreate.hasFailed) && !pendingCreate.promise;
319
341
  };
320
342
  const addCreate = (id, itemValue, change) => {
343
+ const existingPendingCreate = pendingCreates.get(id);
344
+ if ((existingPendingCreate == null ? void 0 : existingPendingCreate.hasFailed) && !existingPendingCreate.promise && change.path.length === 0) {
345
+ existingPendingCreate.change = change;
346
+ }
321
347
  const pendingCreate = beginPendingCreate(id, change);
322
348
  changesById.set(id, pendingCreate.change);
323
349
  creates.set(id, itemValue);
@@ -409,7 +435,8 @@ function syncedCrud(props) {
409
435
  return false;
410
436
  } else {
411
437
  const isDiff = !prevAsObject || !deepEqual(value2, prev);
412
- if (isDiff) {
438
+ const isFailedCreateRetry = isFailedCreateReadyToRetry(value2 == null ? void 0 : value2[fieldId]);
439
+ if (isDiff || isFailedCreateRetry) {
413
440
  itemsChanged.push([getUpdateValue(value2, prev), prev, value2]);
414
441
  }
415
442
  }