@solidjs/signals 0.9.8 → 0.9.10

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,93 @@ 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
+ const parentSource = signal._parentSource;
162
+ const parentLane = parentSource?._optimisticLane ? findLane(parentSource._optimisticLane) : null;
163
+ lane = {
164
+ _source: signal,
165
+ _pendingAsync: new Set(),
166
+ _effectQueues: [[], []],
167
+ _mergedInto: null,
168
+ _transition: activeTransition,
169
+ _parentLane: parentLane
170
+ };
171
+ signalLanes.set(signal, lane);
172
+ activeLanes.add(lane);
173
+ signal._laneVersion = signal._overrideVersion || 0;
174
+ return lane;
175
+ }
176
+ function findLane(lane) {
177
+ while (lane._mergedInto) lane = lane._mergedInto;
178
+ return lane;
179
+ }
180
+ function mergeLanes(lane1, lane2) {
181
+ lane1 = findLane(lane1);
182
+ lane2 = findLane(lane2);
183
+ if (lane1 === lane2) return lane1;
184
+ lane2._mergedInto = lane1;
185
+ for (const node of lane2._pendingAsync) lane1._pendingAsync.add(node);
186
+ lane1._effectQueues[0].push(...lane2._effectQueues[0]);
187
+ lane1._effectQueues[1].push(...lane2._effectQueues[1]);
188
+ return lane1;
189
+ }
190
+ function clearLaneEntry(signal) {
191
+ signalLanes.delete(signal);
192
+ }
193
+ function resolveLane(el) {
194
+ const lane = el._optimisticLane;
195
+ if (!lane) return undefined;
196
+ const root = findLane(lane);
197
+ if (activeLanes.has(root)) return root;
198
+ el._optimisticLane = undefined;
199
+ return undefined;
200
+ }
201
+ function hasActiveOverride(el) {
202
+ return !!(el._optimistic && el._pendingValue !== NOT_PENDING);
203
+ }
204
+ function assignOrMergeLane(el, sourceLane) {
205
+ const sourceRoot = findLane(sourceLane);
206
+ const existing = el._optimisticLane;
207
+ if (existing) {
208
+ if (existing._mergedInto) {
209
+ el._optimisticLane = sourceLane;
210
+ return;
211
+ }
212
+ const existingRoot = findLane(existing);
213
+ if (activeLanes.has(existingRoot)) {
214
+ if (existingRoot !== sourceRoot && !hasActiveOverride(el)) {
215
+ if (sourceRoot._parentLane && findLane(sourceRoot._parentLane) === existingRoot) {
216
+ el._optimisticLane = sourceLane;
217
+ } else if (existingRoot._parentLane && findLane(existingRoot._parentLane) === sourceRoot);
218
+ else {
219
+ mergeLanes(sourceRoot, existingRoot);
220
+ }
221
+ }
222
+ return;
223
+ }
224
+ }
225
+ el._optimisticLane = sourceLane;
226
+ }
227
+ function runLaneEffects(type) {
228
+ for (const lane of activeLanes) {
229
+ if (lane._mergedInto || lane._pendingAsync.size > 0) continue;
230
+ const effects = lane._effectQueues[type - 1];
231
+ if (effects.length) {
232
+ lane._effectQueues[type - 1] = [];
233
+ runQueue(effects, type);
234
+ }
235
+ }
147
236
  }
