🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@solidjs/signals

Package Overview
Dependencies
Maintainers
2
Versions
113
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@solidjs/signals - npm Package Compare versions

Comparing version
2.0.0-beta.20
to
2.0.0-beta.21
+47
-9
dist/prod/core/action.js

@@ -7,5 +7,5 @@ import { globalQueue, activeTransition, currentTransition, setActiveTransition, schedule, flush } from "./scheduler.js";

function restoreTransition(e, n) {
function restoreTransition(e, r) {
globalQueue.initTransition(e);
const t = n();
const t = r();
flush();

@@ -16,2 +16,18 @@ return t;

/**
* The primitive for mutations: imperative async workflows whose *writes span
* an async gap* — optimistic write, server round-trip, reconciling write —
* where intermediate state must not leak and failure must revert cleanly
* (pair with `createOptimistic` / `createOptimisticStore`).
*
* Navigation-shaped updates do not need an action. A plain setter call is
* enough: reads pull the async, and downstream async computeds hold their
* previous values per-node until the new ones are ready (`isPending` /
* `latest` expose the in-flight state). Reach for `action` only when writes
* happen *after* async work, not merely upstream of it.
*
* Framework-level actions (router form actions, server actions) are
* specializations of this primitive: they are actions in exactly this sense —
* the same transactional semantics — with form binding, serialization, and
* submission tracking layered on top. The shared name is deliberate.
*
* Wraps a generator function so each invocation runs as a single transaction

@@ -63,8 +79,8 @@ * (a "transition") that batches every signal/store write between yields. The

*/ function action(e) {
return (...n) => new Promise((t, r) => {
const i = e(...n);
return (...r) => new Promise((t, n) => {
const i = e(...r);
globalQueue.initTransition();
let o = activeTransition;
o.Te.push(i);
const done = (e, n, s = false) => {
const done = (e, r, s = false) => {
o = currentTransition(o);

@@ -75,8 +91,8 @@ const u = o.Te.indexOf(i);

schedule();
s ? r(n) : t(e);
s ? n(r) : t(e);
};
const step = (e, n) => {
const step = (e, r) => {
let t;
try {
t = n ? i.throw(e) : i.next(e);
t = r ? i.throw(e) : i.next(e);
} catch (e) {

@@ -93,3 +109,25 @@ return done(undefined, e, true);

if (e.done) return done(e.value);
if (isThenable(e.value)) return void e.value.then(e => restoreTransition(o, () => step(e)), e => restoreTransition(o, () => step(e, true)));
// Thenable assimilation can itself throw synchronously (a `then`
// getter, or a `then()` method that throws — #2918). Match `await`
// semantics: the failure is thrown back into the generator at the
// yield point (catchable there); if uncaught, step()'s guard settles
// the action so its iterator never leaks in the transition. The
// settled flag implements A+ 2.3.3.3.4.1: a throw after the thenable
// already called a callback is ignored.
let r = false;
try {
if (isThenable(e.value)) return void e.value.then(e => {
if (r) return;
r = true;
restoreTransition(o, () => step(e));
}, e => {
if (r) return;
r = true;
restoreTransition(o, () => step(e, true));
});
} catch (e) {
if (r) return;
r = true;
return void restoreTransition(o, () => step(e, true));
}
restoreTransition(o, () => step(e.value));

@@ -96,0 +134,0 @@ };

+1
-1
import { handleAsync, clearStatus, notifyStatus } from "./async.js";
import { EFFECT_TRACKED, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, STATUS_UNINITIALIZED, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_SYNC, STATUS_PENDING, STATUS_ERROR, REACTIVE_NONE, REACTIVE_SNAPSHOT_STALE, unwrapOverride, OVERRIDE_UNDEFINED, CONFIG_AUTO_DISPOSE, REACTIVE_LAZY, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, defaultContext, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_NO_SNAPSHOT, $REFRESH, REACTIVE_MANUAL_WRITE, NO_SNAPSHOT, STORE_SNAPSHOT_PROPS, EFFECT_USER } from "./constants.js";
import { EFFECT_TRACKED, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, STATUS_UNINITIALIZED, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_SYNC, STATUS_PENDING, STATUS_ERROR, REACTIVE_NONE, REACTIVE_SNAPSHOT_STALE, unwrapOverride, OVERRIDE_UNDEFINED, REACTIVE_LAZY, REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, defaultContext, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_NO_SNAPSHOT, $REFRESH, REACTIVE_MANUAL_WRITE, NO_SNAPSHOT, STORE_SNAPSHOT_PROPS, EFFECT_USER } from "./constants.js";

@@ -5,0 +5,0 @@ import { NotReadyError } from "./error.js";

@@ -46,3 +46,11 @@ import { REACTIVE_IN_HEAP, EFFECT_TRACKED, EFFECT_USER, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MANUAL_WRITE, REACTIVE_ZOMBIE, REACTIVE_CHECK, REACTIVE_DIRTY } from "./constants.js";

e.u = t & -4 | REACTIVE_DIRTY | REACTIVE_IN_HEAP;
} else e.u = t | REACTIVE_IN_HEAP;
} else {
e.u = t | REACTIVE_IN_HEAP;
// An unmarked node entering a marked heap invalidates the markHeap memo:
// `_marked` is only reset by runHeap, so a write between two mid-tick
// pulls (read-time markHeap + updateIfNecessary) would otherwise leave
// this node unmarked and every downstream pull stale until the next
// flush (#2922: the second `latest()` returned the first write's value).
if (E.tE && !(t & REACTIVE_DIRTY)) E.tE = false;
}
if (!(t & REACTIVE_IN_HEAP_HEIGHT)) actualInsertIntoHeap(e, E);

@@ -49,0 +57,0 @@ }

@@ -326,4 +326,8 @@ import { EFFECT_RENDER, EFFECT_USER, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, NOT_PENDING, EFFECT_TRACKED, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_MANUAL_WRITE, STATUS_UNINITIALIZED } from "./constants.js";

// Detach: the stashed transition keeps its batch; ambient work that
// follows lands in a fresh one.
currentBatch = this.N = createBatch();
// follows lands in a fresh one. If the batch is already a separate
// ambient one — action done() restored activeTransition without
// adopting the batch, and an ordinary write landed there before
// the scheduled flush (#2916) — keep it: replacing it would strand
// its queued pending nodes with held _pendingValues forever.
if (this.N === e) currentBatch = this.N = createBatch();
// Run lane effects immediately (before stashing) - lanes with no pending async

@@ -336,3 +340,6 @@ if (activeLanes.size) {

clock++;
scheduled = dirtyQueue.EE >= dirtyQueue.Le;
// A kept ambient batch may hold pending nodes (#2916): stay
// scheduled so the outer drain loop commits them via the plain
// flush path instead of leaving them until the next natural flush.
scheduled = dirtyQueue.EE >= dirtyQueue.Le || this.N.yt.length > 0;
reassignPendingTransition(e.yt);

@@ -339,0 +346,0 @@ activeTransition = null;

@@ -1,4 +0,4 @@

import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, STATUS_UNINITIALIZED, STATUS_PENDING, REACTIVE_MANUAL_WRITE } from "./constants.js";
import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, REACTIVE_ZOMBIE, STATUS_UNINITIALIZED, STATUS_PENDING, REACTIVE_MANUAL_WRITE } from "./constants.js";
import { setSignal, read, context, stale, currentOptimisticLane, prepareComputed, tracking, setLatestReadActive, setContextInternal, optimisticComputed, setPendingCheckActive, latestReadActive, optimisticSignal, pendingCheckActive } from "./core.js";
import { setSignal, prepareComputed, read, context, stale, currentOptimisticLane, tracking, setLatestReadActive, setContextInternal, optimisticComputed, setPendingCheckActive, latestReadActive, optimisticSignal, pendingCheckActive } from "./core.js";

@@ -9,3 +9,3 @@ import { NotReadyError } from "./error.js";

import { insertIntoHeap, queueFor } from "./heap.js";
import { insertIntoHeap, queueFor, markHeap } from "./heap.js";

@@ -18,3 +18,3 @@ import "./invariants.js";

import { GlobalQueue, clock, insertSubs, schedule } from "./scheduler.js";
import { GlobalQueue, clock, insertSubs, schedule, activeTransition } from "./scheduler.js";

@@ -180,2 +180,12 @@ /**

try {
// An untracked latest() read has no reading context, so read() never
// performs its mid-tick pull — a plain write queued between two latest()
// calls left a still-subscribed shadow at its previous speculative value
// until the flush (#2922). Mirror the tracked-read pull here: mark the
// queued staleness through the graph, then bring the shadow up to date.
const e = queueFor(t);
if (t.qe >= e.Le && !(t.u & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
markHeap(e);
prepareComputed(t, true);
}
r = read(t);

@@ -196,2 +206,7 @@ } catch (t) {

}
// A shadow recomputed by the pull above (not at creation) holds its fresh
// speculative value in _pendingValue; a contextless read() only surfaces
// _value. Overrides stay authoritative (A17), and stale readers keep the
// other transition's committed view, matching read()'s own selection.
if (t.ge !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.ve && activeTransition !== t.ve)) return t.ge;
return r;

@@ -198,0 +213,0 @@ }

/**
* The primitive for mutations: imperative async workflows whose *writes span
* an async gap* — optimistic write, server round-trip, reconciling write —
* where intermediate state must not leak and failure must revert cleanly
* (pair with `createOptimistic` / `createOptimisticStore`).
*
* Navigation-shaped updates do not need an action. A plain setter call is
* enough: reads pull the async, and downstream async computeds hold their
* previous values per-node until the new ones are ready (`isPending` /
* `latest` expose the in-flight state). Reach for `action` only when writes
* happen *after* async work, not merely upstream of it.
*
* Framework-level actions (router form actions, server actions) are
* specializations of this primitive: they are actions in exactly this sense —
* the same transactional semantics — with form binding, serialization, and
* submission tracking layered on top. The shared name is deliberate.
*
* Wraps a generator function so each invocation runs as a single transaction

@@ -3,0 +19,0 @@ * (a "transition") that batches every signal/store write between yields. The

/**
* The primitive for mutations: imperative async workflows whose *writes span
* an async gap* — optimistic write, server round-trip, reconciling write —
* where intermediate state must not leak and failure must revert cleanly
* (pair with `createOptimistic` / `createOptimisticStore`).
*
* Navigation-shaped updates do not need an action. A plain setter call is
* enough: reads pull the async, and downstream async computeds hold their
* previous values per-node until the new ones are ready (`isPending` /
* `latest` expose the in-flight state). Reach for `action` only when writes
* happen *after* async work, not merely upstream of it.
*
* Framework-level actions (router form actions, server actions) are
* specializations of this primitive: they are actions in exactly this sense —
* the same transactional semantics — with form binding, serialization, and
* submission tracking layered on top. The shared name is deliberate.
*
* Wraps a generator function so each invocation runs as a single transaction

@@ -3,0 +19,0 @@ * (a "transition") that batches every signal/store write between yields. The

{
"name": "@solidjs/signals",
"version": "2.0.0-beta.20",
"version": "2.0.0-beta.21",
"description": "Solid's reactive primitives: signals, memos, effects, stores, and async-aware computations.",

@@ -5,0 +5,0 @@ "author": "Ryan Carniato",

@@ -115,3 +115,3 @@ # @solidjs/signals

Use `action()` to coordinate async workflows with the reactive graph:
Use `action()` for mutations — imperative async workflows whose writes span an async gap. Each invocation runs as a single transaction: every write between yields batches into one atomic update, and nothing commits until the action completes or the next `yield` resolves.

@@ -124,2 +124,6 @@ ```typescript

Navigation-shaped updates don't need an action. A plain setter call is enough — reads pull the async, downstream async computeds hold their previous values until new ones are ready, and `isPending`/`latest` expose the in-flight state. Reach for `action` when writes happen *after* async work (pairing with optimistic primitives for tentative state that reverts on failure), not when the async is merely downstream of a synchronous write.
Framework-level actions (router form actions, server actions) are specializations of this primitive: the same transactional semantics with form binding, serialization, and submission tracking layered on top.
## Optimistic Updates

@@ -126,0 +130,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display