@solidjs/signals 0.9.8 → 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,9 +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
- runHeap(dirtyQueue, GlobalQueue._update);
354
- }
445
+ if (!incomplete) checkBoundaryChildren(globalQueue);
446
+ if (dirtyQueue._max >= dirtyQueue._min) runHeap(dirtyQueue, GlobalQueue._update);
355
447
  if (resolvePending) {
356
448
  const pendingNodes = globalQueue._pendingNodes;
357
449
  for (let i = 0; i < pendingNodes.length; i++) {
@@ -361,15 +453,17 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
361
453
  n._pendingValue = NOT_PENDING;
362
454
  if (n._type && n._type !== EFFECT_TRACKED) n._modified = true;
363
455
  }
456
+ n._statusFlags &= ~STATUS_UNINITIALIZED;
364
457
  if (n._fn) GlobalQueue._dispose(n, false, true);
365
458
  }
366
459
  pendingNodes.length = 0;
367
460
  const optimisticNodes = completingTransition
368
- ? completingTransition.optimisticNodes
461
+ ? completingTransition._optimisticNodes
369
462
  : globalQueue._optimisticNodes;
370
463
  for (let i = 0; i < optimisticNodes.length; i++) {
371
464
  const n = optimisticNodes[i];
372
465
  const original = n._pendingValue;
466
+ n._optimisticLane = undefined;
373
467
  if (original !== NOT_PENDING && n._value !== original) {
374
468
  n._value = original;
375
469
  insertSubs(n, true);
@@ -379,7 +473,7 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
379
473
  }
380
474
  optimisticNodes.length = 0;
381
475
  const optimisticStores = completingTransition
382
- ? completingTransition.optimisticStores
476
+ ? completingTransition._optimisticStores
383
477
  : globalQueue._optimisticStores;
384
478
  if (GlobalQueue._clearOptimisticStore && optimisticStores.size) {
385
479
  for (const store of optimisticStores) {
@@ -388,13 +482,35 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
388
482
  optimisticStores.clear();
389
483
  schedule();
390
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);
391
507
  }
392
508
  }
393
509
  function trackOptimisticStore(store) {
394
510
  globalQueue._optimisticStores.add(store);
395
511
  schedule();
396
512
  }