148
237
  function setProjectionWriteActive(value) {
149
238
  projectionWriteActive = value;
@@ -156,7 +245,6 @@ function schedule() {
156
245
  class Queue {
157
246
  _parent = null;
158
247
  _queues = [[], []];
159
- _optimisticQueues = [[], []];
160
248
  _children = [];
161
249
  created = clock;
162
250
  addChild(child) {
@@ -170,30 +258,26 @@ class Queue {
170
258
  child._parent = null;
171
259
  }
172
260
  }
173
- notify(node, mask, flags) {
174
- if (this._parent) return this._parent.notify(node, mask, flags);
261
+ notify(node, mask, flags, error) {
262
+ if (this._parent) return this._parent.notify(node, mask, flags, error);
175
263
  return false;
176
264
  }
177
- _runQueue(type, queues, method) {
178
- if (queues[type - 1].length) {
179
- const effects = queues[type - 1];
180
- queues[type - 1] = [];
265
+ run(type) {
266
+ if (this._queues[type - 1].length) {
267
+ const effects = this._queues[type - 1];
268
+ this._queues[type - 1] = [];
181
269
  runQueue(effects, type);
182
270
  }
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");
271
+ for (let i = 0; i < this._children.length; i++) this._children[i].run?.(type);
192
272
  }
193
273
  enqueue(type, fn) {
194
274
  if (type) {
195
- const queue = optimisticReadActive ? this._optimisticQueues : this._queues;
196
- queue[type - 1].push(fn);
275
+ if (currentOptimisticLane) {
276
+ const lane = findLane(currentOptimisticLane);
277
+ lane._effectQueues[type - 1].push(fn);
278
+ } else {
279
+ this._queues[type - 1].push(fn);
280
+ }
197
281
  }
198
282
  schedule();
199
283
  }
@@ -242,23 +326,23 @@ class GlobalQueue extends Queue {
242
326
  this._pendingNodes = [];
243
327
  this._optimisticNodes = [];
244
328
  this._optimisticStores = new Set();
245
- this.runOptimistic(EFFECT_RENDER);
246
- this.runOptimistic(EFFECT_USER);
247
- this.stashQueues(activeTransition.queueStash);
329
+ runLaneEffects(EFFECT_RENDER);
330
+ runLaneEffects(EFFECT_USER);
331
+ this.stashQueues(activeTransition._queueStash);
248
332
  clock++;
249
- scheduled = false;
250
- runTransitionPending(activeTransition.pendingNodes);
333
+ scheduled = dirtyQueue._max >= dirtyQueue._min;
334
+ reassignPendingTransition(activeTransition._pendingNodes);
251
335
  activeTransition = null;
252
336
  finalizePureQueue(null, true);
253
337
  return;
254
338
  }
255
- this._pendingNodes !== activeTransition.pendingNodes &&
256
- this._pendingNodes.push(...activeTransition.pendingNodes);
257
- this.restoreQueues(activeTransition.queueStash);
339
+ this._pendingNodes !== activeTransition._pendingNodes &&
340
+ this._pendingNodes.push(...activeTransition._pendingNodes);
341
+ this.restoreQueues(activeTransition._queueStash);
258
342
  transitions.delete(activeTransition);
259
343
  const completingTransition = activeTransition;
260
344
  activeTransition = null;
261
- runTransitionPending(this._pendingNodes);
345
+ reassignPendingTransition(this._pendingNodes);
262
346
  finalizePureQueue(completingTransition);
263
347
  } else {
264
348
  if (transitions.size) runHeap(zombieQueue, GlobalQueue._update);
@@ -266,23 +350,24 @@ class GlobalQueue extends Queue {
266
350
  }
267
351
  clock++;
268
352
  scheduled = dirtyQueue._max >= dirtyQueue._min;
269
- this.runOptimistic(EFFECT_RENDER);
353
+ runLaneEffects(EFFECT_RENDER);
270
354
  this.run(EFFECT_RENDER);
271
- this.runOptimistic(EFFECT_USER);
355
+ runLaneEffects(EFFECT_USER);
272
356
  this.run(EFFECT_USER);
273
357
  } finally {
274
358
  this._running = false;
275
359
  }
276
360
  }
277
- notify(node, mask, flags) {
361
+ notify(node, mask, flags, error) {
278
362
  if (mask & STATUS_PENDING) {
279
363
  if (flags & STATUS_PENDING) {
364
+ const actualError = error !== undefined ? error : node._error;
280
365
  if (
281
366
  activeTransition &&
282
- node._error &&
283
- !activeTransition.asyncNodes.includes(node._error.cause)
367
+ actualError &&
368
+ !activeTransition._asyncNodes.includes(actualError._source)
284
369
  ) {
285
- activeTransition.asyncNodes.push(node._error.cause);
370
+ activeTransition._asyncNodes.push(actualError._source);
286
371
  schedule();
287
372
  }
288
373
  }
@@ -293,47 +378,63 @@ class GlobalQueue extends Queue {
293
378
  initTransition(transition) {
294
379
  if (transition) transition = currentTransition(transition);
295
380
  if (transition && transition === activeTransition) return;
296
- if (!transition && activeTransition && activeTransition.time === clock) return;
381
+ if (!transition && activeTransition && activeTransition._time === clock) return;
297
382
  if (!activeTransition) {
298
383
  activeTransition = transition ?? {
299
- time: clock,
300
- pendingNodes: [],
301
- asyncNodes: [],
302
- optimisticNodes: [],
303
- optimisticStores: new Set(),
304
- actions: [],
305
- queueStash: { _queues: [[], []], _children: [] },
306
- done: false
384
+ _time: clock,
385
+ _pendingNodes: [],
386
+ _asyncNodes: [],
387
+ _optimisticNodes: [],
388
+ _optimisticStores: new Set(),
389
+ _actions: [],
390
+ _queueStash: { _queues: [[], []], _children: [] },
391
+ _done: false
307
392
  };
308
393
  } else if (transition) {
309
- activeTransition.done = transition;
310
- transition.actions.push(...activeTransition.actions);
311
- transitions.delete(activeTransition);
394
+ const outgoing = activeTransition;
395
+ outgoing._done = transition;
396
+ transition._actions.push(...outgoing._actions);
397
+ for (const lane of activeLanes) {
398
+ if (lane._transition === outgoing) lane._transition = transition;
399
+ }
400
+ transition._optimisticNodes.push(...outgoing._optimisticNodes);
401
+ for (const store of outgoing._optimisticStores) {
402
+ transition._optimisticStores.add(store);
403
+ }
404
+ transitions.delete(outgoing);
312
405
  activeTransition = transition;
313
406
  }
314
407
  transitions.add(activeTransition);
315
- activeTransition.time = clock;
408
+ activeTransition._time = clock;
316
409
  for (let i = 0; i < this._pendingNodes.length; i++) {
317
410
  const n = this._pendingNodes[i];
318
411
  n._transition = activeTransition;
319
- activeTransition.pendingNodes.push(n);
412
+ activeTransition._pendingNodes.push(n);
320
413
  }
321
- this._pendingNodes = activeTransition.pendingNodes;
414
+ this._pendingNodes = activeTransition._pendingNodes;
322
415
  for (let i = 0; i < this._optimisticNodes.length; i++) {
323
416
  const node = this._optimisticNodes[i];
324
417
  node._transition = activeTransition;
325
- activeTransition.optimisticNodes.push(node);
418
+ activeTransition._optimisticNodes.push(node);
326
419
  }
327
- this._optimisticNodes = activeTransition.optimisticNodes;
328
- for (const store of this._optimisticStores) {
329
- activeTransition.optimisticStores.add(store);
420
+ this._optimisticNodes = activeTransition._optimisticNodes;
421
+ for (const lane of activeLanes) {
422
+ if (!lane._transition) lane._transition = activeTransition;
330
423
  }
331
- this._optimisticStores = activeTransition.optimisticStores;
424
+ for (const store of this._optimisticStores) activeTransition._optimisticStores.add(store);
425
+ this._optimisticStores = activeTransition._optimisticStores;
332
426
  }
333
427
  }
334
428
  function insertSubs(node, optimistic = false) {
429
+ const sourceLane = node._optimisticLane || currentOptimisticLane;
335
430
  for (let s = node._subs; s !== null; s = s._nextSub) {
336
- if (optimistic) s._sub._flags |= REACTIVE_OPTIMISTIC_DIRTY;
431
+ if (optimistic && sourceLane) {
432
+ s._sub._flags |= REACTIVE_OPTIMISTIC_DIRTY;
433
+ assignOrMergeLane(s._sub, sourceLane);
434
+ } else if (optimistic) {
435
+ s._sub._flags |= REACTIVE_OPTIMISTIC_DIRTY;
436
+ s._sub._optimisticLane = undefined;
437
+ }
337
438
  const sub = s._sub;
338
439
  if (sub._type === EFFECT_TRACKED) {
339
440
  if (!sub._modified) {
@@ -349,9 +450,8 @@ function insertSubs(node, optimistic = false) {
349
450
  }
350
451
  function finalizePureQueue(completingTransition = null, incomplete = false) {
351
452
  let resolvePending = !incomplete;
352
- if (dirtyQueue._max >= dirtyQueue._min) {
353
- runHeap(dirtyQueue, GlobalQueue._update);
354
- }
453
+ if (!incomplete) checkBoundaryChildren(globalQueue);
454
+ if (dirtyQueue._max >= dirtyQueue._min) runHeap(dirtyQueue, GlobalQueue._update);
355
455
  if (resolvePending) {
356
456
  const pendingNodes = globalQueue._pendingNodes;
357
457
  for (let i = 0; i < pendingNodes.length; i++) {
@@ -361,15 +461,17 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
361
461
  n._pendingValue = NOT_PENDING;
362
462
  if (n._type && n._type !== EFFECT_TRACKED) n._modified = true;
363
463
  }
464
+ n._statusFlags &= ~STATUS_UNINITIALIZED;
364
465
  if (n._fn) GlobalQueue._dispose(n, false, true);
365
466
  }
366
467
  pendingNodes.length = 0;
367
468
  const optimisticNodes = completingTransition
368
- ? completingTransition.optimisticNodes
469
+ ? completingTransition._optimisticNodes
369
470
  : globalQueue._optimisticNodes;
370
471
  for (let i = 0; i < optimisticNodes.length; i++) {
371
472
  const n = optimisticNodes[i];
372
473
  const original = n._pendingValue;
474
+ n._optimisticLane = undefined;
373
475
  if (original !== NOT_PENDING && n._value !== original) {
374
476
  n._value = original;
375
477
  insertSubs(n, true);
@@ -379,7 +481,7 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
379
481
  }
380
482
  optimisticNodes.length = 0;
381
483
  const optimisticStores = completingTransition
382
- ? completingTransition.optimisticStores
484
+ ? completingTransition._optimisticStores
383
485
  : globalQueue._optimisticStores;
384
486
  if (GlobalQueue._clearOptimisticStore && optimisticStores.size) {
385
487
  for (const store of optimisticStores) {
@@ -388,13 +490,35 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
388
490
  optimisticStores.clear();
389
491
  schedule();
390
492
  }
493
+ for (const lane of activeLanes) {
494
+ const owned = completingTransition
495
+ ? lane._transition === completingTransition
496
+ : !lane._transition;
497
+ if (!owned) continue;
498
+ if (!lane._mergedInto) {
499
+ if (lane._effectQueues[0].length) runQueue(lane._effectQueues[0], EFFECT_RENDER);
500
+ if (lane._effectQueues[1].length) runQueue(lane._effectQueues[1], EFFECT_USER);
501
+ }
502
+ if (lane._source._optimisticLane === lane) lane._source._optimisticLane = undefined;
503
+ lane._pendingAsync.clear();
504
+ lane._effectQueues[0].length = 0;
505
+ lane._effectQueues[1].length = 0;
506
+ activeLanes.delete(lane);
507
+ signalLanes.delete(lane._source);
508
+ }
509
+ }
510
+ }
511
+ function checkBoundaryChildren(queue) {
512
+ for (const child of queue._children) {
513
+ child.checkSources?.();
514
+ checkBoundaryChildren(child);
391
515
  }
392
516
  }
393
517
  function trackOptimisticStore(store) {
394
518
  globalQueue._optimisticStores.add(store);
395
519
  schedule();
396
520
  }
397
- function runTransitionPending(pendingNodes) {
521
+ function reassignPendingTransition(pendingNodes) {
398
522
  for (let i = 0; i < pendingNodes.length; i++) {
399
523
  pendingNodes[i]._transition = activeTransition;
400
524
  }
@@ -411,20 +535,21 @@ function runQueue(queue, type) {
411
535
  for (let i = 0; i < queue.length; i++) queue[i](type);
412
536
  }
413
537
  function transitionComplete(transition) {
414
- if (transition.done) return true;
415
- if (transition.actions.length) return false;
538
+ if (transition._done) return true;
539
+ if (transition._actions.length) return false;
416
540
  let done = true;
417
- for (let i = 0; i < transition.asyncNodes.length; i++) {
418
- if (transition.asyncNodes[i]._statusFlags & STATUS_PENDING) {
541
+ for (let i = 0; i < transition._asyncNodes.length; i++) {
542
+ const node = transition._asyncNodes[i];
543
+ if (node._statusFlags & STATUS_PENDING && node._error?._source === node) {
419
544
  done = false;
420
545
  break;
421
546
  }
422
547
  }
423
- done && (transition.done = true);
548
+ done && (transition._done = true);
424
549
  return done;
425
550
  }
426
551
  function currentTransition(transition) {
427
- while (transition.done && typeof transition.done === "object") transition = transition.done;
552
+ while (transition._done && typeof transition._done === "object") transition = transition._done;
428
553
  return transition;
429
554
  }
430
555
  function runInTransition(transition, fn) {
@@ -448,11 +573,11 @@ function action(genFn) {
448
573
  const it = genFn(...args);
449
574
  globalQueue.initTransition();
450
575
  let ctx = activeTransition;
451
- ctx.actions.push(it);
576
+ ctx._actions.push(it);
452
577
  const done = (v, e) => {
453
578
  ctx = currentTransition(ctx);
454
- const i = ctx.actions.indexOf(it);
455
- if (i >= 0) ctx.actions.splice(i, 1);
579
+ const i = ctx._actions.indexOf(it);
580
+ if (i >= 0) ctx._actions.splice(i, 1);
456
581
  activeTransition = ctx;
457
582
  schedule();
458
583
  e ? reject(e) : resolve(v);
@@ -499,7 +624,7 @@ function recompute(el, create = false) {
499
624
  if (el._transition && (!isEffect || activeTransition) && activeTransition !== el._transition)
500
625
  globalQueue.initTransition(el._transition);
501
626
  deleteFromHeap(el, el._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue);
502
- if (el._transition) disposeChildren(el);
627
+ if (el._transition || isEffect === EFFECT_TRACKED) disposeChildren(el);
503
628
  else {
504
629
  markDisposal(el);
505
630
  el._pendingDisposal = el._disposal;
@@ -509,7 +634,8 @@ function recompute(el, create = false) {
509
634
  }
510
635
  }
511
636
  const isOptimisticDirty = !!(el._flags & REACTIVE_OPTIMISTIC_DIRTY);
512
- const hasOptimisticOverride = el._optimistic && el._pendingValue !== NOT_PENDING;
637
+ const hasOverride = hasActiveOverride(el);
638
+ const wasPending = !!(el._statusFlags & STATUS_PENDING);
513
639
  const oldcontext = context;
514
640
  context = el;
515
641
  el._depsTail = null;
@@ -518,17 +644,36 @@ function recompute(el, create = false) {
518
644
  let value = el._pendingValue === NOT_PENDING ? el._value : el._pendingValue;
519
645
  let oldHeight = el._height;
520
646
  let prevTracking = tracking;
521
- let prevOptimisticRead = optimisticReadActive;
647
+ let prevLane = currentOptimisticLane;
522
648
  tracking = true;
523
- if (isOptimisticDirty) setOptimisticReadActive(true);
649
+ if (isOptimisticDirty) {
650
+ const lane = resolveLane(el);
651
+ if (lane) setCurrentOptimisticLane(lane);
652
+ }
524
653
  try {
525
654
  value = handleAsync(el, el._fn(value));
526
655
  clearStatus(el);
656
+ const resolvedLane = resolveLane(el);
657
+ if (resolvedLane) {
658
+ resolvedLane._pendingAsync.delete(el);
659
+ updatePendingSignal(resolvedLane._source);
660
+ }
527
661
  } catch (e) {
528
- if (e instanceof NotReadyError) {
529
- if (e.cause !== el) link(e.cause, el);
530
- notifyStatus(el, STATUS_PENDING, e);
531
- } else notifyStatus(el, STATUS_ERROR, e);
662
+ if (e instanceof NotReadyError && currentOptimisticLane) {
663
+ const lane = findLane(currentOptimisticLane);
664
+ if (lane._source !== el) {
665
+ lane._pendingAsync.add(el);
666
+ el._optimisticLane = lane;
667
+ updatePendingSignal(lane._source);
668
+ }
669
+ }
670
+ notifyStatus(
671
+ el,
672
+ e instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR,
673
+ e,
674
+ undefined,
675
+ e instanceof NotReadyError ? el._optimisticLane : undefined
676
+ );
532
677
  } finally {
533
678
  tracking = prevTracking;
534
679
  el._flags = REACTIVE_NONE;
@@ -544,21 +689,34 @@ function recompute(el, create = false) {
544
689
  if (depsTail !== null) depsTail._nextDep = null;
545
690
  else el._deps = null;
546
691
  }
547
- const valueChanged =
548
- !el._equals ||
549
- !el._equals(el._pendingValue === NOT_PENDING ? el._value : el._pendingValue, value);
692
+ const compareValue = hasOverride
693
+ ? el._value
694
+ : el._pendingValue === NOT_PENDING
695
+ ? el._value
696
+ : el._pendingValue;
697
+ const valueChanged = !el._equals || !el._equals(compareValue, value);
550
698
  if (valueChanged) {
699
+ const prevVisible = hasOverride ? el._value : undefined;
551
700
  if (create || (isEffect && activeTransition !== el._transition) || isOptimisticDirty)
552
701
  el._value = value;
553
702
  else el._pendingValue = value;
554
- insertSubs(el, isOptimisticDirty || hasOptimisticOverride);
703
+ if (hasOverride && !isOptimisticDirty && wasPending) {
704
+ const ov = el._overrideVersion || 0;
705
+ const lv = el._laneVersion || 0;
706
+ if (ov <= lv) el._value = value;
707
+ }
708
+ if (!hasOverride || isOptimisticDirty || el._value !== prevVisible) {
709
+ insertSubs(el, isOptimisticDirty || hasOverride);
710
+ }
711
+ } else if (hasOverride) {
712
+ el._pendingValue = value;
555
713
  } else if (el._height != oldHeight) {
556
714
  for (let s = el._subs; s !== null; s = s._nextSub) {
557
715
  insertIntoHeapHeight(s._sub, s._sub._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue);
558
716
  }
559
717
  }
560
718
  }
561
- setOptimisticReadActive(prevOptimisticRead);
719
+ setCurrentOptimisticLane(prevLane);
562
720
  (!create || el._statusFlags & STATUS_PENDING) &&
563
721
  !el._transition &&
564
722
  !(activeTransition && el._optimistic) &&
@@ -581,27 +739,40 @@ function handleAsync(el, result, setter) {
581
739
  const handleError = error => {
582
740
  if (el._inFlight !== result) return;
583
741
  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);
742
+ notifyStatus(el, error instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR, error);
588
743
  el._time = clock;
589
744
  };
590
745
  const asyncWrite = (value, then) => {
591
746
  if (el._inFlight !== result) return;
747
+ if (el._flags & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
592
748
  globalQueue.initTransition(el._transition);
593
749
  clearStatus(el);
750
+ const lane = resolveLane(el);
751
+ if (lane) lane._pendingAsync.delete(el);
594
752
  if (setter) setter(value);
595
753
  else if (el._optimistic) {
596
- const hasOverride = el._pendingValue !== NOT_PENDING;
754
+ const hadOverride = el._pendingValue !== NOT_PENDING;
597
755
  if (el._fn) el._pendingValue = value;
598
- if (!hasOverride) {
756
+ if (!hadOverride) {
599
757
  el._value = value;
600
758
  insertSubs(el);
601
759
  }
602
760
  el._time = clock;
603
- schedule();
604
- } else setSignal(el, () => value);
761
+ } else if (lane) {
762
+ const prevValue = el._value;
763
+ const equals = el._equals;
764
+ if (!equals || !equals(value, prevValue)) {
765
+ el._value = value;
766
+ el._time = clock;
767
+ if (el._pendingValueComputed) {
768
+ setSignal(el._pendingValueComputed, value);
769
+ }
770
+ insertSubs(el, true);
771
+ }
772
+ } else {
773
+ setSignal(el, () => value);
774
+ }
775
+ schedule();
605
776
  flush();
606
777
  then?.();
607
778
  };
@@ -660,32 +831,51 @@ function handleAsync(el, result, setter) {
660
831
  return syncValue;
661
832
  }
662
833
  function clearStatus(el) {
663
- el._statusFlags = STATUS_NONE;
834
+ el._statusFlags = el._statusFlags & STATUS_UNINITIALIZED;
664
835
  el._error = null;
665
836
  updatePendingSignal(el);
837
+ el._notifyStatus?.();
838
+ }
839
+ function notifyStatus(el, status, error, blockStatus, lane) {
840
+ if (
841
+ status === STATUS_ERROR &&
842
+ !(error instanceof StatusError) &&
843
+ !(error instanceof NotReadyError)
844
+ )
845
+ error = new StatusError(el, error);
846
+ const isSource = error instanceof NotReadyError && error._source === el;
847
+ const isOptimisticBoundary = status === STATUS_PENDING && el._optimistic && !isSource;
848
+ const startsBlocking = isOptimisticBoundary && hasActiveOverride(el);
849
+ if (!blockStatus) {
850
+ el._statusFlags =
851
+ status | (status !== STATUS_ERROR ? el._statusFlags & STATUS_UNINITIALIZED : 0);
852
+ el._error = error;
853
+ updatePendingSignal(el);
854
+ }
855
+ if (lane && !blockStatus) {
856
+ assignOrMergeLane(el, lane);
857
+ }
858
+ if (startsBlocking && activeTransition && error instanceof NotReadyError) {
859
+ const source = error._source;
860
+ if (!activeTransition._asyncNodes.includes(source)) {
861
+ activeTransition._asyncNodes.push(source);
862
+ }
863
+ }
864
+ const downstreamBlockStatus = blockStatus || startsBlocking;
865
+ const downstreamLane = blockStatus || isOptimisticBoundary ? undefined : lane;
666
866
  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
- }
867
+ if (downstreamBlockStatus) {
868
+ el._notifyStatus(status, error);
869
+ } else {
870
+ el._notifyStatus();
675
871
  }
676
- schedule();
872
+ return;
677
873
  }
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
874
  for (let s = el._subs; s !== null; s = s._nextSub) {
685
875
  s._sub._time = clock;
686
876
  if (s._sub._error !== error) {
687
877
  !s._sub._transition && globalQueue._pendingNodes.push(s._sub);
688
- notifyStatus(s._sub, status, error);
878
+ notifyStatus(s._sub, status, error, downstreamBlockStatus, downstreamLane);
689
879
  }
690
880
  }
691
881
  for (let child = el._child; child !== null; child = child._nextChild) {
@@ -693,7 +883,7 @@ function notifyStatus(el, status, error) {
693
883
  s._sub._time = clock;
694
884
  if (s._sub._error !== error) {
695
885
  !s._sub._transition && globalQueue._pendingNodes.push(s._sub);
696
- notifyStatus(s._sub, status, error);
886
+ notifyStatus(s._sub, status, error, downstreamBlockStatus, downstreamLane);
697
887
  }
698
888
  }
699
889
  }
@@ -711,7 +901,7 @@ function updateIfNecessary(el) {
711
901
  }
712
902
  }
713
903
  }
714
- if (el._flags & REACTIVE_DIRTY) {
904
+ if (el._flags & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || (el._error && el._time < clock)) {
715
905
  recompute(el);
716
906
  }
717
907
  el._flags = REACTIVE_NONE;
@@ -929,29 +1119,60 @@ function optimisticComputed(fn, initialValue, options) {
929
1119
  function getPendingSignal(el) {
930
1120
  if (!el._pendingSignal) {
931
1121
  el._pendingSignal = optimisticSignal(false, { pureWrite: true });
1122
+ if (el._parentSource) {
1123
+ el._pendingSignal._parentSource = el;
1124
+ }
932
1125
  if (computePendingState(el)) setSignal(el._pendingSignal, true);
933
1126
  }
934
1127
  return el._pendingSignal;
935
1128
  }
936
1129
  function computePendingState(el) {
937
- if (el._pendingValue !== NOT_PENDING) return true;
938
1130
  const comp = el;
1131
+ if (el._optimistic && el._pendingValue !== NOT_PENDING) {
1132
+ if (comp._statusFlags & STATUS_PENDING && !(comp._statusFlags & STATUS_UNINITIALIZED))
1133
+ return true;
1134
+ if (el._parentSource) {
1135
+ const lane = el._optimisticLane ? findLane(el._optimisticLane) : null;
1136
+ return !!(lane && lane._pendingAsync.size > 0);
1137
+ }
1138
+ return true;
1139
+ }
1140
+ if (el._pendingValue !== NOT_PENDING && !(comp._statusFlags & STATUS_UNINITIALIZED)) return true;
939
1141
  return !!(comp._statusFlags & STATUS_PENDING && !(comp._statusFlags & STATUS_UNINITIALIZED));
940
1142
  }
941
1143
  function getPendingValueComputed(el) {
942
1144
  if (!el._pendingValueComputed) {
943
1145
  const prevPending = pendingReadActive;
944
1146
  pendingReadActive = false;
1147
+ const prevCheck = pendingCheckActive;
1148
+ pendingCheckActive = false;
945
1149
  const prevContext = context;
946
1150
  context = null;
947
1151
  el._pendingValueComputed = optimisticComputed(() => read(el));
1152
+ el._pendingValueComputed._parentSource = el;
948
1153
  context = prevContext;
1154
+ pendingCheckActive = prevCheck;
949
1155
  pendingReadActive = prevPending;
950
1156
  }
951
1157
  return el._pendingValueComputed;
952
1158
  }
953
1159
  function updatePendingSignal(el) {
954
- if (el._pendingSignal) setSignal(el._pendingSignal, computePendingState(el));
1160
+ if (el._pendingSignal) {
1161
+ const pending = computePendingState(el);
1162
+ const sig = el._pendingSignal;
1163
+ setSignal(sig, pending);
1164
+ if (!pending && sig._optimisticLane) {
1165
+ const sourceLane = resolveLane(el);
1166
+ if (sourceLane && sourceLane._pendingAsync.size > 0) {
1167
+ const sigLane = findLane(sig._optimisticLane);
1168
+ if (sigLane !== sourceLane) {
1169
+ mergeLanes(sourceLane, sigLane);
1170
+ }
1171
+ }
1172
+ clearLaneEntry(sig);
1173
+ sig._optimisticLane = undefined;
1174
+ }
1175
+ }
955
1176
  }
956
1177
  function isEqual(a, b) {
957
1178
  return a === b;
@@ -966,6 +1187,22 @@ function untrack(fn) {
966
1187
  }
967
1188
  }
968
1189
  function read(el) {
1190
+ if (pendingReadActive) {
1191
+ const pendingComputed = getPendingValueComputed(el);
1192
+ const prevPending = pendingReadActive;
1193
+ pendingReadActive = false;
1194
+ const value = read(pendingComputed);
1195
+ pendingReadActive = prevPending;
1196
+ if (pendingComputed._statusFlags & STATUS_PENDING) return el._value;
1197
+ if (stale && currentOptimisticLane && pendingComputed._optimisticLane) {
1198
+ const pcLane = findLane(pendingComputed._optimisticLane);
1199
+ const curLane = findLane(currentOptimisticLane);
1200
+ if (pcLane !== curLane && pcLane._pendingAsync.size > 0) {
1201
+ return el._value;
1202
+ }
1203
+ }
1204
+ return value;
1205
+ }
969
1206
  if (pendingCheckActive) {
970
1207
  const target = el._firewall || el;
971
1208
  const pendingSig = getPendingSignal(target);
@@ -977,15 +1214,6 @@ function read(el) {
977
1214
  pendingCheckActive = prevCheck;
978
1215
  return el._value;
979
1216
  }
980
- if (pendingReadActive) {
981
- const pendingComputed = getPendingValueComputed(el);
982
- const prevPending = pendingReadActive;
983
- pendingReadActive = false;
984
- const value = read(pendingComputed);
985
- pendingReadActive = prevPending;
986
- if (pendingComputed._statusFlags & STATUS_PENDING) return el._value;
987
- return value;
988
- }
989
1217
  let c = context;
990
1218
  if (c?._root) c = c._parentComputed;
991
1219
  if (refreshing && el._fn) recompute(el);
@@ -1009,11 +1237,21 @@ function read(el) {
1009
1237
  const asyncCompute = el._firewall || el;
1010
1238
  if (
1011
1239
  c &&
1012
- !optimisticReadActive &&
1013
1240
  asyncCompute._statusFlags & STATUS_PENDING &&
1014
1241
  !(stale && asyncCompute._transition && activeTransition !== asyncCompute._transition)
1015
- )
1016
- throw asyncCompute._error;
1242
+ ) {
1243
+ if (currentOptimisticLane) {
1244
+ const pendingLane = asyncCompute._optimisticLane;
1245
+ const lane = findLane(currentOptimisticLane);
1246
+ if (pendingLane && findLane(pendingLane) === lane && !hasActiveOverride(asyncCompute)) {
1247
+ if (!tracking) link(el, c);
1248
+ throw asyncCompute._error;
1249
+ }
1250
+ } else {
1251
+ if (!tracking) link(el, c);
1252
+ throw asyncCompute._error;
1253
+ }
1254
+ }
1017
1255
  if (el._fn && el._statusFlags & STATUS_ERROR) {
1018
1256
  if (el._time < clock) {
1019
1257
  recompute(el, true);
@@ -1021,7 +1259,7 @@ function read(el) {
1021
1259
  } else throw el._error;
1022
1260
  }
1023
1261
  return !c ||
1024
- optimisticReadActive ||
1262
+ currentOptimisticLane !== null ||
1025
1263
  el._pendingValue === NOT_PENDING ||
1026
1264
  (stale && el._transition && activeTransition !== el._transition)
1027
1265
  ? el._value
@@ -1040,7 +1278,13 @@ function setSignal(el, v) {
1040
1278
  : el._pendingValue;
1041
1279
  if (typeof v === "function") v = v(currentValue);
1042
1280
  const valueChanged = !el._equals || !el._equals(currentValue, v);
1043
- if (!valueChanged) return v;
1281
+ if (!valueChanged) {
1282
+ if (isOptimistic && el._pendingValue !== NOT_PENDING && el._fn) {
1283
+ insertSubs(el, true);
1284
+ schedule();
1285
+ }
1286
+ return v;
1287
+ }
1044
1288
  if (isOptimistic) {
1045
1289
  const alreadyTracked = globalQueue._optimisticNodes.includes(el);
1046
1290
  if (el._transition && alreadyTracked) {
@@ -1052,6 +1296,9 @@ function setSignal(el, v) {
1052
1296
  if (!alreadyTracked) {
1053
1297
  globalQueue._optimisticNodes.push(el);
1054
1298
  }
1299
+ el._overrideVersion = (el._overrideVersion || 0) + 1;
1300
+ const lane = getOrCreateLane(el);
1301
+ el._optimisticLane = lane;
1055
1302
  el._value = v;
1056
1303
  } else {
1057
1304
  if (el._pendingValue === NOT_PENDING) globalQueue._pendingNodes.push(el);
@@ -1156,6 +1403,8 @@ function isPending(fn) {
1156
1403
  try {
1157
1404
  fn();
1158
1405
  return foundPending;
1406
+ } catch {
1407
+ return foundPending;
1159
1408
  } finally {
1160
1409
  pendingCheckActive = prevPendingCheck;
1161
1410
  foundPending = prevFoundPending;
@@ -1228,25 +1477,27 @@ function effect(compute, effect, error, initialValue, options) {
1228
1477
  node._errorFn = error;
1229
1478
  node._cleanup = undefined;
1230
1479
  node._type = options?.render ? EFFECT_RENDER : EFFECT_USER;
1231
- node._notifyStatus = () => {
1232
- if (node._statusFlags & STATUS_ERROR) {
1233
- let error = node._error;
1480
+ node._notifyStatus = (status, error) => {
1481
+ const actualStatus = status !== undefined ? status : node._statusFlags;
1482
+ const actualError = error !== undefined ? error : node._error;
1483
+ if (actualStatus & STATUS_ERROR) {
1484
+ let err = actualError;
1234
1485
  node._queue.notify(node, STATUS_PENDING, 0);
1235
1486
  if (node._type === EFFECT_USER) {
1236
1487
  try {
1237
1488
  return node._errorFn
1238
- ? node._errorFn(error, () => {
1489
+ ? node._errorFn(err, () => {
1239
1490
  node._cleanup?.();
1240
1491
  node._cleanup = undefined;
1241
1492
  })
1242
- : console.error(error);
1493
+ : console.error(err);
1243
1494
  } catch (e) {
1244
- error = e;
1495
+ err = e;
1245
1496
  }
1246
1497
  }
1247
- if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw error;
1498
+ if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
1248
1499
  } else if (node._type === EFFECT_RENDER)
1249
- node._queue.notify(node, STATUS_PENDING | STATUS_ERROR, node._statusFlags);
1500
+ node._queue.notify(node, STATUS_PENDING | STATUS_ERROR, actualStatus, actualError);
1250
1501
  };
1251
1502
  recompute(node, true);
1252
1503
  !options?.defer &&
@@ -1994,18 +2245,27 @@ function clearOptimisticStore(store) {
1994
2245
  if (!target || !target[STORE_OPTIMISTIC_OVERRIDE]) return;
1995
2246
  const override = target[STORE_OPTIMISTIC_OVERRIDE];
1996
2247
  const nodes = target[STORE_NODE];
1997
- if (nodes) {
1998
- for (const key of Reflect.ownKeys(override)) {
1999
- if (nodes[key]) {
2000
- const baseValue =
2001
- target[STORE_OVERRIDE] && key in target[STORE_OVERRIDE]
2002
- ? target[STORE_OVERRIDE][key]
2003
- : target[STORE_VALUE][key];
2004
- const value = baseValue === $DELETED ? undefined : baseValue;
2005
- setSignal(nodes[key], isWrappable(value) ? wrap(value, target) : value);
2248
+ setProjectionWriteActive(true);
2249
+ try {
2250
+ if (nodes) {
2251
+ for (const key of Reflect.ownKeys(override)) {
2252
+ if (nodes[key]) {
2253
+ nodes[key]._optimisticLane = undefined;
2254
+ const baseValue =
2255
+ target[STORE_OVERRIDE] && key in target[STORE_OVERRIDE]
2256
+ ? target[STORE_OVERRIDE][key]
2257
+ : target[STORE_VALUE][key];
2258
+ const value = baseValue === $DELETED ? undefined : baseValue;
2259
+ setSignal(nodes[key], isWrappable(value) ? wrap(value, target) : value);
2260
+ }
2261
+ }
2262
+ if (nodes[$TRACK]) {
2263
+ nodes[$TRACK]._optimisticLane = undefined;
2264
+ setSignal(nodes[$TRACK], undefined);
2006
2265
  }
2007
2266
  }
2008
- nodes[$TRACK] && setSignal(nodes[$TRACK], undefined);
2267
+ } finally {
2268
+ setProjectionWriteActive(false);
2009
2269
  }
2010
2270
  delete target[STORE_OPTIMISTIC_OVERRIDE];
2011
2271
  }
@@ -2453,10 +2713,11 @@ function compare(key, a, b) {
2453
2713
  }
2454
2714
  function boundaryComputed(fn, propagationMask) {
2455
2715
  const node = computed(fn, undefined, { lazy: true });
2456
- node._notifyStatus = () => {
2457
- const flags = node._statusFlags;
2716
+ node._notifyStatus = (status, error) => {
2717
+ const flags = status !== undefined ? status : node._statusFlags;
2718
+ const actualError = error !== undefined ? error : node._error;
2458
2719
  node._statusFlags &= ~node._propagationMask;
2459
- node._queue.notify(node, node._propagationMask, flags);
2720
+ node._queue.notify(node, node._propagationMask, flags, actualError);
2460
2721
  };
2461
2722
  node._propagationMask = propagationMask;
2462
2723
  node._preventAutoDisposal = true;
@@ -2484,7 +2745,7 @@ class ConditionalQueue extends Queue {
2484
2745
  if (!type || read(this._disabled)) return;
2485
2746
  return super.run(type);
2486
2747
  }
2487
- notify(node, type, flags) {
2748
+ notify(node, type, flags, error) {
2488
2749
  if (read(this._disabled)) {
2489
2750
  if (type & STATUS_PENDING) {
2490
2751
  if (flags & STATUS_PENDING) {
@@ -2499,12 +2760,12 @@ class ConditionalQueue extends Queue {
2499
2760
  } else if (this._errorNodes.delete(node)) type &= ~STATUS_ERROR;
2500
2761
  }
2501
2762
  }
2502
- return type ? super.notify(node, type, flags) : true;
2763
+ return type ? super.notify(node, type, flags, error ?? node._error) : true;
2503
2764
  }
2504
2765
  }
2505
2766
  class CollectionQueue extends Queue {
2506
2767
  _collectionType;
2507
- _nodes = new Set();
2768
+ _sources = new Set();
2508
2769
  _disabled = signal(false, { pureWrite: true });
2509
2770
  _initialized = false;
2510
2771
  constructor(type) {
@@ -2515,21 +2776,28 @@ class CollectionQueue extends Queue {
2515
2776
  if (!type || read(this._disabled)) return;
2516
2777
  return super.run(type);
2517
2778
  }
2518
- notify(node, type, flags) {
2779
+ notify(node, type, flags, error) {
2519
2780
  if (
2520
2781
  !(type & this._collectionType) ||
2521
2782
  (this._collectionType & STATUS_PENDING && this._initialized)
2522
2783
  )
2523
- return super.notify(node, type, flags);
2784
+ return super.notify(node, type, flags, error);
2524
2785
  if (flags & this._collectionType) {
2525
- this._nodes.add(node);
2526
- if (this._nodes.size === 1) setSignal(this._disabled, true);
2527
- } else if (this._nodes.size > 0) {
2528
- this._nodes.delete(node);
2529
- if (this._nodes.size === 0) setSignal(this._disabled, false);
2786
+ const source = error?._source || node._error?._source;
2787
+ if (source) {
2788
+ const wasEmpty = this._sources.size === 0;
2789
+ this._sources.add(source);
2790
+ if (wasEmpty) setSignal(this._disabled, true);
2791
+ }
2530
2792
  }
2531
2793
  type &= ~this._collectionType;
2532
- return type ? super.notify(node, type, flags) : true;
2794
+ return type ? super.notify(node, type, flags, error) : true;
2795
+ }
2796
+ checkSources() {
2797
+ for (const source of this._sources) {
2798
+ if (!(source._statusFlags & this._collectionType)) this._sources.delete(source);
2799
+ }
2800
+ if (!this._sources.size) setSignal(this._disabled, false);
2533
2801
  }
2534
2802
  }
2535
2803
  var BoundaryMode;
@@ -2545,8 +2813,12 @@ function createBoundary(fn, condition) {
2545
2813
  const disabled = read(queue._disabled);
2546
2814
  tree._propagationMask = disabled ? STATUS_ERROR | STATUS_PENDING : 0;
2547
2815
  if (!disabled) {
2548
- queue._pendingNodes.forEach(node => queue.notify(node, STATUS_PENDING, STATUS_PENDING));
2549
- queue._errorNodes.forEach(node => queue.notify(node, STATUS_ERROR, STATUS_ERROR));
2816
+ queue._pendingNodes.forEach(node =>
2817
+ queue.notify(node, STATUS_PENDING, STATUS_PENDING, node._error)
2818
+ );
2819
+ queue._errorNodes.forEach(node =>
2820
+ queue.notify(node, STATUS_ERROR, STATUS_ERROR, node._error)
2821
+ );
2550
2822
  queue._pendingNodes.clear();
2551
2823
  queue._errorNodes.clear();
2552
2824
  }
@@ -2572,26 +2844,12 @@ function createCollectionBoundary(type, fn, fallback) {
2572
2844
  function createLoadBoundary(fn, fallback) {
2573
2845
  return createCollectionBoundary(STATUS_PENDING, fn, () => fallback());
2574
2846
  }
2575
- function collectErrorSources(node, sources) {
2576
- let root = true;
2577
- let dep = node._deps;
2578
- while (dep !== null) {
2579
- const source = dep._dep;
2580
- if (source._deps && source._statusFlags & STATUS_ERROR) {
2581
- root = false;
2582
- collectErrorSources(source, sources);
2583
- }
2584
- dep = dep._nextDep;
2585
- }
2586
- root && sources.push(node);
2587
- }
2588
2847
  function createErrorBoundary(fn, fallback) {
2589
2848
  return createCollectionBoundary(STATUS_ERROR, fn, queue => {
2590
- let node = queue._nodes.values().next().value;
2591
- return fallback(node._error, () => {
2592
- const sources = [];
2593
- for (const node of queue._nodes) collectErrorSources(node, sources);
2594
- for (const source of sources) recompute(source);
2849
+ let source = queue._sources.values().next().value;
2850
+ const error = source._error?.cause ?? source._error;
2851
+ return fallback(error, () => {
2852
+ for (const source of queue._sources) recompute(source);
2595
2853
  schedule();
2596
2854
  });
2597
2855
  });