@solidjs/signals 2.0.0-rc.7 → 2.0.0-rc.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/dev-shared.js +5243 -0
  2. package/dist/dev.attribution.js +2333 -0
  3. package/dist/dev.js +1229 -6834
  4. package/dist/observe/affects.js +127 -0
  5. package/dist/observe/attribution.js +1 -0
  6. package/dist/observe/boundaries.js +586 -0
  7. package/dist/observe/core/action.js +167 -0
  8. package/dist/observe/core/async.js +736 -0
  9. package/dist/observe/core/attribution-hooks.js +60 -0
  10. package/dist/observe/core/attribution.js +2318 -0
  11. package/dist/{types-cjs/core/constants.d.cts → observe/core/constants.js} +129 -73
  12. package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
  13. package/dist/observe/core/core.js +1289 -0
  14. package/dist/observe/core/dev.js +232 -0
  15. package/dist/observe/core/effect.js +179 -0
  16. package/dist/observe/core/error.js +80 -0
  17. package/dist/observe/core/external.js +98 -0
  18. package/dist/observe/core/graph.js +155 -0
  19. package/dist/observe/core/heap.js +147 -0
  20. package/dist/observe/core/invariants.js +43 -0
  21. package/dist/observe/core/lanes.js +168 -0
  22. package/dist/observe/core/optimistic.js +438 -0
  23. package/dist/observe/core/owner.js +308 -0
  24. package/dist/observe/core/scheduler.js +1190 -0
  25. package/dist/observe/core/verdict.js +551 -0
  26. package/dist/observe/index.js +55 -0
  27. package/dist/observe/map.js +482 -0
  28. package/dist/{types-cjs/signals.d.cts → observe/signals.js} +295 -317
  29. package/dist/observe/store/index.js +38 -0
  30. package/dist/observe/store/next/optimistic.js +651 -0
  31. package/dist/observe/store/next/projection.js +242 -0
  32. package/dist/observe/store/next/reconcile.js +373 -0
  33. package/dist/observe/store/next/store.js +2213 -0
  34. package/dist/observe/store/next/target.js +51 -0
  35. package/dist/observe/store/store.js +323 -0
  36. package/dist/observe/store/storePath.js +103 -0
  37. package/dist/observe/store/utils.js +210 -0
  38. package/dist/prod/attribution.js +32 -0
  39. package/dist/prod/core/action.js +52 -32
  40. package/dist/prod/core/async.js +171 -127
  41. package/dist/prod/core/constants.js +24 -1
  42. package/dist/prod/core/context.js +3 -3
  43. package/dist/prod/core/core.js +434 -291
  44. package/dist/prod/core/effect.js +54 -34
  45. package/dist/prod/core/external.js +2 -2
  46. package/dist/prod/core/graph.js +32 -32
  47. package/dist/prod/core/heap.js +49 -44
  48. package/dist/prod/core/lanes.js +45 -38
  49. package/dist/prod/core/optimistic.js +207 -73
  50. package/dist/prod/core/owner.js +43 -39
  51. package/dist/prod/core/scheduler.js +319 -190
  52. package/dist/prod/core/verdict.js +67 -67
  53. package/dist/prod/index.js +9 -3
  54. package/dist/prod/map.js +292 -141
  55. package/dist/prod/signals.js +7 -10
  56. package/dist/prod/store/next/optimistic.js +140 -125
  57. package/dist/prod/store/next/projection.js +3 -3
  58. package/dist/prod/store/next/reconcile.js +87 -84
  59. package/dist/prod/store/next/store.js +524 -280
  60. package/dist/prod/store/next/target.js +32 -10
  61. package/dist/prod/store/store.js +12 -14
  62. package/dist/prod/store/utils.js +36 -27
  63. package/dist/types/attribution.d.ts +15 -0
  64. package/dist/types/attribution.prod.d.ts +13 -0
  65. package/dist/types/core/async.d.ts +1 -1
  66. package/dist/types/core/attribution-hooks.d.ts +119 -6
  67. package/dist/types/core/attribution.d.ts +208 -53
  68. package/dist/types/core/constants.d.ts +23 -0
  69. package/dist/types/core/core.d.ts +7 -0
  70. package/dist/types/core/dev.d.ts +124 -26
  71. package/dist/types/core/effect.d.ts +8 -1
  72. package/dist/types/core/index.d.ts +2 -1
  73. package/dist/types/core/lanes.d.ts +8 -1
  74. package/dist/types/core/scheduler.d.ts +60 -0
  75. package/dist/types/core/types.d.ts +29 -9
  76. package/dist/types/index.d.ts +9 -2
  77. package/dist/types/map.d.ts +2 -0
  78. package/dist/types/store/index.d.ts +1 -0
  79. package/dist/types/store/next/store.d.ts +5 -0
  80. package/dist/types/store/next/target.d.ts +36 -9
  81. package/dist/types/store/utils.d.ts +7 -0
  82. package/package.json +21 -17
  83. package/dist/node.cjs +0 -10541
  84. package/dist/node.dev.cjs +0 -13724
  85. package/dist/types-cjs/affects.d.cts +0 -47
  86. package/dist/types-cjs/boundaries.d.cts +0 -175
  87. package/dist/types-cjs/core/action.d.cts +0 -64
  88. package/dist/types-cjs/core/async.d.cts +0 -23
  89. package/dist/types-cjs/core/attribution-hooks.d.cts +0 -127
  90. package/dist/types-cjs/core/attribution.d.cts +0 -495
  91. package/dist/types-cjs/core/core.d.cts +0 -185
  92. package/dist/types-cjs/core/dev.d.cts +0 -136
  93. package/dist/types-cjs/core/effect.d.cts +0 -30
  94. package/dist/types-cjs/core/error.d.cts +0 -56
  95. package/dist/types-cjs/core/external.d.cts +0 -15
  96. package/dist/types-cjs/core/graph.d.cts +0 -28
  97. package/dist/types-cjs/core/heap.d.cts +0 -24
  98. package/dist/types-cjs/core/index.d.cts +0 -13
  99. package/dist/types-cjs/core/invariants.d.cts +0 -59
  100. package/dist/types-cjs/core/lanes.d.cts +0 -54
  101. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  102. package/dist/types-cjs/core/owner.d.cts +0 -124
  103. package/dist/types-cjs/core/scheduler.d.cts +0 -236
  104. package/dist/types-cjs/core/types.d.cts +0 -204
  105. package/dist/types-cjs/core/verdict.d.cts +0 -2
  106. package/dist/types-cjs/index.d.cts +0 -10
  107. package/dist/types-cjs/map.d.cts +0 -69
  108. package/dist/types-cjs/package.json +0 -3
  109. package/dist/types-cjs/store/index.d.cts +0 -18
  110. package/dist/types-cjs/store/next/optimistic.d.cts +0 -25
  111. package/dist/types-cjs/store/next/projection.d.cts +0 -8
  112. package/dist/types-cjs/store/next/reconcile.d.cts +0 -10
  113. package/dist/types-cjs/store/next/store.d.cts +0 -121
  114. package/dist/types-cjs/store/next/target.d.cts +0 -166
  115. package/dist/types-cjs/store/store.d.cts +0 -143
  116. package/dist/types-cjs/store/storePath.d.cts +0 -58
  117. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -1,3 +1,4 @@
