@solidjs/signals 2.0.0-rc.13 → 2.0.0-rc.14

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 (118) hide show
  1. package/dist/dev-shared.js +2000 -3768
  2. package/dist/dev.attribution.js +132 -79
  3. package/dist/dev.js +7226 -8488
  4. package/dist/observe/affects.js +125 -104
  5. package/dist/observe/boundaries.js +458 -665
  6. package/dist/observe/core/action.js +63 -66
  7. package/dist/observe/core/async.js +240 -383
  8. package/dist/observe/core/attribution-queries.js +1 -1
  9. package/dist/observe/core/attribution.js +240 -183
  10. package/dist/observe/core/constants.js +115 -187
  11. package/dist/observe/core/context.js +3 -3
  12. package/dist/observe/core/core.js +840 -1438
  13. package/dist/observe/core/dev.js +3 -3
  14. package/dist/observe/core/effect.js +75 -86
  15. package/dist/observe/core/external.js +5 -5
  16. package/dist/observe/core/graph.js +61 -70
  17. package/dist/observe/core/heap.js +75 -98
  18. package/dist/observe/core/invariants.js +1 -34
  19. package/dist/observe/core/lanes.js +704 -181
  20. package/dist/observe/core/owner.js +80 -101
  21. package/dist/observe/core/scheduler.js +708 -1351
  22. package/dist/observe/core/verdict.js +302 -621
  23. package/dist/observe/flatten.js +79 -0
  24. package/dist/observe/index.js +15 -13
  25. package/dist/observe/map.js +232 -235
  26. package/dist/observe/reveal.js +212 -0
  27. package/dist/observe/signals.js +133 -215
  28. package/dist/observe/store/affects.js +135 -0
  29. package/dist/observe/store/index.js +29 -24
  30. package/dist/observe/store/optimistic.js +538 -0
  31. package/dist/observe/store/projection.js +302 -0
  32. package/dist/observe/store/reconcile.js +257 -0
  33. package/dist/observe/store/store.js +1945 -277
  34. package/dist/observe/store/storePath.js +1 -1
  35. package/dist/{prod/store/next → observe/store}/target.js +17 -18
  36. package/dist/observe/store/types.js +73 -0
  37. package/dist/observe/store/utils.js +1 -13
  38. package/dist/prod/affects.js +127 -106
  39. package/dist/prod/boundaries.js +442 -654
  40. package/dist/prod/core/action.js +71 -74
  41. package/dist/prod/core/async.js +307 -442
  42. package/dist/prod/core/constants.js +112 -186
  43. package/dist/prod/core/context.js +3 -3
  44. package/dist/prod/core/core.js +852 -1458
  45. package/dist/prod/core/effect.js +88 -99
  46. package/dist/prod/core/external.js +5 -5
  47. package/dist/prod/core/graph.js +61 -70
  48. package/dist/prod/core/heap.js +75 -98
  49. package/dist/prod/core/invariants.js +1 -32
  50. package/dist/prod/core/lanes.js +666 -181
  51. package/dist/prod/core/owner.js +75 -96
  52. package/dist/prod/core/scheduler.js +677 -1332
  53. package/dist/prod/core/verdict.js +297 -621
  54. package/dist/prod/flatten.js +77 -0
  55. package/dist/prod/index.js +15 -13
  56. package/dist/prod/map.js +143 -146
  57. package/dist/prod/reveal.js +212 -0
  58. package/dist/prod/signals.js +144 -226
  59. package/dist/prod/store/affects.js +135 -0
  60. package/dist/prod/store/index.js +28 -19
  61. package/dist/prod/store/optimistic.js +533 -0
  62. package/dist/prod/store/projection.js +295 -0
  63. package/dist/prod/store/reconcile.js +257 -0
  64. package/dist/prod/store/store.js +1840 -272
  65. package/dist/prod/store/storePath.js +1 -1
  66. package/dist/{observe/store/next → prod/store}/target.js +13 -17
  67. package/dist/prod/store/types.js +73 -0
  68. package/dist/prod/store/utils.js +1 -11
  69. package/dist/types/affects.d.ts +11 -30
  70. package/dist/types/boundaries.d.ts +62 -176
  71. package/dist/types/carved.d.ts +1 -0
  72. package/dist/types/core/action.d.ts +5 -6
  73. package/dist/types/core/async.d.ts +10 -5
  74. package/dist/types/core/attribution-hooks.d.ts +20 -1
  75. package/dist/types/core/attribution.d.ts +20 -9
  76. package/dist/types/core/constants.d.ts +125 -180
  77. package/dist/types/core/core.d.ts +104 -264
  78. package/dist/types/core/dev.d.ts +2 -2
  79. package/dist/types/core/effect.d.ts +0 -7
  80. package/dist/types/core/graph.d.ts +7 -1
  81. package/dist/types/core/heap.d.ts +5 -7
  82. package/dist/types/core/index.d.ts +5 -5
  83. package/dist/types/core/invariants.d.ts +0 -29
  84. package/dist/types/core/lanes.d.ts +65 -87
  85. package/dist/types/core/owner.d.ts +3 -3
  86. package/dist/types/core/scheduler.d.ts +249 -331
  87. package/dist/types/core/types.d.ts +51 -85
  88. package/dist/types/core/verdict.d.ts +3 -0
  89. package/dist/types/flatten.d.ts +8 -0
  90. package/dist/types/index.d.ts +6 -4
  91. package/dist/types/reveal.d.ts +19 -0
  92. package/dist/types/signals.d.ts +35 -91
  93. package/dist/types/store/affects.d.ts +1 -0
  94. package/dist/types/store/index.d.ts +35 -15
  95. package/dist/types/store/optimistic.d.ts +23 -0
  96. package/dist/types/store/projection.d.ts +18 -0
  97. package/dist/types/store/reconcile.d.ts +3 -0
  98. package/dist/types/store/store.d.ts +165 -143
  99. package/dist/types/store/storePath.d.ts +1 -1
  100. package/dist/types/store/target.d.ts +151 -0
  101. package/dist/types/store/types.d.ts +84 -0
  102. package/package.json +1 -1
  103. package/dist/observe/core/optimistic.js +0 -669
  104. package/dist/observe/store/next/optimistic.js +0 -717
  105. package/dist/observe/store/next/projection.js +0 -266
  106. package/dist/observe/store/next/reconcile.js +0 -375
  107. package/dist/observe/store/next/store.js +0 -2608
  108. package/dist/prod/core/optimistic.js +0 -658
  109. package/dist/prod/store/next/optimistic.js +0 -710
  110. package/dist/prod/store/next/projection.js +0 -257
  111. package/dist/prod/store/next/reconcile.js +0 -373
  112. package/dist/prod/store/next/store.js +0 -2484
  113. package/dist/types/core/optimistic.d.ts +0 -6
  114. package/dist/types/store/next/optimistic.d.ts +0 -25
  115. package/dist/types/store/next/projection.d.ts +0 -27
  116. package/dist/types/store/next/reconcile.d.ts +0 -10
  117. package/dist/types/store/next/store.d.ts +0 -145
  118. package/dist/types/store/next/target.d.ts +0 -196
