@mmstack/primitives 22.5.0 → 22.6.0

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.
@@ -224,6 +224,56 @@ type DeferredSignal<T> = Signal<T> & {
224
224
  */
225
225
  declare function deferredValue<T>(source: Signal<T>, opt?: DeferredValueOptions<T>): DeferredSignal<T>;
226
226
 
227
+ /**
228
+ * Optional observability seam for the concurrency layer (idea/concurrency-devtools.md). A
229
+ * listener, provided via {@link provideConcurrencyInstrumentation}, receives events as
230
+ * transition scopes coordinate pending/suspense/transaction windows and register resources.
231
+ * Zero-cost when absent: the taps are `listener?.hook(...)` behind a once-resolved optional
232
+ * inject, so nothing is allocated or measured unless a listener is installed.
233
+ *
234
+ * Span-shaped hooks (`*Start`) return an opaque handle passed back to their `*End`, and carry
235
+ * `at` epoch-ms stamps — deliberately isomorphic to the telemetry `startSpan`/`SpanHandle` SPI,
236
+ * so a telemetry consumer maps one-to-one.
237
+ */
238
+ type ConcurrencyInstrumentation = {
239
+ pendingStart?(e: {
240
+ scope: string;
241
+ resources: number;
242
+ at: number;
243
+ }): unknown;
244
+ pendingEnd?(handle: unknown, e: {
245
+ at: number;
246
+ }): void;
247
+ transactionStart?(e: {
248
+ scope: string;
249
+ at: number;
250
+ }): unknown;
251
+ transactionEnd?(handle: unknown, e: {
252
+ at: number;
253
+ }): void;
254
+ resourceRegistered?(e: {
255
+ scope: string;
256
+ suspends: boolean;
257
+ }): void;
258
+ resourceRemoved?(e: {
259
+ scope: string;
260
+ }): void;
261
+ abortPending?(e: {
262
+ scope: string;
263
+ aborted: number;
264
+ at: number;
265
+ }): void;
266
+ };
267
+ declare const CONCURRENCY_INSTRUMENTATION: InjectionToken<ConcurrencyInstrumentation>;
268
+ declare function provideConcurrencyInstrumentation(listener: ConcurrencyInstrumentation): Provider;
269
+ /**
270
+ * Chrome DevTools "Performance" custom-tracks preset (idea/concurrency-devtools.md): writes a
271
+ * `performance.measure` for each pending/transaction window onto an "mmstack" extension track,
272
+ * so reactive coordination shows up on the Performance panel timeline. Dev-only, zero backend,
273
+ * no dependencies. Give each measure the scope name for readability.
274
+ */
275
+ declare function perfCustomTracks(track?: string): ConcurrencyInstrumentation;
276
+
227
277
  /**
228
278
  * Structural hold-and-swap as a signal. Given a `target` (the desired value — e.g. the
229
279
  * subtree/def/key you want to show) and a `ready` predicate, returns a signal that keeps
@@ -402,6 +452,13 @@ type TransitionScope = {
402
452
  readonly pending: Signal<boolean>;
403
453
  /** Any *suspending* resource is not ready — drives the first-load placeholder. */
404
454
  suspended(type: SuspendType): boolean;
455
+ /**
456
+ * Register a resource. EVERY `add` must be paired with a `remove` when the
457
+ * registrant goes away — the scope holds the resource strongly and keeps
458
+ * reading its `status` forever otherwise (stale `pending`, pinned memory).
459
+ * Prefer {@link registerResource} / `injectRegisterResource`, which pair the
460
+ * removal with the caller's `DestroyRef` automatically.
461
+ */
405
462
  add(res: ResourceLike, opt?: RegisterOptions): void;
406
463
  remove(res: ResourceLike): void;
407
464
  /**
@@ -455,7 +512,13 @@ type TransitionScope = {
455
512
  */
456
513
  hold<T>(value: Signal<T>): Signal<T>;
457
514
  };
458
- declare function createTransitionScope(): TransitionScope;
515
+ type CreateTransitionScopeOptions = {
516
+ /** Scope identity for instrumentation events (idea/concurrency-devtools.md). */
517
+ readonly name?: string;
518
+ /** Optional observability listener; taps are no-ops when omitted (zero cost). */
519
+ readonly instrumentation?: ConcurrencyInstrumentation;
520
+ };
521
+ declare function createTransitionScope(opt?: CreateTransitionScopeOptions): TransitionScope;
459
522
  /**
460
523
  * The scope→`PendingTasks` bridge: while `scope.pending()` is true, hold an Angular
461
524
  * pending task so SSR serialization waits for the scope's in-flight loads — HTTP loads
@@ -470,7 +533,7 @@ declare function createTransitionScope(): TransitionScope;
470
533
  */
471
534
  declare function bridgeScopeToPendingTasks(scope: TransitionScope, injector?: Injector): void;
472
535
  /** Provide a fresh transition scope at a boundary so its subtree's resources are tracked independently. */
473
- declare function provideTransitionScope(): Provider;
536
+ declare function provideTransitionScope(opt?: CreateTransitionScopeOptions): Provider;
474
537
  declare function injectTransitionScope(): TransitionScope;
475
538
  /**
476
539
  * A transition scope that can be re-pointed at a delegate target at runtime. Reads and
@@ -837,23 +900,6 @@ type MutableSignal<T> = WritableSignal<T> & {
837
900
  * This is because a `.mutate()` call notifies its dependents that it has changed, but if the
838
901
  * reference to a derived object hasn't changed, the `computed` signal will not trigger its
839
902
  * own dependents by default.
840
- *
841
- * @example
842
- * ```ts
843
- * const state = mutable({ user: { name: 'John' }, lastUpdated: new Date() });
844
- *
845
- * // ✅ CORRECT: Deriving a primitive value works as expected.
846
- * const name = computed(() => state().user.name);
847
- *
848
- * // ❌ INCORRECT: This will not update reliably after the first change.
849
- * const userObject = computed(() => state().user);
850
- *
851
- * // ✅ CORRECT: For object derivations, `equal: false` is required.
852
- * const userObjectFixed = computed(() => state().user, { equal: false });
853
- *
854
- * // This mutation will now correctly trigger effects depending on `userObjectFixed`.
855
- * state.mutate(s => s.lastUpdated = new Date());
856
- * ```
857
903
  */
858
904
  declare function mutable<T>(): MutableSignal<T | undefined>;
859
905
  declare function mutable<T>(initial: T): MutableSignal<T>;
