@solidjs/signals 2.0.0-rc.3 → 2.0.0-rc.5
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 +2494 -271
- package/dist/node.cjs +3691 -1595
- package/dist/prod/affects.js +13 -12
- package/dist/prod/boundaries.js +43 -35
- package/dist/prod/core/action.js +3 -3
- package/dist/prod/core/async.js +137 -106
- package/dist/prod/core/constants.js +55 -1
- package/dist/prod/core/core.js +354 -247
- package/dist/prod/core/effect.js +47 -50
- package/dist/prod/core/error.js +13 -1
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +88 -52
- package/dist/prod/core/heap.js +38 -38
- package/dist/prod/core/lanes.js +34 -34
- package/dist/prod/core/optimistic.js +61 -58
- package/dist/prod/core/owner.js +38 -38
- package/dist/prod/core/scheduler.js +401 -174
- package/dist/prod/core/verdict.js +132 -65
- package/dist/prod/index.js +7 -3
- package/dist/prod/map.js +106 -106
- package/dist/prod/signals.js +253 -25
- package/dist/prod/store/index.js +2 -0
- package/dist/prod/store/next/optimistic.js +314 -121
- package/dist/prod/store/next/patch-hooks.js +13 -0
- package/dist/prod/store/next/patch.js +614 -0
- package/dist/prod/store/next/projection.js +23 -19
- package/dist/prod/store/next/reconcile.js +307 -120
- package/dist/prod/store/next/store.js +440 -117
- package/dist/prod/store/next/target.js +13 -4
- package/dist/prod/store/store.js +5 -5
- package/dist/types/core/async.d.ts +2 -0
- package/dist/types/core/attribution.d.ts +9 -4
- package/dist/types/core/constants.d.ts +54 -0
- package/dist/types/core/core.d.ts +34 -21
- package/dist/types/core/dev.d.ts +8 -0
- package/dist/types/core/error.d.ts +9 -0
- package/dist/types/core/graph.d.ts +22 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/scheduler.d.ts +46 -0
- package/dist/types/core/types.d.ts +12 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/signals.d.ts +108 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/optimistic.d.ts +13 -10
- package/dist/types/store/next/patch-hooks.d.ts +41 -0
- package/dist/types/store/next/patch.d.ts +91 -0
- package/dist/types/store/next/projection.d.ts +1 -1
- package/dist/types/store/next/reconcile.d.ts +14 -0
- package/dist/types/store/next/store.d.ts +52 -2
- package/dist/types/store/next/target.d.ts +73 -8
- package/dist/types-cjs/core/async.d.cts +2 -0
- package/dist/types-cjs/core/attribution.d.cts +9 -4
- package/dist/types-cjs/core/constants.d.cts +54 -0
- package/dist/types-cjs/core/core.d.cts +34 -21
- package/dist/types-cjs/core/dev.d.cts +8 -0
- package/dist/types-cjs/core/error.d.cts +9 -0
- package/dist/types-cjs/core/graph.d.cts +22 -0
- package/dist/types-cjs/core/index.d.cts +2 -2
- package/dist/types-cjs/core/scheduler.d.cts +46 -0
- package/dist/types-cjs/core/types.d.cts +12 -0
- package/dist/types-cjs/index.d.cts +3 -3
- package/dist/types-cjs/signals.d.cts +108 -0
- package/dist/types-cjs/store/index.d.cts +2 -0
- package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
- package/dist/types-cjs/store/next/patch-hooks.d.cts +41 -0
- package/dist/types-cjs/store/next/patch.d.cts +91 -0
- package/dist/types-cjs/store/next/projection.d.cts +1 -1
- package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
- package/dist/types-cjs/store/next/store.d.cts +52 -2
- package/dist/types-cjs/store/next/target.d.cts +73 -8
- package/package.json +2 -2
|
@@ -7,14 +7,23 @@ const ownedRaw = new WeakSet;
|
|
|
7
7
|
|
|
8
8
|
/** raw → target. The only raw-keyed lookup; boundary mechanism (O8). */ const storeNextLookup = new WeakMap;
|
|
9
9
|
|
|
10
|
-
function devAssertNeverUserMutation(
|
|
10
|
+
function devAssertNeverUserMutation(e) {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
let optHooks = null;
|
|
15
15
|
|
|
16
|
-
function setOptHooks(
|
|
17
|
-
optHooks =
|
|
16
|
+
function setOptHooks(e) {
|
|
17
|
+
optHooks = e;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
/** Sticky descendants flag walk (§6d): reconcile's keyed pruning descends
|
|
21
|
+
* only where subscriptions exist at/below. Nodes AND patches count. */ function markDescendants(e) {
|
|
22
|
+
let t = e;
|
|
23
|
+
while (t && !t.d) {
|
|
24
|
+
t.d = true;
|
|
25
|
+
t = t.u;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { devAssertNeverUserMutation, markDescendants, optHooks, ownedRaw, setOptHooks, storeNextLookup };
|
package/dist/prod/store/store.js
CHANGED
|
@@ -139,7 +139,7 @@ function ownEnumerableKeys(e) {
|
|
|
139
139
|
// A live scope exists, so affects.ts already installed the mark engine.
|
|
140
140
|
for (const [r, s] of affectsScopes) {
|
|
141
141
|
if (r.o?.t && s.scope.has(t) && (s.key === undefined || s.key === o)) {
|
|
142
|
-
GlobalQueue.
|
|
142
|
+
GlobalQueue.M(e);
|
|
143
143
|
s.inherited.push(e);
|
|
144
144
|
}
|
|
145
145
|
}
|
|
@@ -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.
|
|
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.
|
|
266
|
+
GlobalQueue.Wt(e);
|
|
267
267
|
break;
|
|
268
268
|
}
|
|
269
269
|
const o = t?.[$TARGET];
|
|
@@ -288,11 +288,11 @@ s) {
|
|
|
288
288
|
*
|
|
289
289
|
* @internal
|
|
290
290
|
*/ function getStoreAffectsNodes(e, t) {
|
|
291
|
-
GlobalQueue.
|
|
291
|
+
GlobalQueue.p ||= e => {
|
|
292
292
|
const t = affectsScopes.get(e);
|
|
293
293
|
if (!t) return;
|
|
294
294
|
affectsScopes.delete(e);
|
|
295
|
-
for (let e = 0; e < t.inherited.length; e++) GlobalQueue.
|
|
295
|
+
for (let e = 0; e < t.inherited.length; e++) GlobalQueue.N(t.inherited[e]);
|
|
296
296
|
};
|
|
297
297
|
if (t === undefined) {
|
|
298
298
|
const t = nextAffectsNodeResolver(e, $AFFECTS);
|
|
@@ -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;
|
|
@@ -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
|
|
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
|
|
66
|
-
*
|
|
67
|
-
*
|
|
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
|
/**
|
|
@@ -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";
|
|
@@ -38,7 +37,21 @@ export declare function ext(el: {
|
|
|
38
37
|
* mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
|
|
39
38
|
* the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
|
|
40
39
|
*/
|
|
41
|
-
export declare function createEffectNode<T>(fn: (prev?: T) => T, effectFn: (val: T, prev: T | undefined) => void | (() => void), errorFn: ((err: unknown, cleanup: () => void) => void | (() => void)) | undefined, type: number,
|
|
40
|
+
export declare function createEffectNode<T>(fn: (prev?: T) => T, effectFn: (val: T, prev: T | undefined) => void | (() => void), errorFn: ((err: unknown, cleanup: () => void) => void | (() => void)) | undefined, type: number, options: NodeOptions<T> | undefined): any;
|
|
41
|
+
/**
|
|
42
|
+
* The shared status notifier for effect nodes, installed once by effect.ts
|
|
43
|
+
* at module evaluation (`this`-dispatched — one function serves every
|
|
44
|
+
* effect, so nodes never store it). Boundary computeds keep their own
|
|
45
|
+
* per-node channel on `_x._notifyStatus`, which takes precedence.
|
|
46
|
+
*/
|
|
47
|
+
export declare let effectStatusNotify: ((this: any, status?: number, error?: any) => void) | null;
|
|
48
|
+
export declare function setEffectStatusNotify(fn: NonNullable<typeof effectStatusNotify>): void;
|
|
49
|
+
/** Resolve a node's status notifier: an own `_x` channel (boundaries) wins;
|
|
50
|
+
* effect nodes (`_type` — EFFECT_PURE is 0, and only effect literals carry
|
|
51
|
+
* the field) fall back to the shared notifier. Presence doubles as the
|
|
52
|
+
* "display consumer" membership test in the status walks, exactly as the
|
|
53
|
+
* per-node field did when every effect carried one. */
|
|
54
|
+
export declare function statusNotifierOf(el: any): ((this: any, status?: number, error?: any) => void) | undefined;
|
|
42
55
|
export declare function signal<T>(v: T, options?: NodeOptions<T>): Signal<T>;
|
|
43
56
|
export declare function signal<T>(v: T, options?: NodeOptions<T>, firewall?: Computed<any>): FirewallSignal<T>;
|
|
44
57
|
export declare function optimisticSignal<T>(v: T, options?: NodeOptions<T>): Signal<T>;
|
|
@@ -93,6 +106,19 @@ export declare const READ_SLOW: unique symbol;
|
|
|
93
106
|
* snapshot / transition / lane / dev-strictRead state all take the full
|
|
94
107
|
* resolution. Anything slow returns READ_SLOW; the caller then calls read().
|
|
95
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;
|
|
96
122
|
export declare function readNodeFast<T>(el: Signal<T>): T | typeof READ_SLOW;
|
|
97
123
|
export declare function read<T>(el: Signal<T> | Computed<T>): T;
|
|
98
124
|
/**
|
|
@@ -141,23 +167,10 @@ export declare function setMemo<T>(el: Computed<T>, v: T | ((prev: T) => T)): T;
|
|
|
141
167
|
export declare function runWithOwner<T>(owner: Owner | null, fn: () => T): T;
|
|
142
168
|
export declare function staleValues<T>(fn: () => T, set?: boolean): T;
|
|
143
169
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* write-like invalidation operation: it does not read the target's value, and
|
|
150
|
-
* refreshing a plain signal accessor is a no-op.
|
|
151
|
-
*
|
|
152
|
-
* Use it to invalidate cached async values (e.g. force a re-fetch) without
|
|
153
|
-
* tearing the consumer down.
|
|
154
|
-
*
|
|
155
|
-
* @example
|
|
156
|
-
* ```ts
|
|
157
|
-
* const user = createMemo(async () => fetch(`/users/${id()}`).then(r => r.json()));
|
|
158
|
-
*
|
|
159
|
-
* // Re-fetch on demand
|
|
160
|
-
* <button onClick={() => refresh(user)}>Reload</button>
|
|
161
|
-
* ```
|
|
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.
|
|
162
175
|
*/
|
|
163
|
-
export declare function
|
|
176
|
+
export declare function markRefresh(node: Computed<any>): void;
|
package/dist/types/core/dev.d.ts
CHANGED
|
@@ -33,6 +33,14 @@ export interface DiagnosticCapture {
|
|
|
33
33
|
export interface Diagnostics {
|
|
34
34
|
subscribe(listener: DiagnosticListener): () => void;
|
|
35
35
|
capture(): DiagnosticCapture;
|
|
36
|
+
/**
|
|
37
|
+
* Registers a console footer printed after the first console report of
|
|
38
|
+
* each diagnostic code — a discovery pointer to deeper guidance (e.g.
|
|
39
|
+
* solid-js registers its shipped repair skill). Returning undefined for
|
|
40
|
+
* an event suppresses the footer. Passing undefined unregisters and
|
|
41
|
+
* resets the once-per-code memory.
|
|
42
|
+
*/
|
|
43
|
+
setConsoleFooter(footer: ((event: DiagnosticEvent) => string | undefined) | undefined): void;
|
|
36
44
|
}
|
|
37
45
|
export interface Dev {
|
|
38
46
|
hooks: DevHooks;
|
|
@@ -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
|
}
|
|
@@ -3,4 +3,26 @@ export declare function unlinkSubs(link: Link): Link | null;
|
|
|
3
3
|
export declare function trimStaleDeps(el: Computed<any>): void;
|
|
4
4
|
export declare function clearDeps(el: Computed<unknown>): void;
|
|
5
5
|
export declare function unobserved(el: Computed<unknown>): void;
|
|
6
|
+
/**
|
|
7
|
+
* Deferred dormancy for never-observed auto-dispose computeds (#3078).
|
|
8
|
+
*
|
|
9
|
+
* An untracked top-level read of a subscriber-less observation-lifecycle memo
|
|
10
|
+
* used to call unobserved() inline at the end of read(). That kept the leak
|
|
11
|
+
* closed (the compute links the memo into its deps' sub lists — without a
|
|
12
|
+
* teardown point a never-observed memo is retained by its sources forever;
|
|
13
|
+
* upstream alien-signals has exactly this retention), but it made reads
|
|
14
|
+
* destructive: each read disposed the node, the next read revived it with a
|
|
15
|
+
* full recompute in whatever ambient transition/lane context happened to be
|
|
16
|
+
* current, so consecutive reads could return different answers with no write
|
|
17
|
+
* in between.
|
|
18
|
+
*
|
|
19
|
+
* Instead, reads queue the node here and the scheduler sweeps at the top of
|
|
20
|
+
* the next flush (before runHeap, so a same-tick dirtying is reclaimed
|
|
21
|
+
* instead of recomputed). Reads become idempotent within a tick (the node
|
|
22
|
+
* stays alive and serves its cache, uniform with observed memos) while
|
|
23
|
+
* reclamation still happens within one microtask — the enqueue site arms
|
|
24
|
+
* schedule(), so a flush is guaranteed even when no other work is queued.
|
|
25
|
+
*/
|
|
26
|
+
export declare const dormantNodes: Set<Computed<unknown>>;
|
|
27
|
+
export declare function sweepDormant(): void;
|
|
6
28
|
export declare function link(dep: Signal<any> | Computed<any>, sub: Computed<any>, pendingObserver?: boolean): void;
|
|
@@ -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,
|
|
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
|
|
@@ -103,11 +121,19 @@ export declare class GlobalQueue extends Queue {
|
|
|
103
121
|
static _transitionBlocked: ((transition: Transition) => boolean) | null;
|
|
104
122
|
static _cleanupLanes: ((completingTransition: Transition | null) => void) | null;
|
|
105
123
|
static _runLaneEffects: ((type: number) => void) | null;
|
|
124
|
+
/** Patch-channel optimistic drain (next/patch.ts): optimistic emissions
|
|
125
|
+
* apply at lane-effect timing — visible in flight, unlike the regular
|
|
126
|
+
* effect queues an action stashes. Injected; null when unused. */
|
|
127
|
+
static _drainPatchOptimistic: (() => void) | null;
|
|
106
128
|
static _gatedRead: ((el: Signal<any>, owner: OptimisticNode, c: Computed<any>) => boolean) | null;
|
|
107
129
|
static _laneSuspends: ((owner: OptimisticNode) => boolean) | null;
|
|
108
130
|
static _laneReadsCommitted: ((el: OptimisticNode, owner: OptimisticNode, c: Computed<any>) => boolean) | null;
|
|
109
131
|
static _recomputeLane: ((el: Computed<any>, own: boolean) => OptimisticLane | null | false) | null;
|
|
110
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;
|
|
111
137
|
static _laneAsyncSettled: ((el: Computed<any>) => void) | null;
|
|
112
138
|
static _trackOptimisticStore: ((store: any) => void) | null;
|
|
113
139
|
flush(): void;
|
|
@@ -126,6 +152,14 @@ export declare function armReaskClear(): void;
|
|
|
126
152
|
export declare function insertSubs(node: Signal<any> | Computed<any>, optimistic?: boolean): void;
|
|
127
153
|
export declare let storeCommitHook: (() => void) | null;
|
|
128
154
|
export declare function setStoreCommitHook(fn: () => void): void;
|
|
155
|
+
/** Patch-channel release hook (next/patch.ts): transition-stamped patch
|
|
156
|
+
* emissions are released when THEIR batch commits. Transitions never
|
|
157
|
+
* abort: failed actions still commit (only optimistic overrides revert),
|
|
158
|
+
* and merged-away transitions hand their stash to the survivor
|
|
159
|
+
* (mergeTransitionState) — every stash drains exactly once. Injected like
|
|
160
|
+
* storeCommitHook to stay tree-shakeable. */
|
|
161
|
+
export declare let patchCommitHook: ((batch: Transition) => void) | null;
|
|
162
|
+
export declare function setPatchCommitHook(fn: (batch: Transition) => void): void;
|
|
129
163
|
export declare function finalizePureQueue(completingTransition?: Transition | null, incomplete?: boolean): void;
|
|
130
164
|
/**
|
|
131
165
|
* Count of live `affects()` registrations across the system (including
|
|
@@ -178,3 +212,15 @@ export declare function flush<T>(fn: () => T): T;
|
|
|
178
212
|
export declare function currentTransition(transition: Transition): Transition;
|
|
179
213
|
export declare function setActiveTransition(transition: Transition | null): void;
|
|
180
214
|
export declare function runInTransition<T>(transition: Transition, fn: () => T): T;
|
|
215
|
+
/** Run `fn` with `transition` as BOTH the ambient transaction and the
|
|
216
|
+
* registration batch, restoring both after. runInTransition alone is not
|
|
217
|
+
* enough for code that WRITES on behalf of a transaction from inside someone
|
|
218
|
+
* else's window (optimistic replay re-arming a still-open action's edits
|
|
219
|
+
* during a landing commit, #3123): registrations route through the queue's
|
|
220
|
+
* batch pointer, and a bare activeTransition swap leaves them in the ambient
|
|
221
|
+
* batch — a plain batch "completes" at the next flush and reverts optimistic
|
|
222
|
+
* registrations that were supposed to live with the transaction.
|
|
223
|
+
* initTransition is the wrong tool here: it MERGES the currently ambient
|
|
224
|
+
* transaction into the target, entangling whatever the interrupted window
|
|
225
|
+
* belonged to. */
|
|
226
|
+
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;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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,
|
|
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";
|
package/dist/types/signals.d.ts
CHANGED
|
@@ -491,6 +491,114 @@ export declare function createReaction(effectFn: EffectFunction<undefined> | Eff
|
|
|
491
491
|
* @param fn a reactive expression to resolve
|
|
492
492
|
*/
|
|
493
493
|
export declare function resolve<T>(fn: () => T): Promise<T>;
|
|
494
|
+
/**
|
|
495
|
+
* Invalidates one reactive source, forcing it to re-execute even if its inputs
|
|
496
|
+
* haven't changed, and returns a promise for the target's NEXT QUIESCENT
|
|
497
|
+
* STATE — the re-ask (and anything that supersedes it) has settled.
|
|
498
|
+
*
|
|
499
|
+
* Pass either a Solid-created accessor or a projected store created from
|
|
500
|
+
* `createStore(fn, ...)` / `createProjection(...)`. `refresh()` is a
|
|
501
|
+
* write-like invalidation operation: it does not read the target's value, and
|
|
502
|
+
* refreshing a plain signal accessor is a no-op that resolves immediately.
|
|
503
|
+
*
|
|
504
|
+
* The returned promise is safe to ignore (fire-and-forget refresh is
|
|
505
|
+
* unchanged, and a failed refetch will not surface an unhandled rejection).
|
|
506
|
+
* Awaiting it gives imperative flows the settle point without a reactive
|
|
507
|
+
* read:
|
|
508
|
+
* - Accessor targets resolve with the settled value; store targets resolve
|
|
509
|
+
* with the store node passed (reads through it are fresh after the await).
|
|
510
|
+
* - A failed re-ask rejects with the error (inside an action's generator,
|
|
511
|
+
* `yield refresh(x)` throws back at the yield point and the action reverts
|
|
512
|
+
* like any other failure).
|
|
513
|
+
* - Semantics are quiescence, not flight identity: if another refresh (or
|
|
514
|
+
* any invalidation) supersedes this one mid-flight, the promise waits for
|
|
515
|
+
* — and delivers — whatever finally lands.
|
|
516
|
+
* - Inside an action, truth landing into the held transaction is STAGED;
|
|
517
|
+
* the promise still settles then (matching `resolve()`/`until()`, #2930)
|
|
518
|
+
* and delivers the staged value — the caller's own optimistic override is
|
|
519
|
+
* never the delivered value.
|
|
520
|
+
* - The re-ask itself stays verdict-quiet exactly as before: `isPending`
|
|
521
|
+
* does not flip for a bare refresh (pair with `affects()` for a visible
|
|
522
|
+
* pending window).
|
|
523
|
+
*
|
|
524
|
+
* @example
|
|
525
|
+
* ```ts
|
|
526
|
+
* const user = createMemo(async () => fetch(`/users/${id()}`).then(r => r.json()));
|
|
527
|
+
*
|
|
528
|
+
* // Fire-and-forget re-fetch
|
|
529
|
+
* <button onClick={() => refresh(user)}>Reload</button>;
|
|
530
|
+
*
|
|
531
|
+
* // Imperative settle point
|
|
532
|
+
* const fresh = await refresh(user);
|
|
533
|
+
* ```
|
|
534
|
+
*/
|
|
535
|
+
export declare function refresh<T>(target: Refreshable<T>): Promise<T extends (...args: any) => infer V ? V : T>;
|
|
536
|
+
/** Falsy values a truthy predicate result is narrowed against. */
|
|
537
|
+
export type Truthy<T> = Exclude<T, false | 0 | 0n | "" | null | undefined>;
|
|
538
|
+
export interface UntilOptions {
|
|
539
|
+
/** Reject with `TimeoutError` if the predicate has not turned truthy within
|
|
540
|
+
* this many milliseconds. Strongly recommended when the confirming truth
|
|
541
|
+
* arrives over a transport that can drop (sockets, subscriptions). */
|
|
542
|
+
timeout?: number;
|
|
543
|
+
/** Reject with `signal.reason` on abort. */
|
|
544
|
+
signal?: AbortSignal;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Awaits a reactive predicate and resolves the first time it settles *truthy*,
|
|
548
|
+
* with that (narrowed) value. Falsy results and pending async reads both mean
|
|
549
|
+
* "not yet": the subscription stays live and re-evaluates as sources change.
|
|
550
|
+
* If the predicate settles with an error — a throw, or an async source that
|
|
551
|
+
* rejects — the promise rejects with it, as do timeout and abort.
|
|
552
|
+
*
|
|
553
|
+
* Where {@link resolve} answers "what is this value" (first settled value,
|
|
554
|
+
* whatever it is), `until` answers "when does the world confirm this
|
|
555
|
+
* condition". The difference matters inside an `action()`: `yield until(...)`
|
|
556
|
+
* holds the action's transaction — and any optimistic state riding it — open
|
|
557
|
+
* until the condition is independently true.
|
|
558
|
+
*
|
|
559
|
+
* To make that sound, `until`'s predicate reads the AUTHORITATIVE view — and
|
|
560
|
+
* this is the one read-semantics difference from `resolve`, which reads the
|
|
561
|
+
* normal (transaction's own) view where overrides are visible:
|
|
562
|
+
*
|
|
563
|
+
* - **Optimistic overrides are invisible** to the predicate. Your own
|
|
564
|
+
* tentative write can never satisfy your own ack, even on the
|
|
565
|
+
* single-primitive shape where the optimistic store IS the live-fed store.
|
|
566
|
+
* (Derived computeds serve their normal cached values — express the
|
|
567
|
+
* condition over sources of truth, not derived views of the overlay.)
|
|
568
|
+
* - **Everything else reads normally, including uncommitted transition-staged
|
|
569
|
+
* data.** Real data is real wherever it currently lives. This is
|
|
570
|
+
* load-bearing, not a loophole: truth that arrives *into* the open
|
|
571
|
+
* transaction (a `refresh()` this action issued, an entangled landing)
|
|
572
|
+
* stages and cannot commit until the hold releases — a predicate that
|
|
573
|
+
* refused staged reads would deadlock on the very data it is waiting for.
|
|
574
|
+
*
|
|
575
|
+
* This is the acknowledgment mechanism for mutations confirmed on a live data
|
|
576
|
+
* channel (sockets, subscriptions, live queries) rather than by the mutation's
|
|
577
|
+
* own response: correlate by a client-generated id or version in the predicate,
|
|
578
|
+
* and let truth arrive however it arrives — push, refetch, or another tab.
|
|
579
|
+
*
|
|
580
|
+
* Failure composes with action semantics: a rejection is thrown back into the
|
|
581
|
+
* generator at the `yield` point — catchable there, or the action fails and
|
|
582
|
+
* its optimistic state reverts.
|
|
583
|
+
*
|
|
584
|
+
* Must be called *outside* a tracking scope.
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* ```ts
|
|
588
|
+
* const send = action(async function* (text: string) {
|
|
589
|
+
* const clientId = crypto.randomUUID();
|
|
590
|
+
* setMessages(m => { m.push({ clientId, text, pending: true }); }); // optimistic
|
|
591
|
+
* await socket.send({ clientId, text }); // fire-and-forget transport
|
|
592
|
+
* // Hold until the live source echoes the write (authoritative view —
|
|
593
|
+
* // the optimistic row above cannot satisfy this):
|
|
594
|
+
* yield until(() => messages.some(m => m.clientId === clientId), { timeout: 10_000 });
|
|
595
|
+
* });
|
|
596
|
+
* ```
|
|
597
|
+
*
|
|
598
|
+
* @param fn a reactive predicate over authoritative state
|
|
599
|
+
* @param options optional `timeout` (ms) and abort `signal`
|
|
600
|
+
*/
|
|
601
|
+
export declare function until<T>(fn: () => T, options?: UntilOptions): Promise<Truthy<T>>;
|
|
494
602
|
/**
|
|
495
603
|
* Creates an optimistic signal that can be used to optimistically update a value
|
|
496
604
|
* and then revert it back to the previous value at end of transition.
|
|
@@ -4,6 +4,8 @@ export { isWrappable, $TRACK, $PROXY, $TARGET } from "./store.js";
|
|
|
4
4
|
import type { NoFn, ProjectionOptions, Store, StoreOptions, StoreSetter } from "./store.js";
|
|
5
5
|
import type { Refreshable } from "../core/index.js";
|
|
6
6
|
export { createProjectionNext as createProjection } from "./next/projection.js";
|
|
7
|
+
export { registerPatch, registerRowOps, registerSlotPatchNext as registerSlotPatch, patchableRaw } from "./next/patch.js";
|
|
8
|
+
export { storeIsShallow, storeHasFamily, storeHasOptimisticFamily } from "./next/store.js";
|
|
7
9
|
export { createOptimisticStoreNext as createOptimisticStore } from "./next/optimistic.js";
|
|
8
10
|
/** Public createStore: plain form `(init, options?)` and derived writable
|
|
9
11
|
* form `(fn, seed, options?)`. */
|
|
@@ -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 {
|
|
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>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { StoreNextTarget } from "./target.js";
|
|
2
|
+
import type { RowOps } from "./patch.js";
|
|
3
|
+
/**
|
|
4
|
+
* Patch-channel emission seams (pay-for-use). The store/reconcile/optimistic
|
|
5
|
+
* write paths emit through these installed hook objects instead of importing
|
|
6
|
+
* `patch.js` statically, so the channel tree-shakes out of apps that never
|
|
7
|
+
* register a patch consumer.
|
|
8
|
+
*
|
|
9
|
+
* TWO TIERS, armed at registration (patch.js installs them; it is retained
|
|
10
|
+
* only through its registration exports, which only compiled patch-mode
|
|
11
|
+
* output — via the web runtime's driver module — imports):
|
|
12
|
+
* - VALUE hooks (`patchHooks`): record patches. Armed by `registerPatch` —
|
|
13
|
+
* present in any bundle with one eligible template under patch mode.
|
|
14
|
+
* - ROW hooks (`rowHooks`): list structure (row ops, slot ticks, the
|
|
15
|
+
* identity/keyed diff builders in reconcile.js they drag in). Armed by
|
|
16
|
+
* `registerRowOps`/`registerSlotPatchNext` — the LIST driver's
|
|
17
|
+
* registrations, so value-only bundles never retain the row machinery.
|
|
18
|
+
*
|
|
19
|
+
* Soundness: every emission site is guarded by the matching `pc` channel
|
|
20
|
+
* (`pc.p` for value, `pc.ro`/`pc.sp` for rows), and a target can only
|
|
21
|
+
* acquire that channel through the corresponding registration — so each
|
|
22
|
+
* hook object is installed by the time any guard passes. Type-only imports
|
|
23
|
+
* from `patch.js` are erased.
|
|
24
|
+
*/
|
|
25
|
+
export interface PatchValueHooks {
|
|
26
|
+
emitPatch(t: StoreNextTarget, next: any, prev: any): void;
|
|
27
|
+
emitPatchLocal(t: StoreNextTarget, next: any, prev: any): void;
|
|
28
|
+
emitPatchOptimistic(t: StoreNextTarget, next: any, prev: any): void;
|
|
29
|
+
hasPatches(): boolean;
|
|
30
|
+
demoteToEffects(t: StoreNextTarget): void;
|
|
31
|
+
}
|
|
32
|
+
export interface PatchRowHooks {
|
|
33
|
+
emitRowOps(t: StoreNextTarget, next: any[], ops: RowOps): void;
|
|
34
|
+
emitSlotPatch(t: StoreNextTarget, index: number, next: any, prev: any): void;
|
|
35
|
+
emitSetterRowOps(t: StoreNextTarget, prevRows: any[], nextRows: any[]): void;
|
|
36
|
+
emitRowOpsOptimistic(t: StoreNextTarget, next: any[] | null, ops: RowOps | null): void;
|
|
37
|
+
}
|
|
38
|
+
export declare let patchHooks: PatchValueHooks | null;
|
|
39
|
+
export declare let rowHooks: PatchRowHooks | null;
|
|
40
|
+
export declare function installPatchHooks(hooks: PatchValueHooks): void;
|
|
41
|
+
export declare function installRowHooks(hooks: PatchRowHooks): void;
|