@@ -1,147 +1,169 @@
1
- import { type Signal } from "../core/index.js";
2
- import type { Refreshable } from "../core/index.js";
3
- /** A reactive view of a store's value. Update it through the paired `StoreSetter`. */
4
- export type Store<T> = T;
5
- /**
6
- * A store setter. The callback receives a writable **draft** of the store.
7
- *
8
- * - **Mutate in place (canonical):** `s.foo = 1`, `s.list.push(x)`,
9
- * `s.list.splice(i, 1)`. This is the default form for most updates.
10
- * - **Return a new value:** for shapes where mutation is awkward, most
11
- * commonly removing items (`s => s.list.filter(...)`). Arrays are replaced
12
- * by index (length adjusted); objects are shallow-diffed at the top level
13
- * (keys present in the returned value are written, missing keys deleted).
14
- *
15
- * The setter does **not** perform keyed reconciliation. If you need surviving
16
- * items to keep their store identity across full-array replacement, use the
17
- * projection form — `createStore(fn, seed, { key })` or
18
- * `createProjection(fn, seed, { key })` — whose derive function reconciles
19
- * its return by `options.key`.
20
- */
21
- export type StoreSetter<T> = (fn: (state: T) => T | void) => void;
22
- /** Tuple returned by the plain `createStore(initialValue, options?)` form. */
23
- export type StoreReturn<T> = [get: Store<T>, set: StoreSetter<T>];
24
- /** Tuple returned by the derived `createStore(fn, seed, options?)` form. */
25
- export type ProjectionStoreReturn<T> = [get: Refreshable<Store<T>>, set: StoreSetter<T>];
26
- /** Options shared by all store primitives. */
27
- export interface StoreOptions {
28
- /**
29
- * Debug name (dev and observe builds). Property nodes are labelled
30
- * `<name>.<key>` in attribution output (`todos.title`); a derived store's
31
- * projection node carries the name itself.
32
- */
33
- name?: string;
34
- /** Single-layer store: root keys reactive, values raw records replaced by reference */
35
- shallow?: boolean;
1
+ import type { Signal } from "../core/types.js";
2
+ import { type StoreFamily, type StoreTarget } from "./target.js";
3
+ /** The optimistic machinery (optimistic.ts, S4), installed when it loads
4
+ * — a plain store pays nothing for it (#2883): the plain paths reach it
5
+ * only through a family with `opt` set, which only `createOptimisticStore`
6
+ * sets. */
7
+ export interface OptHooks {
8
+ /** A user setter's draft on an optimistic family: a clone of the
9
+ * writer's view (a staging already on the target is set aside). */
10
+ draft(t: StoreTarget): Record<PropertyKey, any>;
11
+ /** The setter's exit: the draft becomes guesses; returns the staging the
12
+ * draft set aside (`null`: none). */
13
+ writes(t: StoreTarget, pb: Record<PropertyKey, any>): Record<PropertyKey, any> | null;
14
+ /** `src` with the lanes' values over it (`writer`: the next write's base
15
+ * — the unflushed guesses too). */
16
+ view(t: StoreTarget, src: Record<PropertyKey, any>, writer?: boolean): Record<PropertyKey, any>;
17
+ /** An untracked `key in store`: the presence guess, or `undefined`. */
18
+ has(t: StoreTarget, key: PropertyKey): boolean | undefined;
19
+ /** A guessed key's descriptor (`null`: removed; `undefined`: no guess). */
20
+ descriptor(t: StoreTarget, key: PropertyKey): PropertyDescriptor | null | undefined;
21
+ /** The flush's commit: targets no lane holds leave `overlaid`. */
22
+ sweep(): void;
23
+ /** The container comparator's lane arm: an arrangement guess against a
24
+ * landing (or another arrangement), by row identity. */
25
+ arrangement(t: StoreTarget, a: any, b: any): boolean;
26
+ /** A user's `reconcile` on an optimistic family: the keyed diff written
27
+ * into the draft. */
28
+ reconcile(draft: any, incoming: any, keyFn: ((item: any) => any) | null): void;
36
29
  }
37
- /**
38
- * Options for derived/projected stores created with
39
- * `createStore(fn, seed, options?)`, `createProjection(fn, seed, options?)`,
40
- * or `createOptimisticStore(fn, seed, options?)`.
41
- */
42
- export interface ProjectionOptions extends StoreOptions {
43
- /** Key property name or function for reconciliation identity; `null` merges positionally */
44
- key?: string | ((item: NonNullable<any>) => any) | null;
45
- /**
46
- * Treat the seed as commit #0: the store is born committed with the seed's
47
- * contents, shown until the derive's first real answer lands. While that
48
- * first answer is in flight, reads serve the seed everywhere — nothing
49
- * suspends to a `<Loading>` boundary, no transition is held, and
50
- * `isPending` stays false (the seed answers by declaration; first-load
51
- * affordances belong to the data, e.g. a `skeleton: true` field in the
52
- * seed). Once the first answer lands (reconciled into the seed), refetches
53
- * use normal pending semantics with `isPending` true.
54
- *
55
- * The store equivalent of `MemoOptions.loadingValue`; the seed already
56
- * carries the placeholder shape, so this is just the opt-in.
57
- */
58
- seedLoadingValue?: boolean;
30
+ export declare let optHooks: OptHooks | null;
31
+ export declare function installOptHooks(hooks: OptHooks): void;
32
+ /** The store half of `affects()` (store/affects.ts), installed with the
33
+ * stores: a node born on a covered record inherits the live mark; an
34
+ * untracked verdict probe through a record with no node is witnessed. */
35
+ export interface AffectsHooks {
36
+ born(t: StoreTarget, node: Signal<any>, key: PropertyKey): void;
37
+ witness(t: StoreTarget, key: PropertyKey | undefined): void;
59
38
  }
60
- export type NoFn<T> = T extends Function ? never : T;
61
- type DataNode = Signal<any>;
62
- type DataNodes = Record<PropertyKey, DataNode>;
63
- /**
64
- * Brand symbols used internally by the store proxy / projection plumbing.
65
- * Cross-package wiring; not part of the user-facing API.
66
- *
67
- * @internal
68
- */
69
- export declare const $TRACK: unique symbol, $TARGET: unique symbol, $PROXY: unique symbol, $RECORD: unique symbol, $DELETED: unique symbol, $AFFECTS: unique symbol;
70
- export declare const STORE_VALUE = "v", STORE_NODE = "n", STORE_HAS = "h", STORE_PARENT = "u", STORE_DESC = "d", STORE_SHALLOW = "s";
71
- /** Structural view of a store target as shared machinery sees it (the real
72
- * shape is `StoreNextTarget` in ./next/target.ts). */
73
- export type StoreNode = {
74
- [$PROXY]: any;
75
- [STORE_VALUE]: Record<PropertyKey, any>;
76
- [STORE_NODE]?: DataNodes;
77
- [STORE_HAS]?: DataNodes;
78
- [STORE_PARENT]?: StoreNode;
79
- [STORE_SHALLOW]?: boolean;
80
- [STORE_DESC]?: boolean;
81
- };
82
- export declare namespace SolidStore {
83
- interface Unwrappable {
84
- }
39
+ export declare let affectsHooks: AffectsHooks | null;
40
+ export declare function installAffectsHooks(hooks: AffectsHooks): void;
41
+ import { type StoreOptions } from "./types.js";
42
+ export { $PROXY, $TARGET, $TRACK, markRaw } from "./types.js";
43
+ export type SetStoreFunction<T> = (fn: (draft: T) => T | void) => void;
44
+ export declare function wrap<T extends Record<PropertyKey, any>>(value: T, parent?: StoreTarget | null, parentKey?: PropertyKey | null, fam?: StoreFamily | null): T;
45
+ /** Unwrap our own proxies to their current backing; leave everything else.
46
+ * A projection draft wrapper is registered as its target's alias
47
+ * (projection.ts `wrapDraft`) — adopted as a backing, it would forward to
48
+ * itself (#3767). */
49
+ export declare function unwrapValue(v: any): any;
50
+ export declare function sameKey(a: any, b: any): boolean;
51
+ /** The container node's arrangement guess: the rows (or keys) the setter
52
+ * left the container with, in order. Its comparator judges a landing or a
53
+ * revert against it by row identity — by key when the family has one
54
+ * (the server's object for a guessed row is the same row). */
55
+ export declare class LaneView {
56
+ rows: Record<PropertyKey, any>;
57
+ /** The backing the guess is shown over: the committed one at the
58
+ * guess, the composed arrangement after a re-base (optimistic.ts). */
59
+ base: Record<PropertyKey, any>;
60
+ /** Keys the setter REMOVED from the view it saw (a deliberate delete,
61
+ * not a row the truth has yet to show): a re-base does not restore
62
+ * them. `null` when none. */
63
+ removed: Set<unknown> | null;
64
+ constructor(rows: Record<PropertyKey, any>,
65
+ /** The backing the guess is shown over: the committed one at the
66
+ * guess, the composed arrangement after a re-base (optimistic.ts). */
67
+ base: Record<PropertyKey, any>,
68
+ /** Keys the setter REMOVED from the view it saw (a deliberate delete,
69
+ * not a row the truth has yet to show): a re-base does not restore
70
+ * them. `null` when none. */
71
+ removed?: Set<unknown> | null);
85
72
  }
86
- export type NotWrappable = string | number | bigint | symbol | boolean | Function | null | undefined | SolidStore.Unwrappable[keyof SolidStore.Unwrappable];
87
- /**
88
- * Marks a value as raw: no store will ever wrap it — every store presents it
89
- * as-is, tracked by reference at whatever slot holds it and updated by
90
- * replacement. Useful for class instances and external objects (editors,
91
- * scene graphs, Maps) and for record-shaped data updated wholesale. Sticky
92
- * for the value's lifetime.
93
- */
94
- export declare let rawValuesUsed: boolean;
95
- export declare function isRawValue(value: any): boolean;
96
- export declare function markRaw<T>(value: T): T;
97
- export declare function markRawOne(v: any): void;
98
- export declare function markRawIngest(container: any): void;
99
- export declare function isWrappable<T>(obj: T | NotWrappable): obj is T;
100
- export declare function setWriteOverride(value: boolean): void;
101
- export declare function getWriteOverride(): boolean;
102
- export declare function ownEnumerableKeys(o: object): (string | symbol)[];
103
- /**
104
- * Scope inheritance for late-created nodes: every live mark whose identity
105
- * scope contains the owning record's raw — and, for keyed marks, whose key
106
- * is this property — gets counted on the new node. Inherited marks live
107
- * exactly as long as the scope's carrier — the release hook below drops
108
- * them with the entry.
109
- */
110
- export declare function inheritAffectsMarks(node: DataNode, raw: object, property: PropertyKey): void;
111
- /** Next-store node factory for affects carriers/slots: injected by the
112
- * rewrite module (next targets alias the legacy field names, so everything
113
- * here EXCEPT node creation works on them structurally). */
114
- export declare let nextAffectsNodeResolver: ((target: any, key: PropertyKey) => DataNode) | null;
115
- export declare function setNextAffectsNodeResolver(fn: (target: any, key: PropertyKey) => DataNode): void;
116
- /** Next-store optimistic view for the declaration walk (optimistic rows
117
- * pushed before the declaration are in motion too — legacy reads its write
118
- * overlays; next composes armed-node overrides). */
119
- export declare let nextOptimisticViewResolver: ((target: any, raw: any) => any) | null;
120
- export declare function setNextOptimisticViewResolver(fn: (target: any, raw: any) => any): void;
121
- /** @internal birth inheritance for nodes created inside a live mark window —
122
- * exported for the rewrite's node factories. */
123
- export declare function affectsScopesLive(): boolean;
124
- /**
125
- * Witness live mark coverage of a record into the active isPending() probe.
126
- * Tracked reads don't need this — they go through real signal nodes, which
127
- * carry marks directly (declaration walk or birth inheritance). This covers
128
- * UNTRACKED probes reading through records whose nodes never materialized
129
- * (no observer ever subscribed, so no node exists to carry the mark).
130
- * Callers guard on `pendingCheckActive`, so plain reads never pay for this.
131
- *
132
- * @internal
133
- */
134
- export declare function witnessAffectsMark(target: StoreNode, property?: PropertyKey): void;
135
- /**
136
- * Resolves the store nodes an `affects()` declaration marks: with a `key`,
137
- * the named slot's leaf node (upserted so the mark has an addressable
138
- * carrier); without, the record's $AFFECTS carrier plus every LIVE node in
139
- * its subtree (the edges existing readers subscribed through), with the
140
- * subtree's identities snapshotted into the mark's scope so nodes created
141
- * during the window — and untracked probes over captured proxies — resolve
142
- * against it (#2882).
73
+ /** A container node's value as a backing: the base an arrangement guess is
74
+ * shown over, else the value itself. */
75
+ export declare function asBacking(v: any): Record<PropertyKey, any>;
76
+ /** The container node's comparator: a staging is a backing (identity — a
77
+ * pending backing is never "equal" to the committed one); a lane value is
78
+ * an arrangement (`LaneView`), judged by the optimistic machinery. */
79
+ export declare const containerEquals: (this: any, a: any, b: any) => boolean;
80
+ export declare function getNode(target: StoreTarget, key: PropertyKey, accKnown?: -1 | 0 | 1): Signal<any>;
81
+ export declare function getHasNode(target: StoreTarget, key: PropertyKey): Signal<boolean>;
82
+ /** The container node (plan sec. 31.3): created by the first structural subscription
83
+ * or the first write that needs to stage. Its committed value is the
84
+ * committed backing. */
85
+ export declare function getContainerNode(target: StoreTarget): Signal<any>;
86
+ export declare function bumpDeep(t: StoreTarget): void;
87
+ export declare function nameStore(proxy: any, name: string | undefined): void;
88
+ /** Shallow clone into store ownership. Plain data (grade 2) spreads; anything
89
+ * else copies descriptors (accessors kept, everything made configurable). */
90
+ export declare function cloneRaw(source: Record<PropertyKey, any>, t: StoreTarget): Record<PropertyKey, any>;
91
+ /** Downgrade an overlay to a real clone (a consumer needs a container). */
92
+ export declare function materializePB(target: StoreTarget): Record<PropertyKey, any>;
93
+ /** Adoption (a setter's returned replacement; reconcile and projection
94
+ * landings in S3): the incoming object becomes the backing by reference at
95
+ * the fold — no clones, no user-object mutation, ownership resets to
96
+ * shared. It is the container's staging until then (signal parity: a
97
+ * handler reads the committed frame until the flush; a hold keeps it
98
+ * staged with the rest). The nodes are told now, diffed against the view
99
+ * they were LAST told (#3296): the draft's pending backing when one
100
+ * preceded the adoption this batch — its node writes were real, and the
101
+ * adoption's re-stage cancels or confirms each — else the committed
102
+ * backing. */
103
+ export declare function adoptPB(target: StoreTarget, incoming: Record<PropertyKey, any>, notify?: boolean): void;
104
+ export declare function runDirect(fn: () => void): void;
105
+ /** Is the open setter a user's? (reconcile.ts: a user's reconcile on an
106
+ * optimistic family writes the draft.) */
107
+ export declare function userWriting(): boolean;
108
+ /** Two raws that wrap to the same target are the same logical value. */
109
+ export declare function targetsEqual(ov: any, nv: any): boolean;
110
+ /** Positional identity IS content for arrays (R15). */
111
+ export declare function arrayStructureChanged(old: any[], neu: any[]): boolean;
112
+ export declare function membershipChanged(old: Record<PropertyKey, any>, neu: Record<PropertyKey, any>): boolean;
113
+ /** One leaf's notification for an adoption diff (descriptor-aware). */
114
+ export declare function notifyKeyDiff(node: Signal<any>, key: PropertyKey, old: Record<PropertyKey, any>, neu: Record<PropertyKey, any>, probe?: boolean): void;
115
+ export declare function hasAccessorFlag(node: Signal<any>): boolean;
116
+ /** One leaf's notification when the caller already has both values. */
117
+ export declare function notifyKeyValue(node: Signal<any>, key: PropertyKey, ov: any, nv: any, old: Record<PropertyKey, any>, neu: Record<PropertyKey, any>): void;
118
+ /** The structural half of an adoption's notifications (reconcile does the
119
+ * leaves itself): presence by `in`, the container by membership. Presence
120
+ * is DIFFED `old` → `neu` like the leaves (#3743): a presence node is
121
+ * written only where `in` changed between the view it was last told
122
+ * (#3296) and the adoption — `setSignal` joins a held node's transaction
123
+ * before its equality gate (A34 (1)), so repeating an unchanged absence to
124
+ * a node an action holds would make the whole tick the action's (an
125
+ * unrelated `a.value` stayed stale until the action settled). A real
126
+ * change still writes, and on a held node still proposes. A live chained
127
+ * `old` (a store proxy, §7b) reflects the inner store, not what the node
128
+ * was last told — written unconditionally, as before.
143
129
  *
144
- * @internal
145
- */
146
- export declare function getStoreAffectsNodes(target: StoreNode, key?: PropertyKey): DataNode[];
147
- export {};
130
+ * The container is told of an arrangement change when it has structural
131
+ * subscribers OR carries an arrangement guess (S4): for a guessed
132
+ * container the write is the landing that judges the guess (Q-D, plan
133
+ * sec. 39 — confirm, supersede, or hold beneath), not a subscriber
134
+ * notification, and a landing whose truth changed no guessed key's value
135
+ * or presence (a newer question's rows beneath an optimistic push) still
136
+ * answers the arrangement — before, only the presence write reached the
137
+ * lane, by accident of being unconditional. */
138
+ export declare function notifyFoldTail(t: StoreTarget, old: Record<PropertyKey, any>, neu: Record<PropertyKey, any>): void;
139
+ /** An adoption's notifications: every node of the target, diffed `old` →
140
+ * `neu` (value nodes by key, presence by `in`, the container by
141
+ * membership, the deep witness by identity). */
142
+ export declare function notifyFold(t: StoreTarget, old: Record<PropertyKey, any>, neu: Record<PropertyKey, any>): void;
143
+ /** The committed backing: the container node's committed value while a
144
+ * batch is staged on it (an adoption swaps `t.v` eagerly — INTERNALS §3,
145
+ * "adoption is eager by contract" — and the node keeps the frame committed
146
+ * readers under a hold still see), `t.v` otherwise. */
147
+ export declare function committed(t: StoreTarget): Record<PropertyKey, any>;
148
+ /** A chained backing's child (§7b): a raw the inner family owns, or holds
149
+ * at this key, is served as the inner store's wrapper — never a fresh
150
+ * raw-keyed one — so the chained targets are the same objects across
151
+ * settled and pending views. */
152
+ export declare function resolveChainedRaw(target: StoreTarget, key: PropertyKey, v: object): any;
153
+ export declare function storeSetter<T>(proxy: T, fn: (draft: T) => T | void, guard?: boolean): void;
154
+ export declare function createStore<T extends Record<PropertyKey, any>>(initialValue: T, options?: StoreOptions): [T, SetStoreFunction<T>];
155
+ /** True when `proxy` is a SHALLOW store (children served verbatim, slots
156
+ * replaced by reference — #2932). The list driver uses this to choose the
157
+ * slot-patch channel (collected row bodies) over per-record registration. */
158
+ export declare function storeIsShallow(proxy: any): boolean;
159
+ /** True when `proxy` belongs to a projection family (S3). */
160
+ export declare function storeHasFamily(proxy: any): boolean;
161
+ /** True when `proxy` belongs to an OPTIMISTIC family (S4). */
162
+ export declare function storeHasOptimisticFamily(proxy: any): boolean;
163
+ /** Tracking snapshot: subscribes every reachable record's container and deep
164
+ * witness (one node per record, not one per path), then snapshots. */
165
+ export declare function deep<T>(value: T): T;
166
+ /** Non-tracking snapshot: source identity for unowned subtrees (zero copy),
167
+ * a copy for owned ones (they are by definition modified relative to the
168
+ * source). Sees the pending backing (R27). */
169
+ export declare function snapshot<T>(value: T): T;
@@ -1,4 +1,4 @@
1
- import { type NotWrappable } from "./store.js";
1
+ import { type NotWrappable } from "./types.js";
2
2
  type W<T> = Exclude<T, NotWrappable>;
3
3
  type KeyOf<T> = number extends keyof T ? 0 extends 1 & T ? keyof T : [T] extends [never] ? never : [T] extends [readonly unknown[]] ? number : keyof T : keyof T;
4
4
  export type CustomPartial<T> = T extends readonly unknown[] ? "0" extends keyof T ? {
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Store — target & ownership (INTERNALS-STORE-STATE.md §1, §5b; plan sec. 31).
3
+ *
4
+ * The proxy wraps this internal target, never the raw. `v` is the single
5
+ * committed home; the pending backing (`pb`) is the CoW overlay/clone the
6
+ * draft mutates natively — and, on L2, the **container node's staging**
7
+ * (plan sec. 31.3, Q-A): `k` is the `$TRACK` node given a value. Its `_value` is the
8
+ * committed backing, its `_pendingValue` the pending backing, so the
9
+ * scheduler owns the backing's lifetime (park, commit, revert) and the store
10
+ * keeps no fold ledger of its own. The fold at the flush's commit
11
+ * (`_storeCommit`) is the one mutation of owned raw.
12
+ *
13
+ * Creation budget (§5b): one minimal target + one proxy + one lookup entry
14
+ * per read-through object; nodes, presence nodes, the container node and
15
+ * the deep witness are lazy — materialized by subscription, or (the
16
+ * container) by the first write that needs to stage.
17
+ */
18
+ import type { Computed, Owner, Signal } from "../core/types.js";
19
+ /** Projection family (§7b; returns in S3): children wrap into the family's
20
+ * own map (writes land in the projection, never the source family), and the
21
+ * family's node is the firewall every read through it pulls first. */
22
+ export interface StoreFamily {
23
+ /** Optimistic family (S4): the setter's writes are guesses. */
24
+ opt?: boolean;
25
+ /** Targets carrying a lane value on one of their nodes (reads compose the
26
+ * view over the truth — optimistic.ts); dropped lazily once none is. */
27
+ overlaid?: Set<StoreTarget>;
28
+ /** Normalized row-key fn (`options.key`, "id" default, null = unkeyed). */
29
+ key?: ((item: any) => any) | null;
30
+ map: WeakMap<object, StoreTarget>;
31
+ /** The projection computed — the firewall. Assigned after creation. */
32
+ node: Computed<any> | null;
33
+ /** Targets of the family that carry nodes: the derive's pending wakes
34
+ * their readers (A9) — the family's enumerable index, in place of a
35
+ * per-leaf chain; a target leaves it with its last node. */
36
+ live: Set<StoreTarget>;
37
+ shallow?: boolean;
38
+ /** Derive run counter (proj R37): the run whose draft is live. */
39
+ run?: number;
40
+ /** The derive's pending reached the leaves' readers (`wakeFamily`) and no
41
+ * landing has settled them yet: the next commit walks the family
42
+ * (`settleFamily`). A sync commit with no wake behind it walks nothing. */
43
+ woke?: boolean;
44
+ }
45
+ export interface StoreTarget {
46
+ /** Committed backing: source object (shared) or owned clone. */
47
+ v: Record<PropertyKey, any>;
48
+ /** Pending backing for the current batch (null when settled) — the same
49
+ * object the container node stages (`k._pendingValue`). */
50
+ pb: Record<PropertyKey, any> | null;
51
+ /** cached: committed backing is another store's proxy (§7b chained). */
52
+ ch: boolean;
53
+ /** live value-node count (deleted-key sweep fast-out in the fused walk). */
54
+ nc: number;
55
+ /** Lazy per-property subscription nodes (slot nodes). */
56
+ n: Record<PropertyKey, Signal<any>> | null;
57
+ /** Lazy per-key presence nodes (`in` tracks presence, not value — R13). */
58
+ h: Record<PropertyKey, Signal<boolean>> | null;
59
+ /** Lazy CONTAINER node: membership/iteration/`$TRACK`/`length`
60
+ * subscriptions (§6) and the backing's staging home (plan sec. 31.3). */
61
+ k: Signal<any> | null;
62
+ /** Keys written through the traps since the last fold commit. Bounds the
63
+ * setter notify/hold-check to O(written) instead of O(subscribed nodes) —
64
+ * a record with thousands of per-key subscriptions (selection maps) would
65
+ * otherwise pay a full node scan on every write. null = no trap writes
66
+ * this batch (bulk paths fall back to the full scan); WK_ALL = bound
67
+ * unusable (array length write). LOAD-BEARING SHAPE RULE: array proxy
68
+ * targets carry their fields as named properties on a real array, and V8
69
+ * normalizes an array to dictionary properties as the named count grows
70
+ * (empirically at counts ≡ 0 mod 3 from 18 up on V8 13.x) — every trap
71
+ * field read then becomes a hash lookup (~15% uibench, tree suites
72
+ * worst). Future write-side state MUST ride an extension object, not new
73
+ * named fields. */
74
+ wk: Set<PropertyKey> | null;
75
+ /** Lazy deep-witness node: `deep()` subscribes ONE node per record instead
76
+ * of one per path; write paths bump it only when it exists. Separate from
77
+ * `k` so $TRACK/mapArray never rerun on leaf value changes (R9). */
78
+ dk: Signal<number> | null;
79
+ /** Parent target (path copying walks this at commit). */
80
+ u: StoreTarget | null;
81
+ /** Property key of this target in the parent's backing. */
82
+ pk: PropertyKey | null;
83
+ /** The proxy for this target (stable outward identity). */
84
+ px: any;
85
+ /** Sticky descendants flag (§6d). */
86
+ d: boolean;
87
+ /** Sticky accessors-seen flag: an own accessor property was observed on
88
+ * this target (first-read scan, defineProperty, or clone scan). Gates the
89
+ * fold diff's descriptor-safe path and the get trap's descriptor path. */
90
+ a: boolean;
91
+ /** Accessor scan grade: 0 = not yet scanned (adoption resets — adopted data
92
+ * is not rescanned until the next draft), 1 = scanned, 2 = scanned and
93
+ * PLAIN DATA — `Object.prototype` with every own key an enumerable data
94
+ * property. Grade 2 unlocks the spread clone (cloneRaw), bare-assignment
95
+ * overlay writes and flatten (#3360); a non-plain defineProperty through
96
+ * the draft downgrades it to 1. */
97
+ sc: 0 | 1 | 2;
98
+ /** Own-key count: exact at scan, then bumped by set-trap writes of keys new
99
+ * to the container (never decremented — an estimate for the overlay/clone
100
+ * choice only, #3360). */
101
+ kc: number;
102
+ /** Pending backing is a prototype-chain OVERLAY of the committed backing
103
+ * (`Object.create(v)` — own keys are this batch's writes, everything else
104
+ * reads through). O(written) per flush instead of O(container) clones
105
+ * (#3044); commit flattens own keys onto an owned committed backing in
106
+ * place. Plain-data non-array containers qualify; chained backings and
107
+ * accessor containers keep the descriptor clone. `materializePB`
108
+ * downgrades to the clone path when a consumer needs a real container
109
+ * (draft escape). */
110
+ ovl: boolean;
111
+ /** Keys deleted in the overlay window (a prototype overlay cannot shadow
112
+ * a delete); null when none. */
113
+ del: Set<PropertyKey> | null;
114
+ /** Projection family, null for plain stores (§7b). */
115
+ fam: StoreFamily | null;
116
+ /** Shallow store root (values served raw). */
117
+ s: boolean;
118
+ }
119
+ /**
120
+ * Ownership stamp (#3360): every backing the store ALLOCATES (CoW clones,
121
+ * privatized committed backings) carries its owning target under this
122
+ * enumerable symbol. One property write replaces the two weak-collection
123
+ * registrations (ownership set + raw→target map) a fresh object used to pay
124
+ * per draft — V8's identity-hash + ephemeron cost dominated the one-key
125
+ * write floor. Enumerable so a spread copy (the plain-data clone path) stays
126
+ * on the fast path and carries the stamp along.
127
+ *
128
+ * Owned backings are never user-reachable (`snapshot` copies them, the traps
129
+ * hide the key), so every raw key walk in the store must skip `$OWNER`, and
130
+ * ownership is answered by `isOwned` — a user object never carries it.
131
+ * Overlay drafts (`Object.create(v)` over an owned `v`) inherit the stamp.
132
+ */
133
+ export declare const $OWNER: unique symbol;
134
+ /** raw → target for plain (family-less) stores. Family stores use their own
135
+ * map so one raw can wrap once per family (§7b). */
136
+ export declare const storeLookup: WeakMap<object, StoreTarget>;
137
+ export declare function setLookup(raw: object, t: StoreTarget): void;
138
+ export declare function isOwned(raw: object): boolean;
139
+ /** The target wrapping `raw` in `fam` (or the plain lookup). An owned raw
140
+ * answers from its stamp; a user raw from the map. */
141
+ export declare function lookupTarget(raw: object, fam: StoreFamily | null): StoreTarget | undefined;
142
+ /** Test-tier oracle (INTERNALS §5): no write path ever mutates a
143
+ * user-provided (non-owned) object. */
144
+ export declare const ingestedRaw: WeakSet<object> | null;
145
+ export declare function devAssertNeverUserMutation(target: object): void;
146
+ /** Sticky descendants flag up the parent chain (§6d): adoption descends
147
+ * into a changed child pair only where a target with nodes exists below. */
148
+ export declare function markDescendants(target: StoreTarget): void;
149
+ /** Observe tier: the owner in scope at `createStore` (attribution names and
150
+ * owner paths); the `_owner` write never reaches the proxy. */
151
+ export declare const storeOwners: WeakMap<StoreTarget, Owner | null> | null;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Store — public types, brand symbols, wrappability and raw marking. Shared
3
+ * by every store module; carries no reactive machinery of its own.
4
+ */
5
+ import type { Refreshable } from "../core/index.js";
6
+ /** A reactive view of a store's value. Update it through the paired `StoreSetter`. */
7
+ export type Store<T> = T;
8
+ /**
9
+ * A store setter. The callback receives a writable **draft** of the store.
10
+ *
11
+ * - **Mutate in place (canonical):** `s.foo = 1`, `s.list.push(x)`,
12
+ * `s.list.splice(i, 1)`. This is the default form for most updates.
13
+ * - **Return a new value:** for shapes where mutation is awkward, most
14
+ * commonly removing items (`s => s.list.filter(...)`). Arrays are replaced
15
+ * by index (length adjusted); objects are shallow-diffed at the top level
16
+ * (keys present in the returned value are written, missing keys deleted).
17
+ *
18
+ * The setter does **not** perform keyed reconciliation. If you need surviving
19
+ * items to keep their store identity across full-array replacement, use the
20
+ * projection form — `createStore(fn, seed, { key })` or
21
+ * `createProjection(fn, seed, { key })` — whose derive function reconciles
22
+ * its return by `options.key`.
23
+ */
24
+ export type StoreSetter<T> = (fn: (state: T) => T | void) => void;
25
+ /** Tuple returned by the plain `createStore(initialValue, options?)` form. */
26
+ export type StoreReturn<T> = [get: Store<T>, set: StoreSetter<T>];
27
+ /** Tuple returned by the derived `createStore(fn, seed, options?)` form. */
28
+ export type ProjectionStoreReturn<T> = [get: Refreshable<Store<T>>, set: StoreSetter<T>];
29
+ /** Options shared by all store primitives. */
30
+ export interface StoreOptions {
31
+ /**
32
+ * Debug name (dev and observe builds). Property nodes are labelled
33
+ * `<name>.<key>` in attribution output (`todos.title`); a derived store's
34
+ * projection node carries the name itself.
35
+ */
36
+ name?: string;
37
+ /** Single-layer store: root keys reactive, values raw records replaced by reference */
38
+ shallow?: boolean;
39
+ }
40
+ /**
41
+ * Options for derived/projected stores created with
42
+ * `createStore(fn, seed, options?)`, `createProjection(fn, seed, options?)`,
43
+ * or `createOptimisticStore(fn, seed, options?)`.
44
+ */
45
+ export interface ProjectionOptions extends StoreOptions {
46
+ /** Key property name or function for reconciliation identity; `null` merges positionally */
47
+ key?: string | ((item: NonNullable<any>) => any) | null;
48
+ /**
49
+ * Treat the seed as commit #0: the store is born committed with the seed's
50
+ * contents, shown until the derive's first real answer lands. While that
51
+ * first answer is in flight, reads serve the seed everywhere — nothing
52
+ * suspends to a `<Loading>` boundary, no transition is held, and
53
+ * `isPending` stays false (the seed answers by declaration; first-load
54
+ * affordances belong to the data, e.g. a `skeleton: true` field in the
55
+ * seed). Once the first answer lands (reconciled into the seed), refetches
56
+ * use normal pending semantics with `isPending` true.
57
+ *
58
+ * The store equivalent of `MemoOptions.loadingValue`; the seed already
59
+ * carries the placeholder shape, so this is just the opt-in.
60
+ */
61
+ seedLoadingValue?: boolean;
62
+ }
63
+ export type NoFn<T> = T extends Function ? never : T;
64
+ /** Brand keys the proxy answers directly (never user data). */
65
+ export declare const $TRACK: unique symbol, $TARGET: unique symbol, $PROXY: unique symbol, $RECORD: unique symbol;
66
+ export declare namespace SolidStore {
67
+ interface Unwrappable {
68
+ }
69
+ }
70
+ export type NotWrappable = string | number | bigint | symbol | boolean | Function | null | undefined | SolidStore.Unwrappable[keyof SolidStore.Unwrappable];
71
+ export declare let rawValuesUsed: boolean;
72
+ export declare function isRawValue(value: any): boolean;
73
+ /** Mark a value so no store ever wraps it (sticky; it reads as a leaf). */
74
+ export declare function markRaw<T>(value: T): T;
75
+ /** A shallow store's ingested record: raw by contract (values served
76
+ * verbatim, slots replaced by reference). */
77
+ export declare function markRawOne(v: any): void;
78
+ export declare function markRawIngest(container: any): void;
79
+ /** Plain objects and arrays wrap; class instances, DOM nodes, frozen objects
80
+ * and markRaw'd values are leaves. Per-prototype verdict cached. */
81
+ export declare function isWrappable<T>(obj: T | NotWrappable): obj is T;
82
+ export declare function ownEnumerableKeys(o: object): (string | symbol)[];
83
+ export declare let writeOverride: boolean;
84
+ export declare function setWriteOverride(value: boolean): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidjs/signals",
3
- "version": "2.0.0-rc.13",
3
+ "version": "2.0.0-rc.14",
4
4
  "description": "Solid's reactive primitives: signals, memos, effects, stores, and async-aware computations.",
5
5
  "author": "Ryan Carniato",
6
6
  "license": "MIT",