@@ -1800,10 +1846,10 @@ declare function clipboard(opt?: string | SensorRunOptions): ClipboardSignal;
1800
1846
  /**
1801
1847
  * Represents the size of an element.
1802
1848
  */
1803
- interface ElementSize {
1849
+ type ElementSize = {
1804
1850
  width: number;
1805
1851
  height: number;
1806
- }
1852
+ };
1807
1853
  /**
1808
1854
  * Options for configuring the `elementSize` sensor.
1809
1855
  */
@@ -2762,6 +2808,128 @@ type ResolvableTarget = EventTargetLike | Signal<EventTargetLike | null>;
2762
2808
  declare function signalFromEvent<TEvent extends Event>(target: ResolvableTarget, eventName: string, initial: TEvent | null, opt?: SignalFromEventOptions): Signal<TEvent | null>;
2763
2809
  declare function signalFromEvent<TEvent extends Event, U>(target: ResolvableTarget, eventName: string, initial: U, project: (event: TEvent) => U, opt?: SignalFromEventOptions): Signal<U>;
2764
2810
 
2811
+ type Key$2 = string | number;
2812
+ /**
2813
+ * One structural operation. `set` on a key that did not previously exist carries NO `prev`
2814
+ * property (an absent key is not the same as a key holding `undefined` — the merge3 lesson),
2815
+ * which is what lets {@link invertBatch} invert an add into a delete.
2816
+ */
2817
+ type StoreOp = {
2818
+ kind: 'set';
2819
+ path: readonly Key$2[];
2820
+ next: unknown;
2821
+ prev?: unknown;
2822
+ } | {
2823
+ kind: 'delete';
2824
+ path: readonly Key$2[];
2825
+ prev: unknown;
2826
+ };
2827
+ /** One emission: every op derived from one commit window (a tick), in path order. */
2828
+ type OpBatch = {
2829
+ /** Identifies the emitting log — filter your own batches on a shared transport. */
2830
+ readonly origin: string;
2831
+ /** Per-log monotonic batch counter. */
2832
+ readonly version: number;
2833
+ readonly ops: readonly StoreOp[];
2834
+ };
2835
+ /**
2836
+ * Drives an {@link opLog}'s emission reaction. Given the `run` closure (which reads the source in
2837
+ * a tracking context and flushes the delta), a driver arranges for `run` to execute now and again
2838
+ * on every subsequent change, returning a handle that stops it. The default driver is an Angular
2839
+ * `effect` (needs an injector). Supply a custom driver to run an opLog with NO injector; a
2840
+ * renderer-independent one built on `@angular/core/primitives/signals` `createWatch` ships as
2841
+ * `microtaskOpLogDriver` from `@mmstack/worker/host` (the Web Worker seam).
2842
+ */
2843
+ type OpLogDriver = (run: () => void) => {
2844
+ destroy(): void;
2845
+ };
2846
+ type CreateOpLogOptions = {
2847
+ /** Transport identity for emitted batches. Defaults to a random id. */
2848
+ readonly origin?: string;
2849
+ /** Injection context for the default effect-based driver (required outside one). */
2850
+ readonly injector?: Injector;
2851
+ /**
2852
+ * Replaces the default Angular-`effect` emission driver. Supply a custom driver (e.g.
2853
+ * `microtaskOpLogDriver` from `@mmstack/worker/host`) to run an opLog with NO injector. When
2854
+ * given, `injector` is ignored and no injection context is required.
2855
+ */
2856
+ readonly driver?: OpLogDriver;
2857
+ };
2858
+ type OpLog<T extends object> = {
2859
+ /**
2860
+ * Ordered, lossless delivery of every emitted batch. Synchronous — don't write back into
2861
+ * the observed source from inside a callback (route remote data through {@link OpLog.apply}).
2862
+ */
2863
+ subscribe(cb: (batch: OpBatch) => void): () => void;
2864
+ /** The most recent batch — a lossy sampling view (devtools); use `subscribe` for transport. */
2865
+ readonly latest: Signal<OpBatch | null>;
2866
+ /**
2867
+ * Synchronously diff the source and emit any pending change NOW, rather than waiting for the
2868
+ * driver's scheduled run (an app tick, or a custom driver's microtask). Idempotent
2869
+ * and coalescing: writes since the last emission compose into one batch, and a `flush()` with
2870
+ * nothing pending is a no-op. Use it to make emission deterministic — the worker host calls it
2871
+ * to settle its mirror synchronously (tests), and it underpins the flush-before-apply honesty of
2872
+ * {@link OpLog.apply}. Independent of the driver: a later scheduled run simply finds no diff.
2873
+ */
2874
+ flush(): void;
2875
+ /**
2876
+ * Applies ops (a remote batch, a persisted journal entry, an {@link invertBatch} result)
2877
+ * atomically: ONE `set`, one notification wave. Also advances this log's diff baseline in
2878
+ * the same step, so an applied batch produces NO echo emission — sync loops terminate by
2879
+ * construction. Local writes pending in the current tick are flushed (emitted) first, so
2880
+ * they are never silently folded into the applied baseline.
2881
+ */
2882
+ apply(ops: OpBatch | readonly StoreOp[]): void;
2883
+ /** Stops observing and drops subscribers. Also happens when the injection context dies. */
2884
+ destroy(): void;
2885
+ };
2886
+ /**
2887
+ * Pure, store-free application of ops onto a plain root value, returning the next immutable root
2888
+ * (structural-sharing along op paths, missing containers vivified `'auto'`-style). This is the
2889
+ * same transform {@link OpLog.apply} runs, extracted so a replica can fold a received batch into
2890
+ * a value WITHOUT owning a diffing {@link opLog} — e.g. the worker-graph read-replica seam.
2891
+ * Accepts a batch or a bare op list.
2892
+ */
2893
+ declare function applyOps<T>(root: T, ops: OpBatch | readonly StoreOp[]): T;
2894
+ /**
2895
+ * Pure reference-pruned structural diff of two roots into minimal ops (the emission core of
2896
+ * {@link opLog}, exported so code outside a log can produce a batch — e.g. diffing a scratch
2897
+ * draft against a replica's current value to route a write to its owner). Trusts the
2898
+ * copy-on-write contract: an untouched subtree that kept its reference is skipped.
2899
+ */
2900
+ declare function diffOps(prev: unknown, next: unknown): StoreOp[];
2901
+ /**
2902
+ * Inverts a batch for undo: reversed order, `set`↔its own inverse (an add — a `set` with no
2903
+ * `prev` — inverts to a `delete`; a `delete` inverts to a `set` restoring `prev`). Feed the
2904
+ * result to {@link OpLog.apply}. Requires the ops' `prev`s, which in-memory batches always
2905
+ * carry — a wire-serialized batch that stripped them is not invertible.
2906
+ */
2907
+ declare function invertBatch(batch: OpBatch | readonly StoreOp[]): StoreOp[];
2908
+ /**
2909
+ * Observes a copy-on-write signal (a `store`'s root, or any `WritableSignal` holding
2910
+ * immutably-updated objects) and emits its changes as minimal structural op batches — the
2911
+ * shared substrate for sync (ship batches, `apply` remote ones), persistence (journal
2912
+ * batches, replay on boot), undo ({@link invertBatch}), and devtools (`latest`).
2913
+ *
2914
+ * Zero store-core involvement and zero cost when unused: emission is a reference-pruned diff
2915
+ * of the root value per tick (structural sharing makes it O(changed paths)), driven by one
2916
+ * effect. A batch therefore coalesces everything written in one tick — for coarser,
2917
+ * intentional units, stage writes on a `forkStore` and `commit()` (one set → one batch).
2918
+ *
2919
+ * NOT supported on mutable stores/signals: in-place mutation keeps reference identity, which
2920
+ * defeats the diff (same reason `forkStore`'s `'fine'` strategy refuses them) — a dev-mode
2921
+ * warning fires and nothing emits.
2922
+ *
2923
+ * ```ts
2924
+ * const s = store({ todos: [{ done: false }] });
2925
+ * const log = opLog(s, { origin: 'tab-a' });
2926
+ * log.subscribe((b) => channel.postMessage(encode(b))); // ship
2927
+ * channel.onmessage = (m) => log.apply(decode(m.data)); // apply — echo-free
2928
+ * s.todos[0].done.set(true); // → { kind: 'set', path: ['todos', 0, 'done'], … }
2929
+ * ```
2930
+ */
2931
+ declare function opLog<T extends object>(source: WritableSignal<T>, opt?: CreateOpLogOptions): OpLog<T>;
2932
+
2765
2933
  /**
2766
2934
  * @internal Runtime brand carrying a store node's lazily-built leaf probe. Exported (like
2767
2935
  * {@link OPAQUE}) only so the `{ readonly [LEAF]: () => boolean }` brand on the store types is
@@ -3024,6 +3192,32 @@ declare function mutableStore<T extends AnyRecord>(value: T, opt?: CreateSignalO
3024
3192
  */
