@solidjs/signals 0.9.7 → 0.9.9

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/dist/dev.js CHANGED
@@ -1,8 +1,15 @@
1
1
  class NotReadyError extends Error {
2
- cause;
3
- constructor(cause) {
2
+ _source;
3
+ constructor(_source) {
4
4
  super();
5
- this.cause = cause;
5
+ this._source = _source;
6
+ }
7
+ }
8
+ class StatusError extends Error {
9
+ _source;
10
+ constructor(_source, original) {
11
+ super(original instanceof Error ? original.message : String(original), { cause: original });
12
+ this._source = _source;
6
13
  }
7
14
  }
8
15
  class NoOwnerError extends Error {
@@ -26,7 +33,6 @@ const REACTIVE_IN_HEAP_HEIGHT = 1 << 4;
26
33
  const REACTIVE_ZOMBIE = 1 << 5;
27
34
  const REACTIVE_DISPOSED = 1 << 6;
28
35
  const REACTIVE_OPTIMISTIC_DIRTY = 1 << 7;
29
- const STATUS_NONE = 0;
30
36
  const STATUS_PENDING = 1 << 0;
31
37
  const STATUS_ERROR = 1 << 1;
32
38
  const STATUS_UNINITIALIZED = 1 << 2;
@@ -140,10 +146,85 @@ const zombieQueue = { _heap: new Array(2e3).fill(undefined), _marked: false, _mi
140
146
  let clock = 0;
141
147
  let activeTransition = null;
142
148
  let scheduled = false;
143
- let optimisticReadActive = false;
144
149
  let projectionWriteActive = false;
145
- function setOptimisticReadActive(value) {
146
- optimisticReadActive = value;
150
+ const signalLanes = new WeakMap();
151
+ const activeLanes = new Set();
152
+ let currentOptimisticLane = null;
153
+ function setCurrentOptimisticLane(lane) {
154
+ currentOptimisticLane = lane;
155
+ }
156
+ function getOrCreateLane(signal) {
157
+ let lane = signalLanes.get(signal);
158
+ if (lane) {
159
+ return findLane(lane);
160
+ }
161
+ lane = {
162
+ _source: signal,
163
+ _pendingAsync: new Set(),
164
+ _effectQueues: [[], []],
165
+ _mergedInto: null,
166
+ _transition: activeTransition
167
+ };
168
+ signalLanes.set(signal, lane);
169
+ activeLanes.add(lane);
170
+ signal._laneVersion = signal._overrideVersion || 0;
171
+ return lane;
172
+ }
173
+ function findLane(lane) {
174
+ while (lane._mergedInto) lane = lane._mergedInto;
175
+ return lane;
176
+ }
177
+ function mergeLanes(lane1, lane2) {
178
+ lane1 = findLane(lane1);
179
+ lane2 = findLane(lane2);
180
+ if (lane1 === lane2) return lane1;
181
+ lane2._mergedInto = lane1;
182
+ for (const node of lane2._pendingAsync) lane1._pendingAsync.add(node);
183
+ lane1._effectQueues[0].push(...lane2._effectQueues[0]);
184
+ lane1._effectQueues[1].push(...lane2._effectQueues[1]);
185
+ return lane1;
186
+ }
187
+ function clearLaneEntry(signal) {
188
+ signalLanes.delete(signal);
189
+ }
190
+ function resolveLane(el) {
191
+ const lane = el._optimisticLane;
192
+ if (!lane) return undefined;
193
+ const root = findLane(lane);
194
+ if (activeLanes.has(root)) return root;
195
+ el._optimisticLane = undefined;
196
+ return undefined;
197
+ }
198
+ function hasActiveOverride(el) {
199
+ return !!(el._optimistic && el._pendingValue !== NOT_PENDING);
200
+ }
201
+ function assignOrMergeLane(el, sourceLane) {
202
+ const sourceRoot = findLane(sourceLane);
203
+ const existing = el._optimisticLane;
204
+ if (existing) {
205
+ if (existing._mergedInto) {
206
+ el._optimisticLane = sourceLane;
207
+ return;
208
+ }
209
+ const existingRoot = findLane(existing);
210
+ if (activeLanes.has(existingRoot)) {
211
+ if (existingRoot !== sourceRoot && !hasActiveOverride(el)) {
212
+ mergeLanes(sourceRoot, existingRoot);
213
+ }
214
+ return;
215
+ }
216
+ }
217
+ el._optimisticLane = sourceLane;
218
+ }
219
+ function runLaneEffects(type) {
220
+ for (const lane of activeLanes) {
221
+ if (lane._mergedInto || lane._pendingAsync.size > 0) continue;
222
+ const effects = lane._effectQueues[type - 1];
223
+ if (effects.length) {
224
+ lane._effectQueues[type - 1] = [];
225
+ runQueue(effects, type);
226
+ }
227
+ }
147
228
  }
148
229
  function setProjectionWriteActive(value) {
149
230
  projectionWriteActive = value;
@@ -156,7 +237,6 @@ function schedule() {
156
237
  class Queue {
157
238
  _parent = null;
158
239
  _queues = [[], []];
159
- _optimisticQueues = [[], []];
160
240
  _children = [];
161
241
  created = clock;
162
242
  addChild(child) {
@@ -170,30 +250,26 @@ class Queue {
170
250
  child._parent = null;
171
251
  }
172
252
  }
173
- notify(node, mask, flags) {
174
- if (this._parent) return this._parent.notify(node, mask, flags);
253
+ notify(node, mask, flags, error) {
254
+ if (this._parent) return this._parent.notify(node, mask, flags, error);
175
255
  return false;
176
256
  }
177
- _runQueue(type, queues, method) {
178
- if (queues[type - 1].length) {
179
- const effects = queues[type - 1];
180
- queues[type - 1] = [];
257
+ run(type) {
258
+ if (this._queues[type - 1].length) {
259
+ const effects = this._queues[type - 1];
260
+ this._queues[type - 1] = [];
181
261
  runQueue(effects, type);
182
262
  }
183
- for (let i = 0; i < this._children.length; i++) {
184
- this._children[i][method]?.(type);
185
- }
186
- }
187
- run(type) {
188
- this._runQueue(type, this._queues, "run");
189
- }
190
- runOptimistic(type) {
191
- this._runQueue(type, this._optimisticQueues, "runOptimistic");
263
+ for (let i = 0; i < this._children.length; i++) this._children[i].run?.(type);
192
264
  }
193
265
  enqueue(type, fn) {
194
266
  if (type) {
195
- const queue = optimisticReadActive ? this._optimisticQueues : this._queues;
196
- queue[type - 1].push(fn);
267
+ if (currentOptimisticLane) {
268
+ const lane = findLane(currentOptimisticLane);
269
+ lane._effectQueues[type - 1].push(fn);
270
+ } else {
271
+ this._queues[type - 1].push(fn);
272
+ }
197
273
  }
198
274
  schedule();
199
275
  }
@@ -242,23 +318,23 @@ class GlobalQueue extends Queue {
242
318
  this._pendingNodes = [];
243
319
  this._optimisticNodes = [];
244
320
  this._optimisticStores = new Set();
245
- this.runOptimistic(EFFECT_RENDER);
246
- this.runOptimistic(EFFECT_USER);
247
- this.stashQueues(activeTransition.queueStash);
321
+ runLaneEffects(EFFECT_RENDER);
322
+ runLaneEffects(EFFECT_USER);
323
+ this.stashQueues(activeTransition._queueStash);
248
324
  clock++;
249
- scheduled = false;
250
- runTransitionPending(activeTransition.pendingNodes);
325
+ scheduled = dirtyQueue._max >= dirtyQueue._min;
326
+ reassignPendingTransition(activeTransition._pendingNodes);
251
327
  activeTransition = null;
252
328
  finalizePureQueue(null, true);
253
329
  return;
254
330
  }
255
- this._pendingNodes !== activeTransition.pendingNodes &&
256
- this._pendingNodes.push(...activeTransition.pendingNodes);
257
- this.restoreQueues(activeTransition.queueStash);
331
+ this._pendingNodes !== activeTransition._pendingNodes &&
332
+ this._pendingNodes.push(...activeTransition._pendingNodes);
333
+ this.restoreQueues(activeTransition._queueStash);
258
334
  transitions.delete(activeTransition);
259
335
  const completingTransition = activeTransition;
260
336
  activeTransition = null;
261
- runTransitionPending(this._pendingNodes);
337
+ reassignPendingTransition(this._pendingNodes);
262
338
  finalizePureQueue(completingTransition);
263
339
  } else {
264
340
  if (transitions.size) runHeap(zombieQueue, GlobalQueue._update);
@@ -266,23 +342,24 @@ class GlobalQueue extends Queue {
266
342
  }
267
343
  clock++;
268
344
  scheduled = dirtyQueue._max >= dirtyQueue._min;
269
- this.runOptimistic(EFFECT_RENDER);
345
+ runLaneEffects(EFFECT_RENDER);
270
346
  this.run(EFFECT_RENDER);
271
- this.runOptimistic(EFFECT_USER);
347
+ runLaneEffects(EFFECT_USER);
272
348
  this.run(EFFECT_USER);
273
349
  } finally {
274
350
  this._running = false;
275
351
  }
276
352
  }
277
- notify(node, mask, flags) {
353
+ notify(node, mask, flags, error) {
278
354
  if (mask & STATUS_PENDING) {
279
355
  if (flags & STATUS_PENDING) {
356
+ const actualError = error !== undefined ? error : node._error;
280
357
  if (
281
358
  activeTransition &&
282
- node._error &&
283
- !activeTransition.asyncNodes.includes(node._error.cause)
359
+ actualError &&
360
+ !activeTransition._asyncNodes.includes(actualError._source)
284
361
  ) {
285
- activeTransition.asyncNodes.push(node._error.cause);
362
+ activeTransition._asyncNodes.push(actualError._source);
286
363
  schedule();
287
364
  }
288
365
  }
@@ -293,47 +370,63 @@ class GlobalQueue extends Queue {
293
370
  initTransition(transition) {
294
371
  if (transition) transition = currentTransition(transition);
295
372
  if (transition && transition === activeTransition) return;
296
- if (!transition && activeTransition && activeTransition.time === clock) return;
373
+ if (!transition && activeTransition && activeTransition._time === clock) return;
297
374
  if (!activeTransition) {
298
375
  activeTransition = transition ?? {
299
- time: clock,
300
- pendingNodes: [],
301
- asyncNodes: [],
302
- optimisticNodes: [],
303
- optimisticStores: new Set(),
304
- actions: [],
305
- queueStash: { _queues: [[], []], _children: [] },
306
- done: false
376
+ _time: clock,
377
+ _pendingNodes: [],
378
+ _asyncNodes: [],
379
+ _optimisticNodes: [],
380
+ _optimisticStores: new Set(),
381
+ _actions: [],
382
+ _queueStash: { _queues: [[], []], _children: [] },
383
+ _done: false
307
384
  };
308
385
  } else if (transition) {
309
- activeTransition.done = transition;
310
- transition.actions.push(...activeTransition.actions);
311
- transitions.delete(activeTransition);
386
+ const outgoing = activeTransition;
387
+ outgoing._done = transition;
388
+ transition._actions.push(...outgoing._actions);
389
+ for (const lane of activeLanes) {
390
+ if (lane._transition === outgoing) lane._transition = transition;
391
+ }
392
+ transition._optimisticNodes.push(...outgoing._optimisticNodes);
393
+ for (const store of outgoing._optimisticStores) {
394
+ transition._optimisticStores.add(store);
395
+ }
396
+ transitions.delete(outgoing);
312
397
  activeTransition = transition;
313
398
  }
314
399
  transitions.add(activeTransition);
315
- activeTransition.time = clock;
400
+ activeTransition._time = clock;
316
401
  for (let i = 0; i < this._pendingNodes.length; i++) {
317
402
  const n = this._pendingNodes[i];
318
403
  n._transition = activeTransition;
319
- activeTransition.pendingNodes.push(n);
404
+ activeTransition._pendingNodes.push(n);
320
405
  }
321
- this._pendingNodes = activeTransition.pendingNodes;
406
+ this._pendingNodes = activeTransition._pendingNodes;
322
407
  for (let i = 0; i < this._optimisticNodes.length; i++) {
323
408
  const node = this._optimisticNodes[i];
324
409
  node._transition = activeTransition;
325
- activeTransition.optimisticNodes.push(node);
410
+ activeTransition._optimisticNodes.push(node);
326
411
  }
327
- this._optimisticNodes = activeTransition.optimisticNodes;
328
- for (const store of this._optimisticStores) {
329
- activeTransition.optimisticStores.add(store);
412
+ this._optimisticNodes = activeTransition._optimisticNodes;
413
+ for (const lane of activeLanes) {
414
+ if (!lane._transition) lane._transition = activeTransition;
330
415
  }
331
- this._optimisticStores = activeTransition.optimisticStores;
416
+ for (const store of this._optimisticStores) activeTransition._optimisticStores.add(store);
417
+ this._optimisticStores = activeTransition._optimisticStores;
332
418
  }
333
419
  }
334
420
  function insertSubs(node, optimistic = false) {
421
+ const sourceLane = node._optimisticLane || currentOptimisticLane;
335
422
  for (let s = node._subs; s !== null; s = s._nextSub) {
336
- if (optimistic) s._sub._flags |= REACTIVE_OPTIMISTIC_DIRTY;
423
+ if (optimistic && sourceLane) {
424
+ s._sub._flags |= REACTIVE_OPTIMISTIC_DIRTY;
425
+ assignOrMergeLane(s._sub, sourceLane);
426
+ } else if (optimistic) {
427
+ s._sub._flags |= REACTIVE_OPTIMISTIC_DIRTY;
428
+ s._sub._optimisticLane = undefined;
429
+ }
337
430
  const sub = s._sub;
338
431
  if (sub._type === EFFECT_TRACKED) {
339
432
  if (!sub._modified) {
@@ -349,10 +442,8 @@ function insertSubs(node, optimistic = false) {
349
442
  }
350
443
  function finalizePureQueue(completingTransition = null, incomplete = false) {
351
444
  let resolvePending = !incomplete;
352
- if (dirtyQueue._max >= dirtyQueue._min) {
353
- resolvePending = true;
354
- runHeap(dirtyQueue, GlobalQueue._update);
355
- }
445
+ if (!incomplete) checkBoundaryChildren(globalQueue);
446
+ if (dirtyQueue._max >= dirtyQueue._min) runHeap(dirtyQueue, GlobalQueue._update);
356
447
  if (resolvePending) {
357
448
  const pendingNodes = globalQueue._pendingNodes;
358
449
  for (let i = 0; i < pendingNodes.length; i++) {
@@ -362,15 +453,17 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
362
453
  n._pendingValue = NOT_PENDING;
363
454
  if (n._type && n._type !== EFFECT_TRACKED) n._modified = true;
364
455
  }
456
+ n._statusFlags &= ~STATUS_UNINITIALIZED;
365
457
  if (n._fn) GlobalQueue._dispose(n, false, true);
366
458
  }
367
459
  pendingNodes.length = 0;
368
460
  const optimisticNodes = completingTransition
369
- ? completingTransition.optimisticNodes
461
+ ? completingTransition._optimisticNodes
370
462
  : globalQueue._optimisticNodes;
371
463
  for (let i = 0; i < optimisticNodes.length; i++) {
372
464
  const n = optimisticNodes[i];
373
465
  const original = n._pendingValue;
466
+ n._optimisticLane = undefined;
374
467
  if (original !== NOT_PENDING && n._value !== original) {
375
468
  n._value = original;
376
469
  insertSubs(n, true);
@@ -380,7 +473,7 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
380
473
  }
381
474
  optimisticNodes.length = 0;
382
475
  const optimisticStores = completingTransition
383
- ? completingTransition.optimisticStores
476
+ ? completingTransition._optimisticStores
384
477
  : globalQueue._optimisticStores;
385
478
  if (GlobalQueue._clearOptimisticStore && optimisticStores.size) {
386
479
  for (const store of optimisticStores) {
@@ -389,13 +482,35 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
389
482
  optimisticStores.clear();
390
483
  schedule();
391
484
  }
485
+ for (const lane of activeLanes) {
486
+ const owned = completingTransition
487
+ ? lane._transition === completingTransition
488
+ : !lane._transition;
489
+ if (!owned) continue;
490
+ if (!lane._mergedInto) {
491
+ if (lane._effectQueues[0].length) runQueue(lane._effectQueues[0], EFFECT_RENDER);
492
+ if (lane._effectQueues[1].length) runQueue(lane._effectQueues[1], EFFECT_USER);
493
+ }
494
+ if (lane._source._optimisticLane === lane) lane._source._optimisticLane = undefined;
495
+ lane._pendingAsync.clear();
496
+ lane._effectQueues[0].length = 0;
497
+ lane._effectQueues[1].length = 0;
498
+ activeLanes.delete(lane);
499
+ signalLanes.delete(lane._source);
500
+ }
501
+ }
502
+ }
503
+ function checkBoundaryChildren(queue) {
504
+ for (const child of queue._children) {
505
+ child.checkSources?.();
506
+ checkBoundaryChildren(child);
392
507
  }
393
508
  }
394
509
  function trackOptimisticStore(store) {
395
510
  globalQueue._optimisticStores.add(store);
396
511
  schedule();
397
512
  }
398
- function runTransitionPending(pendingNodes) {
513
+ function reassignPendingTransition(pendingNodes) {
399
514
  for (let i = 0; i < pendingNodes.length; i++) {
400
515
  pendingNodes[i]._transition = activeTransition;
401
516
  }
@@ -412,20 +527,20 @@ function runQueue(queue, type) {
412
527
  for (let i = 0; i < queue.length; i++) queue[i](type);
413
528
  }
414
529
  function transitionComplete(transition) {
415
- if (transition.done) return true;
416
- if (transition.actions.length) return false;
530
+ if (transition._done) return true;
531
+ if (transition._actions.length) return false;
417
532
  let done = true;
418
- for (let i = 0; i < transition.asyncNodes.length; i++) {
419
- if (transition.asyncNodes[i]._statusFlags & STATUS_PENDING) {
533
+ for (let i = 0; i < transition._asyncNodes.length; i++) {
534
+ if (transition._asyncNodes[i]._statusFlags & STATUS_PENDING) {
420
535
  done = false;
421
536
  break;
422
537
  }
423
538
  }
424
- done && (transition.done = true);
539
+ done && (transition._done = true);
425
540
  return done;
426
541
  }
427
542
  function currentTransition(transition) {
428
- while (transition.done && typeof transition.done === "object") transition = transition.done;
543
+ while (transition._done && typeof transition._done === "object") transition = transition._done;
429
544
  return transition;
430
545
  }
431
546
  function runInTransition(transition, fn) {
@@ -449,11 +564,11 @@ function action(genFn) {
449
564
  const it = genFn(...args);
450
565
  globalQueue.initTransition();
451
566
  let ctx = activeTransition;
452
- ctx.actions.push(it);
567
+ ctx._actions.push(it);
453
568
  const done = (v, e) => {
454
569
  ctx = currentTransition(ctx);
455
- const i = ctx.actions.indexOf(it);
456
- if (i >= 0) ctx.actions.splice(i, 1);
570
+ const i = ctx._actions.indexOf(it);
571
+ if (i >= 0) ctx._actions.splice(i, 1);
457
572
  activeTransition = ctx;
458
573
  schedule();
459
574
  e ? reject(e) : resolve(v);
@@ -500,7 +615,7 @@ function recompute(el, create = false) {
500
615
  if (el._transition && (!isEffect || activeTransition) && activeTransition !== el._transition)
501
616
  globalQueue.initTransition(el._transition);
502
617
  deleteFromHeap(el, el._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue);
503
- if (el._transition) disposeChildren(el);
618
+ if (el._transition || isEffect === EFFECT_TRACKED) disposeChildren(el);
504
619
  else {
505
620
  markDisposal(el);
506
621
  el._pendingDisposal = el._disposal;
@@ -510,7 +625,8 @@ function recompute(el, create = false) {
510
625
  }
511
626
  }
512
627
  const isOptimisticDirty = !!(el._flags & REACTIVE_OPTIMISTIC_DIRTY);
513
- const hasOptimisticOverride = el._optimistic && el._pendingValue !== NOT_PENDING;
628
+ const hasOverride = hasActiveOverride(el);
629
+ const wasPending = !!(el._statusFlags & STATUS_PENDING);
514
630
  const oldcontext = context;
515
631
  context = el;
516
632
  el._depsTail = null;
@@ -519,17 +635,32 @@ function recompute(el, create = false) {
519
635
  let value = el._pendingValue === NOT_PENDING ? el._value : el._pendingValue;
520
636
  let oldHeight = el._height;
521
637
  let prevTracking = tracking;
522
- let prevOptimisticRead = optimisticReadActive;
638
+ let prevLane = currentOptimisticLane;
523
639
  tracking = true;
524
- if (isOptimisticDirty) setOptimisticReadActive(true);
640
+ if (isOptimisticDirty) {
641
+ const lane = resolveLane(el);
642
+ if (lane) setCurrentOptimisticLane(lane);
643
+ }
525
644
  try {
526
645
  value = handleAsync(el, el._fn(value));
527
646
  clearStatus(el);
647
+ const resolvedLane = resolveLane(el);
648
+ if (resolvedLane) resolvedLane._pendingAsync.delete(el);
528
649
  } catch (e) {
529
- if (e instanceof NotReadyError) {
530
- if (e.cause !== el) link(e.cause, el);
531
- notifyStatus(el, STATUS_PENDING, e);
532
- } else notifyStatus(el, STATUS_ERROR, e);
650
+ if (e instanceof NotReadyError && currentOptimisticLane) {
651
+ const lane = findLane(currentOptimisticLane);
652
+ if (lane._source !== el) {
653
+ lane._pendingAsync.add(el);
654
+ el._optimisticLane = lane;
655
+ }
656
+ }
657
+ notifyStatus(
658
+ el,
659
+ e instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR,
660
+ e,
661
+ undefined,
662
+ e instanceof NotReadyError ? el._optimisticLane : undefined
663
+ );
533
664
  } finally {
534
665
  tracking = prevTracking;
535
666
  el._flags = REACTIVE_NONE;
@@ -545,23 +676,37 @@ function recompute(el, create = false) {
545
676
  if (depsTail !== null) depsTail._nextDep = null;
546
677
  else el._deps = null;
547
678
  }
548
- const valueChanged =
549
- !el._equals ||
550
- !el._equals(el._pendingValue === NOT_PENDING ? el._value : el._pendingValue, value);
679
+ const compareValue = hasOverride
680
+ ? el._value
681
+ : el._pendingValue === NOT_PENDING
682
+ ? el._value
683
+ : el._pendingValue;
684
+ const valueChanged = !el._equals || !el._equals(compareValue, value);
551
685
  if (valueChanged) {
686
+ const prevVisible = hasOverride ? el._value : undefined;
552
687
  if (create || (isEffect && activeTransition !== el._transition) || isOptimisticDirty)
553
688
  el._value = value;
554
689
  else el._pendingValue = value;
555
- insertSubs(el, isOptimisticDirty || hasOptimisticOverride);
690
+ if (hasOverride && !isOptimisticDirty && wasPending) {
691
+ const ov = el._overrideVersion || 0;
692
+ const lv = el._laneVersion || 0;
693
+ if (ov <= lv) el._value = value;
694
+ }
695
+ if (!hasOverride || isOptimisticDirty || el._value !== prevVisible) {
696
+ insertSubs(el, isOptimisticDirty || hasOverride);
697
+ }
698
+ } else if (hasOverride) {
699
+ el._pendingValue = value;
556
700
  } else if (el._height != oldHeight) {
557
701
  for (let s = el._subs; s !== null; s = s._nextSub) {
558
702
  insertIntoHeapHeight(s._sub, s._sub._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue);
559
703
  }
560
704
  }
561
705
  }
562
- setOptimisticReadActive(prevOptimisticRead);
706
+ setCurrentOptimisticLane(prevLane);
563
707
  (!create || el._statusFlags & STATUS_PENDING) &&
564
708
  !el._transition &&
709
+ !(activeTransition && el._optimistic) &&
565
710
  globalQueue._pendingNodes.push(el);
566
711
  el._transition &&
567
712
  isEffect &&
@@ -581,27 +726,40 @@ function handleAsync(el, result, setter) {
581
726
  const handleError = error => {
582
727
  if (el._inFlight !== result) return;
583
728
  globalQueue.initTransition(el._transition);
584
- if (error instanceof NotReadyError) {
585
- if (error.cause !== el) link(error.cause, el);
586
- notifyStatus(el, STATUS_PENDING, error);
587
- } else notifyStatus(el, STATUS_ERROR, error);
729
+ notifyStatus(el, error instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR, error);
588
730
  el._time = clock;
589
731
  };
590
732
  const asyncWrite = (value, then) => {
591
733
  if (el._inFlight !== result) return;
734
+ if (el._flags & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
592
735
  globalQueue.initTransition(el._transition);
593
736
  clearStatus(el);
737
+ const lane = resolveLane(el);
738
+ if (lane) lane._pendingAsync.delete(el);
594
739
  if (setter) setter(value);
595
740
  else if (el._optimistic) {
596
- const hasOverride = el._pendingValue !== NOT_PENDING;
741
+ const hadOverride = el._pendingValue !== NOT_PENDING;
597
742
  if (el._fn) el._pendingValue = value;
598
- if (!hasOverride) {
743
+ if (!hadOverride) {
599
744
  el._value = value;
600
745
  insertSubs(el);
601
746
  }
602
747
  el._time = clock;
603
- schedule();
604
- } else setSignal(el, () => value);
748
+ } else if (lane) {
749
+ const prevValue = el._value;
750
+ const equals = el._equals;
751
+ if (!equals || !equals(value, prevValue)) {
752
+ el._value = value;
753
+ el._time = clock;
754
+ if (el._pendingValueComputed) {
755
+ setSignal(el._pendingValueComputed, value);
756
+ }
757
+ insertSubs(el, true);
758
+ }
759
+ } else {
760
+ setSignal(el, () => value);
761
+ }
762
+ schedule();
605
763
  flush();
606
764
  then?.();
607
765
  };
@@ -660,32 +818,51 @@ function handleAsync(el, result, setter) {
660
818
  return syncValue;
661
819
  }
662
820
  function clearStatus(el) {
663
- el._statusFlags = STATUS_NONE;
821
+ el._statusFlags = el._statusFlags & STATUS_UNINITIALIZED;
664
822
  el._error = null;
665
823
  updatePendingSignal(el);
824
+ el._notifyStatus?.();
825
+ }
826
+ function notifyStatus(el, status, error, blockStatus, lane) {
827
+ if (
828
+ status === STATUS_ERROR &&
829
+ !(error instanceof StatusError) &&
830
+ !(error instanceof NotReadyError)
831
+ )
832
+ error = new StatusError(el, error);
833
+ const isSource = error instanceof NotReadyError && error._source === el;
834
+ const isOptimisticBoundary = status === STATUS_PENDING && el._optimistic && !isSource;
835
+ const startsBlocking = isOptimisticBoundary && hasActiveOverride(el);
836
+ if (!blockStatus) {
837
+ el._statusFlags =
838
+ status | (status !== STATUS_ERROR ? el._statusFlags & STATUS_UNINITIALIZED : 0);
839
+ el._error = error;
840
+ updatePendingSignal(el);
841
+ }
842
+ if (lane && !blockStatus) {
843
+ assignOrMergeLane(el, lane);
844
+ }
845
+ if (startsBlocking && activeTransition && error instanceof NotReadyError) {
846
+ const source = error._source;
847
+ if (!activeTransition._asyncNodes.includes(source)) {
848
+ activeTransition._asyncNodes.push(source);
849
+ }
850
+ }
851
+ const downstreamBlockStatus = blockStatus || startsBlocking;
852
+ const downstreamLane = blockStatus || isOptimisticBoundary ? undefined : lane;
666
853
  if (el._notifyStatus) {
667
- el._notifyStatus();
668
- } else {
669
- if (!el._transition) {
670
- for (let s = el._subs; s !== null; s = s._nextSub) {
671
- if (s._sub._statusFlags & STATUS_PENDING) {
672
- insertIntoHeap(s._sub, s._sub._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue);
673
- }
674
- }
854
+ if (downstreamBlockStatus) {
855
+ el._notifyStatus(status, error);
856
+ } else {
857
+ el._notifyStatus();
675
858
  }
676
- schedule();
859
+ return;
677
860
  }
678
- }
679
- function notifyStatus(el, status, error) {
680
- el._statusFlags = status | (status !== STATUS_ERROR ? el._statusFlags & STATUS_UNINITIALIZED : 0);
681
- el._error = error;
682
- updatePendingSignal(el);
683
- if (el._notifyStatus) return el._notifyStatus();
684
861
  for (let s = el._subs; s !== null; s = s._nextSub) {
685
862
  s._sub._time = clock;
686
863
  if (s._sub._error !== error) {
687
864
  !s._sub._transition && globalQueue._pendingNodes.push(s._sub);
688
- notifyStatus(s._sub, status, error);
865
+ notifyStatus(s._sub, status, error, downstreamBlockStatus, downstreamLane);
689
866
  }
690
867
  }
691
868
  for (let child = el._child; child !== null; child = child._nextChild) {
@@ -693,7 +870,7 @@ function notifyStatus(el, status, error) {
693
870
  s._sub._time = clock;
694
871
  if (s._sub._error !== error) {
695
872
  !s._sub._transition && globalQueue._pendingNodes.push(s._sub);
696
- notifyStatus(s._sub, status, error);
873
+ notifyStatus(s._sub, status, error, downstreamBlockStatus, downstreamLane);
697
874
  }
698
875
  }
699
876
  }
@@ -711,7 +888,7 @@ function updateIfNecessary(el) {
711
888
  }
712
889
  }
713
890
  }
714
- if (el._flags & REACTIVE_DIRTY) {
891
+ if (el._flags & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || (el._error && el._time < clock)) {
715
892
  recompute(el);
716
893
  }
717
894
  el._flags = REACTIVE_NONE;
@@ -934,8 +1111,8 @@ function getPendingSignal(el) {
934
1111
  return el._pendingSignal;
935
1112
  }
936
1113
  function computePendingState(el) {
937
- if (el._pendingValue !== NOT_PENDING) return true;
938
1114
  const comp = el;
1115
+ if (el._pendingValue !== NOT_PENDING && !(comp._statusFlags & STATUS_UNINITIALIZED)) return true;
939
1116
  return !!(comp._statusFlags & STATUS_PENDING && !(comp._statusFlags & STATUS_UNINITIALIZED));
940
1117
  }
941
1118
  function getPendingValueComputed(el) {
@@ -951,7 +1128,22 @@ function getPendingValueComputed(el) {
951
1128
  return el._pendingValueComputed;
952
1129
  }
953
1130
  function updatePendingSignal(el) {
954
- if (el._pendingSignal) setSignal(el._pendingSignal, computePendingState(el));
1131
+ if (el._pendingSignal) {
1132
+ const pending = computePendingState(el);
1133
+ const sig = el._pendingSignal;
1134
+ setSignal(sig, pending);
1135
+ if (!pending && sig._optimisticLane) {
1136
+ const sourceLane = resolveLane(el);
1137
+ if (sourceLane && sourceLane._pendingAsync.size > 0) {
1138
+ const sigLane = findLane(sig._optimisticLane);
1139
+ if (sigLane !== sourceLane) {
1140
+ mergeLanes(sourceLane, sigLane);
1141
+ }
1142
+ }
1143
+ clearLaneEntry(sig);
1144
+ sig._optimisticLane = undefined;
1145
+ }
1146
+ }
955
1147
  }
956
1148
  function isEqual(a, b) {
957
1149
  return a === b;
@@ -1009,11 +1201,21 @@ function read(el) {
1009
1201
  const asyncCompute = el._firewall || el;
1010
1202
  if (
1011
1203
  c &&
1012
- !optimisticReadActive &&
1013
1204
  asyncCompute._statusFlags & STATUS_PENDING &&
1014
1205
  !(stale && asyncCompute._transition && activeTransition !== asyncCompute._transition)
1015
- )
1016
- throw asyncCompute._error;
1206
+ ) {
1207
+ if (currentOptimisticLane) {
1208
+ const pendingLane = asyncCompute._optimisticLane;
1209
+ const lane = findLane(currentOptimisticLane);
1210
+ if (pendingLane && findLane(pendingLane) === lane && !hasActiveOverride(asyncCompute)) {
1211
+ if (!tracking) link(el, c);
1212
+ throw asyncCompute._error;
1213
+ }
1214
+ } else {
1215
+ if (!tracking) link(el, c);
1216
+ throw asyncCompute._error;
1217
+ }
1218
+ }
1017
1219
  if (el._fn && el._statusFlags & STATUS_ERROR) {
1018
1220
  if (el._time < clock) {
1019
1221
  recompute(el, true);
@@ -1021,7 +1223,7 @@ function read(el) {
1021
1223
  } else throw el._error;
1022
1224
  }
1023
1225
  return !c ||
1024
- optimisticReadActive ||
1226
+ currentOptimisticLane !== null ||
1025
1227
  el._pendingValue === NOT_PENDING ||
1026
1228
  (stale && el._transition && activeTransition !== el._transition)
1027
1229
  ? el._value
@@ -1040,16 +1242,27 @@ function setSignal(el, v) {
1040
1242
  : el._pendingValue;
1041
1243
  if (typeof v === "function") v = v(currentValue);
1042
1244
  const valueChanged = !el._equals || !el._equals(currentValue, v);
1043
- if (!valueChanged) return v;
1245
+ if (!valueChanged) {
1246
+ if (isOptimistic && el._pendingValue !== NOT_PENDING && el._fn) {
1247
+ insertSubs(el, true);
1248
+ schedule();
1249
+ }
1250
+ return v;
1251
+ }
1044
1252
  if (isOptimistic) {
1045
- const isFirstWrite = el._pendingValue === NOT_PENDING;
1046
- if (el._transition && !isFirstWrite) {
1253
+ const alreadyTracked = globalQueue._optimisticNodes.includes(el);
1254
+ if (el._transition && alreadyTracked) {
1047
1255
  globalQueue.initTransition(el._transition);
1048
1256
  }
1049
- if (isFirstWrite) {
1257
+ if (el._pendingValue === NOT_PENDING) {
1050
1258
  el._pendingValue = el._value;
1259
+ }
1260
+ if (!alreadyTracked) {
1051
1261
  globalQueue._optimisticNodes.push(el);
1052
1262
  }
1263
+ el._overrideVersion = (el._overrideVersion || 0) + 1;
1264
+ const lane = getOrCreateLane(el);
1265
+ el._optimisticLane = lane;
1053
1266
  el._value = v;
1054
1267
  } else {
1055
1268
  if (el._pendingValue === NOT_PENDING) globalQueue._pendingNodes.push(el);
@@ -1154,6 +1367,8 @@ function isPending(fn) {
1154
1367
  try {
1155
1368
  fn();
1156
1369
  return foundPending;
1370
+ } catch {
1371
+ return foundPending;
1157
1372
  } finally {
1158
1373
  pendingCheckActive = prevPendingCheck;
1159
1374
  foundPending = prevFoundPending;
@@ -1226,25 +1441,27 @@ function effect(compute, effect, error, initialValue, options) {
1226
1441
  node._errorFn = error;
1227
1442
  node._cleanup = undefined;
1228
1443
  node._type = options?.render ? EFFECT_RENDER : EFFECT_USER;
1229
- node._notifyStatus = () => {
1230
- if (node._statusFlags & STATUS_ERROR) {
1231
- let error = node._error;
1444
+ node._notifyStatus = (status, error) => {
1445
+ const actualStatus = status !== undefined ? status : node._statusFlags;
1446
+ const actualError = error !== undefined ? error : node._error;
1447
+ if (actualStatus & STATUS_ERROR) {
1448
+ let err = actualError;
1232
1449
  node._queue.notify(node, STATUS_PENDING, 0);
1233
1450
  if (node._type === EFFECT_USER) {
1234
1451
  try {
1235
1452
  return node._errorFn
1236
- ? node._errorFn(error, () => {
1453
+ ? node._errorFn(err, () => {
1237
1454
  node._cleanup?.();
1238
1455
  node._cleanup = undefined;
1239
1456
  })
1240
- : console.error(error);
1457
+ : console.error(err);
1241
1458
  } catch (e) {
1242
- error = e;
1459
+ err = e;
1243
1460
  }
1244
1461
  }
1245
- if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw error;
1462
+ if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
1246
1463
  } else if (node._type === EFFECT_RENDER)
1247
- node._queue.notify(node, STATUS_PENDING | STATUS_ERROR, node._statusFlags);
1464
+ node._queue.notify(node, STATUS_PENDING | STATUS_ERROR, actualStatus, actualError);
1248
1465
  };
1249
1466
  recompute(node, true);
1250
1467
  !options?.defer &&
@@ -1992,18 +2209,27 @@ function clearOptimisticStore(store) {
1992
2209
  if (!target || !target[STORE_OPTIMISTIC_OVERRIDE]) return;
1993
2210
  const override = target[STORE_OPTIMISTIC_OVERRIDE];
1994
2211
  const nodes = target[STORE_NODE];
1995
- if (nodes) {
1996
- for (const key of Reflect.ownKeys(override)) {
1997
- if (nodes[key]) {
1998
- const baseValue =
1999
- target[STORE_OVERRIDE] && key in target[STORE_OVERRIDE]
2000
- ? target[STORE_OVERRIDE][key]
2001
- : target[STORE_VALUE][key];
2002
- const value = baseValue === $DELETED ? undefined : baseValue;
2003
- setSignal(nodes[key], isWrappable(value) ? wrap(value, target) : value);
2212
+ setProjectionWriteActive(true);
2213
+ try {
2214
+ if (nodes) {
2215
+ for (const key of Reflect.ownKeys(override)) {
2216
+ if (nodes[key]) {
2217
+ nodes[key]._optimisticLane = undefined;
2218
+ const baseValue =
2219
+ target[STORE_OVERRIDE] && key in target[STORE_OVERRIDE]
2220
+ ? target[STORE_OVERRIDE][key]
2221
+ : target[STORE_VALUE][key];
2222
+ const value = baseValue === $DELETED ? undefined : baseValue;
2223
+ setSignal(nodes[key], isWrappable(value) ? wrap(value, target) : value);
2224
+ }
2225
+ }
2226
+ if (nodes[$TRACK]) {
2227
+ nodes[$TRACK]._optimisticLane = undefined;
2228
+ setSignal(nodes[$TRACK], undefined);
2004
2229
  }
2005
2230
  }
2006
- nodes[$TRACK] && setSignal(nodes[$TRACK], undefined);
2231
+ } finally {
2232
+ setProjectionWriteActive(false);
2007
2233
  }
2008
2234
  delete target[STORE_OPTIMISTIC_OVERRIDE];
2009
2235
  }
@@ -2451,10 +2677,11 @@ function compare(key, a, b) {
2451
2677
  }
2452
2678
  function boundaryComputed(fn, propagationMask) {
2453
2679
  const node = computed(fn, undefined, { lazy: true });
2454
- node._notifyStatus = () => {
2455
- const flags = node._statusFlags;
2680
+ node._notifyStatus = (status, error) => {
2681
+ const flags = status !== undefined ? status : node._statusFlags;
2682
+ const actualError = error !== undefined ? error : node._error;
2456
2683
  node._statusFlags &= ~node._propagationMask;
2457
- node._queue.notify(node, node._propagationMask, flags);
2684
+ node._queue.notify(node, node._propagationMask, flags, actualError);
2458
2685
  };
2459
2686
  node._propagationMask = propagationMask;
2460
2687
  node._preventAutoDisposal = true;
@@ -2482,7 +2709,7 @@ class ConditionalQueue extends Queue {
2482
2709
  if (!type || read(this._disabled)) return;
2483
2710
  return super.run(type);
2484
2711
  }
2485
- notify(node, type, flags) {
2712
+ notify(node, type, flags, error) {
2486
2713
  if (read(this._disabled)) {
2487
2714
  if (type & STATUS_PENDING) {
2488
2715
  if (flags & STATUS_PENDING) {
@@ -2497,12 +2724,12 @@ class ConditionalQueue extends Queue {
2497
2724
  } else if (this._errorNodes.delete(node)) type &= ~STATUS_ERROR;
2498
2725
  }
2499
2726
  }
2500
- return type ? super.notify(node, type, flags) : true;
2727
+ return type ? super.notify(node, type, flags, error ?? node._error) : true;
2501
2728
  }
2502
2729
  }
2503
2730
  class CollectionQueue extends Queue {
2504
2731
  _collectionType;
2505
- _nodes = new Set();
2732
+ _sources = new Set();
2506
2733
  _disabled = signal(false, { pureWrite: true });
2507
2734
  _initialized = false;
2508
2735
  constructor(type) {
@@ -2513,21 +2740,28 @@ class CollectionQueue extends Queue {
2513
2740
  if (!type || read(this._disabled)) return;
2514
2741
  return super.run(type);
2515
2742
  }
2516
- notify(node, type, flags) {
2743
+ notify(node, type, flags, error) {
2517
2744
  if (
2518
2745
  !(type & this._collectionType) ||
2519
2746
  (this._collectionType & STATUS_PENDING && this._initialized)
2520
2747
  )
2521
- return super.notify(node, type, flags);
2748
+ return super.notify(node, type, flags, error);
2522
2749
  if (flags & this._collectionType) {
2523
- this._nodes.add(node);
2524
- if (this._nodes.size === 1) setSignal(this._disabled, true);
2525
- } else if (this._nodes.size > 0) {
2526
- this._nodes.delete(node);
2527
- if (this._nodes.size === 0) setSignal(this._disabled, false);
2750
+ const source = error?._source || node._error?._source;
2751
+ if (source) {
2752
+ const wasEmpty = this._sources.size === 0;
2753
+ this._sources.add(source);
2754
+ if (wasEmpty) setSignal(this._disabled, true);
2755
+ }
2528
2756
  }
2529
2757
  type &= ~this._collectionType;
2530
- return type ? super.notify(node, type, flags) : true;
2758
+ return type ? super.notify(node, type, flags, error) : true;
2759
+ }
2760
+ checkSources() {
2761
+ for (const source of this._sources) {
2762
+ if (!(source._statusFlags & this._collectionType)) this._sources.delete(source);
2763
+ }
2764
+ if (!this._sources.size) setSignal(this._disabled, false);
2531
2765
  }
2532
2766
  }
2533
2767
  var BoundaryMode;
@@ -2543,8 +2777,12 @@ function createBoundary(fn, condition) {
2543
2777
  const disabled = read(queue._disabled);
2544
2778
  tree._propagationMask = disabled ? STATUS_ERROR | STATUS_PENDING : 0;
2545
2779
  if (!disabled) {
2546
- queue._pendingNodes.forEach(node => queue.notify(node, STATUS_PENDING, STATUS_PENDING));
2547
- queue._errorNodes.forEach(node => queue.notify(node, STATUS_ERROR, STATUS_ERROR));
2780
+ queue._pendingNodes.forEach(node =>
2781
+ queue.notify(node, STATUS_PENDING, STATUS_PENDING, node._error)
2782
+ );
2783
+ queue._errorNodes.forEach(node =>
2784
+ queue.notify(node, STATUS_ERROR, STATUS_ERROR, node._error)
2785
+ );
2548
2786
  queue._pendingNodes.clear();
2549
2787
  queue._errorNodes.clear();
2550
2788
  }
@@ -2570,26 +2808,12 @@ function createCollectionBoundary(type, fn, fallback) {
2570
2808
  function createLoadBoundary(fn, fallback) {
2571
2809
  return createCollectionBoundary(STATUS_PENDING, fn, () => fallback());
2572
2810
  }
2573
- function collectErrorSources(node, sources) {
2574
- let root = true;
2575
- let dep = node._deps;
2576
- while (dep !== null) {
2577
- const source = dep._dep;
2578
- if (source._deps && source._statusFlags & STATUS_ERROR) {
2579
- root = false;
2580
- collectErrorSources(source, sources);
2581
- }
2582
- dep = dep._nextDep;
2583
- }
2584
- root && sources.push(node);
2585
- }
2586
2811
  function createErrorBoundary(fn, fallback) {
2587
2812
  return createCollectionBoundary(STATUS_ERROR, fn, queue => {
2588
- let node = queue._nodes.values().next().value;
2589
- return fallback(node._error, () => {
2590
- const sources = [];
2591
- for (const node of queue._nodes) collectErrorSources(node, sources);
2592
- for (const source of sources) recompute(source);
2813
+ let source = queue._sources.values().next().value;
2814
+ const error = source._error?.cause ?? source._error;
2815
+ return fallback(error, () => {
2816
+ for (const source of queue._sources) recompute(source);
2593
2817
  schedule();
2594
2818
  });
2595
2819
  });