@solidjs/signals 0.9.2 → 0.9.3

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
@@ -34,6 +34,7 @@ const EFFECT_USER = 2;
34
34
  const NOT_PENDING = {};
35
35
  const SUPPORTS_PROXY = typeof Proxy === "function";
36
36
  const defaultContext = {};
37
+ const $REFRESH = Symbol("refresh");
37
38
  function actualInsertIntoHeap(n, heap) {
38
39
  const parentHeight =
39
40
  (n._parent?._root ? n._parent._parentComputed?._height : n._parent?._height) ?? -1;
@@ -141,7 +142,7 @@ let scheduled = false;
141
142
  function schedule() {
142
143
  if (scheduled) return;
143
144
  scheduled = true;
144
- if (!globalQueue._running) queueMicrotask(flush);
145
+ if (!globalQueue._running) Promise.resolve().then(() => queueMicrotask(flush));
145
146
  }
146
147
  class Queue {
147
148
  _parent = null;
@@ -226,6 +227,8 @@ class GlobalQueue extends Queue {
226
227
  return;
227
228
  }
228
229
  this._pendingNodes.push(...activeTransition.pendingNodes);
230
+ this._optimisticNodes !== activeTransition.optimisticNodes &&
231
+ this._optimisticNodes.push(...activeTransition.optimisticNodes);
229
232
  this.restoreQueues(activeTransition.queueStash);
230
233
  transitions.delete(activeTransition);
231
234
  activeTransition = null;
@@ -255,11 +258,12 @@ class GlobalQueue extends Queue {
255
258
  initTransition(node) {
256
259
  if (activeTransition && activeTransition.time === clock) return;
257
260
  if (!activeTransition) {
258
- activeTransition = node._transition ?? {
261
+ activeTransition = node?._transition ?? {
259
262
  time: clock,
260
263
  pendingNodes: [],
261
264
  asyncNodes: [],
262
265
  optimisticNodes: [],
266
+ actions: [],
263
267
  queueStash: { _queues: [[], []], _children: [] },
264
268
  done: false
265
269
  };
@@ -345,6 +349,7 @@ function runQueue(queue, type) {
345
349
  }
346
350
  function transitionComplete(transition) {
347
351
  if (transition.done) return true;
352
+ if (transition.actions.length) return false;
348
353
  let done = true;
349
354
  for (let i = 0; i < transition.asyncNodes.length; i++) {
350
355
  if (transition.asyncNodes[i]._statusFlags & STATUS_PENDING) {
@@ -355,11 +360,40 @@ function transitionComplete(transition) {
355
360
  done && (transition.done = true);
356
361
  return done;
357
362
  }
358
- function runInTransition(el, recompute) {
363
+ function runInTransition(transition, fn) {
359
364
  const prevTransition = activeTransition;
360
- activeTransition = el._transition;
361
- recompute(el);
362
- activeTransition = prevTransition;
365
+ try {
366
+ activeTransition = transition;
367
+ return fn();
368
+ } finally {
369
+ activeTransition = prevTransition;
370
+ }
371
+ }
372
+ function action(genFn) {
373
+ return (...args) => {
374
+ const iterator = genFn(...args);
375
+ globalQueue.initTransition();
376
+ let ctx = activeTransition;
377
+ ctx.actions.push(iterator);
378
+ const step = input => {
379
+ let nextValue = iterator.next(input);
380
+ if (nextValue instanceof Promise) return nextValue.then(process);
381
+ process(nextValue);
382
+ };
383
+ const process = result => {
384
+ if (result.done) {
385
+ ctx.actions.splice(ctx.actions.indexOf(iterator), 1);
386
+ activeTransition = ctx;
387
+ schedule();
388
+ flush();
389
+ return;
390
+ }
391
+ const yielded = result.value;
392
+ if (yielded instanceof Promise) return yielded.then(step);
393
+ runInTransition(ctx, () => step(yielded));
394
+ };
395
+ runInTransition(ctx, () => step());
396
+ };
363
397
  }
364
398
  GlobalQueue._update = recompute;
365
399
  GlobalQueue._dispose = disposeChildren;
@@ -426,7 +460,7 @@ function recompute(el, create = false) {
426
460
  const valueChanged =
427
461
  !el._equals ||
428
462
  !el._equals(
429
- el._pendingValue === NOT_PENDING || el._optimistic || honoraryOptimistic
463
+ el._pendingValue === NOT_PENDING || (el._optimistic && el._transition) || honoraryOptimistic
430
464
  ? el._value
431
465
  : el._pendingValue,
432
466
  value
@@ -449,7 +483,7 @@ function recompute(el, create = false) {
449
483
  (!create || el._statusFlags & STATUS_PENDING) &&
450
484
  !el._transition &&
451
485
  globalQueue._pendingNodes.push(el);
452
- el._transition && honoraryOptimistic && runInTransition(el, recompute);
486
+ el._transition && honoraryOptimistic && runInTransition(el._transition, () => recompute(el));
453
487
  }
454
488
  function handleAsync(el, result, setter) {
455
489
  const isObject = typeof result === "object" && result !== null;
@@ -755,17 +789,6 @@ function read(el) {
755
789
  }
756
790
  }
757
791
  }
758
- if (pendingCheck) {
759
- if (!el._pendingCheck) {
760
- el._pendingCheck = signal(false);
761
- el._pendingCheck._optimistic = true;
762
- el._pendingCheck._set = v => setSignal(el._pendingCheck, v);
763
- }
764
- const prev = pendingCheck;
765
- pendingCheck = null;
766
- prev._value = read(el._pendingCheck) || prev._value;
767
- pendingCheck = prev;
768
- }
769
792
  if (pendingValueCheck) {
770
793
  if (!el._pendingSignal) {
771
794
  el._pendingSignal = signal(el._value);
@@ -778,10 +801,23 @@ function read(el) {
778
801
  pendingValueCheck = true;
779
802
  }
780
803
  }
781
- if (el._statusFlags & STATUS_PENDING && !pendingCheck) {
782
- if ((c && !stale) || el._statusFlags & STATUS_UNINITIALIZED) throw el._error;
804
+ const asyncCompute = el._firewall || el;
805
+ if (pendingCheck) {
806
+ if (!asyncCompute._pendingCheck) {
807
+ asyncCompute._pendingCheck = signal(false);
808
+ asyncCompute._pendingCheck._optimistic = true;
809
+ asyncCompute._pendingCheck._set = v => setSignal(asyncCompute._pendingCheck, v);
810
+ }
811
+ const prev = pendingCheck;
812
+ pendingCheck = null;
813
+ prev._value = read(asyncCompute._pendingCheck) || prev._value;
814
+ pendingCheck = prev;
815
+ }
816
+ if (!pendingCheck && asyncCompute._statusFlags & STATUS_PENDING) {
817
+ if ((c && !stale) || asyncCompute._statusFlags & STATUS_UNINITIALIZED || el._firewall)
818
+ throw asyncCompute._error;
783
819
  else if (c && stale) {
784
- setStatusFlags(c, c._statusFlags | 1, el._error);
820
+ setStatusFlags(c, c._statusFlags | STATUS_PENDING, asyncCompute._error);
785
821
  }
786
822
  }
787
823
  if (el._statusFlags & STATUS_ERROR) {
@@ -814,7 +850,9 @@ function setSignal(el, v) {
814
850
  const valueChanged =
815
851
  !el._equals ||
816
852
  !el._equals(
817
- el._pendingValue === NOT_PENDING || el._optimistic ? el._value : el._pendingValue,
853
+ el._pendingValue === NOT_PENDING || (el._optimistic && el._transition)
854
+ ? el._value
855
+ : el._pendingValue,
818
856
  v
819
857
  );
820
858
  if (!valueChanged && !el._statusFlags) return v;
@@ -928,6 +966,10 @@ function refresh(fn) {
928
966
  let prevRefreshing = refreshing;
929
967
  refreshing = true;
930
968
  try {
969
+ if (typeof fn !== "function") {
970
+ recompute(fn[$REFRESH]);
971
+ return fn;
972
+ }
931
973
  return untrack(fn);
932
974
  } finally {
933
975
  refreshing = prevRefreshing;
@@ -1136,11 +1178,7 @@ function unwrap(value) {
1136
1178
  return value?.[$TARGET]?.[STORE_NODE] ?? value;
1137
1179
  }
1138
1180
  function getOverrideValue(value, override, nodes, key) {
1139
- return nodes && key in nodes
1140
- ? read(nodes[key])
1141
- : override && key in override
1142
- ? override[key]
1143
- : value[key];
1181
+ return override && key in override ? override[key] : value[key];
1144
1182
  }
1145
1183
  function getAllKeys(value, override, next) {
1146
1184
  const keys = getKeys(value, override);
@@ -1226,10 +1264,12 @@ function applyState(next, state, keyFn, all) {
1226
1264
  } else target[STORE_NODE][j] && setSignal(target[STORE_NODE][j], wrap(next[j], target));
1227
1265
  }
1228
1266
  if (start < next.length) changed = true;
1229
- } else if (prevLength && next.length) {
1267
+ } else if (next.length) {
1230
1268
  for (let i = 0, len = next.length; i < len; i++) {
1231
1269
  const item = getOverrideValue(previous, override, nodes, i);
1232
- isWrappable(item) && applyState(next[i], wrap(item, target), keyFn, all);
1270
+ isWrappable(item)
1271
+ ? applyState(next[i], wrap(item, target), keyFn, all)
1272
+ : target[STORE_NODE][i] && setSignal(target[STORE_NODE][i], next[i]);
1233
1273
  }
1234
1274
  }
1235
1275
  if (prevLength !== next.length) {
@@ -1279,36 +1319,74 @@ function reconcile(value, key, all = false) {
1279
1319
  function createProjectionInternal(fn, initialValue = {}, options) {
1280
1320
  let node;
1281
1321
  const wrappedMap = new WeakMap();
1322
+ const wrapper = s => {
1323
+ s[STORE_WRAP] = wrapProjection;
1324
+ s[STORE_LOOKUP] = wrappedMap;
1325
+ Object.defineProperty(s, STORE_FIREWALL, {
1326
+ get() {
1327
+ return node;
1328
+ },
1329
+ configurable: true
1330
+ });
1331
+ };
1282
1332
  const wrapProjection = source => {
1283
1333
  if (wrappedMap.has(source)) return wrappedMap.get(source);
1284
1334
  if (source[$TARGET]?.[STORE_WRAP] === wrapProjection) return source;
1285
- const wrapped = createStoreProxy(source, storeTraps, {
1286
- [STORE_WRAP]: wrapProjection,
1287
- [STORE_LOOKUP]: wrappedMap,
1288
- [STORE_FIREWALL]() {
1289
- return node;
1290
- }
1291
- });
1335
+ const wrapped = createStoreProxy(source, storeTraps, wrapper);
1292
1336
  wrappedMap.set(source, wrapped);
1293
1337
  return wrapped;
1294
1338
  };
1295
1339
  const wrappedStore = wrapProjection(initialValue);
1296
1340
  node = computed(() => {
1297
1341
  const owner = node || getOwner();
1298
- storeSetter(wrappedStore, s => {
1342
+ storeSetter(new Proxy(wrappedStore, writeTraps), s => {
1299
1343
  const value = handleAsync(owner, fn(s), value => {
1300
- value !== s &&
1344
+ value !== wrappedStore &&
1301
1345
  value !== undefined &&
1302
1346
  storeSetter(wrappedStore, reconcile(value, options?.key || "id", options?.all));
1347
+ setSignal(node, undefined);
1303
1348
  });
1304
- value !== s && value !== undefined && reconcile(value, options?.key || "id", options?.all)(s);
1349
+ value !== wrappedStore &&
1350
+ value !== undefined &&
1351
+ reconcile(value, options?.key || "id", options?.all)(wrappedStore);
1305
1352
  });
1306
1353
  });
1354
+ node._preventAutoDisposal = true;
1307
1355
  return { store: wrappedStore, node: node };
1308
1356
  }
1309
1357
  function createProjection(fn, initialValue = {}, options) {
1310
1358
  return createProjectionInternal(fn, initialValue, options).store;
1311
1359
  }
1360
+ const writeTraps = {
1361
+ get(_, prop) {
1362
+ let value;
1363
+ setWriteOverride(true);
1364
+ try {
1365
+ value = _[prop];
1366
+ } finally {
1367
+ setWriteOverride(false);
1368
+ }
1369
+ return typeof value === "object" && value !== null ? new Proxy(value, writeTraps) : value;
1370
+ },
1371
+ set(_, prop, value) {
1372
+ setWriteOverride(true);
1373
+ try {
1374
+ _[prop] = value;
1375
+ } finally {
1376
+ setWriteOverride(false);
1377
+ }
1378
+ return true;
1379
+ },
1380
+ deleteProperty(_, prop) {
1381
+ setWriteOverride(true);
1382
+ try {
1383
+ delete _[prop];
1384
+ } finally {
1385
+ setWriteOverride(false);
1386
+ }
1387
+ return true;
1388
+ }
1389
+ };
1312
1390
  const $TRACK = Symbol("STORE_TRACK"),
1313
1391
  $DEEP = Symbol("STORE_DEEP"),
1314
1392
  $TARGET = Symbol("STORE_TARGET"),
@@ -1328,7 +1406,7 @@ function createStoreProxy(value, traps = storeTraps, extend) {
1328
1406
  newTarget = [];
1329
1407
  newTarget.v = value;
1330
1408
  } else newTarget = { v: value };
1331
- extend && Object.assign(newTarget, extend);
1409
+ extend && extend(newTarget);
1332
1410
  return (newTarget[$PROXY] = new Proxy(newTarget, traps));
1333
1411
  }
1334
1412
  const storeLookup = new WeakMap();
@@ -1341,6 +1419,13 @@ function wrap(value, target) {
1341
1419
  function isWrappable(obj) {
1342
1420
  return obj != null && typeof obj === "object" && !Object.isFrozen(obj);
1343
1421
  }
1422
+ let writeOverride = false;
1423
+ function setWriteOverride(value) {
1424
+ writeOverride = value;
1425
+ }
1426
+ function writeOnly(proxy) {
1427
+ return writeOverride || !!Writing?.has(proxy);
1428
+ }
1344
1429
  function getNodes(target, type) {
1345
1430
  let nodes = target[type];
1346
1431
  if (!nodes) target[type] = nodes = Object.create(null);
@@ -1361,9 +1446,7 @@ function getNode(nodes, property, value, firewall, equals = isEqual) {
1361
1446
  }
1362
1447
  function trackSelf(target, symbol = $TRACK) {
1363
1448
  getObserver() &&
1364
- read(
1365
- getNode(getNodes(target, STORE_NODE), symbol, undefined, target[STORE_FIREWALL]?.(), false)
1366
- );
1449
+ read(getNode(getNodes(target, STORE_NODE), symbol, undefined, target[STORE_FIREWALL], false));
1367
1450
  }
1368
1451
  function getKeys(source, override, enumerable = true) {
1369
1452
  const baseKeys = untrack(() => (enumerable ? Object.keys(source) : Reflect.ownKeys(source)));
@@ -1389,6 +1472,7 @@ const storeTraps = {
1389
1472
  get(target, property, receiver) {
1390
1473
  if (property === $TARGET) return target;
1391
1474
  if (property === $PROXY) return receiver;
1475
+ if (property === $REFRESH) return target[STORE_FIREWALL];
1392
1476
  if (property === $TRACK || property === $DEEP) {
1393
1477
  trackSelf(target, property);
1394
1478
  return receiver;
@@ -1402,7 +1486,7 @@ const storeTraps = {
1402
1486
  const desc = Object.getOwnPropertyDescriptor(storeValue, property);
1403
1487
  if (desc && desc.get) return desc.get.call(receiver);
1404
1488
  }
1405
- if (Writing?.has(receiver)) {
1489
+ if (writeOnly(receiver)) {
1406
1490
  let value =
1407
1491
  tracked && (overridden || !proxySource)
1408
1492
  ? tracked._pendingValue !== NOT_PENDING
@@ -1412,7 +1496,7 @@ const storeTraps = {
1412
1496
  value === $DELETED && (value = undefined);
1413
1497
  if (!isWrappable(value)) return value;
1414
1498
  const wrapped = wrap(value, target);
1415
- Writing.add(wrapped);
1499
+ Writing?.add(wrapped);
1416
1500
  return wrapped;
1417
1501
  }
1418
1502
  let value = tracked
@@ -1435,7 +1519,7 @@ const storeTraps = {
1435
1519
  nodes,
1436
1520
  property,
1437
1521
  isWrappable(value) ? wrap(value, target) : value,
1438
- target[STORE_FIREWALL]?.()
1522
+ target[STORE_FIREWALL]
1439
1523
  )
1440
1524
  );
1441
1525
  }
@@ -1449,12 +1533,12 @@ const storeTraps = {
1449
1533
  ? target[STORE_OVERRIDE][property] !== $DELETED
1450
1534
  : property in target[STORE_VALUE];
1451
1535
  getObserver() &&
1452
- read(getNode(getNodes(target, STORE_HAS), property, has, target[STORE_FIREWALL]?.()));
1536
+ read(getNode(getNodes(target, STORE_HAS), property, has, target[STORE_FIREWALL]));
1453
1537
  return has;
1454
1538
  },
1455
1539
  set(target, property, rawValue) {
1456
1540
  const store = target[$PROXY];
1457
- if (Writing?.has(target[$PROXY])) {
1541
+ if (writeOnly(store)) {
1458
1542
  untrack(() => {
1459
1543
  const state = target[STORE_VALUE];
1460
1544
  const base = state[property];
@@ -1489,7 +1573,7 @@ const storeTraps = {
1489
1573
  return true;
1490
1574
  },
1491
1575
  deleteProperty(target, property) {
1492
- if (Writing?.has(target[$PROXY]) && target[STORE_OVERRIDE]?.[property] !== $DELETED) {
1576
+ if (writeOnly(target[$PROXY]) && target[STORE_OVERRIDE]?.[property] !== $DELETED) {
1493
1577
  untrack(() => {
1494
1578
  const prev =
1495
1579
  target[STORE_OVERRIDE] && property in target[STORE_OVERRIDE]
@@ -2214,6 +2298,7 @@ export {
2214
2298
  NoOwnerError,
2215
2299
  NotReadyError,
2216
2300
  SUPPORTS_PROXY,
2301
+ action,
2217
2302
  createBoundary,
2218
2303
  createContext,
2219
2304
  createEffect,