397
- function runTransitionPending(pendingNodes) {
513
+ function reassignPendingTransition(pendingNodes) {
398
514
  for (let i = 0; i < pendingNodes.length; i++) {
399
515
  pendingNodes[i]._transition = activeTransition;
400
516
  }
@@ -411,20 +527,20 @@ function runQueue(queue, type) {
411
527
  for (let i = 0; i < queue.length; i++) queue[i](type);
412
528
  }
413
529
  function transitionComplete(transition) {
414
- if (transition.done) return true;
415
- if (transition.actions.length) return false;
530
+ if (transition._done) return true;
531
+ if (transition._actions.length) return false;
416
532
  let done = true;
417
- for (let i = 0; i < transition.asyncNodes.length; i++) {
418
- 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) {
419
535
  done = false;
420
536
  break;
421
537
  }
422
538
  }
423
- done && (transition.done = true);
539
+ done && (transition._done = true);
424
540
  return done;
425
541
  }
426
542
  function currentTransition(transition) {
427
- while (transition.done && typeof transition.done === "object") transition = transition.done;
543
+ while (transition._done && typeof transition._done === "object") transition = transition._done;
428
544
  return transition;
429
545
  }
430
546
  function runInTransition(transition, fn) {
@@ -448,11 +564,11 @@ function action(genFn) {
448
564
  const it = genFn(...args);
449
565
  globalQueue.initTransition();
450
566
  let ctx = activeTransition;
451
- ctx.actions.push(it);
567
+ ctx._actions.push(it);
452
568
  const done = (v, e) => {
453
569
  ctx = currentTransition(ctx);
454
- const i = ctx.actions.indexOf(it);
455
- if (i >= 0) ctx.actions.splice(i, 1);
570
+ const i = ctx._actions.indexOf(it);
571
+ if (i >= 0) ctx._actions.splice(i, 1);
456
572
  activeTransition = ctx;
457
573
  schedule();
458
574
  e ? reject(e) : resolve(v);
@@ -499,7 +615,7 @@ function recompute(el, create = false) {
499
615
  if (el._transition && (!isEffect || activeTransition) && activeTransition !== el._transition)
500
616
  globalQueue.initTransition(el._transition);
501
617
  deleteFromHeap(el, el._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue);
502
- if (el._transition) disposeChildren(el);
618
+ if (el._transition || isEffect === EFFECT_TRACKED) disposeChildren(el);
503
619
  else {
504
620
  markDisposal(el);
505
621
  el._pendingDisposal = el._disposal;
@@ -509,7 +625,8 @@ function recompute(el, create = false) {
509
625
  }
510
626
  }
511
627
  const isOptimisticDirty = !!(el._flags & REACTIVE_OPTIMISTIC_DIRTY);
512
- const hasOptimisticOverride = el._optimistic && el._pendingValue !== NOT_PENDING;
628
+ const hasOverride = hasActiveOverride(el);
629
+ const wasPending = !!(el._statusFlags & STATUS_PENDING);
513
630
  const oldcontext = context;
514
631
  context = el;
515
632
  el._depsTail = null;
@@ -518,17 +635,32 @@ function recompute(el, create = false) {
518
635
  let value = el._pendingValue === NOT_PENDING ? el._value : el._pendingValue;
519
636
  let oldHeight = el._height;
520
637
  let prevTracking = tracking;
521
- let prevOptimisticRead = optimisticReadActive;
638
+ let prevLane = currentOptimisticLane;
522
639
  tracking = true;
523
- if (isOptimisticDirty) setOptimisticReadActive(true);
640
+ if (isOptimisticDirty) {
641
+ const lane = resolveLane(el);
642
+ if (lane) setCurrentOptimisticLane(lane);
643
+ }
524
644
  try {
525
645
  value = handleAsync(el, el._fn(value));
526
646
  clearStatus(el);
647
+ const resolvedLane = resolveLane(el);
648
+ if (resolvedLane) resolvedLane._pendingAsync.delete(el);
527
649
  } 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);
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
+ );
532
664
  } finally {
533
665
  tracking = prevTracking;
534
666
  el._flags = REACTIVE_NONE;
@@ -544,21 +676,34 @@ function recompute(el, create = false) {
544
676
  if (depsTail !== null) depsTail._nextDep = null;
545
677
  else el._deps = null;
546
678
  }
547
- const valueChanged =
548
- !el._equals ||
549
- !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);
550
685
  if (valueChanged) {
686
+ const prevVisible = hasOverride ? el._value : undefined;
551
687
  if (create || (isEffect && activeTransition !== el._transition) || isOptimisticDirty)
552
688
  el._value = value;
553
689
  else el._pendingValue = value;
554
- 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;
555
700
  } else if (el._height != oldHeight) {
556
701
  for (let s = el._subs; s !== null; s = s._nextSub) {
557
702
  insertIntoHeapHeight(s._sub, s._sub._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue);
558
703
  }
559
704
  }
560
705
  }
561
- setOptimisticReadActive(prevOptimisticRead);
706
+ setCurrentOptimisticLane(prevLane);
562
707
  (!create || el._statusFlags & STATUS_PENDING) &&
563
708
  !el._transition &&
564
709
  !(activeTransition && el._optimistic) &&
@@ -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,7 +1242,13 @@ 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
1253
  const alreadyTracked = globalQueue._optimisticNodes.includes(el);
1046
1254
  if (el._transition && alreadyTracked) {
@@ -1052,6 +1260,9 @@ function setSignal(el, v) {
1052
1260
  if (!alreadyTracked) {
1053
1261
  globalQueue._optimisticNodes.push(el);
1054
1262
  }
1263
+ el._overrideVersion = (el._overrideVersion || 0) + 1;
1264
+ const lane = getOrCreateLane(el);
1265
+ el._optimisticLane = lane;
1055
1266
  el._value = v;
1056
1267
  } else {
1057
1268
  if (el._pendingValue === NOT_PENDING) globalQueue._pendingNodes.push(el);
@@ -1156,6 +1367,8 @@ function isPending(fn) {
1156
1367
  try {
1157
1368
  fn();
1158
1369
  return foundPending;
1370
+ } catch {
1371
+ return foundPending;
1159
1372
  } finally {
1160
1373
  pendingCheckActive = prevPendingCheck;
1161
1374
  foundPending = prevFoundPending;
@@ -1228,25 +1441,27 @@ function effect(compute, effect, error, initialValue, options) {
1228
1441
  node._errorFn = error;
1229
1442
  node._cleanup = undefined;
1230
1443
  node._type = options?.render ? EFFECT_RENDER : EFFECT_USER;
1231
- node._notifyStatus = () => {
1232
- if (node._statusFlags & STATUS_ERROR) {
1233
- 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;
1234
1449
  node._queue.notify(node, STATUS_PENDING, 0);
1235
1450
  if (node._type === EFFECT_USER) {
1236
1451
  try {
1237
1452
  return node._errorFn
1238
- ? node._errorFn(error, () => {
1453
+ ? node._errorFn(err, () => {
1239
1454
  node._cleanup?.();
1240
1455
  node._cleanup = undefined;
1241
1456
  })
1242
- : console.error(error);
1457
+ : console.error(err);
1243
1458
  } catch (e) {
1244
- error = e;
1459
+ err = e;
1245
1460
  }
1246
1461
  }
1247
- if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw error;
1462
+ if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
1248
1463
  } else if (node._type === EFFECT_RENDER)
1249
- node._queue.notify(node, STATUS_PENDING | STATUS_ERROR, node._statusFlags);
1464
+ node._queue.notify(node, STATUS_PENDING | STATUS_ERROR, actualStatus, actualError);
1250
1465
  };
1251
1466
  recompute(node, true);
1252
1467
  !options?.defer &&
@@ -1994,18 +2209,27 @@ function clearOptimisticStore(store) {
1994
2209
  if (!target || !target[STORE_OPTIMISTIC_OVERRIDE]) return;
1995
2210
  const override = target[STORE_OPTIMISTIC_OVERRIDE];
1996
2211
  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);
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);
2006
2229
  }
2007
2230
  }
2008
- nodes[$TRACK] && setSignal(nodes[$TRACK], undefined);
2231
+ } finally {
2232
+ setProjectionWriteActive(false);
2009
2233
  }
2010
2234
  delete target[STORE_OPTIMISTIC_OVERRIDE];
2011
2235
  }
@@ -2453,10 +2677,11 @@ function compare(key, a, b) {
2453
2677
  }
2454
2678
  function boundaryComputed(fn, propagationMask) {
2455
2679
  const node = computed(fn, undefined, { lazy: true });
2456
- node._notifyStatus = () => {
2457
- 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;
2458
2683
  node._statusFlags &= ~node._propagationMask;
2459
- node._queue.notify(node, node._propagationMask, flags);
2684
+ node._queue.notify(node, node._propagationMask, flags, actualError);
2460
2685
  };
2461
2686
  node._propagationMask = propagationMask;
2462
2687
  node._preventAutoDisposal = true;
@@ -2484,7 +2709,7 @@ class ConditionalQueue extends Queue {
2484
2709
  if (!type || read(this._disabled)) return;
2485
2710
  return super.run(type);
2486
2711
  }
2487
- notify(node, type, flags) {
2712
+ notify(node, type, flags, error) {
2488
2713
  if (read(this._disabled)) {
2489
2714
  if (type & STATUS_PENDING) {
2490
2715
  if (flags & STATUS_PENDING) {
@@ -2499,12 +2724,12 @@ class ConditionalQueue extends Queue {
2499
2724
  } else if (this._errorNodes.delete(node)) type &= ~STATUS_ERROR;
2500
2725
  }
2501
2726
  }
2502
- return type ? super.notify(node, type, flags) : true;
2727
+ return type ? super.notify(node, type, flags, error ?? node._error) : true;
2503
2728
  }
2504
2729
  }
2505
2730
  class CollectionQueue extends Queue {
2506
2731
  _collectionType;
2507
- _nodes = new Set();
2732
+ _sources = new Set();
2508
2733
  _disabled = signal(false, { pureWrite: true });
2509
2734
  _initialized = false;
2510
2735
  constructor(type) {
@@ -2515,21 +2740,28 @@ class CollectionQueue extends Queue {
2515
2740
  if (!type || read(this._disabled)) return;
2516
2741
  return super.run(type);
2517
2742
  }
2518
- notify(node, type, flags) {
2743
+ notify(node, type, flags, error) {
2519
2744
  if (
2520
2745
  !(type & this._collectionType) ||
2521
2746
  (this._collectionType & STATUS_PENDING && this._initialized)
2522
2747
  )
2523
- return super.notify(node, type, flags);
2748
+ return super.notify(node, type, flags, error);
2524
2749
  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);
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
+ }
2530
2756
  }
2531
2757
  type &= ~this._collectionType;
2532
- 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);
2533
2765
  }
2534
2766
  }
2535
2767
  var BoundaryMode;
@@ -2545,8 +2777,12 @@ function createBoundary(fn, condition) {
2545
2777
  const disabled = read(queue._disabled);
2546
2778
  tree._propagationMask = disabled ? STATUS_ERROR | STATUS_PENDING : 0;
2547
2779
  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));
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
+ );
2550
2786
  queue._pendingNodes.clear();
2551
2787
  queue._errorNodes.clear();
2552
2788
  }
@@ -2572,26 +2808,12 @@ function createCollectionBoundary(type, fn, fallback) {
2572
2808
  function createLoadBoundary(fn, fallback) {
2573
2809
  return createCollectionBoundary(STATUS_PENDING, fn, () => fallback());
2574
2810
  }
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
2811
  function createErrorBoundary(fn, fallback) {
2589
2812
  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);
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);
2595
2817
  schedule();
2596
2818
  });
2597
2819
  });