3025
3193
  noUnionLeaves?: boolean;
3026
3194
  }): MutableSignalStore<T>;
3195
+ /**
3196
+ * Builds a DI-less store context — the shared proxy-cache and cleanup registry that {@link toStore}
3197
+ * normally resolves from the injector — so a `store`/`toStore`/`opLog` graph can run with NO Angular
3198
+ * injection context. Spread the result into the options:
3199
+ *
3200
+ * ```ts
3201
+ * import { microtaskOpLogDriver } from '@mmstack/worker/host';
3202
+ * const ctx = createStoreContext();
3203
+ * const s = store({ todos: [] }, ctx);
3204
+ * const log = opLog(s, { driver: microtaskOpLogDriver(), origin: 'worker' }); // no injector anywhere
3205
+ * ```
3206
+ *
3207
+ * **This is a worker-only fallback — do NOT use it on the main thread.** DI is the default and
3208
+ * correct path in an app: the injector scopes the proxy-cache/cleanup singletons per app instance,
3209
+ * which on the SERVER keeps one request's store identity from bleeding into another's (the exact
3210
+ * hazard a module-scope singleton would reintroduce). A Web Worker is safe because it is a single
3211
+ * store graph per thread and never runs during SSR (spawn is a `PLATFORM_ID === 'server'` no-op),
3212
+ * so there is no cross-request scope to contaminate. Never hoist a `createStoreContext()` to module
3213
+ * scope on a shared/main thread.
3214
+ *
3215
+ * **Share ONE context across every store in a worker** — the same way `providedIn: 'root'` shares
3216
+ * one cache across all of an app's stores. `@mmstack/worker/host` memoizes this per worker
3217
+ * (`workerStoreContext()`); reach for `createStoreContext()` directly only in a bare
3218
+ * (non-worker-host) DI-less setup, and hold the single instance yourself.
3219
+ */
3220
+ declare function createStoreContext(): toStoreOptions;
3027
3221
 
3028
3222
  /**
3029
3223
  * A 3-way merge of a forked value against a changed base: given the common `ancestor` (the base
@@ -3067,6 +3261,8 @@ type Fork<T> = {
3067
3261
  commit(): void;
3068
3262
  /** Drop staged writes — the fork reads through to the base again. */
3069
3263
  discard(): void;
3264
+ /** The staged delta vs the CURRENT base, as structural ops (inspect, persist, invert). */
3265
+ ops(): StoreOp[];
3070
3266
  };
3071
3267
  /**
3072
3268
  * Per-path 3-way merge. Reference-equality short-circuits do the work: a subtree the fork never
@@ -3093,86 +3289,355 @@ type ForkStoreOptions<T> = toStoreOptions & {
3093
3289
  };
3094
3290
  declare function forkStore<T extends Record<string, any>>(base: WritableSignalStore<T>, opt?: ForkStoreOptions<T>): Fork<T>;
3095
3291
 
3292
+ /** Hybrid logical clock stamp: physical epoch ms + logical counter for same-ms ordering. */
3293
+ type Hlc = {
3294
+ readonly p: number;
3295
+ readonly l: number;
3296
+ };
3297
+ /** Total order over stamps alone; ties break on `writer` via {@link compareTotal}. */
3298
+ declare function compareHlc(a: Hlc, b: Hlc): number;
3299
+ /** The protocol's total order: (hlc.p, hlc.l, writer). Never returns 0 for distinct writers. */
3300
+ declare function compareTotal(a: Hlc, writerA: string, b: Hlc, writerB: string): number;
3301
+ type HlcClock = {
3302
+ /** Stamp for a locally-emitted envelope: monotonic even when wall time stalls or rewinds. */
3303
+ next(): Hlc;
3304
+ /** Fold an observed remote stamp in, so subsequent local stamps sort after it. */
3305
+ observe(remote: Hlc): void;
3306
+ };
3307
+ /**
3308
+ * HLC per Kulkarni et al.: convergence never depends on wall clocks, but LWW fairness
3309
+ * degrades under large skew, so observing a remote clock far ahead warns in dev mode.
3310
+ */
3311
+ declare function createHlcClock(now?: () => number): HlcClock;
3312
+
3313
+ declare const OP_PROTO_VERSION = 1;
3096
3314
  type Key = string | number;
