@solidjs/signals 2.0.0-beta.23 → 2.0.0-beta.25

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.
@@ -281,8 +281,8 @@ class GlobalQueue extends Queue {
281
281
  static ae=null;
282
282
  static he=null;
283
283
  static un=null;
284
- static St=null;
285
284
  static dt=null;
285
+ static St=null;
286
286
  static Rt=null;
287
287
  static ze=null;
288
288
  static k=null;
@@ -296,13 +296,13 @@ class GlobalQueue extends Queue {
296
296
  // once the gate holds.
297
297
  static Pt=null;
298
298
  static En=null;
299
- static Tn=null;
300
299
  static dn=null;
300
+ static Tn=null;
301
301
  static In=null;
302
302
  static Ct=null;
303
303
  static At=null;
304
304
  static Ot=null;
305
- static We=null;
305
+ static Ye=null;
306
306
  static Ke=null;
307
307
  static Me=null;
308
308
  static Nn=null;
@@ -509,7 +509,7 @@ function commitPendingNode(e) {
509
509
  }
510
510
  t.se &= ~REACTIVE_MANUAL_WRITE;
511
511
  if (!(t.S & STATUS_PENDING)) t.S &= ~STATUS_UNINITIALIZED;
512
- if (t.Le !== null || t.ye !== null) GlobalQueue.Ce(t, false, true);
512
+ if (t.ye !== null || t.Le !== null) GlobalQueue.Ce(t, false, true);
513
513
  if (e.be || e.ge) GlobalQueue.un(e);
514
514
  }
515
515
 
@@ -562,7 +562,7 @@ function finalizePureQueue(e = null, t = false) {
562
562
  if (t.cn.size) GlobalQueue.Dt(t.cn, e);
563
563
  sweepTransientStoreNodes();
564
564
  // Lanes only enter activeLanes through the engine's getOrCreateLane.
565
- if (activeLanes.size) GlobalQueue.dn(e);
565
+ if (activeLanes.size) GlobalQueue.Tn(e);
566
566
  }
567
567
  }
568
568
 
@@ -668,7 +668,7 @@ function transitionComplete(e) {
668
668
  // Override blockage lives with the engine. Absent hook = "no optimistic
669
669
  // blockage", which is exact: only _optimisticWrite (engine) pushes to
670
670
  // _optimisticNodes, so without the engine the loop was vacuous anyway.
671
- if (t && e.Ze.length && GlobalQueue.Tn(e)) t = false;
671
+ if (t && e.Ze.length && GlobalQueue.dn(e)) t = false;
672
672
  t && (e.fn = true);
673
673
  return t;
674
674
  }
@@ -343,9 +343,9 @@ GlobalQueue.he = updateChildCompanions;
343
343
 
344
344
  GlobalQueue.un = snapCompanionsToState;
345
345
 
346
- GlobalQueue.St = latestRead;
346
+ GlobalQueue.dt = latestRead;
347
347
 
348
- GlobalQueue.dt = pendingCheckRead;
348
+ GlobalQueue.St = pendingCheckRead;
349
349
 
350
350
  GlobalQueue.Rt = recordFreshRead;
351
351
 
