@solidjs/signals 2.0.0-rc.5 → 2.0.0-rc.7
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 +2467 -1322
- package/dist/node.cjs +2037 -2428
- package/dist/node.dev.cjs +13724 -0
- package/dist/prod/boundaries.js +3 -1
- package/dist/prod/core/async.js +47 -20
- package/dist/prod/core/attribution-hooks.js +3 -0
- package/dist/prod/core/constants.js +9 -1
- package/dist/prod/core/context.js +10 -16
- package/dist/prod/core/core.js +260 -188
- package/dist/prod/core/dev.js +2 -0
- package/dist/prod/core/effect.js +41 -32
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +35 -31
- package/dist/prod/core/heap.js +34 -40
- package/dist/prod/core/lanes.js +44 -30
- package/dist/prod/core/optimistic.js +55 -45
- package/dist/prod/core/owner.js +35 -35
- package/dist/prod/core/scheduler.js +176 -177
- package/dist/prod/core/verdict.js +53 -52
- package/dist/prod/index.js +0 -2
- package/dist/prod/map.js +104 -94
- package/dist/prod/signals.js +119 -35
- package/dist/prod/store/next/optimistic.js +217 -163
- package/dist/prod/store/next/projection.js +2 -2
- package/dist/prod/store/next/reconcile.js +140 -288
- package/dist/prod/store/next/store.js +338 -252
- package/dist/prod/store/store.js +2 -2
- package/dist/types/core/attribution-hooks.d.ts +75 -0
- package/dist/types/core/attribution.d.ts +313 -9
- package/dist/types/core/constants.d.ts +8 -0
- package/dist/types/core/core.d.ts +12 -3
- package/dist/types/core/dev.d.ts +56 -9
- package/dist/types/core/heap.d.ts +5 -3
- package/dist/types/core/invariants.d.ts +1 -1
- package/dist/types/core/lanes.d.ts +10 -0
- package/dist/types/core/scheduler.d.ts +14 -4
- package/dist/types/signals.d.ts +10 -11
- package/dist/types/store/index.d.ts +3 -6
- package/dist/types/store/next/optimistic.d.ts +4 -2
- package/dist/types/store/next/reconcile.d.ts +5 -12
- package/dist/types/store/next/store.d.ts +3 -9
- package/dist/types/store/next/target.d.ts +30 -46
- package/dist/types/store/store.d.ts +14 -9
- package/dist/types-cjs/core/attribution-hooks.d.cts +75 -0
- package/dist/types-cjs/core/attribution.d.cts +313 -9
- package/dist/types-cjs/core/constants.d.cts +8 -0
- package/dist/types-cjs/core/core.d.cts +12 -3
- package/dist/types-cjs/core/dev.d.cts +56 -9
- package/dist/types-cjs/core/heap.d.cts +5 -3
- package/dist/types-cjs/core/invariants.d.cts +1 -1
- package/dist/types-cjs/core/lanes.d.cts +10 -0
- package/dist/types-cjs/core/scheduler.d.cts +14 -4
- package/dist/types-cjs/signals.d.cts +10 -11
- package/dist/types-cjs/store/index.d.cts +3 -6
- package/dist/types-cjs/store/next/optimistic.d.cts +4 -2
- package/dist/types-cjs/store/next/reconcile.d.cts +5 -12
- package/dist/types-cjs/store/next/store.d.cts +3 -9
- package/dist/types-cjs/store/next/target.d.cts +30 -46
- package/dist/types-cjs/store/store.d.cts +14 -9
- package/package.json +3 -2
- package/dist/prod/store/next/patch-hooks.js +0 -13
- package/dist/prod/store/next/patch.js +0 -614
- package/dist/types/store/next/patch-hooks.d.ts +0 -41
- package/dist/types/store/next/patch.d.ts +0 -91
- package/dist/types-cjs/store/next/patch-hooks.d.cts +0 -41
- package/dist/types-cjs/store/next/patch.d.cts +0 -91
|
@@ -24,6 +24,16 @@ export declare function getOrCreateLane(signal: Signal<any>): OptimisticLane;
|
|
|
24
24
|
* Union-find: find the root lane.
|
|
25
25
|
*/
|
|
26
26
|
export declare function findLane(lane: OptimisticLane): OptimisticLane;
|
|
27
|
+
/**
|
|
28
|
+
* Is the lane held? `_pendingAsync` records the async the lane OWNS (derived
|
|
29
|
+
* under it); the transaction's reporter map records the async a render effect
|
|
30
|
+
* OBSERVED pending with no boundary taking it (INV-3, the one registration
|
|
31
|
+
* site). A hold needs both — the same rule the transaction itself uses, so a
|
|
32
|
+
* memo nobody renders, or one a fallback-showing boundary caught, cannot tear
|
|
33
|
+
* a frame and holds nothing (#3289). An orphan lane has no observation record
|
|
34
|
+
* and never holds.
|
|
35
|
+
*/
|
|
36
|
+
export declare function laneHeld(lane: OptimisticLane): boolean;
|
|
27
37
|
/**
|
|
28
38
|
* Merge two lanes when their dependency graphs overlap.
|
|
29
39
|
*/
|
|
@@ -9,7 +9,11 @@ export declare let clock: number;
|
|
|
9
9
|
export declare let activeTransition: Transition | null;
|
|
10
10
|
export declare let projectionWriteActive: boolean;
|
|
11
11
|
export declare let _hitUnhandledAsync: boolean;
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Consume the unhandled-async hit. Returns whether this is the first report
|
|
14
|
+
* of the current enforcement window — the caller warns only then.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resetUnhandledAsync(): boolean;
|
|
13
17
|
/**
|
|
14
18
|
* Toggles the dev-mode "must be inside a `<Loading>` boundary" enforcement
|
|
15
19
|
* window. Only `render()` calls this — wrapping the initial mount so that a
|
|
@@ -130,9 +134,10 @@ export declare class GlobalQueue extends Queue {
|
|
|
130
134
|
static _laneReadsCommitted: ((el: OptimisticNode, owner: OptimisticNode, c: Computed<any>) => boolean) | null;
|
|
131
135
|
static _recomputeLane: ((el: Computed<any>, own: boolean) => OptimisticLane | null | false) | null;
|
|
132
136
|
static _laneAsyncPending: ((el: Computed<any>) => void) | null;
|
|
133
|
-
/** Authoritative-view reader wakeup
|
|
134
|
-
* Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which
|
|
135
|
-
* carve-out read can set, so `!` invocations are safe once
|
|
137
|
+
/** Authoritative-view reader wakeup: installed by until() and refresh() before
|
|
138
|
+
* their first read. Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which
|
|
139
|
+
* only such a reader's carve-out read can set, so `!` invocations are safe once
|
|
140
|
+
* the gate holds (#3303). */
|
|
136
141
|
static _notifyAuthoritativeObservers: ((el: Signal<any> | Computed<any>) => void) | null;
|
|
137
142
|
static _laneAsyncSettled: ((el: Computed<any>) => void) | null;
|
|
138
143
|
static _trackOptimisticStore: ((store: any) => void) | null;
|
|
@@ -209,6 +214,11 @@ export declare const globalQueue: GlobalQueue;
|
|
|
209
214
|
*/
|
|
210
215
|
export declare function flush(): void;
|
|
211
216
|
export declare function flush<T>(fn: () => T): T;
|
|
217
|
+
/** A fresh, unentered transaction (#3146): the optimistic store's truth
|
|
218
|
+
* flight DECLARES an owned transaction instead of relying on whatever the
|
|
219
|
+
* ambient adoption machinery stamped on its firewall. Activate it with
|
|
220
|
+
* initTransition; it is a plain batch until then. */
|
|
221
|
+
export declare function createTransition(): Transition;
|
|
212
222
|
export declare function currentTransition(transition: Transition): Transition;
|
|
213
223
|
export declare function setActiveTransition(transition: Transition | null): void;
|
|
214
224
|
export declare function runInTransition<T>(transition: Transition, fn: () => T): T;
|
package/dist/types/signals.d.ts
CHANGED
|
@@ -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.
|
|
@@ -410,6 +399,16 @@ export declare function createRenderEffect<T>(compute: ComputeFunction<undefined
|
|
|
410
399
|
* Creates a tracked reactive effect where dependency tracking and side effects happen
|
|
411
400
|
* in the same scope.
|
|
412
401
|
*
|
|
402
|
+
* @deprecated Do not use in new code. For a side effect that follows reactive
|
|
403
|
+
* state, use `createEffect(compute, effect)` — it separates tracking from the
|
|
404
|
+
* side effect, knows its dependencies before it runs, and participates in
|
|
405
|
+
* async and transitions. For one-time DOM work after render (measuring,
|
|
406
|
+
* attaching third-party widgets to a ref), use `onSettled`. Tracking from
|
|
407
|
+
* inside the effect phase — the only thing this primitive adds — is retained
|
|
408
|
+
* solely to ease 1.x migration: it runs beside user-effect callbacks after
|
|
409
|
+
* values commit, never holds a transition, and cannot observe a write staged
|
|
410
|
+
* earlier in the same flush by a signal it has not read yet.
|
|
411
|
+
*
|
|
413
412
|
* WARNING: Because tracking and effects happen in the same scope, this primitive
|
|
414
413
|
* may run multiple times for a single change or show tearing (reading inconsistent
|
|
415
414
|
* state). Use only when dynamic subscription patterns require same-scope tracking.
|
|
@@ -4,15 +4,12 @@ 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
7
|
export { storeIsShallow, storeHasFamily, storeHasOptimisticFamily } from "./next/store.js";
|
|
9
8
|
export { createOptimisticStoreNext as createOptimisticStore } from "./next/optimistic.js";
|
|
10
|
-
/** Public createStore: plain form `(
|
|
9
|
+
/** Public createStore: plain form `(initialValue, options?)` and derived writable
|
|
11
10
|
* form `(fn, seed, options?)`. */
|
|
12
|
-
export declare function createStore<T extends object = {}>(
|
|
13
|
-
|
|
14
|
-
}): [get: Store<T>, set: StoreSetter<T>];
|
|
15
|
-
export declare function createStore<T extends object = {}>(fn: (store: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, store: Partial<T> | Store<NoFn<T>>, options?: ProjectionOptions): [get: Refreshable<Store<T>>, set: StoreSetter<T>];
|
|
11
|
+
export declare function createStore<T extends object = {}>(initialValue: NoFn<T> | Store<NoFn<T>>, options?: StoreOptions): [get: Store<T>, set: StoreSetter<T>];
|
|
12
|
+
export declare function createStore<T extends object = {}>(fn: (draft: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, seed: Partial<T> | Store<NoFn<T>>, options?: ProjectionOptions): [get: Refreshable<Store<T>>, set: StoreSetter<T>];
|
|
16
13
|
export declare function reconcile<T extends U, U>(value: T, key?: string | ((item: NonNullable<any>) => any) | null): (state: U) => T;
|
|
17
14
|
export declare function snapshot<T>(value: T): T;
|
|
18
15
|
export declare function deep<T>(value: T): T;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type Refreshable } from "../../core/index.js";
|
|
1
2
|
import { type Transition } from "../../core/scheduler.js";
|
|
2
|
-
import { type NoFn, type ProjectionOptions, type Store, type StoreSetter } from "../store.js";
|
|
3
|
+
import { type NoFn, type ProjectionOptions, type Store, type StoreOptions, type StoreSetter } from "../store.js";
|
|
3
4
|
import type { StoreNextTarget } from "./target.js";
|
|
4
5
|
/** #3164 fold: a stamped truth is HELD (masked from ordinary readers until
|
|
5
6
|
* the reveal) only while its transition is live AND retaining optimism —
|
|
@@ -8,7 +9,8 @@ import type { StoreNextTarget } from "./target.js";
|
|
|
8
9
|
* staged values to converge (normal speculation). Resolves merges first:
|
|
9
10
|
* merge unions optimistic nodes/stores into the target. */
|
|
10
11
|
export declare function transitionHoldsOptimism(transition: Transition): boolean;
|
|
11
|
-
export declare function createOptimisticStoreNext<T extends object = {}>(
|
|
12
|
+
export declare function createOptimisticStoreNext<T extends object = {}>(initialValue: NoFn<T> | Store<NoFn<T>>, options?: StoreOptions): [get: Store<T>, set: StoreSetter<T>];
|
|
13
|
+
export declare function createOptimisticStoreNext<T extends object = {}>(fn: (draft: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, seed: Partial<T> | Store<NoFn<T>>, options?: ProjectionOptions): [get: Refreshable<Store<T>>, set: StoreSetter<T>];
|
|
12
14
|
/** Diff the draft against the current OPTIMISTIC VIEW (committed + active
|
|
13
15
|
* overrides — the same view the draft was seeded from) and emit engine writes
|
|
14
16
|
* for exactly the changed keys. Visible-view diffing keeps no-op writes from
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
import type { RowOps } from "./patch.js";
|
|
2
|
-
import { type StoreNextTarget } from "./target.js";
|
|
3
1
|
type KeyFn = (item: any) => any;
|
|
4
2
|
export declare function reconcileNextState(value: any, state: any, key: string | KeyFn | null | undefined, replace?: boolean): void;
|
|
3
|
+
/** Setter-channel row ops (the fold site calls this for array targets with
|
|
4
|
+
* ops consumers): structural mutation through the setter — push/splice/index
|
|
5
5
|
/** Key equality for EVERY key comparison in this module (re-audit 2, P1-5):
|
|
6
|
-
* SameValueZero, matching the Map-based
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* row ops MUST agree on key equality or retained DOM rows go stale. */
|
|
6
|
+
* SameValueZero, matching the adoption window's Map-based matcher — NaN keys
|
|
7
|
+
* are equal to themselves, so aligned NaN rows stay aligned in the prefix
|
|
8
|
+
* walk instead of forever misaligning. */
|
|
10
9
|
export declare function sameKey(a: any, b: any): boolean;
|
|
11
|
-
export declare function emitSetterRowOps(t: StoreNextTarget, prevRows: any[], nextRows: any[]): void;
|
|
12
|
-
/** Identity-keyed structural diff, returned rather than emitted: shared by
|
|
13
|
-
* the setter channel (regular queue) and the OPTIMISTIC write channel (lane
|
|
14
|
-
* queue) — same retention semantics, different dispatch timing. Returns
|
|
15
|
-
* null when the lists are identity-aligned (no structure changed). */
|
|
16
|
-
export declare function buildIdentityRowOps(prevRows: any[], nextRows: any[]): RowOps | null;
|
|
17
10
|
export {};
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import type { Signal } from "../../core/types.js";
|
|
2
|
-
import { type StoreNextFamily, type StoreNextTarget
|
|
3
|
-
/** Lazily allocate the patch-channel extension (one literal shape). */
|
|
4
|
-
export declare function pcOf(t: StoreNextTarget): PatchChannel;
|
|
2
|
+
import { type StoreNextFamily, type StoreNextTarget } from "./target.js";
|
|
5
3
|
export declare function wrapNext<T extends Record<PropertyKey, any>>(value: T, parent?: StoreNextTarget | null, parentKey?: PropertyKey | null, fam?: StoreNextFamily | null): T;
|
|
6
4
|
/** Unwrap our own proxies to their current backing; leave everything else. */
|
|
7
5
|
export declare function unwrapValue(v: any): any;
|
|
8
|
-
export declare function getNode(target: StoreNextTarget, key: PropertyKey, current: any): Signal<any>;
|
|
6
|
+
export declare function getNode(target: StoreNextTarget, key: PropertyKey, current: any, accKnown?: -1 | 0 | 1): Signal<any>;
|
|
9
7
|
export declare function getHasNode(target: StoreNextTarget, key: PropertyKey, present: boolean): Signal<boolean>;
|
|
10
8
|
export declare function getKeySetNode(target: StoreNextTarget): Signal<number>;
|
|
11
9
|
/** Deep-witness bump: any value/shape change on a record with a live deep()
|
|
12
10
|
* subscriber notifies it. One null check when unused. */
|
|
13
11
|
export declare function bumpDeep(t: StoreNextTarget): void;
|
|
14
|
-
/** Scanned plainness for patch admission (patchableRaw): runs the one-time
|
|
15
|
-
* accessor scan if it hasn't happened yet — the sticky `a` flag alone is not
|
|
16
|
-
* trustworthy before a scan (it starts false and is discovered lazily). */
|
|
17
|
-
export declare function targetIsPlain(target: StoreNextTarget): boolean;
|
|
18
12
|
/** Downgrade a prototype-overlay pending backing to the clone path: builds
|
|
19
13
|
* the real container (committed + overlay writes − deletes) that fold will
|
|
20
14
|
* SWAP in as the committed backing, exactly as if the draft had started on
|
|
@@ -90,7 +84,7 @@ export type SetStoreNextFunction<T> = (fn: (draft: T) => T | void) => void;
|
|
|
90
84
|
* replacements as adoptions. `guard=false` skips the owned-scope dev guard —
|
|
91
85
|
* projection recomputes legitimately write from inside their computed. */
|
|
92
86
|
export declare function storeSetterNext<T>(proxy: T, fn: (draft: T) => T | void, guard?: boolean): void;
|
|
93
|
-
export declare function createStoreNext<T extends Record<PropertyKey, any>>(
|
|
87
|
+
export declare function createStoreNext<T extends Record<PropertyKey, any>>(initialValue: T, shallow?: boolean): [T, SetStoreNextFunction<T>];
|
|
94
88
|
/** True when `proxy` is a SHALLOW store (children served verbatim, slots
|
|
95
89
|
* replaced by reference — #2932). The list driver uses this to choose the
|
|
96
90
|
* slot-patch channel (collected row bodies) over per-record registration. */
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* entry per read-through object; zero layer slots; nodes, has-nodes, and the
|
|
13
13
|
* key-set node are lazy, materialized only by subscription.
|
|
14
14
|
*/
|
|
15
|
-
import type { Computed,
|
|
15
|
+
import type { Computed, Signal } from "../../core/types.js";
|
|
16
16
|
/** Projection family (§7b): children wrap into the family's own map (writes
|
|
17
17
|
* land in the projection, never the source family), and every node created
|
|
18
18
|
* under the family carries the projection computed as its firewall. */
|
|
@@ -34,6 +34,16 @@ export interface StoreNextFamily {
|
|
|
34
34
|
* landing under an active override. Dead members prune lazily at each
|
|
35
35
|
* landing (retainingTransition). */
|
|
36
36
|
rt?: Set<any>;
|
|
37
|
+
/** Flight-owned transaction (#3146): declared when a truth-flight
|
|
38
|
+
* registers (the ask's transaction — created by the flight's own pending
|
|
39
|
+
* throw when none was ambient, the causing write's when one was), renewed
|
|
40
|
+
* per settle event once the previous reveal committed. Bare optimistic
|
|
41
|
+
* writes and landings route into it BY DECLARATION; the transitionBlocked
|
|
42
|
+
* store-half checks it for ownership instead of reconstructing it from
|
|
43
|
+
* `_optimisticStores` membership. null = no flight declared one (sync
|
|
44
|
+
* derive, or a loading-window flight — the loading rail is
|
|
45
|
+
* transaction-invisible, #2933). */
|
|
46
|
+
ft?: any;
|
|
37
47
|
/** Normalized row-key fn (same resolution as the projection channels:
|
|
38
48
|
* `options.key`, "id" default, null = unkeyed). The staged-landing walk
|
|
39
49
|
* reads it: key-matched rows keep their proxy identity across a fold. */
|
|
@@ -43,40 +53,6 @@ export interface StoreNextFamily {
|
|
|
43
53
|
node: Computed<any> | null;
|
|
44
54
|
shallow?: boolean;
|
|
45
55
|
}
|
|
46
|
-
/** Write-side patch-channel state (stage 2), grouped off the target's named
|
|
47
|
-
* fields — see the shape rule on `StoreNextTarget.pc`. One literal shape,
|
|
48
|
-
* allocated by `pcOf` on first use. */
|
|
49
|
-
export interface PatchChannel {
|
|
50
|
-
/** Slot-patch hooks for shallow arrays — the reconcile walk emits
|
|
51
|
-
* (i, next, prev) for key-aligned value-replaced slots through the patch
|
|
52
|
-
* apply queue (records are raw, no per-record targets exist).
|
|
53
|
-
* MULTI-CONSUMER (external audit): one array can drive several lists. */
|
|
54
|
-
sp: {
|
|
55
|
-
fn: (index: number, next: any, prev: any) => void;
|
|
56
|
-
owner: Owner | null;
|
|
57
|
-
}[] | null;
|
|
58
|
-
/** Patch-channel consumers (next/patch.ts): per-record compiled patch
|
|
59
|
-
* entries, multi-consumer. null when unpatched (the common case). */
|
|
60
|
-
p: object[] | null;
|
|
61
|
-
/** Same-batch coalescing stamp (re-audit 2/3): the container array this
|
|
62
|
-
* channel last pushed a non-forced SELF entry into, plus that entry. A
|
|
63
|
-
* later same-batch emission UPDATES the queued entry's `next` in place
|
|
64
|
-
* (latest state wins — adoption REPLACES the captured object, so dropping
|
|
65
|
-
* the later emission would apply stale state) while `prev` stays the
|
|
66
|
-
* batch's earliest. The drain clears both stamps so a quiet record
|
|
67
|
-
* retains nothing from its last batch. */
|
|
68
|
-
qa: unknown;
|
|
69
|
-
qe: unknown;
|
|
70
|
-
/** Row-ops consumers (next/patch.ts, PR-B): structural list ops —
|
|
71
|
-
* (nextRows, { prefix, sources, removed }) at apply timing. */
|
|
72
|
-
ro: object[] | null;
|
|
73
|
-
/** Keys written through the traps since the last fold commit. Bounds the
|
|
74
|
-
* setter notify/hold-check to O(written) instead of O(subscribed nodes) —
|
|
75
|
-
* a record with thousands of per-key subscriptions (selection maps) would
|
|
76
|
-
* otherwise pay a full node scan on every write. null = no trap writes
|
|
77
|
-
* this batch (bulk paths fall back to the full scan). */
|
|
78
|
-
wk: Set<PropertyKey> | null;
|
|
79
|
-
}
|
|
80
56
|
export interface StoreNextTarget {
|
|
81
57
|
/** Committed backing: source object (shared) or owned clone. */
|
|
82
58
|
v: Record<PropertyKey, any>;
|
|
@@ -92,15 +68,19 @@ export interface StoreNextTarget {
|
|
|
92
68
|
h: Record<PropertyKey, Signal<boolean>> | null;
|
|
93
69
|
/** Lazy key-set node: membership/iteration/$TRACK subscriptions (§6). */
|
|
94
70
|
k: Signal<number> | null;
|
|
95
|
-
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
|
|
71
|
+
/** Keys written through the traps since the last fold commit. Bounds the
|
|
72
|
+
* setter notify/hold-check to O(written) instead of O(subscribed nodes) —
|
|
73
|
+
* a record with thousands of per-key subscriptions (selection maps) would
|
|
74
|
+
* otherwise pay a full node scan on every write. null = no trap writes
|
|
75
|
+
* this batch (bulk paths fall back to the full scan); WK_ALL = bound
|
|
76
|
+
* unusable (array length write). LOAD-BEARING SHAPE RULE: array proxy
|
|
77
|
+
* targets carry their fields as named properties on a real array, and V8
|
|
78
|
+
* normalizes an array to dictionary properties as the named count grows
|
|
79
|
+
* (empirically at counts ≡ 0 mod 3 from 18 up on V8 13.x) — every trap
|
|
80
|
+
* field read then becomes a hash lookup (~15% uibench, tree suites
|
|
81
|
+
* worst). Future write-side state MUST ride an extension object, not new
|
|
82
|
+
* named fields. */
|
|
83
|
+
wk: Set<PropertyKey> | null;
|
|
104
84
|
/** Lazy deep-witness node: `deep()` subscribes ONE node per record instead
|
|
105
85
|
* of one per path; write paths bump it only when it exists. Separate from
|
|
106
86
|
* `k` so $TRACK/mapArray never rerun on leaf value changes (R9). */
|
|
@@ -120,8 +100,12 @@ export interface StoreNextTarget {
|
|
|
120
100
|
/** Accessor scan performed (scan-once on first trap read; adopted data is
|
|
121
101
|
* not rescanned — legacy-parity behavior). */
|
|
122
102
|
sc: boolean;
|
|
123
|
-
/**
|
|
124
|
-
|
|
103
|
+
/** Adoption diff base, non-null when the backing was swapped by adoption
|
|
104
|
+
* this batch: the view the nodes were LAST TOLD — the pre-batch committed
|
|
105
|
+
* backing, or the draft's pending backing when a draft preceded the
|
|
106
|
+
* adoption (its setter-exit notifications already moved the nodes, #3296).
|
|
107
|
+
* The deferred fold diffs incoming against this, never against committed. */
|
|
108
|
+
ab: Record<PropertyKey, any> | null;
|
|
125
109
|
/** Pending backing is a prototype-chain OVERLAY of the committed backing
|
|
126
110
|
* (`Object.create(v)` — own keys are this batch's writes, everything else
|
|
127
111
|
* reads through). O(written) per flush instead of O(container) clones
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Signal } from "../core/index.js";
|
|
2
2
|
import type { Refreshable } from "../core/index.js";
|
|
3
|
-
/** A
|
|
4
|
-
export type Store<T> =
|
|
3
|
+
/** A reactive view of a store's value. Update it through the paired `StoreSetter`. */
|
|
4
|
+
export type Store<T> = T;
|
|
5
5
|
/**
|
|
6
6
|
* A store setter. The callback receives a writable **draft** of the store.
|
|
7
7
|
*
|
|
@@ -14,25 +14,30 @@ export type Store<T> = Readonly<T>;
|
|
|
14
14
|
*
|
|
15
15
|
* The setter does **not** perform keyed reconciliation. If you need surviving
|
|
16
16
|
* items to keep their store identity across full-array replacement, use the
|
|
17
|
-
* projection form — `createStore(fn, seed, { key })` or
|
|
18
|
-
* whose derive function reconciles
|
|
17
|
+
* projection form — `createStore(fn, seed, { key })` or
|
|
18
|
+
* `createProjection(fn, seed, { key })` — whose derive function reconciles
|
|
19
|
+
* its return by `options.key`.
|
|
19
20
|
*/
|
|
20
21
|
export type StoreSetter<T> = (fn: (state: T) => T | void) => void;
|
|
21
|
-
/** Tuple returned by the plain `createStore(initialValue)` form. */
|
|
22
|
+
/** Tuple returned by the plain `createStore(initialValue, options?)` form. */
|
|
22
23
|
export type StoreReturn<T> = [get: Store<T>, set: StoreSetter<T>];
|
|
23
24
|
/** Tuple returned by the derived `createStore(fn, seed, options?)` form. */
|
|
24
25
|
export type ProjectionStoreReturn<T> = [get: Refreshable<Store<T>>, set: StoreSetter<T>];
|
|
25
|
-
/**
|
|
26
|
+
/** Options shared by all store primitives. */
|
|
26
27
|
export interface StoreOptions {
|
|
27
28
|
/** Debug name (dev mode only) */
|
|
28
29
|
name?: string;
|
|
30
|
+
/** Single-layer store: root keys reactive, values raw records replaced by reference */
|
|
31
|
+
shallow?: boolean;
|
|
29
32
|
}
|
|
30
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Options for derived/projected stores created with
|
|
35
|
+
* `createStore(fn, seed, options?)`, `createProjection(fn, seed, options?)`,
|
|
36
|
+
* or `createOptimisticStore(fn, seed, options?)`.
|
|
37
|
+
*/
|
|
31
38
|
export interface ProjectionOptions extends StoreOptions {
|
|
32
39
|
/** Key property name or function for reconciliation identity; `null` merges positionally */
|
|
33
40
|
key?: string | ((item: NonNullable<any>) => any) | null;
|
|
34
|
-
/** Single-layer store: root keys reactive, values raw records replaced by reference */
|
|
35
|
-
shallow?: boolean;
|
|
36
41
|
/**
|
|
37
42
|
* Treat the seed as commit #0: the store is born committed with the seed's
|
|
38
43
|
* contents, shown until the derive's first real answer lands. While that
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Transition } from "./scheduler.cjs";
|
|
1
2
|
import type { Computed, Signal } from "./types.cjs";
|
|
2
3
|
/**
|
|
3
4
|
* Dev-only observability hook points for the reactive core.
|
|
@@ -36,6 +37,17 @@ export interface AttributionHooks {
|
|
|
36
37
|
write(el: Signal<any> | Computed<any>, prev: unknown, value: unknown): void;
|
|
37
38
|
/** refresh() invalidated this node (self-invalidation, no dep changed). */
|
|
38
39
|
refreshed(el: Computed<any>): void;
|
|
40
|
+
/**
|
|
41
|
+
* A new async flight entered the system (`_inFlight` was just assigned
|
|
42
|
+
* during a recompute of `el`). Always fired inside the owning recompute —
|
|
43
|
+
* both call paths (core's recompute and the projection self-registration)
|
|
44
|
+
* run within one — so the engine can read the current frame stack to link
|
|
45
|
+
* the flight to the change that caused it (waterfall chaining). `flight`
|
|
46
|
+
* is the registered thenable/iterable itself: the engine keys a first-seen
|
|
47
|
+
* origin registry on its identity, so shared and preloader-marked promises
|
|
48
|
+
* carry their true start time instead of the moment the graph saw them.
|
|
49
|
+
*/
|
|
50
|
+
flightStart(el: Computed<any>, flight: object): void;
|
|
39
51
|
/** An async landing is about to apply its value (before any branch). */
|
|
40
52
|
asyncStart(el: Computed<any>): void;
|
|
41
53
|
/**
|
|
@@ -47,6 +59,69 @@ export interface AttributionHooks {
|
|
|
47
59
|
* from the node's state against its asyncStart snapshot.
|
|
48
60
|
*/
|
|
49
61
|
asyncEnd(el: Computed<any>, prev: unknown, value: unknown, direct: boolean): void;
|
|
62
|
+
/**
|
|
63
|
+
* An effect's imperative half (its effect callback) is about to run /
|
|
64
|
+
* has run. Both fire outside the run's try; `effectRunEnd` fires whether
|
|
65
|
+
* or not the callback threw. Writes between the two are the effect's.
|
|
66
|
+
*/
|
|
67
|
+
effectRunStart(el: Computed<any>): void;
|
|
68
|
+
effectRunEnd(el: Computed<any>): void;
|
|
69
|
+
/**
|
|
70
|
+
* One synchronous step of an `action()` generator is about to run / has
|
|
71
|
+
* run (`it.next()`/`it.throw()` up to the next yield). `it` is the
|
|
72
|
+
* invocation's iterator — stable identity across its steps; `name` the
|
|
73
|
+
* generator function's name. Writes between the two are the action's.
|
|
74
|
+
*/
|
|
75
|
+
actionStepStart(it: object, name: string | undefined): void;
|
|
76
|
+
actionStepEnd(it: object): void;
|
|
77
|
+
/**
|
|
78
|
+
* A flush found `t` incomplete (transitionComplete's false verdict): its
|
|
79
|
+
* writes stay staged and its queues are about to be parked. Fired BEFORE
|
|
80
|
+
* this flush's lane effects (the visible acknowledgers — isPending
|
|
81
|
+
* companions, optimistic values) run; `holdEnd` fires from the root
|
|
82
|
+
* stashQueues call after them, so effect runs between the two are runs that
|
|
83
|
+
* painted *during* the hold.
|
|
84
|
+
*/
|
|
85
|
+
holdStart(t: Transition): void;
|
|
86
|
+
holdEnd(): void;
|
|
87
|
+
/**
|
|
88
|
+
* `t` was judged complete (transitionComplete's true verdict, before `_done`
|
|
89
|
+
* flips). Fired before its held writes commit, so `t._pendingNodes` still
|
|
90
|
+
* lists what was staged.
|
|
91
|
+
*/
|
|
92
|
+
transitionSettled(t: Transition): void;
|
|
93
|
+
/** `outgoing` was folded into `target` (`outgoing._done = target`). */
|
|
94
|
+
transitionMerged(target: Transition, outgoing: Transition): void;
|
|
95
|
+
/**
|
|
96
|
+
* A store setter batch replaced the container at `path` (e.g. `store.user`)
|
|
97
|
+
* with a different one (both non-null, same array-ness, not the same
|
|
98
|
+
* logical slot), and this is the leaf census of the new container against
|
|
99
|
+
* the old: `total` leaves (own keys, or items) in the new one, `unchanged`
|
|
100
|
+
* of which are the same value as before (identity, judged on unwrapped
|
|
101
|
+
* values — object keys compared by key, array items by membership), and
|
|
102
|
+
* `prevTotal` leaves in the old one. Containers above 64 leaves are not
|
|
103
|
+
* announced. Fired per written key from the write channel's notify. The
|
|
104
|
+
* engine decides whether the replacement was a spread-copy worth a
|
|
105
|
+
* diagnostic.
|
|
106
|
+
*/
|
|
107
|
+
storeReplaced(path: string, isArray: boolean, total: number, unchanged: number, prevTotal: number): void;
|
|
108
|
+
/**
|
|
109
|
+
* A `mapArray` update both disposed and created rows: `removed` are the
|
|
110
|
+
* items whose rows were disposed, `created` the items that got new rows,
|
|
111
|
+
* `newLen` the list's new length, `keyed` whether a key function is in use
|
|
112
|
+
* (false = identity or by-index). Fired after commit. The engine judges
|
|
113
|
+
* whether the churn replaced equivalent records (unstable identity).
|
|
114
|
+
*/
|
|
115
|
+
listChurn(el: Computed<any>, removed: unknown[], created: unknown[], newLen: number, keyed: boolean): void;
|
|
116
|
+
/**
|
|
117
|
+
* A loading boundary started (`shown` true) or stopped showing its
|
|
118
|
+
* fallback. `boundary` is the boundary's queue (stable identity); `tree`
|
|
119
|
+
* its bound subtree computed when already constructed — the first show can
|
|
120
|
+
* fire while the subtree is still being built — whose owner chain names
|
|
121
|
+
* the boundary. Fired at the source-set transitions (first pending source
|
|
122
|
+
* registers / last one clears), not per flush.
|
|
123
|
+
*/
|
|
124
|
+
boundaryFallback(boundary: object, tree: Computed<any> | undefined, shown: boolean): void;
|
|
50
125
|
}
|
|
51
126
|
export declare let attrHooks: AttributionHooks | null;
|
|
52
127
|
export declare function setAttributionHooks(hooks: AttributionHooks | null): void;
|