@solidjs/signals 2.0.0-rc.4 → 2.0.0-rc.6

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.
Files changed (57) hide show
  1. package/dist/dev.js +1618 -232
  2. package/dist/node.cjs +2262 -1199
  3. package/dist/prod/boundaries.js +4 -1
  4. package/dist/prod/core/async.js +152 -101
  5. package/dist/prod/core/constants.js +55 -1
  6. package/dist/prod/core/core.js +305 -234
  7. package/dist/prod/core/effect.js +28 -28
  8. package/dist/prod/core/error.js +13 -1
  9. package/dist/prod/core/external.js +2 -2
  10. package/dist/prod/core/graph.js +27 -27
  11. package/dist/prod/core/heap.js +30 -30
  12. package/dist/prod/core/lanes.js +32 -32
  13. package/dist/prod/core/optimistic.js +57 -57
  14. package/dist/prod/core/owner.js +34 -34
  15. package/dist/prod/core/scheduler.js +346 -152
  16. package/dist/prod/core/verdict.js +122 -65
  17. package/dist/prod/index.js +3 -3
  18. package/dist/prod/map.js +106 -106
  19. package/dist/prod/signals.js +321 -26
  20. package/dist/prod/store/next/optimistic.js +363 -151
  21. package/dist/prod/store/next/patch.js +6 -6
  22. package/dist/prod/store/next/projection.js +25 -21
  23. package/dist/prod/store/next/store.js +223 -113
  24. package/dist/prod/store/store.js +2 -2
  25. package/dist/types/core/async.d.ts +2 -0
  26. package/dist/types/core/attribution-hooks.d.ts +11 -0
  27. package/dist/types/core/attribution.d.ts +57 -4
  28. package/dist/types/core/constants.d.ts +54 -0
  29. package/dist/types/core/core.d.ts +19 -20
  30. package/dist/types/core/dev.d.ts +8 -2
  31. package/dist/types/core/error.d.ts +9 -0
  32. package/dist/types/core/index.d.ts +2 -2
  33. package/dist/types/core/scheduler.d.ts +39 -0
  34. package/dist/types/core/types.d.ts +12 -0
  35. package/dist/types/index.d.ts +3 -3
  36. package/dist/types/signals.d.ts +108 -11
  37. package/dist/types/store/next/optimistic.d.ts +13 -10
  38. package/dist/types/store/next/projection.d.ts +1 -1
  39. package/dist/types/store/next/store.d.ts +22 -0
  40. package/dist/types/store/next/target.d.ts +25 -0
  41. package/dist/types-cjs/core/async.d.cts +2 -0
  42. package/dist/types-cjs/core/attribution-hooks.d.cts +11 -0
  43. package/dist/types-cjs/core/attribution.d.cts +57 -4
  44. package/dist/types-cjs/core/constants.d.cts +54 -0
  45. package/dist/types-cjs/core/core.d.cts +19 -20
  46. package/dist/types-cjs/core/dev.d.cts +8 -2
  47. package/dist/types-cjs/core/error.d.cts +9 -0
  48. package/dist/types-cjs/core/index.d.cts +2 -2
  49. package/dist/types-cjs/core/scheduler.d.cts +39 -0
  50. package/dist/types-cjs/core/types.d.cts +12 -0
  51. package/dist/types-cjs/index.d.cts +3 -3
  52. package/dist/types-cjs/signals.d.cts +108 -11
  53. package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
  54. package/dist/types-cjs/store/next/projection.d.cts +1 -1
  55. package/dist/types-cjs/store/next/store.d.cts +22 -0
  56. package/dist/types-cjs/store/next/target.d.cts +25 -0
  57. package/package.json +1 -1