package/dist/prod/map.js CHANGED
@@ -118,22 +118,29 @@ function updateKeyedMap() {
118
118
  this.Mt = t.slice(0);
119
119
  this.jt = s;
120
120
  } else {
121
- let o, c, a, f, u, p, w, l, d, O = new Array(s), m = new Array(s);
122
- r = this.Ut ? new Array(s) : undefined;
123
- n = this.$t ? new Array(s) : undefined;
121
+ let o, c, a, f, u, p, w, l, d;
124
122
  // skip common prefix
125
123
  for (o = 0, c = Math.min(this.jt, s); o < c && (this.Mt[o] === t[o] || this.Ut && compare(this.Gt, this.Mt[o], t[o])); o++) {
126
124
  if (this.Ut) setSignal(this.Ut[o], t[o]);
127
125
  }
128
- // common suffix
126
+ // skip common suffix — counted only; retained entries land in one pass
127
+ // at commit instead of being staged and copied twice
129
128
  for (c = this.jt - 1, a = s - 1; c >= o && a >= o && (this.Mt[c] === t[a] || this.Ut && compare(this.Gt, this.Mt[c], t[a])); c--,
130
- a--) {
131
- O[a] = this.xt[c];
132
- m[a] = this.Ft[c];
133
- r && (r[a] = this.Ut[c]);
134
- n && (n[a] = this.$t[c]);
129
+ a--) ;
130
+ // no structural change (every position matched in place at equal
131
+ // length — the common post-reconcile shape): keep the same mapped
132
+ // array identity so downstream consumers don't re-run at all
133
+ if (o === s && this.jt === s) {
134
+ this.Mt = t.slice(0);
135
+ return;
135
136
  }
136
- // 0) prepare a map of all indices in newItems, scanning backwards so we encounter them in natural order
137
+ const O = s - this.jt;
138
+ const m = new Array(s);
139
+ const _ = new Array(s);
140
+ r = this.Ut ? new Array(s) : undefined;
141
+ n = this.$t ? new Array(s) : undefined;
142
+ // 0) prepare a map of all indices in the changed window of newItems,
143
+ // scanning backwards so we encounter them in natural order
137
144
  p = new Map;
138
145
  w = new Array(a + 1);
139
146
  for (e = a; e >= o; e--) {
@@ -143,14 +150,16 @@ function updateKeyedMap() {
143
150
  w[e] = i === undefined ? -1 : i;
144
151
  p.set(u, e);
145
152
  }
146
- // 1) step through all old items and see if they can be found in the new set; if so, save them in a temp array and mark them moved; if not, queue them for disposal at commit
153
+ // 1) step through the old changed window and see if items can be found
154
+ // in the new set; if so, stage them at their new positions; if not,
155
+ // queue them for disposal at commit
147
156
  for (i = o; i <= c; i++) {
148
157
  f = this.Mt[i];
149
158
  u = this.Gt ? this.Gt(f) : f;
150
159
  e = p.get(u);
151
160
  if (e !== undefined && e !== -1) {
152
- O[e] = this.xt[i];
153
- m[e] = this.Ft[i];
161
+ m[e] = this.xt[i];
162
+ _[e] = this.Ft[i];
154
163
  r && (r[e] = this.Ut[i]);
155
164
  n && (n[e] = this.$t[i]);
156
165
  e = w[e];
@@ -159,33 +168,48 @@ function updateKeyedMap() {
159
168
  }
160
169
  // 2) create new rows into the temp arrays; an abort disposes only these
161
170
  try {
162
- for (e = o; e < s; e++) {
163
- if (e in O) continue;
164
- (d ??= []).push(m[e] = createOwner());
165
- O[e] = runWithOwner(m[e], h);
171
+ for (e = o; e <= a; e++) {
172
+ if (_[e] !== undefined) continue;
173
+ (d ??= []).push(_[e] = createOwner());
174
+ m[e] = runWithOwner(_[e], h);
166
175
  }
167
176
  } catch (t) {
168
177
  if (d) for (i = 0; i < d.length; i++) d[i].dispose();
169
178
  throw t;
170
179
  }
171
- // 3) commit: land positions, then dispose exited rows
172
- for (e = o; e < s; e++) {
173
- this.xt[e] = O[e];
174
- this.Ft[e] = m[e];
180
+ // 3) commit: land the retained prefix and suffix plus the staged window
181
+ // into the fresh arrays, swap them in (new identity for downstream
182
+ // change propagation), then dispose exited rows
183
+ for (i = 0; i < o; i++) {
184
+ m[i] = this.xt[i];
185
+ _[i] = this.Ft[i];
186
+ r && (r[i] = this.Ut[i]);
187
+ n && (n[i] = this.$t[i]);
188
+ }
189
+ for (e = o; e <= a; e++) {
190
+ if (r) setSignal(r[e], t[e]);
191
+ if (n) setSignal(n[e], e);
192
+ }
193
+ for (e = a + 1; e < s; e++) {
194
+ m[e] = this.xt[e - O];
195
+ _[e] = this.Ft[e - O];
175
196
  if (r) {
176
- this.Ut[e] = r[e];
177
- setSignal(this.Ut[e], t[e]);
197
+ r[e] = this.Ut[e - O];
198
+ setSignal(r[e], t[e]);
178
199
  }
179
200
  if (n) {
180
- this.$t[e] = n[e];
181
- setSignal(this.$t[e], e);
201
+ n[e] = this.$t[e - O];
202
+ if (O !== 0) setSignal(n[e], e);
182
203
  }
183
204
  }
184
- if (l) for (i = 0; i < l.length; i++) l[i].dispose();
185
- // 4) in case the new set is shorter than the old, set the length of the mapped array
186
- this.xt = this.xt.slice(0, this.jt = s);
187
- // 5) save a copy of the mapped items for the next update
205
+ this.xt = m;
206
+ this.Ft = _;
207
+ r && (this.Ut = r);
208
+ n && (this.$t = n);
209
+ this.jt = s;
210
+ // save a copy of the mapped items for the next update
188
211
  this.Mt = t.slice(0);
212
+ if (l) for (i = 0; i < l.length; i++) l[i].dispose();
189
213
  }
190
214
  });
191
215
  return this.xt;
@@ -14,19 +14,21 @@ import { installOptimisticEngine } from "../core/optimistic.js";
14
14
 
15
15
  import { runProjectionComputed } from "./projection.js";
16
16
 
17
- import { storeSetter, $TARGET, STORE_OPTIMISTIC_OVERRIDE, STORE_NODE, STORE_OPTIMISTIC_OWNERS, getOverlayLayer, STORE_VALUE, $DELETED, isWrappable, wrap, visibleNodeValue, $TRACK, notifySelf, STORE_WRAP, createStoreProxy, storeTraps, STORE_LOOKUP, STORE_OPTIMISTIC, STORE_FIREWALL } from "./store.js";
17
+ import { storeSetter, $TARGET, STORE_OPTIMISTIC_OVERRIDE, STORE_NODE, STORE_OPTIMISTIC_OWNERS, getOverlayLayer, STORE_VALUE, $DELETED, isWrappable, wrap, visibleNodeValue, $TRACK, notifySelf, STORE_WRAP, createStoreProxy, storeTraps, STORE_LOOKUP, STORE_SHALLOW, markRawIngest, STORE_OPTIMISTIC, STORE_FIREWALL } from "./store.js";
18
18
 
19
- function createOptimisticStore(e, t, r) {
19
+ function createOptimisticStore(e, t, i) {
20
20
  // Register clear function with scheduler; store nodes marked
21
21
  // STORE_OPTIMISTIC take the engine's write path, so install it before any
22
22
  // node can be created.
23
23
  installOptimisticEngine();
24
24
  GlobalQueue.Dt ||= clearOptimisticStores;
25
- const i = typeof e === "function";
26
- const o = i ? t : e;
27
- const n = i ? e : undefined;
25
+ const r = typeof e === "function";
26
+ // Plain form: the second slot carries options.
27
+ if (!r && i === undefined) i = t;
28
+ const o = r ? t : e;
29
+ const n = r ? e : undefined;
28
30
  // Create optimistic projection store
29
- const {store: c} = createOptimisticProjectionInternal(n, o, r);
31
+ const {store: c} = createOptimisticProjectionInternal(n, o, i);
30
32
  return [ c, e => storeSetter(c, e) ];
31
33
  }
32
34
 
@@ -35,8 +37,8 @@ function createOptimisticStore(e, t, r) {
35
37
  // scheduler's flush tail carries only a size-guarded hook call. The
36
38
  // completing transition scopes each clear to its own layer keys (#2899).
37
39
  function clearOptimisticStores(e, t) {
38
- for (const r of e) {
39
- const e = r[$TARGET];
40
+ for (const i of e) {
41
+ const e = i[$TARGET];
40
42
  if (e?.[STORE_OPTIMISTIC_OVERRIDE]) clearOptimisticOverride(e, t);
41
43
  }
42
44
  e.clear();
@@ -54,9 +56,9 @@ function clearOptimisticStores(e, t) {
54
56
  * fresh authoritative data) consumes everything — the correction supersedes
55
57
  * every tentative layer.
56
58
  */ function clearOptimisticOverride(e, t) {
57
- const r = e[STORE_OPTIMISTIC_OVERRIDE];
58
- if (!r) return;
59
- const i = e[STORE_NODE];
59
+ const i = e[STORE_OPTIMISTIC_OVERRIDE];
60
+ if (!i) return;
61
+ const r = e[STORE_NODE];
60
62
  const o = e[STORE_OPTIMISTIC_OWNERS];
61
63
  const n = t !== undefined;
62
64
  let c = false;
@@ -66,7 +68,7 @@ function clearOptimisticStores(e, t) {
66
68
  const O = projectionWriteActive;
67
69
  setProjectionWriteActive(true);
68
70
  try {
69
- for (const O of Reflect.ownKeys(r)) {
71
+ for (const O of Reflect.ownKeys(i)) {
70
72
  if (n) {
71
73
  let e = o?.[O] ?? null;
72
74
  // Resolve merge chains (entangled actions settle as one); path-compress
@@ -83,19 +85,19 @@ function clearOptimisticStores(e, t) {
83
85
  }
84
86
  }
85
87
  }
86
- delete r[O];
88
+ delete i[O];
87
89
  if (o) delete o[O];
88
90
  c = true;
89
- const T = i?.[O];
91
+ const T = r?.[O];
90
92
  if (T) {
91
93
  // Clear lane association so effects go to regular queue
92
94
  T.je = undefined;
93
95
  // Re-read from base — this key left the optimistic layer above, so the
94
96
  // overlay resolves to STORE_OVERRIDE or STORE_VALUE.
95
97
  const t = getOverlayLayer(e, O);
96
- const r = t ? t[O] : e[STORE_VALUE][O];
97
- const i = r === $DELETED ? undefined : r;
98
- const o = isWrappable(i) ? wrap(i, e) : i;
98
+ const i = t ? t[O] : e[STORE_VALUE][O];
99
+ const r = i === $DELETED ? undefined : i;
100
+ const o = isWrappable(r) ? wrap(r, e) : r;
99
101
  const n = visibleNodeValue(T);
100
102
  T.Ee = NOT_PENDING;
101
103
  T.sn = null;
@@ -108,12 +110,14 @@ function clearOptimisticStores(e, t) {
108
110
  }
109
111
  }
110
112
  if (!s) {
111
- delete e[STORE_OPTIMISTIC_OVERRIDE];
112
- delete e[STORE_OPTIMISTIC_OWNERS];
113
+ // Assignment, not delete: StoreNode targets share one pre-initialized
114
+ // hidden class (see createStoreProxy) and a delete would demote it.
115
+ e[STORE_OPTIMISTIC_OVERRIDE] = undefined;
116
+ e[STORE_OPTIMISTIC_OWNERS] = undefined;
113
117
  }
114
118
  // Notify $TRACK
115
- if (c && i?.[$TRACK]) {
116
- i[$TRACK].je = undefined;
119
+ if (c && r?.[$TRACK]) {
120
+ r[$TRACK].je = undefined;
117
121
  notifySelf(e);
118
122
  }
119
123
  } finally {
@@ -121,17 +125,22 @@ function clearOptimisticStores(e, t) {
121
125
  }
122
126
  }
123
127
 
124
- function createOptimisticProjectionInternal(e, t, r) {
125
- let i;
128
+ function createOptimisticProjectionInternal(e, t, i) {
129
+ let r;
126
130
  const o = new WeakMap;
131
+ const n = !!i?.shallow;
127
132
  const wrapper = e => {
128
133
  e[STORE_WRAP] = wrapProjection;
129
134
  e[STORE_LOOKUP] = o;
135
+ if (n) {
136
+ e[STORE_SHALLOW] = true;
137
+ markRawIngest(e[STORE_VALUE]);
138
+ }
130
139
  e[STORE_OPTIMISTIC] = true;
131
140
  // Mark as optimistic store
132
141
  Object.defineProperty(e, STORE_FIREWALL, {
133
142
  get() {
134
- return i;
143
+ return r;
135
144
  },
136
145
  configurable: true
137
146
  });
@@ -143,7 +152,7 @@ function createOptimisticProjectionInternal(e, t, r) {
143
152
  o.set(e, t);
144
153
  return t;
145
154
  };
146
- const n = wrapProjection(t);
155
+ const c = wrapProjection(t);
147
156
  // If there's a projection function, create a computed to drive it
148
157
  if (e) {
149
158
  // All writes inside firewall recompute must go to STORE_OVERRIDE (base), not
@@ -152,7 +161,7 @@ function createOptimisticProjectionInternal(e, t, r) {
152
161
  // async yields because they fire outside any enclosing try/finally. It also
153
162
  // consumes stale optimistic overlays once fresh projected data lands.
154
163
  const clearProjectionOverride = () => {
155
- const e = n[$TARGET];
164
+ const e = c[$TARGET];
156
165
  if (e?.[STORE_OPTIMISTIC_OVERRIDE]) clearOptimisticOverride(e);
157
166
  };
158
167
  const wrapCommit = e => {
@@ -165,19 +174,19 @@ function createOptimisticProjectionInternal(e, t, r) {
165
174
  setProjectionWriteActive(t);
166
175
  }
167
176
  };
168
- i = computed(() => {
177
+ r = computed(() => {
169
178
  setProjectionWriteActive(true);
170
179
  try {
171
- runProjectionComputed(n, e, r?.key || "id", wrapCommit, clearProjectionOverride);
180
+ runProjectionComputed(c, e, i?.key || "id", wrapCommit, clearProjectionOverride);
172
181
  } finally {
173
182
  setProjectionWriteActive(false);
174
183
  }
175
184
  }, undefined);
176
- i.T &= ~CONFIG_AUTO_DISPOSE;
185
+ r.T &= ~CONFIG_AUTO_DISPOSE;
177
186
  }
178
187
  return {
179
- store: n,
180
- node: i
188
+ store: c,
189
+ node: r
181
190
  };
182
191
  }
183
192
 
@@ -14,14 +14,21 @@ import { CONFIG_AUTO_DISPOSE } from "../core/constants.js";
14
14
 
15
15
  import { reconcile } from "./reconcile.js";
16
16
 
17
- import { storeSetter, $TARGET, STORE_WRAP, createStoreProxy, storeTraps, setWriteOverride, STORE_LOOKUP, STORE_FIREWALL } from "./store.js";
17
+ import { storeSetter, $TARGET, STORE_WRAP, createStoreProxy, storeTraps, setWriteOverride, STORE_LOOKUP, STORE_SHALLOW, markRawIngest, STORE_VALUE, STORE_FIREWALL } from "./store.js";
18
18
 
19
19
  function createProjectionInternal(e, r, t) {
20
20
  let o;
21
- const i = new WeakMap;
21
+ const n = new WeakMap;
22
+ // A shallow projection's children are raw and never wrapped, so the
23
+ // wrapper only ever runs for the root — flagging unconditionally is safe.
24
+ const i = !!t?.shallow;
22
25
  const wrapper = e => {
23
26
  e[STORE_WRAP] = wrapProjection;
24
- e[STORE_LOOKUP] = i;
27
+ e[STORE_LOOKUP] = n;
28
+ if (i) {
29
+ e[STORE_SHALLOW] = true;
30
+ markRawIngest(e[STORE_VALUE]);
31
+ }
25
32
  Object.defineProperty(e, STORE_FIREWALL, {
26
33
  get() {
27
34
  return o;
@@ -30,20 +37,20 @@ function createProjectionInternal(e, r, t) {
30
37
  });
31
38
  };
32
39
  const wrapProjection = e => {
33
- if (i.has(e)) return i.get(e);
40
+ if (n.has(e)) return n.get(e);
34
41
  if (e[$TARGET]?.[STORE_WRAP] === wrapProjection) return e;
35
42
  const r = createStoreProxy(e, storeTraps, wrapper);
36
- i.set(e, r);
43
+ n.set(e, r);
37
44
  return r;
38
45
  };
39
- const n = wrapProjection(r);
46
+ const c = wrapProjection(r);
40
47
  o = computed(() => {
41
48
  if (!o) o = getOwner();
42
- runProjectionComputed(n, e, t?.key || "id");
49
+ runProjectionComputed(c, e, t?.key || "id");
43
50
  }, undefined);
44
51
  o.T &= ~CONFIG_AUTO_DISPOSE;
45
52
  return {
46
- store: n,
53
+ store: c,
47
54
  node: o
48
55
  };
49
56
  }
@@ -106,22 +113,22 @@ function createProjectionInternal(e, r, t) {
106
113
  * re-enters `setProjectionWriteActive` so reconciles target `STORE_OVERRIDE`
107
114
  * instead of `STORE_OPTIMISTIC_OVERRIDE` even when an async yield fires outside
108
115
  * the outer `setProjectionWriteActive` scope.
109
- */ function runProjectionComputed(e, r, t, o, i) {
110
- const n = getOwner();
116
+ */ function runProjectionComputed(e, r, t, o, n) {
117
+ const i = getOwner();
111
118
  let c = false;
112
119
  let s;
113
- const u = new Proxy(e, createWriteTraps(() => !c || n.Te === s, i));
114
- storeSetter(u, i => {
115
- s = r(i);
120
+ const u = new Proxy(e, createWriteTraps(() => !c || i.Te === s, n));
121
+ storeSetter(u, n => {
122
+ s = r(n);
116
123
  c = true;
117
124
  const commit = r => {
118
- if (r === i || r === undefined) return;
125
+ if (r === n || r === undefined) return;
119
126
  const write = () => storeSetter(e, reconcile(r, t));
120
127
  o ? o(write) : write();
121
128
  };
122
- commit(handleAsync(n, s, commit));
129
+ commit(handleAsync(i, s, commit));
123
130
  });
124
- return n;
131
+ return i;
125
132
  }
126
133
 
127
134
  function createWriteTraps(e, r) {
@@ -151,12 +158,12 @@ function createWriteTraps(e, r) {
151
158
  }
152
159
  return t;
153
160
  },
154
- set(t, o, i) {
161
+ set(t, o, n) {
155
162
  if (e && !e()) return true;
156
163
  setWriteOverride(true);
157
164
  setProjectionWriteActive(true);
158
165
  try {
159
- t[o] = i;
166
+ t[o] = n;
160
167
  r?.();
161
168
  } finally {
162
169
  setWriteOverride(false);