1
+ import { type Transition } from "./scheduler.js";
1
2
  import type { Computed, NodeOptions, Owner } from "./types.js";
2
3
  export interface Effect<T> extends Computed<T>, Owner {
3
4
  _effectFn: (val: T, prev: T | undefined) => void | (() => void);
@@ -5,7 +6,13 @@ export interface Effect<T> extends Computed<T>, Owner {
5
6
  _modified: boolean;
6
7
  _prevValue: T | undefined;
7
8
  _type: number;
8
- _boundRunEffect?: () => void;
9
+ _boundRunEffect?: (type: number) => void;
10
+ /** The transaction whose staged view produced `_value` (null = committed
11
+ * view). Effects have one value slot and do not entangle transactions, so
12
+ * a second transaction recomputing the same effect overwrites a value the
13
+ * first one still owes a run for; see the contested-effect arm of recompute
14
+ * (#3322). */
15
+ _valueTransition: Transition | null;
9
16
  }
10
17
  /**
11
18
  * Effects are the leaf nodes of our reactive graph. When their sources change, they are
@@ -9,5 +9,6 @@ export type { Computed, Disposable, FirewallSignal, Link, Owner, Root, Signal, N
9
9
  export { effect, trackedEffect, type Effect, type TrackedEffect } from "./effect.js";
10
10
  export { action } from "./action.js";
11
11
  export { flush, Queue, GlobalQueue, enforceLoadingBoundary, resetErrorHalt, type IQueue, type QueueCallback } from "./scheduler.js";
12
- export { DEV, type Dev, type DevHooks, type DiagnosticCapture, type DiagnosticCode, type DiagnosticEvent, type DiagnosticKind, type Diagnostics, type DiagnosticSeverity } from "./dev.js";
12
+ export type { AttributionHooks, InteractionRef, NavigationRef, OriginRef } from "./attribution-hooks.js";
13
+ export { DEV, OBSERVE, type AttributionSlot, type Dev, type Observe, type DevHooks, type DiagnosticCapture, type DiagnosticCode, type DiagnosticEvent, type DiagnosticKind, type DiagnosticListener, type Diagnostics, type DiagnosticSeverity, type DiagnosticSubject } from "./dev.js";
13
14
  export * from "./constants.js";
@@ -26,12 +26,19 @@ export declare function getOrCreateLane(signal: Signal<any>): OptimisticLane;
26
26
  export declare function findLane(lane: OptimisticLane): OptimisticLane;
27
27
  /**
28
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
29
+ * under it); a transaction's reporter map records the async a render effect
30
30
  * OBSERVED pending with no boundary taking it (INV-3, the one registration
31
31
  * site). A hold needs both — the same rule the transaction itself uses, so a
32
32
  * memo nobody renders, or one a fallback-showing boundary caught, cannot tear
33
33
  * a frame and holds nothing (#3289). An orphan lane has no observation record
34
34
  * and never holds.
35
+ *
36
+ * The observation is looked up per NODE, in whichever live transaction
37
+ * recorded it — not in this lane's transaction. Lanes merge across
38
+ * transactions (#2912: ownership never travels through lanes), so after a
39
+ * merge the root's transaction holds the observations of only one member;
40
+ * the async the other member's transaction observed must hold the merged
41
+ * reveal just the same (A15 for lanes, #3335).
35
42
  */
36
43
  export declare function laneHeld(lane: OptimisticLane): boolean;
37
44
  /**
@@ -8,6 +8,11 @@ export declare const zombieQueue: Heap;
8
8
  export declare let clock: number;
9
9
  export declare let activeTransition: Transition | null;
10
10
  export declare let projectionWriteActive: boolean;
11
+ /** > 0 while an action's generator body is on the stack (the synchronous
12
+ * slice between yields). Maintained by action.ts around `it.next()`. */
13
+ export declare let actionStepDepth: number;
14
+ export declare function enterActionStep(): void;
15
+ export declare function exitActionStep(): void;
11
16
  export declare let _hitUnhandledAsync: boolean;
12
17
  /**
13
18
  * Consume the unhandled-async hit. Returns whether this is the first report
@@ -43,6 +48,11 @@ export interface Transition {
43
48
  _queueStash: QueueStub;
44
49
  _done: boolean | Transition;
45
50
  _gatedSubs: Set<Computed<any>>;
51
+ /** Effects whose single value slot was written under this transaction AND
52
+ * another live one (#3322). Re-dirtied at commit, ahead of the effect
53
+ * phase, so the run publishes a value derived from the committed world
54
+ * rather than whichever transaction's staged view wrote last. */
55
+ _contested: Computed<any>[] | null;
46
56
  }