@@ -252,7 +252,7 @@ s) {
252
252
  // Callers guard on `pendingCheckActive`, which only flips inside
253
253
  // isPending() — the verdict layer is loaded and its hook installed.
254
254
  const o = e[STORE_NODE]?.[$AFFECTS];
255
- if (o?.o?.t) GlobalQueue.wt(o);
255
+ if (o?.o?.t) GlobalQueue.Wt(o);
256
256
  if (affectsScopes.size) {
257
257
  // Chained backings (§7b): a wrapper's STORE_VALUE can be another store's
258
258
  // proxy — marks cover by identity of the BASE raw, so resolve the chain
@@ -263,7 +263,7 @@ s) {
263
263
  let t = r;
264
264
  for (;;) {
265
265
  if (s.scope.has(t)) {
266
- GlobalQueue.wt(e);
266
+ GlobalQueue.Wt(e);
267
267
  break;
268
268
  }
269
269
  const o = t?.[$TARGET];
@@ -16,6 +16,8 @@ export declare function releaseSettledDependents(el: Computed<any>): void;
16
16
  export declare function settleErroredDependents(el: Computed<any>, error: any): void;
17
17
  export declare function settlePendingSource(el: Computed<any>): void;
18
18
  export declare function isThenable<T>(value: T | PromiseLike<T>): value is PromiseLike<T>;
19
+ /** Fire and clear a node's iterator-flight cancellation hook (#3122). */
20
+ export declare function releaseFlightTeardown(el: Computed<any>): void;
19
21
  export declare function handleAsync<T>(el: Computed<T>, result: T | PromiseLike<T> | AsyncIterable<T>, setter?: (value: T) => void): T;
20
22
  export declare function clearStatus(el: Computed<any>, clearUninitialized?: boolean): void;
21
23
  export declare function notifyStatus(el: Computed<any>, status: number, error: any, blockStatus?: boolean, lane?: OptimisticLane): void;
@@ -36,6 +36,17 @@ export interface AttributionHooks {
36
36
  write(el: Signal<any> | Computed<any>, prev: unknown, value: unknown): void;
37
37
  /** refresh() invalidated this node (self-invalidation, no dep changed). */
38
38
  refreshed(el: Computed<any>): void;
39
+ /**
40
+ * A new async flight entered the system (`_inFlight` was just assigned
41
+ * during a recompute of `el`). Always fired inside the owning recompute —
42
+ * both call paths (core's recompute and the projection self-registration)
43
+ * run within one — so the engine can read the current frame stack to link
44
+ * the flight to the change that caused it (waterfall chaining). `flight`
45
+ * is the registered thenable/iterable itself: the engine keys a first-seen
46
+ * origin registry on its identity, so shared and preloader-marked promises
47
+ * carry their true start time instead of the moment the graph saw them.
48
+ */
49
+ flightStart(el: Computed<any>, flight: object): void;
39
50
  /** An async landing is about to apply its value (before any branch). */
40
51
  asyncStart(el: Computed<any>): void;
41
52
  /**
@@ -60,11 +60,16 @@ export interface RerunEvent {
60
60
  /** Wall time of this run including nested recomputes (ms). */
61
61
  totalMs: number;
62
62
  /**
63
- * Whether the run committed a changed value. A PLAIN memo run with
63
+ * Whether the run produced a changed value. A PLAIN memo run with
64
64
  * `changed: false` was pure waste — the equality cutoff stopped it from
65
- * notifying anyone; an effect run with `changed: false` computed without
66
- * firing its effect phase. Summed as `wastedMs` in costs() (plain,
67
- * non-held runs only see `phase`).
65
+ * notifying anyone. Effects run with `_equals: false` in core (their
66
+ * effect phase re-fires on every recompute), so the engine derives this
67
+ * fact itself: an effect run whose compute output is identical to the
68
+ * previous run's reports `changed: false` — the phase re-fired with the
69
+ * same input, pure waste. Side-effect-only computes (`undefined` output)
70
+ * are exempt: identity of `undefined` proves nothing about their work.
71
+ * Summed as `wastedMs` in costs() (plain, non-held runs only — see
72
+ * `phase`).
68
73
  */
69
74
  changed: boolean;
70
75
  /**
@@ -138,6 +143,24 @@ export interface AttributionOptions {
138
143
  * disables.
139
144
  */
140
145
  wideWrites?: number | false;
146
+ /**
147
+ * Async-waterfall warning: emit a diagnostic when an async flight that
148
+ * could only start after an upstream flight resolved (its recompute's
149
+ * cause chain reaches the upstream's async landing, and its origin
150
+ * post-dates that landing) forms a sequential chain of 2+ flights, each
151
+ * of which took at least `minFlightMs` (default 50ms). The duration gate
152
+ * is one safety valve for what the graph cannot see: a settled
153
+ * preload/cache hit resolves fast and never warns. In-flight preloads are
154
+ * absolved by origin: `markFlight()` stamps (and first-seen identity)
155
+ * prove work predated the upstream landing — parallel, not sequential.
156
+ * Chains of 2 emit at `info` severity, structured channel only (a
157
+ * dependent fetch is sometimes intrinsic, and unmarked external preloads
158
+ * are invisible); 3+ escalate to `warn` with console output. `false`
159
+ * disables.
160
+ */
161
+ waterfalls?: {
162
+ minFlightMs: number;
163
+ } | false;
141
164
  }
142
165
  export interface ScopeCost {
143
166
  name: string;
@@ -181,6 +204,36 @@ export interface Attribution {
181
204
  scopes: ScopeCost[];
182
205
  writes: WriteCost[];
183
206
  };
207
+ /**
208
+ * Every graph-provable sequential flight chain observed since enable()
209
+ * (ring-buffered like history()). Facts, not verdicts: chains are recorded
210
+ * regardless of the duration gate — the ASYNC_WATERFALL diagnostic is the
211
+ * thresholded view of the same data.
212
+ */
213
+ waterfalls(): readonly WaterfallRecord[];
214
+ /**
215
+ * Cooperative preload declaration: stamp a flight object (promise or async
216
+ * iterable) with its true kickoff time BEFORE the reactive graph sees it.
217
+ * A route preloader or query cache calls this on the promise it hands out
218
+ * (on the WRAPPER it mints, with the original kickoff time — wrapping
219
+ * defeats identity tracking otherwise); any dependent that later awaits it
220
+ * is then judged against the real start — work already in the air when its
221
+ * upstream landed is parallel, never a waterfall link. Callable while
222
+ * attribution is disabled (marks made at navigation time must survive a
223
+ * later enable()). Dev-only, like the whole DEV surface.
224
+ */
225
+ markFlight(flight: object, startedAt?: number): void;
184
226
  format: typeof formatRerun;
185
227
  }
228
+ /** One landed flight: its node name, wall duration, and upstream chain. */
229
+ export interface FlightLink {
230
+ name: string;
231
+ ms: number;
232
+ }
233
+ export interface WaterfallRecord {
234
+ /** Sequential flights, oldest first, ending at the flight that landed. */
235
+ chain: FlightLink[];
236
+ /** Summed wall time of the chain — the serialized cost. */
237
+ sequentialMs: number;
238
+ }
186
239
  export declare const attribution: Attribution;
@@ -51,6 +51,60 @@ export declare const CONFIG_CHILD_COMPANIONS: number;
51
51
  * moved into the cold extension (§12), and an unconditional `_x` deref per
52
52
  * marked node measurably taxed the propagation hot path (diamond -22%). */
53
53
  export declare const CONFIG_FW_CHILDREN: number;
54
+ /** Authoritative-view reader (`until()`): while this node computes, reads
55
+ * dodge active optimistic OVERRIDES only — the predicate must observe
56
+ * arriving truth, never the caller's own tentative writes (which would
57
+ * trivially satisfy it). Everything else reads normally, INCLUDING
58
+ * transition-staged `_pendingValue`: staged data is authoritative (optimism
59
+ * lives only in override slots), and a hold that refused staged reads would
60
+ * deadlock on data the open transaction itself is holding (a refresh the
61
+ * action issued lands staged and cannot commit until the hold releases).
62
+ * read() checks the bit on the reading computation (`context`) directly — no
63
+ * ambient flag — so a shared computed the predicate pulls recomputes as
64
+ * itself (no bit) under the normal view, and its cache never forks. */
65
+ export declare const CONFIG_AUTHORITATIVE_READ: number;
66
+ /** Sticky mark: an authoritative-view reader read this node PAST an active
67
+ * override. The ack shape — an authoritative arrival EQUAL to the override —
68
+ * rides paths that are deliberately silent under A17 (every ordinary reader
69
+ * sees the override, so an equal landing changes nothing for them). A marked
70
+ * node notifies those readers on such paths anyway, so the landed truth is
71
+ * seen without re-firing ordinary subscribers. Never cleared — only nodes an
72
+ * until() predicate observed mid-override pay. */
73
+ export declare const CONFIG_AUTHORITATIVE_OBSERVED: number;
74
+ /** Promise-delivery effect (resolve()/until()): commits its computed value
75
+ * directly even when recomputing under its own held transition. These
76
+ * effects deliver applies on a microtask (#2930) instead of the stashed
77
+ * effect queues, so the value must ride the same immediate schedule — a
78
+ * staged value with an immediate apply delivers stale state (resolve) or
79
+ * deadlocks the hold (until). Safe because the node is a private leaf: no
80
+ * subscriber reads an effect's value, only its own apply does. */
81
+ export declare const CONFIG_DIRECT_COMMIT: number;
82
+ /** Fresh-pull reader (awaitable `refresh()`'s waiter effect): a read of a
83
+ * dirty source recomputes it inline even when the height gate defers to the
84
+ * flush. Closes the same-flush ordering race where a waiter created
85
+ * alongside a refresh() mark read the PRE-re-ask value as settled and
86
+ * delivered stale; with the pull, the waiter either parks on the re-ask's
87
+ * pending window (async — woken by the settle walk, which runs on every
88
+ * landing including equal-value ones) or serves its sync answer. resolve()
89
+ * deliberately keeps that race — its contract is "first settled value"
90
+ * (#2930), not "next quiescent state". */
91
+ export declare const CONFIG_FRESH_READ: number;
92
+ /** HELD truth (#3164): this node's staged `_pendingValue` is confirming
93
+ * truth riding a transaction that retains optimism, revealed only at that
94
+ * transaction's settle. Two arming sites, one meaning: the store fold
95
+ * (a landing staged into the retaining transaction) and until()'s
96
+ * flip-entanglement (a foreign carrier's staged write, stolen when it
97
+ * flipped the awaited predicate truthy). Until the reveal, ordinary
98
+ * readers — lane and speculative recomputes included — keep committed:
99
+ * the staging notified subscribers as a plain write, so without the mask
100
+ * a mid-hold recompute composes live optimism with the confirming truth,
101
+ * a frame no timeline contains (GabbeV's union tear). Authoritative
102
+ * readers (until()'s predicate) and latest() tunnel through — the
103
+ * exemption that keeps holds deadlock-free. Override-covered nodes never
104
+ * arm: the override is their display and its revert their notification
105
+ * (A17). Cleared at commit (the commit IS the reveal); subscribers masked
106
+ * during the hold are woken by finalizePureQueue's post-revert pass. */
107
+ export declare const CONFIG_HELD_TRUTH: number;
54
108
  export declare const STATUS_NONE = 0;
55
109
  export declare const STATUS_PENDING: number;
56
110
  export declare const STATUS_ERROR: number;
@@ -1,4 +1,3 @@
1
- import { type Refreshable } from "./constants.js";
2
1
  import { type OptimisticLane } from "./lanes.js";
3
2
  import type { Computed, FirewallSignal, NodeExtension, NodeOptions, Owner, Signal } from "./types.js";
4
3
  export declare const PRIMITIVE_IN_FORBIDDEN_SCOPE_MESSAGE = "[PRIMITIVE_IN_FORBIDDEN_SCOPE] Cannot create reactive primitives inside createTrackedEffect or owner-backed onSettled";
@@ -107,6 +106,19 @@ export declare const READ_SLOW: unique symbol;
107
106
  * snapshot / transition / lane / dev-strictRead state all take the full
108
107
  * resolution. Anything slow returns READ_SLOW; the caller then calls read().
109
108
  */
109
+ /**
110
+ * Wake only authoritative-view readers (until() predicates) subscribed to `el`.
111
+ * The A17-silent ack paths — an authoritative arrival equal to the active
112
+ * override — use this so the predicate re-evaluates without re-firing
113
+ * ordinary subscribers whose visible (override) value did not change.
114
+ * Pay-for-use: reached through GlobalQueue._notifyAuthoritativeObservers,
115
+ * installed at first until() call — apps that never use until() shake it.
116
+ */
117
+ export declare function notifyAuthoritativeObservers(el: Signal<any> | Computed<any>): void;
118
+ /** Installs the until() machinery hook. Idempotent; called by until() before
119
+ * any authoritative-view read happens (same late-binding contract as the
120
+ * optimistic engine). */
121
+ export declare function installAuthoritativeRead(): void;
110
122
  export declare function readNodeFast<T>(el: Signal<T>): T | typeof READ_SLOW;
111
123
  export declare function read<T>(el: Signal<T> | Computed<T>): T;
112
124
  /**
@@ -155,23 +167,10 @@ export declare function setMemo<T>(el: Computed<T>, v: T | ((prev: T) => T)): T;
155
167
  export declare function runWithOwner<T>(owner: Owner | null, fn: () => T): T;
156
168
  export declare function staleValues<T>(fn: () => T, set?: boolean): T;
157
169
  /**
158
- * Invalidates one reactive source, forcing it to re-execute even if its inputs
159
- * haven't changed.
160
- *
161
- * Pass either a Solid-created accessor or a projected store created from
162
- * `createStore(fn, ...)` / `createProjection(...)`. `refresh()` is a
163
- * write-like invalidation operation: it does not read the target's value, and
164
- * refreshing a plain signal accessor is a no-op.
165
- *
166
- * Use it to invalidate cached async values (e.g. force a re-fetch) without
167
- * tearing the consumer down.
168
- *
169
- * @example
170
- * ```ts
171
- * const user = createMemo(async () => fetch(`/users/${id()}`).then(r => r.json()));
172
- *
173
- * // Re-fetch on demand
174
- * <button onClick={() => refresh(user)}>Reload</button>
175
- * ```
170
+ * Core marking half of `refresh()` (the public wrapper lives in signals.ts
171
+ * it validates the target, marks through here, then builds the quiescence
172
+ * promise on the resolve()/until() effect machinery). Flags the node's next
173
+ * recompute as a quiet re-ask and schedules it; no-ops for non-derived or
174
+ * disposed targets and for same-tick manual writes.
176
175
  */
177
- export declare function refresh<T>(target: Refreshable<T>): void;
176
+ export declare function markRefresh(node: Computed<any>): void;
@@ -6,8 +6,14 @@ export interface DevHooks {
6
6
  onUpdate?: () => void;
7
7
  onStoreNodeUpdate?: (state: any, property: PropertyKey, value: any, prev: any) => void;
8
8
  }
9
- export type DiagnosticSeverity = "warn" | "error";
10
- export type DiagnosticCode = "STRICT_READ_UNTRACKED" | "PENDING_ASYNC_UNTRACKED_READ" | "PENDING_ASYNC_FORBIDDEN_SCOPE" | "REACTIVE_WRITE_IN_OWNED_SCOPE" | "ACTION_CALLED_IN_OWNED_SCOPE" | "RUN_WITH_DISPOSED_OWNER" | "NO_OWNER_CLEANUP" | "CLEANUP_IN_FORBIDDEN_SCOPE" | "SETTLED_CLEANUP_UNOWNED" | "FLUSH_IN_EFFECT_CALLBACK" | "PRIMITIVE_IN_FORBIDDEN_SCOPE" | "NO_OWNER_EFFECT" | "NO_OWNER_BOUNDARY" | "ASYNC_OUTSIDE_LOADING_BOUNDARY" | "INVALID_REFRESH_TARGET" | "INVALID_AFFECTS_TARGET" | "MISSING_EFFECT_FN" | "SYNC_NODE_RECEIVED_ASYNC" | "REACTIVITY_HALTED" | "INVARIANT_VIOLATION" | "HUGE_FAN_OUT" | "HUGE_FAN_IN" | "HOT_SCOPE_RERUNS" | "HOT_SCOPE_TIME" | "WIDE_SCOPE_DEPS" | "UNSTABLE_MEMO_OUTPUT" | "WIDE_WRITE";
9
+ /**
10
+ * `info` is the advisory tier: a structural fact worth surfacing that is not
11
+ * presumptively a bug (e.g. a 2-deep sequential fetch chain, which may be an
12
+ * intrinsic data dependency). Budget/assertion consumers should treat only
13
+ * `warn`/`error` as failures unless they opt in to `info`.
14
+ */
15
+ export type DiagnosticSeverity = "info" | "warn" | "error";
16
+ export type DiagnosticCode = "STRICT_READ_UNTRACKED" | "PENDING_ASYNC_UNTRACKED_READ" | "PENDING_ASYNC_FORBIDDEN_SCOPE" | "REACTIVE_WRITE_IN_OWNED_SCOPE" | "ACTION_CALLED_IN_OWNED_SCOPE" | "RUN_WITH_DISPOSED_OWNER" | "NO_OWNER_CLEANUP" | "CLEANUP_IN_FORBIDDEN_SCOPE" | "SETTLED_CLEANUP_UNOWNED" | "SETTLE_WALK_UNINITIALIZED_SOURCE" | "FLUSH_IN_EFFECT_CALLBACK" | "PRIMITIVE_IN_FORBIDDEN_SCOPE" | "NO_OWNER_EFFECT" | "NO_OWNER_BOUNDARY" | "ASYNC_OUTSIDE_LOADING_BOUNDARY" | "INVALID_REFRESH_TARGET" | "INVALID_AFFECTS_TARGET" | "MISSING_EFFECT_FN" | "SYNC_NODE_RECEIVED_ASYNC" | "REACTIVITY_HALTED" | "INVARIANT_VIOLATION" | "HUGE_FAN_OUT" | "HUGE_FAN_IN" | "HOT_SCOPE_RERUNS" | "HOT_SCOPE_TIME" | "WIDE_SCOPE_DEPS" | "UNSTABLE_MEMO_OUTPUT" | "WIDE_WRITE" | "ASYNC_WATERFALL" | "HOT_SCOPE_FANOUT";
11
17
  export type DiagnosticKind = "strict-read" | "async" | "write" | "lifecycle" | "owner" | "error" | "perf" | "graph";
12
18
  /** First warning when a node's live edge count reaches this size. */
13
19
  export declare const GRAPH_SIZE_WARN_AT = 2000;
@@ -39,6 +39,15 @@ export declare class StatusError extends Error {
39
39
  }
40
40
  /** Return the user's error from an internal status wrapper. */
41
41
  export declare function unwrapStatusError(error: unknown): unknown;
42
+ /**
43
+ * Rejection value of `until(fn, { timeout })` when the predicate does not turn
44
+ * truthy within the window. Inside an `action()`, the rejection is thrown back
45
+ * in at the `yield` point — catchable there, or the action fails and its
46
+ * optimistic state reverts.
47
+ */
48
+ export declare class TimeoutError extends Error {
49
+ constructor(message?: string);
50
+ }
42
51
  export declare class NoOwnerError extends Error {
43
52
  constructor();
44
53
  }
@@ -1,5 +1,5 @@
1
- export { ContextNotFoundError, NoOwnerError, NotReadyError } from "./error.js";
2
- export { isEqual, untrack, runWithOwner, computed, signal, read, setSignal, setMemo, suppressComputedRecompute, optimisticSignal, optimisticComputed, refresh, staleValues, setSnapshotCapture, markSnapshotScope, releaseSnapshotScope, clearSnapshots } from "./core.js";
1
+ export { ContextNotFoundError, NoOwnerError, NotReadyError, TimeoutError } from "./error.js";
2
+ export { isEqual, untrack, runWithOwner, computed, signal, read, setSignal, setMemo, suppressComputedRecompute, optimisticSignal, optimisticComputed, installAuthoritativeRead, markRefresh, staleValues, setSnapshotCapture, markSnapshotScope, releaseSnapshotScope, clearSnapshots } from "./core.js";
3
3
  export { enableExternalSource, _resetExternalSourceConfig, type ExternalSourceFactory, type ExternalSource, type ExternalSourceConfig } from "./external.js";
4
4
  export { createOwner, createRoot, dispose, getNextChildId, getObserver, getOwner, isDisposed, cleanup, peekNextChildId } from "./owner.js";
5
5
  export { createContext, getContext, setContext, type Context, type ContextRecord } from "./context.js";
@@ -40,6 +40,24 @@ export interface Transition {
40
40
  _done: boolean | Transition;
41
41
  _gatedSubs: Set<Computed<any>>;
42
42
  }
43
+ /**
44
+ * Flip-entanglement (#3164 follow-up): `until()` is a declaration of
45
+ * relatedness — the predicate names the condition that confirms the awaiting
46
+ * transaction. When the predicate settles truthy, every live foreign
47
+ * transition whose staged write it read IS the confirming event by the
48
+ * user's own definition, so it merges into the awaiting transaction and
49
+ * reveals at the joint settle — the cross-primitive twin of the family fold
50
+ * (a landing on an optimism-carrying family joins the retaining
51
+ * transaction). Non-flipping updates never pass through here: falsy
52
+ * evaluations don't entangle, so unrelated traffic on the watched sources
53
+ * reveals freely on its own schedule.
54
+ *
55
+ * Runs inside the predicate's compute (pure phase) — the confirming
56
+ * transition's stamps are still live and its commit decision hasn't run, so
57
+ * the merge lands before any reveal. Only the tree-shaken graphs that call
58
+ * `until()` retain this.
59
+ */
60
+ export declare function entangleConfirmingTransitions(obs: Computed<any>, target: Transition): void;
43
61
  export declare function schedule(): void;
44
62
  /**
45
63
  * Permanently halts the reactive system. Called when a user error escapes
@@ -112,6 +130,10 @@ export declare class GlobalQueue extends Queue {
112
130
  static _laneReadsCommitted: ((el: OptimisticNode, owner: OptimisticNode, c: Computed<any>) => boolean) | null;
113
131
  static _recomputeLane: ((el: Computed<any>, own: boolean) => OptimisticLane | null | false) | null;
114
132
  static _laneAsyncPending: ((el: Computed<any>) => void) | null;
133
+ /** Authoritative-view reader wakeup (until()): installed at first until() call.
134
+ * Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which only until()'s
135
+ * carve-out read can set, so `!` invocations are safe once the gate holds. */
136
+ static _notifyAuthoritativeObservers: ((el: Signal<any> | Computed<any>) => void) | null;
115
137
  static _laneAsyncSettled: ((el: Computed<any>) => void) | null;
116
138
  static _trackOptimisticStore: ((store: any) => void) | null;
117
139
  flush(): void;
@@ -187,6 +209,23 @@ export declare const globalQueue: GlobalQueue;
187
209
  */
188
210
  export declare function flush(): void;
189
211
  export declare function flush<T>(fn: () => T): T;
212
+ /** A fresh, unentered transaction (#3146): the optimistic store's truth
213
+ * flight DECLARES an owned transaction instead of relying on whatever the
214
+ * ambient adoption machinery stamped on its firewall. Activate it with
215
+ * initTransition; it is a plain batch until then. */
216
+ export declare function createTransition(): Transition;
190
217
  export declare function currentTransition(transition: Transition): Transition;
191
218
  export declare function setActiveTransition(transition: Transition | null): void;
192
219
  export declare function runInTransition<T>(transition: Transition, fn: () => T): T;
220
+ /** Run `fn` with `transition` as BOTH the ambient transaction and the
221
+ * registration batch, restoring both after. runInTransition alone is not
222
+ * enough for code that WRITES on behalf of a transaction from inside someone
223
+ * else's window (optimistic replay re-arming a still-open action's edits
224
+ * during a landing commit, #3123): registrations route through the queue's
225
+ * batch pointer, and a bare activeTransition swap leaves them in the ambient
226
+ * batch — a plain batch "completes" at the next flush and reverts optimistic
227
+ * registrations that were supposed to live with the transaction.
228
+ * initTransition is the wrong tool here: it MERGES the currently ambient
229
+ * transaction into the target, entangling whatever the interrupted window
230
+ * belonged to. */
231
+ export declare function runAsTransitionBatch<T>(transition: Transition, fn: () => T): T;
@@ -27,6 +27,11 @@ export interface NodeOptions<T> {
27
27
  ownedWrite?: boolean;
28
28
  /** Exclude this signal from snapshot capture (internal — not part of public API) */
29
29
  _noSnapshot?: boolean;
30
+ /** Extra CONFIG_* bits OR'd into the node's config at creation (internal —
31
+ * not part of public API). Used by resolve()/until() for
32
+ * CONFIG_DIRECT_COMMIT / CONFIG_AUTHORITATIVE_READ, keeping the per-flag
33
+ * option arms out of the core creation path. */
34
+ _extraConfig?: number;
30
35
  unobserved?: () => void;
31
36
  lazy?: boolean;
32
37
  sync?: boolean;
@@ -73,6 +78,13 @@ export interface NodeExtension {
73
78
  */
74
79
  _affectsCount: number;
75
80
  _inFlight: PromiseLike<any> | AsyncIterable<any> | null;
81
+ /** Cancellation for the CURRENT iterator flight (#3122): closes the
82
+ * iterator (`it.return()`), idempotent. Fired at the sites that release
83
+ * `_inFlight` so a superseded stream stops at supersede time — its owner
84
+ * cleanup registration may ride the zombie-disposal channel, which a held
85
+ * transition defers until the SUPERSEDING flight settles. Null for plain
86
+ * promise flights (no cancellation hook exists). */
87
+ _flightTeardown: (() => void) | null;
76
88
  _error: unknown;
77
89
  _blocked: boolean | undefined;
78
90
  _pendingSources: Set<Computed<any>> | undefined;
@@ -1,9 +1,9 @@
1
- export { $REFRESH, ContextNotFoundError, NoOwnerError, NotReadyError, action, createContext, createOwner, createRoot, runWithOwner, flush, getNextChildId, peekNextChildId, getContext, setContext, getOwner, isDisposed, getObserver, isEqual, untrack, isPending, latest, refresh, SUPPORTS_PROXY, setSnapshotCapture, markSnapshotScope, releaseSnapshotScope, clearSnapshots, enforceLoadingBoundary, enableExternalSource, resetErrorHalt } from "./core/index.js";
1
+ export { $REFRESH, ContextNotFoundError, NoOwnerError, NotReadyError, TimeoutError, action, createContext, createOwner, createRoot, runWithOwner, flush, getNextChildId, peekNextChildId, getContext, setContext, getOwner, isDisposed, getObserver, isEqual, untrack, isPending, latest, SUPPORTS_PROXY, setSnapshotCapture, markSnapshotScope, releaseSnapshotScope, clearSnapshots, enforceLoadingBoundary, enableExternalSource, resetErrorHalt } from "./core/index.js";
2
2
  import { type Dev } from "./core/index.js";
3
3
  export declare const DEV: Dev | undefined;
4
4
  export type { Owner, Context, ContextRecord, IQueue, ExternalSourceFactory, ExternalSource, ExternalSourceConfig, Refreshable, Dev, DevHooks, DiagnosticCapture, DiagnosticCode, DiagnosticEvent, DiagnosticKind, Diagnostics, DiagnosticSeverity } from "./core/index.js";
5
- export { createSignal, createMemo, createEffect, createRenderEffect, createTrackedEffect, createReaction, createOptimistic, resolve, onSettled, onCleanup } from "./signals.js";
6
- export type { Accessor, SourceAccessor, Setter, Signal, ComputeFunction, EffectFunction, EffectBundle, EffectOptions, SignalOptions, MemoOptions, NoInfer } from "./signals.js";
5
+ export { createSignal, createMemo, createEffect, createRenderEffect, createTrackedEffect, createReaction, createOptimistic, refresh, resolve, until, onSettled, onCleanup } from "./signals.js";
6
+ export type { Truthy, UntilOptions, Accessor, SourceAccessor, Setter, Signal, ComputeFunction, EffectFunction, EffectBundle, EffectOptions, SignalOptions, MemoOptions, NoInfer } from "./signals.js";
7
7
  export { affects } from "./affects.js";
8
8
  export { mapArray, repeat, type Maybe } from "./map.js";
9
9
  export * from "./store/index.js";
@@ -365,17 +365,6 @@ export declare function createMemo<T>(compute: ComputeFunction<undefined | NoInf
365
365
  * @description https://docs.solidjs.com/reference/basic-reactivity/create-effect
366
366
  */
367
367
  export declare function createEffect<T>(compute: ComputeFunction<undefined | NoInfer<T>, T>, effectFn: EffectFunction<NoInfer<T>, T> | EffectBundle<NoInfer<T>, T>, options?: EffectOptions): void;
368
- /**
369
- * @deprecated `createEffect(compute)` (single argument) is no longer supported.
370
- * Pass a separate effect function as the second argument:
371
- * `createEffect(compute, effect)`. See [MISSING_EFFECT_FN].
372
- *
373
- * - For a side effect that reacts to changes, split the work:
374
- * `createEffect(() => signal(), value => doWork(value))`.
375
- * - For a derived value, use `createMemo(() => signal())`.
376
- * - For a one-shot side effect at construction time, just call the function.
377
- */
378
- export declare function createEffect<T>(compute: ComputeFunction<undefined | NoInfer<T>, T>): never;
379
368
  /**
380
369
  * Creates a reactive computation that runs during the render phase as DOM elements
381
370
  * are created and updated but not necessarily connected.
@@ -491,6 +480,114 @@ export declare function createReaction(effectFn: EffectFunction<undefined> | Eff
491
480
  * @param fn a reactive expression to resolve
492
481
  */
493
482
  export declare function resolve<T>(fn: () => T): Promise<T>;
483
+ /**
484
+ * Invalidates one reactive source, forcing it to re-execute even if its inputs
485
+ * haven't changed, and returns a promise for the target's NEXT QUIESCENT
486
+ * STATE — the re-ask (and anything that supersedes it) has settled.
487
+ *
488
+ * Pass either a Solid-created accessor or a projected store created from
489
+ * `createStore(fn, ...)` / `createProjection(...)`. `refresh()` is a
490
+ * write-like invalidation operation: it does not read the target's value, and
491
+ * refreshing a plain signal accessor is a no-op that resolves immediately.
492
+ *
493
+ * The returned promise is safe to ignore (fire-and-forget refresh is
494
+ * unchanged, and a failed refetch will not surface an unhandled rejection).
495
+ * Awaiting it gives imperative flows the settle point without a reactive
496
+ * read:
497
+ * - Accessor targets resolve with the settled value; store targets resolve
498
+ * with the store node passed (reads through it are fresh after the await).
499
+ * - A failed re-ask rejects with the error (inside an action's generator,
500
+ * `yield refresh(x)` throws back at the yield point and the action reverts
501
+ * like any other failure).
502
+ * - Semantics are quiescence, not flight identity: if another refresh (or
503
+ * any invalidation) supersedes this one mid-flight, the promise waits for
504
+ * — and delivers — whatever finally lands.
505
+ * - Inside an action, truth landing into the held transaction is STAGED;
506
+ * the promise still settles then (matching `resolve()`/`until()`, #2930)
507
+ * and delivers the staged value — the caller's own optimistic override is
508
+ * never the delivered value.
509
+ * - The re-ask itself stays verdict-quiet exactly as before: `isPending`
510
+ * does not flip for a bare refresh (pair with `affects()` for a visible
511
+ * pending window).
512
+ *
513
+ * @example
514
+ * ```ts
515
+ * const user = createMemo(async () => fetch(`/users/${id()}`).then(r => r.json()));
516
+ *
517
+ * // Fire-and-forget re-fetch
518
+ * <button onClick={() => refresh(user)}>Reload</button>;
519
+ *
520
+ * // Imperative settle point
521
+ * const fresh = await refresh(user);
522
+ * ```
523
+ */
524
+ export declare function refresh<T>(target: Refreshable<T>): Promise<T extends (...args: any) => infer V ? V : T>;
525
+ /** Falsy values a truthy predicate result is narrowed against. */
526
+ export type Truthy<T> = Exclude<T, false | 0 | 0n | "" | null | undefined>;
527
+ export interface UntilOptions {
528
+ /** Reject with `TimeoutError` if the predicate has not turned truthy within
529
+ * this many milliseconds. Strongly recommended when the confirming truth
530
+ * arrives over a transport that can drop (sockets, subscriptions). */
531
+ timeout?: number;
532
+ /** Reject with `signal.reason` on abort. */
533
+ signal?: AbortSignal;
534
+ }
535
+ /**
536
+ * Awaits a reactive predicate and resolves the first time it settles *truthy*,
537
+ * with that (narrowed) value. Falsy results and pending async reads both mean
538
+ * "not yet": the subscription stays live and re-evaluates as sources change.
539
+ * If the predicate settles with an error — a throw, or an async source that
540
+ * rejects — the promise rejects with it, as do timeout and abort.
541
+ *
542
+ * Where {@link resolve} answers "what is this value" (first settled value,
543
+ * whatever it is), `until` answers "when does the world confirm this
544
+ * condition". The difference matters inside an `action()`: `yield until(...)`
545
+ * holds the action's transaction — and any optimistic state riding it — open
546
+ * until the condition is independently true.
547
+ *
548
+ * To make that sound, `until`'s predicate reads the AUTHORITATIVE view — and
549
+ * this is the one read-semantics difference from `resolve`, which reads the
550
+ * normal (transaction's own) view where overrides are visible:
551
+ *
552
+ * - **Optimistic overrides are invisible** to the predicate. Your own
553
+ * tentative write can never satisfy your own ack, even on the
554
+ * single-primitive shape where the optimistic store IS the live-fed store.
555
+ * (Derived computeds serve their normal cached values — express the
556
+ * condition over sources of truth, not derived views of the overlay.)
557
+ * - **Everything else reads normally, including uncommitted transition-staged
558
+ * data.** Real data is real wherever it currently lives. This is
559
+ * load-bearing, not a loophole: truth that arrives *into* the open
560
+ * transaction (a `refresh()` this action issued, an entangled landing)
561
+ * stages and cannot commit until the hold releases — a predicate that
562
+ * refused staged reads would deadlock on the very data it is waiting for.
563
+ *
564
+ * This is the acknowledgment mechanism for mutations confirmed on a live data
565
+ * channel (sockets, subscriptions, live queries) rather than by the mutation's
566
+ * own response: correlate by a client-generated id or version in the predicate,
567
+ * and let truth arrive however it arrives — push, refetch, or another tab.
568
+ *
569
+ * Failure composes with action semantics: a rejection is thrown back into the
570
+ * generator at the `yield` point — catchable there, or the action fails and
571
+ * its optimistic state reverts.
572
+ *
573
+ * Must be called *outside* a tracking scope.
574
+ *
575
+ * @example
576
+ * ```ts
577
+ * const send = action(async function* (text: string) {
578
+ * const clientId = crypto.randomUUID();
579
+ * setMessages(m => { m.push({ clientId, text, pending: true }); }); // optimistic
580
+ * await socket.send({ clientId, text }); // fire-and-forget transport
581
+ * // Hold until the live source echoes the write (authoritative view —
582
+ * // the optimistic row above cannot satisfy this):
583
+ * yield until(() => messages.some(m => m.clientId === clientId), { timeout: 10_000 });
584
+ * });
585
+ * ```
586
+ *
587
+ * @param fn a reactive predicate over authoritative state
588
+ * @param options optional `timeout` (ms) and abort `signal`
589
+ */
590
+ export declare function until<T>(fn: () => T, options?: UntilOptions): Promise<Truthy<T>>;
494
591
  /**
495
592
  * Creates an optimistic signal that can be used to optimistically update a value
496
593
  * and then revert it back to the previous value at end of transition.
@@ -1,20 +1,23 @@
1
+ import { type Transition } from "../../core/scheduler.js";
1
2
  import { type NoFn, type ProjectionOptions, type Store, type StoreSetter } from "../store.js";
2
- import type { StoreNextFamily, StoreNextTarget } from "./target.js";
3
+ import type { StoreNextTarget } from "./target.js";
4
+ /** #3164 fold: a stamped truth is HELD (masked from ordinary readers until
5
+ * the reveal) only while its transition is live AND retaining optimism —
6
+ * overrides are what make partial-coverage composition a tear. A plain
7
+ * async transition carries no overrides, so downstream computes must see
8
+ * staged values to converge (normal speculation). Resolves merges first:
9
+ * merge unions optimistic nodes/stores into the target. */
10
+ export declare function transitionHoldsOptimism(transition: Transition): boolean;
3
11
  export declare function createOptimisticStoreNext<T extends object = {}>(first: T | ((store: T) => void | T | Promise<void | T> | AsyncIterable<void | T>), second?: NoFn<T> | Store<NoFn<T>>, options?: ProjectionOptions): [get: Store<T>, set: StoreSetter<T>];
4
12
  /** Diff the draft against the current OPTIMISTIC VIEW (committed + active
5
13
  * overrides — the same view the draft was seeded from) and emit engine writes
6
14
  * for exactly the changed keys. Visible-view diffing keeps no-op writes from
7
15
  * entangling lanes (RUL-10 / opt R38). */
8
16
  export declare function notifyOptimisticWrites(t: StoreNextTarget, pb: Record<PropertyKey, any>): void;
9
- /**
10
- * Landing consumption (RUL-2): fresh authoritative data supersedes every
11
- * tentative override in the family. Mirrors legacy clearProjectionOverride —
12
- * drop the override, clear lane/ownership, notify subscribers whose visible
13
- * value changes (reversion effects go to regular queues via the projection
14
- * write posture the caller holds).
15
- */
16
- export declare function consumeOverridesNext(fam: StoreNextFamily): void;
17
17
  /** Optimistic-view composition for snapshot/deep (O1: snapshot is the CURRENT
18
18
  * view, lane values included; a fresh copy per call during pending windows —
19
- * RUL-12). Returns `src` untouched when no override is active on `t`. */
19
+ * RUL-12). Returns `src` untouched when no override is active on `t`.
20
+ * Authoritative-view reads (until()'s predicate) skip composition entirely:
21
+ * the predicate observes authoritative truth, never the caller's tentative
22
+ * overlay. (Write-side emission callers never run under such a compute.) */
20
23
  export declare function optimisticView(t: StoreNextTarget, src: Record<PropertyKey, any>): Record<PropertyKey, any>;
@@ -5,4 +5,4 @@ export declare function createProjectionNext<T extends object = {}>(fn: (draft:
5
5
  * masks the recompute for the tick (core R31 — the manual write wins over a
6
6
  * same-flush dependency change). */
7
7
  export declare function createStoreDerivedNext<T extends object = {}>(fn: (draft: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, seed: Partial<T> | Store<NoFn<T>>, options?: ProjectionOptions): [Refreshable<Store<T>>, (f: (draft: T) => T | void) => void];
8
- export declare function runProjectionComputedNext<T extends object>(wrappedStore: Store<T>, fn: (draft: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, key: string | ((item: NonNullable<any>) => any) | null, wrapCommit?: (write: () => void) => void, onDraftWrite?: () => void): Computed<void | T>;
8
+ export declare function runProjectionComputedNext<T extends object>(wrappedStore: Store<T>, fn: (draft: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, key: string | ((item: NonNullable<any>) => any) | null, wrapCommit?: (write: () => void, value: T) => void, aroundDraftWrite?: (op: () => void) => void): Computed<void | T>;
@@ -30,9 +30,16 @@ export declare function materializePB(target: StoreNextTarget): void;
30
30
  * authoritative base (R21/R32).
31
31
  */
32
32
  export declare function adoptPB(target: StoreNextTarget, incoming: Record<PropertyKey, any>, eager?: boolean): void;
33
+ /** Parked truth-staged pending backings (#3164 fold): a tentative draft that
34
+ * opens while a folded landing's backing is live moves the staged container
35
+ * here (see ensurePB); the tentative discard in notifyOptimisticWrites
36
+ * restores it in place of the usual null. */
37
+ export declare const stagedTruthPB: WeakMap<StoreNextTarget, Record<PropertyKey, any>>;
33
38
  /** Same logical slot: both values resolve to one (re-pointed) child target —
34
39
  * adoption preserved identity, so the slot did not change (R9). */
35
40
  export declare function targetsEqual(ov: any, nv: any): boolean;
41
+ export declare function arrayStructureChanged(old: any[], neu: any[]): boolean;
42
+ export declare function membershipChanged(old: Record<PropertyKey, any>, neu: Record<PropertyKey, any>): boolean;
36
43
  /**
37
44
  * The fold diff walks SUBSCRIPTION KEYS ONLY (legacy parity: `for key in
38
45
  * nodes`): nodes exist exactly where something tracked, so unobserved data
@@ -62,6 +69,21 @@ export declare function runAuthoritative<T>(fn: () => T): T;
62
69
  /** Active optimistic override on an armed node (armed slot idles at
63
70
  * NOT_PENDING; undefined = unarmed plain node). */
64
71
  export declare function hasActiveOverride(node: Signal<any>): boolean;
72
+ /** The reading computation is until()'s authoritative-view predicate — same
73
+ * source of truth as core read()'s A17 carve-out (`context`, which persists
74
+ * under untrack). optimisticView()'s composition gate consults exactly this:
75
+ * write-side machinery (patch emission, tentative re-application) must keep
76
+ * composing even when it runs inside an authoritative-write bracket. */
77
+ export declare function authoritativeRead(): boolean;
78
+ /** Serve-side authoritative gate: until()'s predicate PLUS truth authors —
79
+ * the projection derive's draft (wrapDraft trap brackets, runAuthoritative;
80
+ * the same posture pair ensurePB classifies drafts by). A source computing
81
+ * the next truth must never read its callers' tentative overlays: a derive
82
+ * continuation's `store.push` computing its index from an action's
83
+ * optimistic row landed truth in the wrong slot and corrupted committed
84
+ * state (#3108). Trap-level overlay serves gate on this so values, length,
85
+ * membership, and keys leave the authoritative view together. */
86
+ export declare function authoritativeServe(): boolean;
65
87
  export type SetStoreNextFunction<T> = (fn: (draft: T) => T | void) => void;
66
88
  /** Low-level setter primitive: opens write mode on a next proxy, runs `fn`,
67
89
  * emits write-time notifications at outermost exit, applies returned