3097
3315
  /**
3098
- * One structural operation. `set` on a key that did not previously exist carries NO `prev`
3099
- * property (an absent key is not the same as a key holding `undefined` the merge3 lesson),
3100
- * which is what lets {@link invertBatch} invert an add into a delete.
3316
+ * The wire/journal record (op-protocol RFC §3). `writer` is an opaque principal pseudonym
3317
+ * natural identity never enters the envelope; `origin` identifies the emitting log instance.
3101
3318
  */
3102
- type StoreOp = {
3103
- kind: 'set';
3104
- path: readonly Key[];
3105
- next: unknown;
3106
- prev?: unknown;
3107
- } | {
3108
- kind: 'delete';
3109
- path: readonly Key[];
3110
- prev: unknown;
3111
- };
3112
- /** One emission: every op derived from one commit window (a tick), in path order. */
3113
- type OpBatch = {
3114
- /** Identifies the emitting log — filter your own batches on a shared transport. */
3319
+ type OpEnvelope = {
3320
+ readonly proto: number;
3115
3321
  readonly origin: string;
3116
- /** Per-log monotonic batch counter. */
3322
+ readonly writer: string;
3117
3323
  readonly version: number;
3324
+ readonly hlc: Hlc;
3325
+ readonly policyVersion: number;
3118
3326
  readonly ops: readonly StoreOp[];
3119
3327
  };
3120
- type CreateOpLogOptions = {
3121
- /** Transport identity for emitted batches. Defaults to a random id. */
3328
+ declare const CONFLICT_BRAND = "~mmstackConflict";
3329
+ /**
3330
+ * A preserved (jj-style) conflict: both sides survive as data, sync never blocks, and
3331
+ * resolution is just a later write. String-branded so it survives structured clone.
3332
+ */
3333
+ type Conflicted<T = unknown> = {
3334
+ readonly [CONFLICT_BRAND]: true;
3335
+ readonly mine: T;
3336
+ readonly theirs: T;
3337
+ readonly ancestor?: T;
3338
+ };
3339
+ declare function isConflicted<T = unknown>(value: unknown): value is Conflicted<T>;
3340
+ type MergeContext = {
3341
+ readonly path: readonly Key[];
3342
+ };
3343
+ /**
3344
+ * Resolves a concurrent set-vs-set collision. Called with a deterministic argument order
3345
+ * (`mine` = the side winning the total order) so every peer computes the same value.
3346
+ */
3347
+ type MergeFn = (ancestor: unknown, mine: unknown, theirs: unknown, ctx: MergeContext) => unknown;
3348
+ type MergePolicyEntry = {
3349
+ /** `'todos.*.title'` or a segment array; `'*'` matches exactly one segment. */
3350
+ readonly path: string | readonly Key[];
3351
+ readonly merge: MergeFn;
3352
+ };
3353
+ declare const lww: MergeFn;
3354
+ declare const mergeThree: MergeFn;
3355
+ declare const preserve: MergeFn;
3356
+ /**
3357
+ * Identity-aware array merge (op-protocol RFC §12 v0): reconciles two concurrent versions of
3358
+ * an array item-wise by a user-provided identity, instead of last-writer-wins on the whole
3359
+ * array. Items are matched by key; per-item fields merge via `merge3` against the ancestor
3360
+ * item; items added on either side survive; an item removed on either side and unedited on
3361
+ * the other stays removed. Item ORDER follows `mine` (the total-order winner), with `theirs`-
3362
+ * only additions appended — positional merging is out of scope (fractional indexing is the
3363
+ * known upgrade if dogfooding demands it). Arrays still TRAVEL as whole-value sets; identity
3364
+ * only shapes conflict resolution, so the wire format is untouched.
3365
+ */
3366
+ declare function keyedArray(identity: (item: unknown) => unknown, opt?: {
3367
+ item?: MergeFn;
3368
+ }): MergeFn;
3369
+ type ConvergingApply = {
3370
+ /**
3371
+ * Fold an envelope into the register map and return the ops the local store must apply
3372
+ * (post-dominance, post-policy, including replays of newer descendant winners). Pass
3373
+ * `local: true` for envelopes this peer emitted itself: registered, nothing returned.
3374
+ */
3375
+ ingest(env: OpEnvelope, opt?: {
3376
+ local?: boolean;
3377
+ }): StoreOp[];
3378
+ /** Drop all registers (snapshot compaction / rehydration boundary). */
3379
+ reset(): void;
3380
+ };
3381
+ /**
3382
+ * The unsequenced-topology convergence core (op-protocol RFC §4): a per-path last-writer-wins
3383
+ * register map over the total order (hlc, writer), with subtree dominance. Order-independent:
3384
+ * any arrival order of the same envelope set yields the same state.
3385
+ */
3386
+ declare function createConvergingApply(opt?: {
3387
+ policies?: readonly MergePolicyEntry[];
3388
+ }): ConvergingApply;
3389
+ type RebaseResult<T = unknown> = {
3390
+ root: T;
3391
+ /** Pending batches re-based onto the remote state, `prev`s refreshed. */
3392
+ pending: StoreOp[][];
3393
+ };
3394
+ /**
3395
+ * The shared rebase routine (op-protocol RFC §5): invert pending, apply remote, re-apply
3396
+ * pending through the merge policies. Pure — branching's `rebase()` and the sequenced relay
3397
+ * client both call this.
3398
+ */
3399
+ declare function rebaseOps<T>(root: T, pending: readonly (readonly StoreOp[])[], remote: readonly StoreOp[], policies?: readonly MergePolicyEntry[]): RebaseResult<T>;
3400
+ /**
3401
+ * A per-path-policy `ForkStrategy` for `forkStore`: a three-way reconcile built from the
3402
+ * shared rebase (invert mine → apply theirs' delta → re-apply mine through the policies).
3403
+ * Paths only one side touched resolve like `merge3`; paths BOTH touched go through the
3404
+ * matching {@link MergePolicyEntry} (`lww` default — fork wins, matching `'fine'`; or
3405
+ * `mergeThree` / `preserve` / custom). Same copy-on-write contract as `'fine'`.
3406
+ */
3407
+ declare function policyStrategy<T>(policies: readonly MergePolicyEntry[]): (ancestor: T, mine: T, theirs: T) => T;
3408
+ type OpSyncOptions = {
3409
+ /** Opaque principal pseudonym — provided by the app, never minted here (RFC §3). */
3410
+ readonly writer: string;
3122
3411
  readonly origin?: string;
3123
- /** Injection context for the observing effect (required outside one). */
3412
+ readonly policyVersion?: number;
3413
+ readonly policies?: readonly MergePolicyEntry[];
3414
+ readonly clock?: HlcClock;
3124
3415
  readonly injector?: Injector;
3416
+ readonly driver?: OpLogDriver;
3417
+ /** A version gap from a known origin (missed envelopes) — the resync hook. */
3418
+ readonly onGap?: (origin: string, expected: number, got: number) => void;
3125
3419
  };
3126
- type OpLog<T extends object> = {
3420
+ type OpSync<T = unknown> = {
3421
+ readonly origin: string;
3422
+ /** Locally-emitted envelopes, ready for a transport. */
3423
+ subscribe(cb: (env: OpEnvelope) => void): () => void;
3424
+ /** Converging apply of a remote envelope (echo-free; own-origin envelopes are ignored). */
3425
+ receive(env: OpEnvelope): void;
3426
+ /** Synchronously emit any pending local delta now. */
3427
+ flush(): void;
3428
+ /** Per-origin latest versions — the handshake watermark. */
3429
+ watermark(): Record<string, number>;
3430
+ /** The current root + watermark, for answering a peer's hello. */
3431
+ snapshot(): {
3432
+ root: T;
3433
+ wm: Record<string, number>;
3434
+ };
3127
3435
  /**
3128
- * Ordered, lossless delivery of every emitted batch. Synchronousdon't write back into
3129
- * the observed source from inside a callback (route remote data through {@link OpLog.apply}).
3436
+ * Emit the CURRENT root as a root-set envelopethe fresh-room seed of the relay
3437
+ * contract (a room's snapshot root becomes complete once seeded).
3130
3438
  */
3131
- subscribe(cb: (batch: OpBatch) => void): () => void;
3132
- /** The most recent batch — a lossy sampling view (devtools); use `subscribe` for transport. */
3133
- readonly latest: Signal<OpBatch | null>;
3439
+ seed(): void;
3134
3440
  /**
3135
- * Applies ops (a remote batch, a persisted journal entry, an {@link invertBatch} result)
3136
- * atomically: ONE `set`, one notification wave. Also advances this log's diff baseline in
3137
- * the same step, so an applied batch produces NO echo emission — sync loops terminate by
3138
- * construction. Local writes pending in the current tick are flushed (emitted) first, so
3139
- * they are never silently folded into the applied baseline.
3441
+ * Replace local state with a peer's snapshot, atomically (one notification wave).
3442
+ * Local envelopes the snapshot doesn't cover (per its watermark) are re-applied on
3443
+ * top, so writes made before hydration are never silently lost.
3140
3444
  */
3141
- apply(ops: OpBatch | readonly StoreOp[]): void;
3142
- /** Stops observing and drops subscribers. Also happens when the injection context dies. */
3445
+ hydrate(root: T, wm?: Record<string, number>): void;
3446
+ destroy(): void;
3447
+ };
3448
+ declare function opSync<T extends object>(source: WritableSignal<T>, opt: OpSyncOptions): OpSync<T>;
3449
+
3450
+ type StoreHistory = {
3451
+ readonly canUndo: Signal<boolean>;
3452
+ readonly canRedo: Signal<boolean>;
3453
+ /** Revert the most recent tracked change; a no-op when nothing is undoable. */
3454
+ undo(): void;
3455
+ /** Re-apply the most recently undone change. */
3456
+ redo(): void;
3457
+ /** Forget all tracked history (e.g. after a save boundary). */
3458
+ clear(): void;
3143
3459
  destroy(): void;
3144
3460
  };
3461
+ type StoreHistoryOptions = CreateOpLogOptions & {
3462
+ /** Max entries kept per stack (default 100). */
3463
+ readonly limit?: number;
3464
+ /**
3465
+ * The change stream to track. Defaults to self-diffing `source` (every change to the store
3466
+ * becomes undoable). For collaborative-safe undo, pass a sync client's LOCAL envelope stream
3467
+ * (e.g. an `opSync`'s `subscribe`, which fires only for this peer's own writes) — remote
3468
+ * peers' changes then never land on your undo stack.
3469
+ */
3470
+ readonly track?: {
3471
+ subscribe(cb: (batch: OpBatch) => void): () => void;
3472
+ };
3473
+ };
3145
3474
  /**
3146
- * Inverts a batch for undo: reversed order, `set`↔its own inverse (an add a `set` with no
3147
- * `prev` inverts to a `delete`; a `delete` inverts to a `set` restoring `prev`). Feed the
3148
- * result to {@link OpLog.apply}. Requires the ops' `prev`s, which in-memory batches always
3149
- * carry a wire-serialized batch that stripped them is not invertible.
3475
+ * Undo/redo for a copy-on-write store, built on the op-log: each tracked change is stored as
3476
+ * its inverse batch, so `undo()` is one `apply` and history costs only the diffs, not full
3477
+ * snapshots. Redoing is invert-of-the-inverse. A new edit made after an undo clears the redo
3478
+ * stack (linear history). Applying a redo/undo does not itself re-enter history.
3479
+ *
3480
+ * Composes with sync for collaborative undo: pass `track: syncClient` so only YOUR writes are
3481
+ * undoable, while `undo()` emits a normal op that propagates to peers (it writes through the
3482
+ * store, which the sync client picks up).
3150
3483
  */
3151
- declare function invertBatch(batch: OpBatch | readonly StoreOp[]): StoreOp[];
3484
+ declare function storeHistory<T extends object>(source: WritableSignal<T>, opt?: StoreHistoryOptions): StoreHistory;
3485
+
3486
+ type MaybePromise<T> = T | Promise<T>;
3152
3487
  /**
3153
- * Observes a copy-on-write signal (a `store`'s root, or any `WritableSignal` holding
3154
- * immutably-updated objects) and emits its changes as minimal structural op batches the
3155
- * shared substrate for sync (ship batches, `apply` remote ones), persistence (journal
3156
- * batches, replay on boot), undo ({@link invertBatch}), and devtools (`latest`).
3488
+ * The minimal async key/value contract persistence needs. Deliberately matches `idb-keyval`'s
3489
+ * top-level `get`/`set`/`del` so its module drops in with no wrapper (`persist(s, { key, store:
3490
+ * idbKeyval })`). Any store backed by structured clone (idb-keyval, Dexie) can hold complex values
3491
+ * without a serialize hook. A Dexie table needs a tiny adapter because it names things differently:
3157
3492
  *
3158
- * Zero store-core involvement and zero cost when unused: emission is a reference-pruned diff
3159
- * of the root value per tick (structural sharing makes it O(changed paths)), driven by one
3160
- * effect. A batch therefore coalesces everything written in one tick — for coarser,
3161
- * intentional units, stage writes on a `forkStore` and `commit()` (one set → one batch).
3493
+ * ```ts
3494
+ * const table = db.table<{ key: string; value: unknown }>('kv');
3495
+ * const asyncStore: AsyncStore = {
3496
+ * get: (k) => table.get(k).then((r) => r?.value),
3497
+ * set: (k, v) => table.put({ key: k, value: v }).then(() => undefined),
3498
+ * del: (k) => table.delete(k),
3499
+ * };
3500
+ * ```
3501
+ */
3502
+ type AsyncStore = {
3503
+ get(key: string): MaybePromise<unknown>;
3504
+ set(key: string, value: unknown): MaybePromise<void>;
3505
+ del(key: string): MaybePromise<void>;
3506
+ };
3507
+ /** Persistence options — the reader-side settings, independent of how the store was created. */
3508
+ type PersistOptions<T> = {
3509
+ /** Storage key for this store's snapshot. Required per call. */
3510
+ readonly key: string;
3511
+ /** The async backend. Falls back to the provided default (see {@link providePersistedStoreOptions}). */
3512
+ readonly store?: AsyncStore;
3513
+ /** Encode before writing. Default identity: structured-clone backends keep complex values. */
3514
+ readonly serialize?: (value: T) => unknown;
3515
+ /** Decode after reading. Default identity. */
3516
+ readonly deserialize?: (raw: unknown) => T;
3517
+ /**
3518
+ * Current schema version of the persisted value. When set, snapshots are written wrapped in a
3519
+ * small version envelope, and a snapshot stamped with an older version is passed through
3520
+ * {@link PersistOptions.migrate} on boot before it is adopted. A snapshot from a *newer* version
3521
+ * than this build is left untouched (a newer client wrote it).
3522
+ */
3523
+ readonly version?: number;
3524
+ /**
3525
+ * Bring a snapshot from an older `version` up to the current shape. It runs during boot, which
3526
+ * is already async, so it may be async too: lazy-import the migration ladder here and only pay
3527
+ * for it when there is old data to migrate. Receives the decoded old value and the version it
3528
+ * was written with (`0` for a pre-versioning snapshot).
3529
+ */
3530
+ readonly migrate?: (data: unknown, fromVersion: number) => MaybePromise<T>;
3531
+ /** Coalesce writes by this many ms (default 300). A flush/teardown always writes immediately. */
3532
+ readonly writeDebounceMs?: number;
3533
+ readonly injector?: Injector;
3534
+ };
3535
+ type PersistedStoreOptions<T extends object> = CreateSignalOptions<T> & toStoreOptions & PersistOptions<T>;
3536
+ /**
3537
+ * App-wide defaults for {@link persist} / {@link persistedStore}. Only cross-type settings live
3538
+ * here; `serialize`/`deserialize` are per-call because they depend on the store's value type.
3539
+ */
3540
+ type PersistedStoreDefaults = {
3541
+ readonly store?: AsyncStore;
3542
+ readonly writeDebounceMs?: number;
3543
+ };
3544
+ declare const PERSISTED_STORE_OPTIONS: InjectionToken<PersistedStoreDefaults>;
3545
+ /**
3546
+ * Wire the {@link AsyncStore} backend (and any shared debounce) once, override per call. The
3547
+ * typical use is to install idb-keyval at bootstrap so every `persist`/`persistedStore` persists
3548
+ * without re-passing the backend.
3162
3549
  *
3163
- * NOT supported on mutable stores/signals: in-place mutation keeps reference identity, which
3164
- * defeats the diff (same reason `forkStore`'s `'fine'` strategy refuses them) — a dev-mode
3165
- * warning fires and nothing emits.
3550
+ * @example
3551
+ * import * as idbKeyval from 'idb-keyval';
3552
+ * providePersistedStoreOptions({ store: idbKeyval });
3553
+ */
3554
+ declare function providePersistedStoreOptions(opt: PersistedStoreDefaults): Provider;
3555
+ /** Persistence controls for a store, from {@link persist}. */
3556
+ type PersistHandle = {
3557
+ /**
3558
+ * `false` until the first read from the backend settles (or immediately `true` on the server
3559
+ * and when no backend is configured). Gate first paint on it if a stale-flash matters.
3560
+ */
3561
+ readonly hydrated: Signal<boolean>;
3562
+ /** Force any pending debounced write to the backend now. */
3563
+ flush(): Promise<void>;
3564
+ /** Remove the snapshot from the backend and reset the store to the value it held when attached. */
3565
+ clear(): Promise<void>;
3566
+ };
3567
+ /**
3568
+ * A store plus its persistence controls. Shaped like {@link Fork} (a `.store` field, not the
3569
+ * store itself) because the store is a proxy where any property access resolves a child path,
3570
+ * so controls cannot live on it directly.
3571
+ */
3572
+ type PersistedStore<T extends object> = {
3573
+ /** The live store. Reads are synchronous; it holds the initial value until hydration lands. */
3574
+ readonly store: WritableSignalStore<T>;
3575
+ } & PersistHandle;
3576
+ /**
3577
+ * Attach durable local persistence to an EXISTING store: its whole-value snapshot is written to an
3578
+ * async backend (IndexedDB via idb-keyval or Dexie) and restored on boot. A reader over the store,
3579
+ * so it composes with the other op-log readers (`tabSync`, `@mmstack/mesh`) on the same store — a
3580
+ * persisted, synced graph is just two readers. Local durability, not sync.
3581
+ *
3582
+ * Because the backend is async, hydration cannot precede the first read: the store keeps its current
3583
+ * value, then adopts the persisted snapshot once the backend answers, UNLESS a write happened first
3584
+ * (an explicit boot-time write wins over stale disk). Writes are coalesced and flushed on teardown
3585
+ * and on page hide, so the last change is never lost. On the server it is a no-op.
3586
+ *
3587
+ * When the persisted shape evolves, pass `version` and a `migrate` hook: an older snapshot is
3588
+ * brought forward on boot before it is adopted, then re-persisted in the new shape. Because boot is
3589
+ * already async, `migrate` may be async, so the migration ladder can be lazy-imported.
3590
+ */
3591
+ declare function persist<T extends object>(source: WritableSignalStore<T>, opt: PersistOptions<T>): PersistHandle;
3592
+ /**
3593
+ * A `store` with {@link persist} already attached: a whole-value snapshot persisted to an async
3594
+ * backend and restored on boot. Equivalent to `const s = store(initial); persist(s, opt)` — reach
3595
+ * for `persist` directly when you want persistence on a store you already have (e.g. to also
3596
+ * `meshSync` it).
3597
+ */
3598
+ declare function persistedStore<T extends object>(initial: T, opt: PersistedStoreOptions<T>): PersistedStore<T>;
3599
+
3600
+ /** Identity selector for keyed array reconciliation: a property name, or a function per item. */
3601
+ type ReconcileKey = string | ((item: any) => unknown);
3602
+ /**
3603
+ * Produces a value equal to `next` but sharing as much of `prev`'s reference structure as possible:
3604
+ * an object subtree that did not change keeps its `prev` reference, and array items are matched by
3605
+ * `key` so a surviving item keeps its identity across a reorder/insert/remove (only added items are
3606
+ * new, only removed items are dropped). This is what lets a derived store recompute without tearing
3607
+ * down every downstream `computed` that reads an unchanged part of it.
3608
+ */
3609
+ declare function reconcile<T>(prev: T, next: T, key?: ReconcileKey): T;
3610
+ type ProjectionOptions = toStoreOptions & {
3611
+ /** Identity key for reconciling array items (default `'id'`). */
3612
+ readonly key?: ReconcileKey;
3613
+ };
3614
+ /**
3615
+ * A derived STORE, the store-shaped counterpart to `computed`. `fn` receives a mutable draft seeded
3616
+ * with the current value and either mutates it in place or returns a new value; whichever it does,
3617
+ * the result is reconciled against the previous value (see {@link reconcile}) so unchanged subtrees
3618
+ * keep reference identity and keyed array items keep their proxy identity. Reading through the
3619
+ * returned store is fine-grained: a `computed` over one field only recomputes when that field
3620
+ * actually changes, even though the whole projection re-ran.
3621
+ *
3622
+ * Recompute is pull-based, exactly like `computed`: the projection is memoized and re-runs on the
3623
+ * first read after a signal `fn` depends on changes, so reads are always coherent (no waiting on an
3624
+ * effect flush) and nothing recomputes while nobody reads. `fn` must be pure, it runs inside the
3625
+ * reactive computation. Prefer `computed` for a plain value; reach for `projection` when you want
3626
+ * the per-property tracking of a store on top of a derivation.
3166
3627
  *
3167
3628
  * ```ts
3168
- * const s = store({ todos: [{ done: false }] });
3169
- * const log = opLog(s, { origin: 'tab-a' });
3170
- * log.subscribe((b) => channel.postMessage(encode(b))); // ship
3171
- * channel.onmessage = (m) => log.apply(decode(m.data)); // apply — echo-free
3172
- * s.todos[0].done.set(true); // → { kind: 'set', path: ['todos', 0, 'done'], … }
3629
+ * const active = projection<User[]>(() => users().filter((u) => u.active), [], { key: 'id' });
3630
+ * // active[0].name(); surviving users keep identity across recomputes
3173
3631
  * ```
3632
+ *
3633
+ * Needs an injection context (or an explicit `injector`) for the store layer's cleanup on the main
3634
+ * thread; with an explicit store context (`createStoreContext()`) it is injector-free, so it also
3635
+ * runs on a worker host.
3636
+ *
3637
+ * @param fn receives the current draft; mutate it, or return new data.
3638
+ * @param seed the initial value, held before the first run.
3174
3639
  */
3175
- declare function opLog<T extends object>(source: WritableSignal<T>, opt?: CreateOpLogOptions): OpLog<T>;
3640
+ declare function projection<T extends object>(fn: (draft: T) => void | T, seed: T, opt?: ProjectionOptions): SignalStore<T>;
3176
3641
 
3177
3642
  /**
3178
3643
  * Interface for storage mechanisms compatible with the `stored` signal.
@@ -3339,10 +3804,25 @@ type SyncSignalOptions = {
3339
3804
  */
3340
3805
  injector?: Injector;
3341
3806
  };
3807
+ /**
3808
+ * Store mode (`tabSync(store, …)`): syncs structural OPS instead of whole values — concurrent
3809
+ * edits to different leaves merge instead of clobbering, and a joining tab hydrates from a
3810
+ * peer via the hello exchange (up-to-date / snapshot; op-protocol RFC §6).
3811
+ */
3812
+ type StoreTabSyncOptions = SyncSignalOptions & {
3813
+ /** Principal pseudonym on emitted envelopes. Tabs share one user, so a default is fine. */
3814
+ writer?: string;
3815
+ /** Per-path merge policies (`lww` default; `mergeThree`, `preserve`, or custom). */
3816
+ policies?: readonly MergePolicyEntry[];
3817
+ /** How long a joining tab waits for a peer's answer before deciding it IS the base. */
3818
+ helloTimeoutMs?: number;
3819
+ /** Max response jitter — first responder wins, others cancel. */
3820
+ jitterMs?: number;
3821
+ };
3342
3822
  /**
3343
3823
  * @example tabSync(signal('dark'), { id: 'theme' })
3344
3824
  */
3345
- declare function tabSync<T extends WritableSignal<any>>(sig: T, opt: SyncSignalOptions | string): T;
3825
+ declare function tabSync<T extends WritableSignal<any>>(sig: T, opt: StoreTabSyncOptions | SyncSignalOptions | string): T;
3346
3826
  /**
3347
3827
  * @deprecated Use `tabSync` with `SyncSignalOptions` instead and pass the options as the second argument
3348
3828
  * @throws {Error} When deterministic ID generation fails and no explicit ID is provided
@@ -3632,5 +4112,5 @@ type CreateHistoryOptions<T> = Omit<CreateSignalOptions<T[]>, 'equal'> & {
3632
4112
  */
3633
4113
  declare function withHistory<T>(sourceOrValue: WritableSignal<T> | T, opt?: CreateHistoryOptions<T>): SignalWithHistory<T>;
3634
4114
 
3635
- export { MmActivity, MmTransition, MmViewTransitionName, PAUSABLE_OPTIONS, SuspenseBoundary, SuspenseBoundaryBase, UnscopedSuspenseBoundary, activeTransaction, batteryStatus, bridgeScopeToPendingTasks, chunked, clipboard, combineWith, createAttributedPending, createForwardingScope, createTransaction, createTransitionScope, debounce, debounced, deferredValue, derived, distinct, elementSize, elementVisibility, extendStore, filter, filterWith, focusWithin, forkStore, geolocation, getTransitionScope, holdUntilReady, idle, indexArray, injectPaused, injectRegisterResource, injectStartTransaction, injectStartTransition, injectTransitionScope, invertBatch, isDerivation, isLeaf, isMutable, isOpaque, isStore, keepPrevious, keyArray, latest, map, mapArray, mapObject, mediaQuery, merge3, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, opLog, opaque, orientation, pageVisibility, pairwise, pausableComputed, pausableEffect, pausableSignal, pipeable, piped, pointerDrag, pooled, pooledArray, pooledMap, pooledSet, prefersDarkMode, prefersReducedMotion, provideForwardingTransitionScope, providePausableOptions, providePaused, provideTransitionScope, registerResource, resolvePause, scan, scrollPosition, select, sensor, sensors, signalFromEvent, startWith, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, use, windowSize, withHistory };
3636
- export type { BatteryStatus, ClipboardSignal, Computation, CreateChunkedOptions, CreateDebouncedOptions, CreateHistoryOptions, CreateLatestOptions, CreateOpLogOptions, CreatePooledOptions, CreateProvidedPooledOptions, CreateStoredOptions, CreateThrottledOptions, DebouncedSignal, DeferStrategy, DeferredSignal, DeferredValueOptions, DerivedSignal, ElementSize, ElementSizeOptions, ElementSizeSignal, ElementVisibilityOptions, ElementVisibilitySignal, ExtendStoreOptions, Fork, ForkStoreOptions, ForkStrategy, ForwardingTransitionScope, Frame, GeolocationOptions, GeolocationSignal, IdleOptions, IdleSignal, LatestSignal, MmTransitionContext, MousePositionOptions, MousePositionSignal, MutableSignal, MutableSignalStore, NetworkStatusSignal, OpBatch, OpLog, Opaque, PausableOptions, PauseOption, PipeableSignal, PointerDragOptions, PointerDragSignal, PointerDragState, PointerModifiers, PointerPoint, ReconcileFn, RegisterOptions, ResourceLike, ScreenOrientation, ScreenOrientationState, ScrollPosition, ScrollPositionOptions, ScrollPositionSignal, SensorRunOptions, SignalFromEventOptions, SignalStore, SignalWithHistory, StoreOp, StoreOptions, StoredSignal, SuspendType, ThrottledSignal, Transaction, TransactionRef, TransitionRef, TransitionScope, UntilOptions, UseSource, Vivify, WindowSize, WindowSizeOptions, WindowSizeSignal, WithVivify, WritableSignalStore, toStoreOptions };
4115
+ export { CONCURRENCY_INSTRUMENTATION, MmActivity, MmTransition, MmViewTransitionName, OP_PROTO_VERSION, PAUSABLE_OPTIONS, PERSISTED_STORE_OPTIONS, SuspenseBoundary, SuspenseBoundaryBase, UnscopedSuspenseBoundary, activeTransaction, applyOps, batteryStatus, bridgeScopeToPendingTasks, chunked, clipboard, combineWith, compareHlc, compareTotal, createAttributedPending, createConvergingApply, createForwardingScope, createHlcClock, createStoreContext, createTransaction, createTransitionScope, debounce, debounced, deferredValue, derived, diffOps, distinct, elementSize, elementVisibility, extendStore, filter, filterWith, focusWithin, forkStore, geolocation, getTransitionScope, holdUntilReady, idle, indexArray, injectPaused, injectRegisterResource, injectStartTransaction, injectStartTransition, injectTransitionScope, invertBatch, isConflicted, isDerivation, isLeaf, isMutable, isOpaque, isStore, keepPrevious, keyArray, keyedArray, latest, lww, map, mapArray, mapObject, mediaQuery, merge3, mergeThree, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, opLog, opSync, opaque, orientation, pageVisibility, pairwise, pausableComputed, pausableEffect, pausableSignal, perfCustomTracks, persist, persistedStore, pipeable, piped, pointerDrag, policyStrategy, pooled, pooledArray, pooledMap, pooledSet, prefersDarkMode, prefersReducedMotion, preserve, projection, provideConcurrencyInstrumentation, provideForwardingTransitionScope, providePausableOptions, providePaused, providePersistedStoreOptions, provideTransitionScope, rebaseOps, reconcile, registerResource, resolvePause, scan, scrollPosition, select, sensor, sensors, signalFromEvent, startWith, store, storeHistory, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, use, windowSize, withHistory };
4116
+ export type { AsyncStore, BatteryStatus, ClipboardSignal, Computation, ConcurrencyInstrumentation, Conflicted, ConvergingApply, CreateChunkedOptions, CreateDebouncedOptions, CreateHistoryOptions, CreateLatestOptions, CreateOpLogOptions, CreatePooledOptions, CreateProvidedPooledOptions, CreateStoredOptions, CreateThrottledOptions, CreateTransitionScopeOptions, DebouncedSignal, DeferStrategy, DeferredSignal, DeferredValueOptions, DerivedSignal, ElementSize, ElementSizeOptions, ElementSizeSignal, ElementVisibilityOptions, ElementVisibilitySignal, ExtendStoreOptions, Fork, ForkStoreOptions, ForkStrategy, ForwardingTransitionScope, Frame, GeolocationOptions, GeolocationSignal, Hlc, HlcClock, IdleOptions, IdleSignal, LatestSignal, MergeContext, MergeFn, MergePolicyEntry, MmTransitionContext, MousePositionOptions, MousePositionSignal, MutableSignal, MutableSignalStore, NetworkStatusSignal, OpBatch, OpEnvelope, OpLog, OpLogDriver, OpSync, OpSyncOptions, Opaque, PausableOptions, PauseOption, PersistHandle, PersistOptions, PersistedStore, PersistedStoreDefaults, PersistedStoreOptions, PipeableSignal, PointerDragOptions, PointerDragSignal, PointerDragState, PointerModifiers, PointerPoint, ProjectionOptions, RebaseResult, ReconcileFn, ReconcileKey, RegisterOptions, ResourceLike, ScreenOrientation, ScreenOrientationState, ScrollPosition, ScrollPositionOptions, ScrollPositionSignal, SensorRunOptions, SignalFromEventOptions, SignalStore, SignalWithHistory, StoreHistory, StoreHistoryOptions, StoreOp, StoreOptions, StoreTabSyncOptions, StoredSignal, SuspendType, SyncSignalOptions, ThrottledSignal, Transaction, TransactionRef, TransitionRef, TransitionScope, UntilOptions, UseSource, Vivify, WindowSize, WindowSizeOptions, WindowSizeSignal, WithVivify, WritableSignalStore, toStoreOptions };