47
57
  /**
48
58
  * Flip-entanglement (#3164 follow-up): `until()` is a declaration of
@@ -131,6 +141,11 @@ export declare class GlobalQueue extends Queue {
131
141
  static _drainPatchOptimistic: (() => void) | null;
132
142
  static _gatedRead: ((el: Signal<any>, owner: OptimisticNode, c: Computed<any>) => boolean) | null;
133
143
  static _laneSuspends: ((owner: OptimisticNode) => boolean) | null;
144
+ /** Is the node routed through a LIVE lane (`resolveLane`)? read()'s reveal
145
+ * carve-out asks before showing a foreign-held pending node's committed
146
+ * value: a lane-derived flight's inputs are already revealed through the
147
+ * lane (#3334). Gated on CONFIG_HAS_LANE, which only the engine sets. */
148
+ static _laneLive: ((el: Computed<any>) => boolean) | null;
134
149
  static _laneReadsCommitted: ((el: OptimisticNode, owner: OptimisticNode, c: Computed<any>) => boolean) | null;
135
150
  static _recomputeLane: ((el: Computed<any>, own: boolean) => OptimisticLane | null | false) | null;
136
151
  static _laneAsyncPending: ((el: Computed<any>) => void) | null;
@@ -140,6 +155,26 @@ export declare class GlobalQueue extends Queue {
140
155
  * the gate holds (#3303). */
141
156
  static _notifyAuthoritativeObservers: ((el: Signal<any> | Computed<any>) => void) | null;
142
157
  static _laneAsyncSettled: ((el: Computed<any>) => void) | null;
158
+ /** A18 supersession (#3331): own-source truth `value` landed under an active
159
+ * override. The engine decides whether the graph re-derives — the value
160
+ * differs from the override and is not a stale (older-action) answer (mark
161
+ * the node, demote its lane cascade, notify), or returns to it after an
162
+ * earlier differing arrival (clear the mark, notify) — and owns the
163
+ * authoritative-observer wake for a silent confirm. Installed with the
164
+ * optimistic engine; only reachable on a node that has an override. */
165
+ static _supersedeOverride: ((el: Signal<any> | Computed<any>, value: unknown) => void) | null;
166
+ /** read()'s value for a TRACKED reader of a superseded node (#3331): the
167
+ * staged truth, unless the reader is a stale (render) reader of another
168
+ * transaction — then the displayed override, as it keeps a foreign
169
+ * transaction's committed value over its staged write. */
170
+ static _supersededRead: ((el: Signal<any> | Computed<any>) => unknown) | null;
171
+ /** setSignal's authoritative (projection-write) landing on an override-
172
+ * covered node (#3331 store twin): stage the truth for its transaction's
173
+ * commit whatever its relation to the committed value — a landing equal to
174
+ * committed still differs from the override — then _supersedeOverride
175
+ * decides. Installed with the optimistic engine; only reachable on a node
176
+ * that has an override. */
177
+ static _landOnOverride: (<T>(el: Signal<T> | Computed<T>, v: T | ((prev: T) => T)) => T) | null;
143
178
  static _trackOptimisticStore: ((store: any) => void) | null;
144
179
  flush(): void;
145
180
  notify(node: Computed<any>, mask: number, flags: number, error?: any): boolean;
@@ -154,6 +189,21 @@ export declare let reaskArmed: boolean;
154
189
  export declare let notifyEpoch: number;
155
190
  export declare function bumpNotifyEpoch(): void;
156
191
  export declare function armReaskClear(): void;
192
+ /** Provenance of the work currently running (A18 supersession, #3331): the
193
+ * invocation sequence of the action whose ambient window this is — set by
194
+ * action() for each slice; the flush that ends the window clears it — or,
195
+ * inside an async landing, the sequence captured when that flight was
196
+ * registered (asyncWrite sets it for the landing's synchronous propagation,
197
+ * so a sync recompute downstream of the landing — an optimistic wrapper over
198
+ * the async source — derives under the flight's provenance, and flights it
199
+ * registers inherit it). 0 is mainline: no action, always the current
200
+ * question. An override stamps this at its write (`_overrideStamp`); an
201
+ * answer whose flight an OLDER action issued is a stale question the user
202
+ * has since changed — it holds silently to commit instead of superseding. A
203
+ * slow source must not leak back in over a newer intent. Transactions merge,
204
+ * so the transition object cannot say WHICH action asked; this can. */
205
+ export declare let origin: number;
206
+ export declare function setOrigin(seq: number): number;
157
207
  export declare function insertSubs(node: Signal<any> | Computed<any>, optimistic?: boolean): void;
158
208
  export declare let storeCommitHook: (() => void) | null;
159
209
  export declare function setStoreCommitHook(fn: () => void): void;
@@ -220,6 +270,16 @@ export declare function flush<T>(fn: () => T): T;
220
270
  * initTransition; it is a plain batch until then. */
221
271
  export declare function createTransition(): Transition;
222
272
  export declare function currentTransition(transition: Transition): Transition;
273
+ /**
274
+ * The live transition blocked on `source` — the one whose render reader
275
+ * observed it pending (INV-3 records the observation in whichever transaction
276
+ * was active when the reader was notified). The observation is a fact about
277
+ * the node, so a hold check must not assume it was recorded in the transaction
278
+ * it happens to hold — lanes merge across transactions (#2912), and a merged
279
+ * root's transaction knows nothing of the async its members' transactions
280
+ * observed (#3335). Null when nobody is waiting.
281
+ */
282
+ export declare function waitingTransition(source: Computed<any>): Transition | null;
223
283
  export declare function setActiveTransition(transition: Transition | null): void;
224
284
  export declare function runInTransition<T>(transition: Transition, fn: () => T): T;
225
285
  /** Run `fn` with `transition` as BOTH the ambient transaction and the
@@ -68,6 +68,16 @@ export interface NodeExtension {
68
68
  * layer's STORE_OPTIMISTIC_OWNERS stamps (#2899). `null` = ambient write.
69
69
  */
70
70
  _overrideOwner: Transition | null | undefined;
71
+ /** `clock` at the active override's write. A sync recompute in the same
72
+ * tick derives from inputs that predate the override and does not
73
+ * supersede it (A18 supersession ordering, #3331). */
74
+ _overrideTime: number;
75
+ /** Provenance of the active override's write: the scheduler's `origin` (the
76
+ * asking action's invocation sequence; 0 = mainline). An arriving answer
77
+ * whose flight an older action issued asked a question the override has
78
+ * since changed: it holds to commit instead of superseding (A18
79
+ * supersession provenance, #3331). */
80
+ _overrideStamp: number;
71
81
  _optimisticLane: OptimisticLane | undefined;
72
82
  _pendingSignal: Signal<boolean> | undefined;
73
83
  _latestValueComputed: Computed<any> | undefined;
@@ -112,13 +122,19 @@ export interface NodeExtension {
112
122
  export interface RawSignal<T> {
113
123
  _subs: Link | null;
114
124
  _subsTail: Link | null;
125
+ _value: T;
115
126
  /**
116
- * DEV-only live subscriber count. Maintained by `link`/`unlinkSubs` for
117
- * graph-size diagnostics; undefined in production.
127
+ * Observe-tier label (`name` option, or the node kind: `signal`,
128
+ * `computed`, `effect`…). A slot in the observe/dev literals — never a
129
+ * post-construction write — and absent from the prod literals entirely.
118
130
  */
119
- _subCount?: number;
120
- _value: T;
121
131
  _name?: string;
132
+ /**
133
+ * Observe-tier: the owner in scope when a user-facing signal was created
134
+ * (`registerGraph`), so diagnostics about the signal get an owner path.
135
+ * A slot in the observe/dev `signal()` literal; absent from prod.
136
+ */
137
+ _owner?: Owner | null;
122
138
  _equals: false | ((a: T, b: T) => boolean);
123
139
  _config: number;
124
140
  _time: number;
@@ -137,7 +153,10 @@ export interface RawSignal<T> {
137
153
  }
138
154
  export interface FirewallSignal<T> extends RawSignal<T> {
139
155
  _firewall: Computed<any>;
156
+ /** Doubly-linked child chain on the firewall's extension (`_x._child` is
157
+ * the head): released leaves unlink in O(1) (#3351). */
140
158
  _nextChild: FirewallSignal<unknown> | null;
159
+ _prevChild: FirewallSignal<unknown> | null;
141
160
  }
142
161
  export type Signal<T> = RawSignal<T> | FirewallSignal<T>;
143
162
  export interface Owner {
@@ -156,15 +175,16 @@ export interface Owner {
156
175
  _prevSibling: Owner | null;
157
176
  /** Cold extension — see NodeExtension (owners use the zombie-pair slots). */
158
177
  _x: NodeExtension | null;
178
+ /**
179
+ * Observe-tier label: the `name` option, the node kind (`computed`,
180
+ * `effect`…), or the component label the rendering layer writes on a root
181
+ * (`<App>`). A slot in the observe/dev literals; absent from prod.
182
+ */
183
+ _name?: string;
159
184
  }
160
185
  export interface Computed<T> extends RawSignal<T>, Owner {
161
186
  _deps: Link | null;
162
187
  _depsTail: Link | null;
163
- /**
164
- * DEV-only live source count. Maintained by `link`/`unlinkSubs` for
165
- * graph-size diagnostics; undefined in production.
166
- */
167
- _depCount?: number;
168
188
  /** Recompute-pass counter; bumped when dep revalidation starts. */
169
189
  _depGen: number;
170
190
  _flags: number;
@@ -1,7 +1,14 @@
1
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
- import { type Dev } from "./core/index.js";
2
+ import { type Dev, type Observe } from "./core/index.js";
3
+ /**
4
+ * Observe tier (diagnostics channel, attribution hook slot + interaction
5
+ * frame): dev and observe builds. The attribution engine itself is the
6
+ * `@solidjs/signals/attribution` entry.
7
+ */
8
+ export declare const OBSERVE: Observe | undefined;
9
+ /** Dev tier (devtools hooks, graph traversal, console reporting): dev builds only. */
3
10
  export declare const DEV: Dev | undefined;
4
- export type { Owner, Context, ContextRecord, IQueue, ExternalSourceFactory, ExternalSource, ExternalSourceConfig, Refreshable, Dev, DevHooks, DiagnosticCapture, DiagnosticCode, DiagnosticEvent, DiagnosticKind, Diagnostics, DiagnosticSeverity } from "./core/index.js";
11
+ export type { Owner, Context, ContextRecord, IQueue, ExternalSourceFactory, ExternalSource, ExternalSourceConfig, Refreshable, AttributionHooks, AttributionSlot, InteractionRef, NavigationRef, OriginRef, Dev, Observe, DevHooks, DiagnosticCapture, DiagnosticCode, DiagnosticEvent, DiagnosticKind, DiagnosticListener, Diagnostics, DiagnosticSeverity, DiagnosticSubject } from "./core/index.js";
5
12
  export { createSignal, createMemo, createEffect, createRenderEffect, createTrackedEffect, createReaction, createOptimistic, refresh, resolve, until, onSettled, onCleanup } from "./signals.js";
6
13
  export type { Truthy, UntilOptions, Accessor, SourceAccessor, Setter, Signal, ComputeFunction, EffectFunction, EffectBundle, EffectOptions, SignalOptions, MemoOptions, NoInfer } from "./signals.js";
7
14
  export { affects } from "./affects.js";
@@ -47,6 +47,8 @@ export declare function mapArray<Item, MappedItem>(list: Accessor<Maybe<readonly
47
47
  fallback?: Accessor<any>;
48
48
  name?: string;
49
49
  }): Accessor<MappedItem[]>;
50
+ /** @internal */
51
+ export declare function __smallMoveHits(): number;
50
52
  /**
51
53
  * Reactively renders a callback `count` times, reusing previously-rendered
52
54
  * entries when only the count changes. Underlying helper for `<Repeat>`.
@@ -1,6 +1,7 @@
1
1
  export type { Store, StoreReturn, ProjectionStoreReturn, StoreSetter, StoreNode, StoreOptions, ProjectionOptions, NotWrappable, SolidStore } from "./store.js";
2
2
  export type { Merge, Omit } from "./utils.js";
3
3
  export { isWrappable, $TRACK, $PROXY, $TARGET } from "./store.js";
4
+ export { mergeSources } from "./utils.js";
4
5
  import type { NoFn, ProjectionOptions, Store, StoreOptions, StoreSetter } from "./store.js";
5
6
  import type { Refreshable } from "../core/index.js";
6
7
  export { createProjectionNext as createProjection } from "./next/projection.js";
@@ -15,6 +15,11 @@ export declare function bumpDeep(t: StoreNextTarget): void;
15
15
  * the clone path. Consumers that need a complete container (reconcile's
16
16
  * diff walks, drafts escaping into other storage) call this. */
17
17
  export declare function materializePB(target: StoreNextTarget): void;
18
+ /** Resolve the held committed view (#3074): answers the masked old backing
19
+ * while the hold is live, and lazily clears a hold whose transition has
20
+ * committed (transitions merge — resolve through currentTransition, same as
21
+ * foldHeld's node stamps). */
22
+ export declare function heldMaskView(t: StoreNextTarget): Record<PropertyKey, any> | null;
18
23
  /**
19
24
  * Adoption (2026-08-16c): the incoming object becomes the committed backing
20
25
  * IMMEDIATELY — reconcile is eagerly visible to every reader (shipped
@@ -97,9 +97,17 @@ export interface StoreNextTarget {
97
97
  * this target (first-read scan, defineProperty, or clone scan). Gates the
98
98
  * fold diff's descriptor-safe path and the get trap's descriptor path. */
99
99
  a: boolean;
100
- /** Accessor scan performed (scan-once on first trap read; adopted data is
101
- * not rescanned legacy-parity behavior). */
102
- sc: boolean;
100
+ /** Accessor scan grade: 0 = not yet scanned (adoption resets — adopted data
101
+ * is not rescanned until the next draft), 1 = scanned, 2 = scanned and
102
+ * PLAIN DATA — `Object.prototype` with every own key an enumerable data
103
+ * property. Grade 2 unlocks the spread clone (cloneRaw), bare-assignment
104
+ * overlay writes and flatten (#3360); a non-plain defineProperty through
105
+ * the draft downgrades it to 1. */
106
+ sc: 0 | 1 | 2;
107
+ /** Own-key count: exact at scan, then bumped by set-trap writes of keys new
108
+ * to the container (never decremented — an estimate for the overlay/clone
109
+ * choice only, #3360). */
110
+ kc: number;
103
111
  /** Adoption diff base, non-null when the backing was swapped by adoption
104
112
  * this batch: the view the nodes were LAST TOLD — the pre-batch committed
105
113
  * backing, or the draft's pending backing when a draft preceded the
@@ -110,7 +118,9 @@ export interface StoreNextTarget {
110
118
  * (`Object.create(v)` — own keys are this batch's writes, everything else
111
119
  * reads through). O(written) per flush instead of O(container) clones
112
120
  * (#3044); commit flattens own keys onto an owned committed backing in
113
- * place. Only plain-data non-array non-family containers qualify;
121
+ * place. Plain-data non-array containers qualify, including projection
122
+ * and derived-store families (#3352); optimistic families, chained
123
+ * backings, and accessor containers keep the descriptor clone.
114
124
  * `materializePB` downgrades to the clone path when a consumer needs a
115
125
  * real container (reconcile, draft escape). */
116
126
  ovl: boolean;
@@ -134,13 +144,30 @@ export interface StoreNextTarget {
134
144
  ht: any;
135
145
  }
136
146
  /**
137
- * Ownership (first cut, decision 2026-08-16d): one WeakSet of store-owned
138
- * backings serving both the production identity-skip guard and the __TEST__
139
- * no-mutation oracle.
147
+ * Ownership stamp (#3360): every backing the store ALLOCATES (CoW clones,
148
+ * privatized committed backings) carries its owning target under this
149
+ * enumerable symbol. One property write replaces the two weak-collection
150
+ * registrations (ownership set + raw→target map) a fresh object used to pay
151
+ * per draft — V8's identity-hash + ephemeron cost dominated the one-key
152
+ * write floor. Enumerable so a spread copy (the plain-data clone path) stays
153
+ * on the fast path and carries the stamp along.
154
+ *
155
+ * Owned backings are never user-reachable (`snapshot` copies them, the traps
156
+ * hide the key), so every raw key walk in the store must skip `$OWNER`, and
157
+ * ownership is answered by `isOwned` — a user object never carries it.
158
+ * Overlay drafts (`Object.create(v)` over an owned `v`) inherit the stamp.
140
159
  */
141
- export declare const ownedRaw: WeakSet<object>;
142
- /** raw → target. The only raw-keyed lookup; boundary mechanism (O8). */
160
+ export declare const $OWNER: unique symbol;
161
+ /** raw → target for UNOWNED backings (user-ingested, adopted); owned
162
+ * backings resolve through their `$OWNER` stamp. Boundary mechanism (O8). */
143
163
  export declare const storeNextLookup: WeakMap<object, StoreNextTarget>;
164
+ /** A backing the store allocated and may mutate in place. */
165
+ export declare function isOwned(raw: object): boolean;
166
+ /** raw → target within a family (`null` = plain stores / the global map).
167
+ * The stamp answers for backings owned by a target OF THAT FAMILY; anything
168
+ * else (user objects, adoptees, another family's backings the family
169
+ * re-registered for its own wrapper) resolves through the family's map. */
170
+ export declare function lookupTarget(raw: object, fam: StoreNextFamily | null): StoreNextTarget | undefined;
144
171
  /** __TEST__ oracle: every object ingested from a user (never mutate). */
145
172
  export declare const ingestedRaw: WeakSet<object> | null;
146
173
  export declare function devAssertNeverUserMutation(target: object): void;
@@ -21,6 +21,13 @@ type _Merge<T extends unknown[], Curr = {}> = T extends [
21
21
  ...infer Rest
22
22
  ] ? _Merge<Rest, Override<Curr, Next>> : T extends [...infer Rest, infer Next | (() => infer Next)] ? Override<_Merge<Rest, Curr>, Next> : T extends [] ? Curr : T extends (infer I | (() => infer I))[] ? OverrideSpread<Curr, I> : Curr;
23
23
  export type Merge<T extends unknown[]> = Simplify<_Merge<T>>;
24
+ /** @internal The flattened sources behind a `merge()` PROXY, or undefined.
25
+ * Only the proxy form: its writes are no-ops, so the sources are the whole
26
+ * truth. merge()'s plain-object form also records `$SOURCES` (so nested
27
+ * merges flatten), but it is a real object callers may mutate afterwards
28
+ * (html's tagged templates assign props after spreading) — those own writes
29
+ * live on the object, not in the sources, so it must be read directly. */
30
+ export declare function mergeSources(o: any): any[] | undefined;
24
31
  /**
25
32
  * Merges multiple props-like objects into a single proxy that *preserves
26
33
  * reactivity*. Reads are forwarded to the right-most source that defines the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidjs/signals",
3
- "version": "2.0.0-rc.7",
3
+ "version": "2.0.0-rc.8",
4
4
  "description": "Solid's reactive primitives: signals, memos, effects, stores, and async-aware computations.",
5
5
  "author": "Ryan Carniato",
6
6
  "license": "MIT",
@@ -13,12 +13,15 @@
13
13
  "publishConfig": {
14
14
  "access": "public"
15
15
  },
16
- "main": "./dist/node.cjs",
16
+ "main": "./dist/prod/index.js",
17
17
  "module": "./dist/prod/index.js",
18
18
  "unpkg": "./dist/prod/index.js",
19
19
  "jsdelivr": "./dist/prod/index.js",
20
20
  "types": "./dist/types/index.d.ts",
21
21
  "type": "module",
22
+ "engines": {
23
+ "node": ">=22.12.0"
24
+ },
22
25
  "sideEffects": false,
23
26
  "files": [
24
27
  "dist",
@@ -26,25 +29,26 @@
26
29
  ],
27
30
  "exports": {
28
31
  ".": {
29
- "import": {
30
- "types": "./dist/types/index.d.ts",
31
- "test": "./dist/dev.js",
32
- "development": "./dist/dev.js",
33
- "default": "./dist/prod/index.js"
34
- },
35
- "require": {
36
- "types": "./dist/types-cjs/index.d.cts",
37
- "development": "./dist/node.dev.cjs",
38
- "default": "./dist/node.cjs"
39
- }
32
+ "types": "./dist/types/index.d.ts",
33
+ "test": "./dist/dev.js",
34
+ "development": "./dist/dev.js",
35
+ "observe": "./dist/observe/index.js",
36
+ "default": "./dist/prod/index.js"
37
+ },
38
+ "./attribution": {
39
+ "types": "./dist/types/attribution.d.ts",
40
+ "test": "./dist/dev.attribution.js",
41
+ "development": "./dist/dev.attribution.js",
42
+ "observe": "./dist/observe/attribution.js",
43
+ "default": "./dist/prod/attribution.js"
40
44
  },
41
45
  "./package.json": "./package.json"
42
46
  },
43
47
  "scripts": {
44
48
  "build": "npm-run-all -nl build:* && pnpm types",
45
- "build:clean": "rimraf dist/dev dist/prod dist/node dist/dev.js dist/prod.js dist/node.cjs dist/node.dev.cjs",
46
- "build:js": "rollup -c && node ./scripts/mangle-props.mjs dist/prod dist/node.cjs && node ./scripts/check-pure.mjs dist/prod",
47
- "types": "tsc -p tsconfig.build.json && node ../../scripts/sync-dual-types.mjs ./dist/types ./dist/types-cjs",
49
+ "build:clean": "rimraf dist/dev dist/prod dist/observe dist/*.js",
50
+ "build:js": "rollup -c && node ./scripts/mangle-props.mjs dist/prod dist/observe && node ./scripts/check-pure.mjs dist/prod && node ./scripts/check-pure.mjs dist/observe",
51
+ "types": "tsc -p tsconfig.build.json",
48
52
  "test": "vitest run",
49
53
  "test:watch": "vitest watch tests",
50
54
  "test:gc": "node --expose-gc ./vitest.js",
@@ -67,4 +71,4 @@
67
71
  "vite": "^7.0.0",
68
72
  "vitest": "^4.1.6"
69
73
  }
70
- }
74
+ }