@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
|
@@ -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;
|
|
@@ -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.cjs";
|
|
2
2
|
import { type Dev } from "./core/index.cjs";
|
|
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.cjs";
|
|
5
|
-
export { createSignal, createMemo, createEffect, createRenderEffect, createTrackedEffect, createReaction, createOptimistic, resolve, onSettled, onCleanup } from "./signals.cjs";
|
|
6
|
-
export type { Accessor, SourceAccessor, Setter, Signal, ComputeFunction, EffectFunction, EffectBundle, EffectOptions, SignalOptions, MemoOptions, NoInfer } from "./signals.cjs";
|
|
5
|
+
export { createSignal, createMemo, createEffect, createRenderEffect, createTrackedEffect, createReaction, createOptimistic, refresh, resolve, until, onSettled, onCleanup } from "./signals.cjs";
|
|
6
|
+
export type { Truthy, UntilOptions, Accessor, SourceAccessor, Setter, Signal, ComputeFunction, EffectFunction, EffectBundle, EffectOptions, SignalOptions, MemoOptions, NoInfer } from "./signals.cjs";
|
|
7
7
|
export { affects } from "./affects.cjs";
|
|
8
8
|
export { mapArray, repeat, type Maybe } from "./map.cjs";
|
|
9
9
|
export * from "./store/index.cjs";
|
|
@@ -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.cjs";
|
|
|
4
4
|
import type { NoFn, ProjectionOptions, Store, StoreOptions, StoreSetter } from "./store.cjs";
|
|
5
5
|
import type { Refreshable } from "../core/index.cjs";
|
|
6
6
|
export { createProjectionNext as createProjection } from "./next/projection.cjs";
|
|
7
|
+
export { registerPatch, registerRowOps, registerSlotPatchNext as registerSlotPatch, patchableRaw } from "./next/patch.cjs";
|
|
8
|
+
export { storeIsShallow, storeHasFamily, storeHasOptimisticFamily } from "./next/store.cjs";
|
|
7
9
|
export { createOptimisticStoreNext as createOptimisticStore } from "./next/optimistic.cjs";
|
|
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.cjs";
|
|
1
2
|
import { type NoFn, type ProjectionOptions, type Store, type StoreSetter } from "../store.cjs";
|
|
2
|
-
import type {
|
|
3
|
+
import type { StoreNextTarget } from "./target.cjs";
|
|
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.cjs";
|
|
2
|
+
import type { RowOps } from "./patch.cjs";
|
|
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;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Owner } from "../../core/types.cjs";
|
|
2
|
+
import { type StoreNextTarget } from "./target.cjs";
|
|
3
|
+
export type PatchFn = (next: any, prev: any, force?: boolean) => void;
|
|
4
|
+
interface PatchEntry {
|
|
5
|
+
fn: PatchFn;
|
|
6
|
+
owner: Owner | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Emit a record's visibility transition. Callers gate on `hasPatches()` and
|
|
10
|
+
* `t.d` cheaply; this function re-checks and walks ancestors (§4b).
|
|
11
|
+
*/
|
|
12
|
+
export declare function emitPatch(t: StoreNextTarget, next: any, prev: any): void;
|
|
13
|
+
/** Emission for sites that already stand at the record with both sides in
|
|
14
|
+
* hand and have already handled ancestors (the adoption walk descends —
|
|
15
|
+
* parents were visited first), so no bubbling walk. */
|
|
16
|
+
export declare function emitPatchLocal(t: StoreNextTarget, next: any, prev: any): void;
|
|
17
|
+
export declare function emitPatchOptimistic(t: StoreNextTarget, next: any, prev: any): void;
|
|
18
|
+
/** Row-ops emission at OPTIMISTIC (lane) timing: user drafts on an
|
|
19
|
+
* optimistic family must show structure IN FLIGHT — bypassing the
|
|
20
|
+
* transition stash exactly like emitPatchOptimistic. Two forms:
|
|
21
|
+
* - `ops` given (write site): `nextRows` is the draft's intended visible
|
|
22
|
+
* list, ops the identity diff against the pre-write optimistic view.
|
|
23
|
+
* - `ops === null` (revert site): RESYNC — the consumer rebuilds retention
|
|
24
|
+
* by row identity against the live post-revert view, resolved from the
|
|
25
|
+
* target at drain time (overrides are gone by then, so `pb ?? v` IS the
|
|
26
|
+
* committed truth). */
|
|
27
|
+
export declare function emitRowOpsOptimistic(t: StoreNextTarget, nextRows: any[] | null, ops: RowOps | null): void;
|
|
28
|
+
/** Test-only accounting probe: the live registration count must return to
|
|
29
|
+
* baseline across register/unbind/demote cycles. @internal */
|
|
30
|
+
export declare function patchCountForTests(): number;
|
|
31
|
+
export declare function hasPatches(): boolean;
|
|
32
|
+
export declare function registerPatch(record: any, fn: PatchFn): () => void;
|
|
33
|
+
/** Dual-driver bind probe (compiler runtime contract): when `record` is a
|
|
34
|
+
* patchable store record, returns its CURRENT raw backing (the driver's
|
|
35
|
+
* initial force-apply reads it directly — no proxy traffic, no tracking);
|
|
36
|
+
* returns undefined otherwise (driver falls back to the effect path).
|
|
37
|
+
* Not patchable: non-records, non-proxies, accessor-bearing records
|
|
38
|
+
* (patches read raw — getters need tracked evaluation), broken chains. */
|
|
39
|
+
export declare function patchableRaw(record: any): Record<PropertyKey, any> | undefined;
|
|
40
|
+
/** Accessor demotion (design §5): a record that acquires an accessor after
|
|
41
|
+
* registration stops being patchable — reads must go through tracked
|
|
42
|
+
* evaluation. Clears patches and repairs the global count; callers re-drive
|
|
43
|
+
* the pulled bodies (demoteToEffects). */
|
|
44
|
+
export declare function demotePatches(t: StoreNextTarget): PatchEntry[] | null;
|
|
45
|
+
/** The demotion re-drive (re-audit blocker 3): each pulled body becomes the
|
|
46
|
+
* SAME dual-driver effect fallback the web runtime would have chosen had the
|
|
47
|
+
* record carried the accessor at bind — a tracked compute pass (next === prev
|
|
48
|
+
* short-circuits every compare into a pure read THROUGH THE PROXY, so getter
|
|
49
|
+
* dependencies track) plus an untracked force-apply at effect timing.
|
|
50
|
+
*
|
|
51
|
+
* Creation is DEFERRED to the effect phase: the trap that discovers the
|
|
52
|
+
* accessor runs mid-draft, and an effect's initial pass must not read
|
|
53
|
+
* through the proxy inside the write window. The record's own transition
|
|
54
|
+
* for that draft is covered by the new effect's initial force-apply.
|
|
55
|
+
*
|
|
56
|
+
* Known edge (documented): a demoted LIST-ROW body re-drives under its
|
|
57
|
+
* registering owner (the list owner), so per-row severing on removal is
|
|
58
|
+
* lost for demoted rows — the effect lives until the LIST disposes. Rows
|
|
59
|
+
* only demote when user code defines an accessor on a row record at
|
|
60
|
+
* runtime. */
|
|
61
|
+
export declare function demoteToEffects(t: StoreNextTarget): void;
|
|
62
|
+
/** Structural ops for one keyed-array transition. `prefix` rows key-matched
|
|
63
|
+
* in place; for each later index i (absolute), `sources[i - prefix]` is the
|
|
64
|
+
* OLD index its row retained from, or -1 for a new row. `removed` holds the
|
|
65
|
+
* dropped old row values (unbind/teardown handles). Aligned value ticks emit
|
|
66
|
+
* NOTHING — ops exist only when structure changed. */
|
|
67
|
+
export interface RowOps {
|
|
68
|
+
prefix: number;
|
|
69
|
+
sources: number[];
|
|
70
|
+
removed: any[];
|
|
71
|
+
}
|
|
72
|
+
/** `ops === null` is the RESYNC form (optimistic revert): the consumer
|
|
73
|
+
* rebuilds retention by row identity against `next` (the live view). */
|
|
74
|
+
export type RowOpsFn = (next: any[], ops: RowOps | null) => void;
|
|
75
|
+
/** Register a structural-ops consumer on a keyed store array (the list
|
|
76
|
+
* container's channel — what `For` consumes through the seam). */
|
|
77
|
+
export declare function registerRowOps(array: any, fn: RowOpsFn): () => void;
|
|
78
|
+
/** Slot patches (shallow arrays) ride the same apply queue: the walk emits
|
|
79
|
+
* per aligned value-replaced slot; application happens at effect phase under
|
|
80
|
+
* the registration owner's lifetime. */
|
|
81
|
+
export declare function emitSlotPatch(t: StoreNextTarget, index: number, next: any, prev: any): void;
|
|
82
|
+
/** Slot patch for shallow arrays: the reconcile walk emits (index, next,
|
|
83
|
+
* prev) for KEY-ALIGNED value-replaced slots (structure rides row ops), and
|
|
84
|
+
* the emission queues through the patch apply queue — effect-phase timing,
|
|
85
|
+
* transition stamping, disposed-owner drop — like every other channel. */
|
|
86
|
+
export declare function registerSlotPatchNext(arr: any, fn: (index: number, next: any, prev: any) => void): () => void;
|
|
87
|
+
/** Row-ops ride the SAME apply queue/timing as record patches: transition-
|
|
88
|
+
* stamped, applied at effect phase, in emission order (structure before the
|
|
89
|
+
* new rows' own patches can exist; retained rows' value patches commute). */
|
|
90
|
+
export declare function emitRowOps(t: StoreNextTarget, next: any[], ops: RowOps): void;
|
|
91
|
+
export {};
|
|
@@ -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,
|
|
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>;
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
import type { RowOps } from "./patch.cjs";
|
|
2
|
+
import { type StoreNextTarget } from "./target.cjs";
|
|
1
3
|
type KeyFn = (item: any) => any;
|
|
2
4
|
export declare function reconcileNextState(value: any, state: any, key: string | KeyFn | null | undefined, replace?: boolean): void;
|
|
5
|
+
/** Key equality for EVERY key comparison in this module (re-audit 2, P1-5):
|
|
6
|
+
* SameValueZero, matching the Map-based matchers (buildRowOps, the adoption
|
|
7
|
+
* window) — NaN keys are equal to themselves, so aligned NaN rows stay
|
|
8
|
+
* aligned in the prefix walk instead of forever misaligning. Adoption and
|
|
9
|
+
* row ops MUST agree on key equality or retained DOM rows go stale. */
|
|
10
|
+
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;
|
|
3
17
|
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Signal } from "../../core/types.cjs";
|
|
2
|
-
import { type StoreNextFamily, type StoreNextTarget } from "./target.cjs";
|
|
2
|
+
import { type StoreNextFamily, type StoreNextTarget, type PatchChannel } from "./target.cjs";
|
|
3
|
+
/** Lazily allocate the patch-channel extension (one literal shape). */
|
|
4
|
+
export declare function pcOf(t: StoreNextTarget): PatchChannel;
|
|
3
5
|
export declare function wrapNext<T extends Record<PropertyKey, any>>(value: T, parent?: StoreNextTarget | null, parentKey?: PropertyKey | null, fam?: StoreNextFamily | null): T;
|
|
4
6
|
/** Unwrap our own proxies to their current backing; leave everything else. */
|
|
5
7
|
export declare function unwrapValue(v: any): any;
|
|
@@ -9,6 +11,10 @@ export declare function getKeySetNode(target: StoreNextTarget): Signal<number>;
|
|
|
9
11
|
/** Deep-witness bump: any value/shape change on a record with a live deep()
|
|
10
12
|
* subscriber notifies it. One null check when unused. */
|
|
11
13
|
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;
|
|
12
18
|
/** Downgrade a prototype-overlay pending backing to the clone path: builds
|
|
13
19
|
* the real container (committed + overlay writes − deletes) that fold will
|
|
14
20
|
* SWAP in as the committed backing, exactly as if the draft had started on
|
|
@@ -24,9 +30,16 @@ export declare function materializePB(target: StoreNextTarget): void;
|
|
|
24
30
|
* authoritative base (R21/R32).
|
|
25
31
|
*/
|
|
26
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>>;
|
|
27
38
|
/** Same logical slot: both values resolve to one (re-pointed) child target —
|
|
28
39
|
* adoption preserved identity, so the slot did not change (R9). */
|
|
29
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;
|
|
30
43
|
/**
|
|
31
44
|
* The fold diff walks SUBSCRIPTION KEYS ONLY (legacy parity: `for key in
|
|
32
45
|
* nodes`): nodes exist exactly where something tracked, so unobserved data
|
|
@@ -56,6 +69,21 @@ export declare function runAuthoritative<T>(fn: () => T): T;
|
|
|
56
69
|
/** Active optimistic override on an armed node (armed slot idles at
|
|
57
70
|
* NOT_PENDING; undefined = unarmed plain node). */
|
|
58
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;
|
|
59
87
|
export type SetStoreNextFunction<T> = (fn: (draft: T) => T | void) => void;
|
|
60
88
|
/** Low-level setter primitive: opens write mode on a next proxy, runs `fn`,
|
|
61
89
|
* emits write-time notifications at outermost exit, applies returned
|
|
@@ -63,8 +91,30 @@ export type SetStoreNextFunction<T> = (fn: (draft: T) => T | void) => void;
|
|
|
63
91
|
* projection recomputes legitimately write from inside their computed. */
|
|
64
92
|
export declare function storeSetterNext<T>(proxy: T, fn: (draft: T) => T | void, guard?: boolean): void;
|
|
65
93
|
export declare function createStoreNext<T extends Record<PropertyKey, any>>(init: T, shallow?: boolean): [T, SetStoreNextFunction<T>];
|
|
94
|
+
/** True when `proxy` is a SHALLOW store (children served verbatim, slots
|
|
95
|
+
* replaced by reference — #2932). The list driver uses this to choose the
|
|
96
|
+
* slot-patch channel (collected row bodies) over per-record registration. */
|
|
97
|
+
export declare function storeIsShallow(proxy: any): boolean;
|
|
98
|
+
/** True when `proxy` belongs to a projection/optimistic FAMILY. The list
|
|
99
|
+
* driver must DECLINE family arrays (external audit finding): family
|
|
100
|
+
* structural changes never emit row/slot ops (the setter channel is
|
|
101
|
+
* fam-gated; optimistic writes ride node overrides), and the proxy identity
|
|
102
|
+
* is stable so the each-watch cannot catch the change either — an engaged
|
|
103
|
+
* list would freeze on optimistic/projection structural updates. Record-
|
|
104
|
+
* level family patches are unaffected (they have their own emission). */
|
|
105
|
+
export declare function storeHasFamily(proxy: any): boolean;
|
|
106
|
+
/** True when `proxy` belongs to an OPTIMISTIC family specifically. The list
|
|
107
|
+
* driver declines these (audit finding, narrowed): optimistic user writes
|
|
108
|
+
* ride node-level overrides — they never enter the reconcile walk, so no
|
|
109
|
+
* row/slot ops are emitted and an engaged list would freeze on optimistic
|
|
110
|
+
* structural changes. PROJECTION (non-optimistic) families are drivable:
|
|
111
|
+
* their recomputes go through the reconcile walk, whose emissions are
|
|
112
|
+
* transition-stamped in the apply queue like any other (equivalence-matrix
|
|
113
|
+
* gated). Re-admitting optimistic families requires a lane-timed structural
|
|
114
|
+
* emission mirroring emitPatchOptimistic, plus revert resync. */
|
|
115
|
+
export declare function storeHasOptimisticFamily(proxy: any): boolean;
|
|
66
116
|
/** Tracking deep snapshot (`deep()` for next targets): subscribes to the
|
|
67
|
-
* key-set and
|
|
117
|
+
* key-set and deep-witness node at every reachable level, then returns the
|
|
68
118
|
* plain view. Shared references and cycles handled via the visited set. */
|
|
69
119
|
export declare function deepNext<T>(value: T): T;
|
|
70
120
|
/**
|