@mmstack/primitives 20.10.0 → 20.11.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.
package/index.d.ts CHANGED
@@ -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
@@ -455,7 +505,13 @@ type TransitionScope = {
455
505
  */
456
506
  hold<T>(value: Signal<T>): Signal<T>;
457
507
  };
458
- declare function createTransitionScope(): TransitionScope;
508
+ type CreateTransitionScopeOptions = {
509
+ /** Scope identity for instrumentation events (idea/concurrency-devtools.md). */
510
+ readonly name?: string;
511
+ /** Optional observability listener; taps are no-ops when omitted (zero cost). */
512
+ readonly instrumentation?: ConcurrencyInstrumentation;
513
+ };
514
+ declare function createTransitionScope(opt?: CreateTransitionScopeOptions): TransitionScope;
459
515
  /**
460
516
  * The scope→`PendingTasks` bridge: while `scope.pending()` is true, hold an Angular
461
517
  * pending task so SSR serialization waits for the scope's in-flight loads — HTTP loads
@@ -470,7 +526,7 @@ declare function createTransitionScope(): TransitionScope;
470
526
  */
471
527
  declare function bridgeScopeToPendingTasks(scope: TransitionScope, injector?: Injector): void;
472
528
  /** Provide a fresh transition scope at a boundary so its subtree's resources are tracked independently. */
473
- declare function provideTransitionScope(): Provider;
529
+ declare function provideTransitionScope(opt?: CreateTransitionScopeOptions): Provider;
474
530
  declare function injectTransitionScope(): TransitionScope;
475
531
  /**
476
532
  * A transition scope that can be re-pointed at a delegate target at runtime. Reads and
@@ -837,23 +893,6 @@ type MutableSignal<T> = WritableSignal<T> & {
837
893
  * This is because a `.mutate()` call notifies its dependents that it has changed, but if the
838
894
  * reference to a derived object hasn't changed, the `computed` signal will not trigger its
839
895
  * 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
896
  */
858
897
  declare function mutable<T>(): MutableSignal<T | undefined>;
859
898
  declare function mutable<T>(initial: T): MutableSignal<T>;
@@ -1800,10 +1839,10 @@ declare function clipboard(opt?: string | SensorRunOptions): ClipboardSignal;
1800
1839
  /**
1801
1840
  * Represents the size of an element.
1802
1841
  */
1803
- interface ElementSize {
1842
+ type ElementSize = {
1804
1843
  width: number;
1805
1844
  height: number;
1806
- }
1845
+ };
1807
1846
  /**
1808
1847
  * Options for configuring the `elementSize` sensor.
1809
1848
  */
@@ -2762,6 +2801,128 @@ type ResolvableTarget = EventTargetLike | Signal<EventTargetLike | null>;
2762
2801
  declare function signalFromEvent<TEvent extends Event>(target: ResolvableTarget, eventName: string, initial: TEvent | null, opt?: SignalFromEventOptions): Signal<TEvent | null>;
2763
2802
  declare function signalFromEvent<TEvent extends Event, U>(target: ResolvableTarget, eventName: string, initial: U, project: (event: TEvent) => U, opt?: SignalFromEventOptions): Signal<U>;
2764
2803
 
2804
+ type Key$2 = string | number;
2805
+ /**
2806
+ * One structural operation. `set` on a key that did not previously exist carries NO `prev`
2807
+ * property (an absent key is not the same as a key holding `undefined` — the merge3 lesson),
2808
+ * which is what lets {@link invertBatch} invert an add into a delete.
2809
+ */
2810
+ type StoreOp = {
2811
+ kind: 'set';
2812
+ path: readonly Key$2[];
2813
+ next: unknown;
2814
+ prev?: unknown;
2815
+ } | {
2816
+ kind: 'delete';
2817
+ path: readonly Key$2[];
2818
+ prev: unknown;
2819
+ };
2820
+ /** One emission: every op derived from one commit window (a tick), in path order. */
2821
+ type OpBatch = {
2822
+ /** Identifies the emitting log — filter your own batches on a shared transport. */
2823
+ readonly origin: string;
2824
+ /** Per-log monotonic batch counter. */
2825
+ readonly version: number;
2826
+ readonly ops: readonly StoreOp[];
2827
+ };
2828
+ /**
2829
+ * Drives an {@link opLog}'s emission reaction. Given the `run` closure (which reads the source in
2830
+ * a tracking context and flushes the delta), a driver arranges for `run` to execute now and again
2831
+ * on every subsequent change, returning a handle that stops it. The default driver is an Angular
2832
+ * `effect` (needs an injector). Supply a custom driver to run an opLog with NO injector; a
2833
+ * renderer-independent one built on `@angular/core/primitives/signals` `createWatch` ships as
2834
+ * `microtaskOpLogDriver` from `@mmstack/worker/host` (the Web Worker seam).
2835
+ */
2836
+ type OpLogDriver = (run: () => void) => {
2837
+ destroy(): void;
2838
+ };
2839
+ type CreateOpLogOptions = {
2840
+ /** Transport identity for emitted batches. Defaults to a random id. */
2841
+ readonly origin?: string;
2842
+ /** Injection context for the default effect-based driver (required outside one). */
2843
+ readonly injector?: Injector;
2844
+ /**
2845
+ * Replaces the default Angular-`effect` emission driver. Supply a custom driver (e.g.
2846
+ * `microtaskOpLogDriver` from `@mmstack/worker/host`) to run an opLog with NO injector. When
2847
+ * given, `injector` is ignored and no injection context is required.
2848
+ */
2849
+ readonly driver?: OpLogDriver;
2850
+ };
2851
+ type OpLog<T extends object> = {
2852
+ /**
2853
+ * Ordered, lossless delivery of every emitted batch. Synchronous — don't write back into
2854
+ * the observed source from inside a callback (route remote data through {@link OpLog.apply}).
2855
+ */
2856
+ subscribe(cb: (batch: OpBatch) => void): () => void;
2857
+ /** The most recent batch — a lossy sampling view (devtools); use `subscribe` for transport. */
2858
+ readonly latest: Signal<OpBatch | null>;
2859
+ /**
2860
+ * Synchronously diff the source and emit any pending change NOW, rather than waiting for the
2861
+ * driver's scheduled run (an app tick, or a custom driver's microtask). Idempotent
2862
+ * and coalescing: writes since the last emission compose into one batch, and a `flush()` with
2863
+ * nothing pending is a no-op. Use it to make emission deterministic — the worker host calls it
2864
+ * to settle its mirror synchronously (tests), and it underpins the flush-before-apply honesty of
2865
+ * {@link OpLog.apply}. Independent of the driver: a later scheduled run simply finds no diff.
2866
+ */
2867
+ flush(): void;
2868
+ /**
2869
+ * Applies ops (a remote batch, a persisted journal entry, an {@link invertBatch} result)
2870
+ * atomically: ONE `set`, one notification wave. Also advances this log's diff baseline in
2871
+ * the same step, so an applied batch produces NO echo emission — sync loops terminate by
2872
+ * construction. Local writes pending in the current tick are flushed (emitted) first, so
2873
+ * they are never silently folded into the applied baseline.
2874
+ */
2875
+ apply(ops: OpBatch | readonly StoreOp[]): void;
2876
+ /** Stops observing and drops subscribers. Also happens when the injection context dies. */
2877
+ destroy(): void;
2878
+ };
2879
+ /**
2880
+ * Pure, store-free application of ops onto a plain root value, returning the next immutable root
2881
+ * (structural-sharing along op paths, missing containers vivified `'auto'`-style). This is the
2882
+ * same transform {@link OpLog.apply} runs, extracted so a replica can fold a received batch into
2883
+ * a value WITHOUT owning a diffing {@link opLog} — e.g. the worker-graph read-replica seam.
2884
+ * Accepts a batch or a bare op list.
2885
+ */
2886
+ declare function applyOps<T>(root: T, ops: OpBatch | readonly StoreOp[]): T;
2887
+ /**
2888
+ * Pure reference-pruned structural diff of two roots into minimal ops (the emission core of
2889
+ * {@link opLog}, exported so code outside a log can produce a batch — e.g. diffing a scratch
2890
+ * draft against a replica's current value to route a write to its owner). Trusts the
2891
+ * copy-on-write contract: an untouched subtree that kept its reference is skipped.
2892
+ */
2893
+ declare function diffOps(prev: unknown, next: unknown): StoreOp[];
2894
+ /**
2895
+ * Inverts a batch for undo: reversed order, `set`↔its own inverse (an add — a `set` with no
2896
+ * `prev` — inverts to a `delete`; a `delete` inverts to a `set` restoring `prev`). Feed the
2897
+ * result to {@link OpLog.apply}. Requires the ops' `prev`s, which in-memory batches always
2898
+ * carry — a wire-serialized batch that stripped them is not invertible.
2899
+ */
2900
+ declare function invertBatch(batch: OpBatch | readonly StoreOp[]): StoreOp[];
2901
+ /**
2902
+ * Observes a copy-on-write signal (a `store`'s root, or any `WritableSignal` holding
2903
+ * immutably-updated objects) and emits its changes as minimal structural op batches — the
2904
+ * shared substrate for sync (ship batches, `apply` remote ones), persistence (journal
2905
+ * batches, replay on boot), undo ({@link invertBatch}), and devtools (`latest`).
2906
+ *
2907
+ * Zero store-core involvement and zero cost when unused: emission is a reference-pruned diff
2908
+ * of the root value per tick (structural sharing makes it O(changed paths)), driven by one
2909
+ * effect. A batch therefore coalesces everything written in one tick — for coarser,
2910
+ * intentional units, stage writes on a `forkStore` and `commit()` (one set → one batch).
2911
+ *
2912
+ * NOT supported on mutable stores/signals: in-place mutation keeps reference identity, which
2913
+ * defeats the diff (same reason `forkStore`'s `'fine'` strategy refuses them) — a dev-mode
2914
+ * warning fires and nothing emits.
2915
+ *
2916
+ * ```ts
2917
+ * const s = store({ todos: [{ done: false }] });
2918
+ * const log = opLog(s, { origin: 'tab-a' });
2919
+ * log.subscribe((b) => channel.postMessage(encode(b))); // ship
2920
+ * channel.onmessage = (m) => log.apply(decode(m.data)); // apply — echo-free
2921
+ * s.todos[0].done.set(true); // → { kind: 'set', path: ['todos', 0, 'done'], … }
2922
+ * ```
2923
+ */
2924
+ declare function opLog<T extends object>(source: WritableSignal<T>, opt?: CreateOpLogOptions): OpLog<T>;
2925
+
2765
2926
  /**
2766
2927
  * @internal Runtime brand carrying a store node's lazily-built leaf probe. Exported (like
2767
2928
  * {@link OPAQUE}) only so the `{ readonly [LEAF]: () => boolean }` brand on the store types is
@@ -3024,6 +3185,32 @@ declare function mutableStore<T extends AnyRecord>(value: T, opt?: CreateSignalO
3024
3185
  */
3025
3186
  noUnionLeaves?: boolean;
3026
3187
  }): MutableSignalStore<T>;
3188
+ /**
3189
+ * Builds a DI-less store context — the shared proxy-cache and cleanup registry that {@link toStore}
3190
+ * normally resolves from the injector — so a `store`/`toStore`/`opLog` graph can run with NO Angular
3191
+ * injection context. Spread the result into the options:
3192
+ *
3193
+ * ```ts
3194
+ * import { microtaskOpLogDriver } from '@mmstack/worker/host';
3195
+ * const ctx = createStoreContext();
3196
+ * const s = store({ todos: [] }, ctx);
3197
+ * const log = opLog(s, { driver: microtaskOpLogDriver(), origin: 'worker' }); // no injector anywhere
3198
+ * ```
3199
+ *
3200
+ * **This is a worker-only fallback — do NOT use it on the main thread.** DI is the default and
3201
+ * correct path in an app: the injector scopes the proxy-cache/cleanup singletons per app instance,
3202
+ * which on the SERVER keeps one request's store identity from bleeding into another's (the exact
3203
+ * hazard a module-scope singleton would reintroduce). A Web Worker is safe because it is a single
3204
+ * store graph per thread and never runs during SSR (spawn is a `PLATFORM_ID === 'server'` no-op),
3205
+ * so there is no cross-request scope to contaminate. Never hoist a `createStoreContext()` to module
3206
+ * scope on a shared/main thread.
3207
+ *
3208
+ * **Share ONE context across every store in a worker** — the same way `providedIn: 'root'` shares
3209
+ * one cache across all of an app's stores. `@mmstack/worker/host` memoizes this per worker
3210
+ * (`workerStoreContext()`); reach for `createStoreContext()` directly only in a bare
3211
+ * (non-worker-host) DI-less setup, and hold the single instance yourself.
3212
+ */
3213
+ declare function createStoreContext(): toStoreOptions;
3027
3214
 
3028
3215
  /**
3029
3216
  * A 3-way merge of a forked value against a changed base: given the common `ancestor` (the base
@@ -3067,6 +3254,8 @@ type Fork<T> = {
3067
3254
  commit(): void;
3068
3255
  /** Drop staged writes — the fork reads through to the base again. */
3069
3256
  discard(): void;
3257
+ /** The staged delta vs the CURRENT base, as structural ops (inspect, persist, invert). */
3258
+ ops(): StoreOp[];
3070
3259
  };
3071
3260
  /**
3072
3261
  * Per-path 3-way merge. Reference-equality short-circuits do the work: a subtree the fork never
@@ -3093,86 +3282,355 @@ type ForkStoreOptions<T> = toStoreOptions & {
3093
3282
  };
3094
3283
  declare function forkStore<T extends Record<string, any>>(base: WritableSignalStore<T>, opt?: ForkStoreOptions<T>): Fork<T>;
3095
3284
 
3285
+ /** Hybrid logical clock stamp: physical epoch ms + logical counter for same-ms ordering. */
3286
+ type Hlc = {
3287
+ readonly p: number;
3288
+ readonly l: number;
3289
+ };
3290
+ /** Total order over stamps alone; ties break on `writer` via {@link compareTotal}. */
3291
+ declare function compareHlc(a: Hlc, b: Hlc): number;
3292
+ /** The protocol's total order: (hlc.p, hlc.l, writer). Never returns 0 for distinct writers. */
3293
+ declare function compareTotal(a: Hlc, writerA: string, b: Hlc, writerB: string): number;
3294
+ type HlcClock = {
3295
+ /** Stamp for a locally-emitted envelope: monotonic even when wall time stalls or rewinds. */
3296
+ next(): Hlc;
3297
+ /** Fold an observed remote stamp in, so subsequent local stamps sort after it. */
3298
+ observe(remote: Hlc): void;
3299
+ };
3300
+ /**
3301
+ * HLC per Kulkarni et al.: convergence never depends on wall clocks, but LWW fairness
3302
+ * degrades under large skew, so observing a remote clock far ahead warns in dev mode.
3303
+ */
3304
+ declare function createHlcClock(now?: () => number): HlcClock;
3305
+
3306
+ declare const OP_PROTO_VERSION = 1;
3096
3307
  type Key = string | number;
3097
3308
  /**
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.
3309
+ * The wire/journal record (op-protocol RFC §3). `writer` is an opaque principal pseudonym
3310
+ * natural identity never enters the envelope; `origin` identifies the emitting log instance.
3101
3311
  */
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. */
3312
+ type OpEnvelope = {
3313
+ readonly proto: number;
3115
3314
  readonly origin: string;
3116
- /** Per-log monotonic batch counter. */
3315
+ readonly writer: string;
3117
3316
  readonly version: number;
3317
+ readonly hlc: Hlc;
3318
+ readonly policyVersion: number;
3118
3319
  readonly ops: readonly StoreOp[];
3119
3320
  };
3120
- type CreateOpLogOptions = {
3121
- /** Transport identity for emitted batches. Defaults to a random id. */
3321
+ declare const CONFLICT_BRAND = "~mmstackConflict";
3322
+ /**
3323
+ * A preserved (jj-style) conflict: both sides survive as data, sync never blocks, and
3324
+ * resolution is just a later write. String-branded so it survives structured clone.
3325
+ */
3326
+ type Conflicted<T = unknown> = {
3327
+ readonly [CONFLICT_BRAND]: true;
3328
+ readonly mine: T;
3329
+ readonly theirs: T;
3330
+ readonly ancestor?: T;
3331
+ };
3332
+ declare function isConflicted<T = unknown>(value: unknown): value is Conflicted<T>;
3333
+ type MergeContext = {
3334
+ readonly path: readonly Key[];
3335
+ };
3336
+ /**
3337
+ * Resolves a concurrent set-vs-set collision. Called with a deterministic argument order
3338
+ * (`mine` = the side winning the total order) so every peer computes the same value.
3339
+ */
3340
+ type MergeFn = (ancestor: unknown, mine: unknown, theirs: unknown, ctx: MergeContext) => unknown;
3341
+ type MergePolicyEntry = {
3342
+ /** `'todos.*.title'` or a segment array; `'*'` matches exactly one segment. */
3343
+ readonly path: string | readonly Key[];
3344
+ readonly merge: MergeFn;
3345
+ };
3346
+ declare const lww: MergeFn;
3347
+ declare const mergeThree: MergeFn;
3348
+ declare const preserve: MergeFn;
3349
+ /**
3350
+ * Identity-aware array merge (op-protocol RFC §12 v0): reconciles two concurrent versions of
3351
+ * an array item-wise by a user-provided identity, instead of last-writer-wins on the whole
3352
+ * array. Items are matched by key; per-item fields merge via `merge3` against the ancestor
3353
+ * item; items added on either side survive; an item removed on either side and unedited on
3354
+ * the other stays removed. Item ORDER follows `mine` (the total-order winner), with `theirs`-
3355
+ * only additions appended — positional merging is out of scope (fractional indexing is the
3356
+ * known upgrade if dogfooding demands it). Arrays still TRAVEL as whole-value sets; identity
3357
+ * only shapes conflict resolution, so the wire format is untouched.
3358
+ */
3359
+ declare function keyedArray(identity: (item: unknown) => unknown, opt?: {
3360
+ item?: MergeFn;
3361
+ }): MergeFn;
3362
+ type ConvergingApply = {
3363
+ /**
3364
+ * Fold an envelope into the register map and return the ops the local store must apply
3365
+ * (post-dominance, post-policy, including replays of newer descendant winners). Pass
3366
+ * `local: true` for envelopes this peer emitted itself: registered, nothing returned.
3367
+ */
3368
+ ingest(env: OpEnvelope, opt?: {
3369
+ local?: boolean;
3370
+ }): StoreOp[];
3371
+ /** Drop all registers (snapshot compaction / rehydration boundary). */
3372
+ reset(): void;
3373
+ };
3374
+ /**
3375
+ * The unsequenced-topology convergence core (op-protocol RFC §4): a per-path last-writer-wins
3376
+ * register map over the total order (hlc, writer), with subtree dominance. Order-independent:
3377
+ * any arrival order of the same envelope set yields the same state.
3378
+ */
3379
+ declare function createConvergingApply(opt?: {
3380
+ policies?: readonly MergePolicyEntry[];
3381
+ }): ConvergingApply;
3382
+ type RebaseResult<T = unknown> = {
3383
+ root: T;
3384
+ /** Pending batches re-based onto the remote state, `prev`s refreshed. */
3385
+ pending: StoreOp[][];
3386
+ };
3387
+ /**
3388
+ * The shared rebase routine (op-protocol RFC §5): invert pending, apply remote, re-apply
3389
+ * pending through the merge policies. Pure — branching's `rebase()` and the sequenced relay
3390
+ * client both call this.
3391
+ */
3392
+ declare function rebaseOps<T>(root: T, pending: readonly (readonly StoreOp[])[], remote: readonly StoreOp[], policies?: readonly MergePolicyEntry[]): RebaseResult<T>;
3393
+ /**
3394
+ * A per-path-policy `ForkStrategy` for `forkStore`: a three-way reconcile built from the
3395
+ * shared rebase (invert mine → apply theirs' delta → re-apply mine through the policies).
3396
+ * Paths only one side touched resolve like `merge3`; paths BOTH touched go through the
3397
+ * matching {@link MergePolicyEntry} (`lww` default — fork wins, matching `'fine'`; or
3398
+ * `mergeThree` / `preserve` / custom). Same copy-on-write contract as `'fine'`.
3399
+ */
3400
+ declare function policyStrategy<T>(policies: readonly MergePolicyEntry[]): (ancestor: T, mine: T, theirs: T) => T;
3401
+ type OpSyncOptions = {
3402
+ /** Opaque principal pseudonym — provided by the app, never minted here (RFC §3). */
3403
+ readonly writer: string;
3122
3404
  readonly origin?: string;
3123
- /** Injection context for the observing effect (required outside one). */
3405
+ readonly policyVersion?: number;
3406
+ readonly policies?: readonly MergePolicyEntry[];
3407
+ readonly clock?: HlcClock;
3124
3408
  readonly injector?: Injector;
3409
+ readonly driver?: OpLogDriver;
3410
+ /** A version gap from a known origin (missed envelopes) — the resync hook. */
3411
+ readonly onGap?: (origin: string, expected: number, got: number) => void;
3125
3412
  };
3126
- type OpLog<T extends object> = {
3413
+ type OpSync<T = unknown> = {
3414
+ readonly origin: string;
3415
+ /** Locally-emitted envelopes, ready for a transport. */
3416
+ subscribe(cb: (env: OpEnvelope) => void): () => void;
3417
+ /** Converging apply of a remote envelope (echo-free; own-origin envelopes are ignored). */
3418
+ receive(env: OpEnvelope): void;
3419
+ /** Synchronously emit any pending local delta now. */
3420
+ flush(): void;
3421
+ /** Per-origin latest versions — the handshake watermark. */
3422
+ watermark(): Record<string, number>;
3423
+ /** The current root + watermark, for answering a peer's hello. */
3424
+ snapshot(): {
3425
+ root: T;
3426
+ wm: Record<string, number>;
3427
+ };
3127
3428
  /**
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}).
3429
+ * Emit the CURRENT root as a root-set envelopethe fresh-room seed of the relay
3430
+ * contract (a room's snapshot root becomes complete once seeded).
3130
3431
  */
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>;
3432
+ seed(): void;
3134
3433
  /**
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.
3434
+ * Replace local state with a peer's snapshot, atomically (one notification wave).
3435
+ * Local envelopes the snapshot doesn't cover (per its watermark) are re-applied on
3436
+ * top, so writes made before hydration are never silently lost.
3140
3437
  */
3141
- apply(ops: OpBatch | readonly StoreOp[]): void;
3142
- /** Stops observing and drops subscribers. Also happens when the injection context dies. */
3438
+ hydrate(root: T, wm?: Record<string, number>): void;
3439
+ destroy(): void;
3440
+ };
3441
+ declare function opSync<T extends object>(source: WritableSignal<T>, opt: OpSyncOptions): OpSync<T>;
3442
+
3443
+ type StoreHistory = {
3444
+ readonly canUndo: Signal<boolean>;
3445
+ readonly canRedo: Signal<boolean>;
3446
+ /** Revert the most recent tracked change; a no-op when nothing is undoable. */
3447
+ undo(): void;
3448
+ /** Re-apply the most recently undone change. */
3449
+ redo(): void;
3450
+ /** Forget all tracked history (e.g. after a save boundary). */
3451
+ clear(): void;
3143
3452
  destroy(): void;
3144
3453
  };
3454
+ type StoreHistoryOptions = CreateOpLogOptions & {
3455
+ /** Max entries kept per stack (default 100). */
3456
+ readonly limit?: number;
3457
+ /**
3458
+ * The change stream to track. Defaults to self-diffing `source` (every change to the store
3459
+ * becomes undoable). For collaborative-safe undo, pass a sync client's LOCAL envelope stream
3460
+ * (e.g. an `opSync`'s `subscribe`, which fires only for this peer's own writes) — remote
3461
+ * peers' changes then never land on your undo stack.
3462
+ */
3463
+ readonly track?: {
3464
+ subscribe(cb: (batch: OpBatch) => void): () => void;
3465
+ };
3466
+ };
3145
3467
  /**
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.
3468
+ * Undo/redo for a copy-on-write store, built on the op-log: each tracked change is stored as
3469
+ * its inverse batch, so `undo()` is one `apply` and history costs only the diffs, not full
3470
+ * snapshots. Redoing is invert-of-the-inverse. A new edit made after an undo clears the redo
3471
+ * stack (linear history). Applying a redo/undo does not itself re-enter history.
3472
+ *
3473
+ * Composes with sync for collaborative undo: pass `track: syncClient` so only YOUR writes are
3474
+ * undoable, while `undo()` emits a normal op that propagates to peers (it writes through the
3475
+ * store, which the sync client picks up).
3150
3476
  */
3151
- declare function invertBatch(batch: OpBatch | readonly StoreOp[]): StoreOp[];
3477
+ declare function storeHistory<T extends object>(source: WritableSignal<T>, opt?: StoreHistoryOptions): StoreHistory;
3478
+
3479
+ type MaybePromise<T> = T | Promise<T>;
3152
3480
  /**
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`).
3481
+ * The minimal async key/value contract persistence needs. Deliberately matches `idb-keyval`'s
3482
+ * top-level `get`/`set`/`del` so its module drops in with no wrapper (`persist(s, { key, store:
3483
+ * idbKeyval })`). Any store backed by structured clone (idb-keyval, Dexie) can hold complex values
3484
+ * without a serialize hook. A Dexie table needs a tiny adapter because it names things differently:
3157
3485
  *
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).
3486
+ * ```ts
3487
+ * const table = db.table<{ key: string; value: unknown }>('kv');
3488
+ * const asyncStore: AsyncStore = {
3489
+ * get: (k) => table.get(k).then((r) => r?.value),
3490
+ * set: (k, v) => table.put({ key: k, value: v }).then(() => undefined),
3491
+ * del: (k) => table.delete(k),
3492
+ * };
3493
+ * ```
3494
+ */
3495
+ type AsyncStore = {
3496
+ get(key: string): MaybePromise<unknown>;
3497
+ set(key: string, value: unknown): MaybePromise<void>;
3498
+ del(key: string): MaybePromise<void>;
3499
+ };
3500
+ /** Persistence options — the reader-side settings, independent of how the store was created. */
3501
+ type PersistOptions<T> = {
3502
+ /** Storage key for this store's snapshot. Required per call. */
3503
+ readonly key: string;
3504
+ /** The async backend. Falls back to the provided default (see {@link providePersistedStoreOptions}). */
3505
+ readonly store?: AsyncStore;
3506
+ /** Encode before writing. Default identity: structured-clone backends keep complex values. */
3507
+ readonly serialize?: (value: T) => unknown;
3508
+ /** Decode after reading. Default identity. */
3509
+ readonly deserialize?: (raw: unknown) => T;
3510
+ /**
3511
+ * Current schema version of the persisted value. When set, snapshots are written wrapped in a
3512
+ * small version envelope, and a snapshot stamped with an older version is passed through
3513
+ * {@link PersistOptions.migrate} on boot before it is adopted. A snapshot from a *newer* version
3514
+ * than this build is left untouched (a newer client wrote it).
3515
+ */
3516
+ readonly version?: number;
3517
+ /**
3518
+ * Bring a snapshot from an older `version` up to the current shape. It runs during boot, which
3519
+ * is already async, so it may be async too: lazy-import the migration ladder here and only pay
3520
+ * for it when there is old data to migrate. Receives the decoded old value and the version it
3521
+ * was written with (`0` for a pre-versioning snapshot).
3522
+ */
3523
+ readonly migrate?: (data: unknown, fromVersion: number) => MaybePromise<T>;
3524
+ /** Coalesce writes by this many ms (default 300). A flush/teardown always writes immediately. */
3525
+ readonly writeDebounceMs?: number;
3526
+ readonly injector?: Injector;
3527
+ };
3528
+ type PersistedStoreOptions<T extends object> = CreateSignalOptions<T> & toStoreOptions & PersistOptions<T>;
3529
+ /**
3530
+ * App-wide defaults for {@link persist} / {@link persistedStore}. Only cross-type settings live
3531
+ * here; `serialize`/`deserialize` are per-call because they depend on the store's value type.
3532
+ */
3533
+ type PersistedStoreDefaults = {
3534
+ readonly store?: AsyncStore;
3535
+ readonly writeDebounceMs?: number;
3536
+ };
3537
+ declare const PERSISTED_STORE_OPTIONS: InjectionToken<PersistedStoreDefaults>;
3538
+ /**
3539
+ * Wire the {@link AsyncStore} backend (and any shared debounce) once, override per call. The
3540
+ * typical use is to install idb-keyval at bootstrap so every `persist`/`persistedStore` persists
3541
+ * without re-passing the backend.
3162
3542
  *
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.
3543
+ * @example
3544
+ * import * as idbKeyval from 'idb-keyval';
3545
+ * providePersistedStoreOptions({ store: idbKeyval });
3546
+ */
3547
+ declare function providePersistedStoreOptions(opt: PersistedStoreDefaults): Provider;
3548
+ /** Persistence controls for a store, from {@link persist}. */
3549
+ type PersistHandle = {
3550
+ /**
3551
+ * `false` until the first read from the backend settles (or immediately `true` on the server
3552
+ * and when no backend is configured). Gate first paint on it if a stale-flash matters.
3553
+ */
3554
+ readonly hydrated: Signal<boolean>;
3555
+ /** Force any pending debounced write to the backend now. */
3556
+ flush(): Promise<void>;
3557
+ /** Remove the snapshot from the backend and reset the store to the value it held when attached. */
3558
+ clear(): Promise<void>;
3559
+ };
3560
+ /**
3561
+ * A store plus its persistence controls. Shaped like {@link Fork} (a `.store` field, not the
3562
+ * store itself) because the store is a proxy where any property access resolves a child path,
3563
+ * so controls cannot live on it directly.
3564
+ */
3565
+ type PersistedStore<T extends object> = {
3566
+ /** The live store. Reads are synchronous; it holds the initial value until hydration lands. */
3567
+ readonly store: WritableSignalStore<T>;
3568
+ } & PersistHandle;
3569
+ /**
3570
+ * Attach durable local persistence to an EXISTING store: its whole-value snapshot is written to an
3571
+ * async backend (IndexedDB via idb-keyval or Dexie) and restored on boot. A reader over the store,
3572
+ * so it composes with the other op-log readers (`tabSync`, `@mmstack/mesh`) on the same store — a
3573
+ * persisted, synced graph is just two readers. Local durability, not sync.
3574
+ *
3575
+ * Because the backend is async, hydration cannot precede the first read: the store keeps its current
3576
+ * value, then adopts the persisted snapshot once the backend answers, UNLESS a write happened first
3577
+ * (an explicit boot-time write wins over stale disk). Writes are coalesced and flushed on teardown
3578
+ * and on page hide, so the last change is never lost. On the server it is a no-op.
3579
+ *
3580
+ * When the persisted shape evolves, pass `version` and a `migrate` hook: an older snapshot is
3581
+ * brought forward on boot before it is adopted, then re-persisted in the new shape. Because boot is
3582
+ * already async, `migrate` may be async, so the migration ladder can be lazy-imported.
3583
+ */
3584
+ declare function persist<T extends object>(source: WritableSignalStore<T>, opt: PersistOptions<T>): PersistHandle;
3585
+ /**
3586
+ * A `store` with {@link persist} already attached: a whole-value snapshot persisted to an async
3587
+ * backend and restored on boot. Equivalent to `const s = store(initial); persist(s, opt)` — reach
3588
+ * for `persist` directly when you want persistence on a store you already have (e.g. to also
3589
+ * `meshSync` it).
3590
+ */
3591
+ declare function persistedStore<T extends object>(initial: T, opt: PersistedStoreOptions<T>): PersistedStore<T>;
3592
+
3593
+ /** Identity selector for keyed array reconciliation: a property name, or a function per item. */
3594
+ type ReconcileKey = string | ((item: any) => unknown);
3595
+ /**
3596
+ * Produces a value equal to `next` but sharing as much of `prev`'s reference structure as possible:
3597
+ * an object subtree that did not change keeps its `prev` reference, and array items are matched by
3598
+ * `key` so a surviving item keeps its identity across a reorder/insert/remove (only added items are
3599
+ * new, only removed items are dropped). This is what lets a derived store recompute without tearing
3600
+ * down every downstream `computed` that reads an unchanged part of it.
3601
+ */
3602
+ declare function reconcile<T>(prev: T, next: T, key?: ReconcileKey): T;
3603
+ type ProjectionOptions = toStoreOptions & {
3604
+ /** Identity key for reconciling array items (default `'id'`). */
3605
+ readonly key?: ReconcileKey;
3606
+ };
3607
+ /**
3608
+ * A derived STORE, the store-shaped counterpart to `computed`. `fn` receives a mutable draft seeded
3609
+ * with the current value and either mutates it in place or returns a new value; whichever it does,
3610
+ * the result is reconciled against the previous value (see {@link reconcile}) so unchanged subtrees
3611
+ * keep reference identity and keyed array items keep their proxy identity. Reading through the
3612
+ * returned store is fine-grained: a `computed` over one field only recomputes when that field
3613
+ * actually changes, even though the whole projection re-ran.
3614
+ *
3615
+ * Recompute is pull-based, exactly like `computed`: the projection is memoized and re-runs on the
3616
+ * first read after a signal `fn` depends on changes, so reads are always coherent (no waiting on an
3617
+ * effect flush) and nothing recomputes while nobody reads. `fn` must be pure, it runs inside the
3618
+ * reactive computation. Prefer `computed` for a plain value; reach for `projection` when you want
3619
+ * the per-property tracking of a store on top of a derivation.
3166
3620
  *
3167
3621
  * ```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'], … }
3622
+ * const active = projection<User[]>(() => users().filter((u) => u.active), [], { key: 'id' });
3623
+ * // active[0].name(); surviving users keep identity across recomputes
3173
3624
  * ```
3625
+ *
3626
+ * Needs an injection context (or an explicit `injector`) for the store layer's cleanup on the main
3627
+ * thread; with an explicit store context (`createStoreContext()`) it is injector-free, so it also
3628
+ * runs on a worker host.
3629
+ *
3630
+ * @param fn receives the current draft; mutate it, or return new data.
3631
+ * @param seed the initial value, held before the first run.
3174
3632
  */
3175
- declare function opLog<T extends object>(source: WritableSignal<T>, opt?: CreateOpLogOptions): OpLog<T>;
3633
+ declare function projection<T extends object>(fn: (draft: T) => void | T, seed: T, opt?: ProjectionOptions): SignalStore<T>;
3176
3634
 
3177
3635
  /**
3178
3636
  * Interface for storage mechanisms compatible with the `stored` signal.
@@ -3339,10 +3797,25 @@ type SyncSignalOptions = {
3339
3797
  */
3340
3798
  injector?: Injector;
3341
3799
  };
3800
+ /**
3801
+ * Store mode (`tabSync(store, …)`): syncs structural OPS instead of whole values — concurrent
3802
+ * edits to different leaves merge instead of clobbering, and a joining tab hydrates from a
3803
+ * peer via the hello exchange (up-to-date / snapshot; op-protocol RFC §6).
3804
+ */
3805
+ type StoreTabSyncOptions = SyncSignalOptions & {
3806
+ /** Principal pseudonym on emitted envelopes. Tabs share one user, so a default is fine. */
3807
+ writer?: string;
3808
+ /** Per-path merge policies (`lww` default; `mergeThree`, `preserve`, or custom). */
3809
+ policies?: readonly MergePolicyEntry[];
3810
+ /** How long a joining tab waits for a peer's answer before deciding it IS the base. */
3811
+ helloTimeoutMs?: number;
3812
+ /** Max response jitter — first responder wins, others cancel. */
3813
+ jitterMs?: number;
3814
+ };
3342
3815
  /**
3343
3816
  * @example tabSync(signal('dark'), { id: 'theme' })
3344
3817
  */
3345
- declare function tabSync<T extends WritableSignal<any>>(sig: T, opt: SyncSignalOptions | string): T;
3818
+ declare function tabSync<T extends WritableSignal<any>>(sig: T, opt: StoreTabSyncOptions | SyncSignalOptions | string): T;
3346
3819
  /**
3347
3820
  * @deprecated Use `tabSync` with `SyncSignalOptions` instead and pass the options as the second argument
3348
3821
  * @throws {Error} When deterministic ID generation fails and no explicit ID is provided
@@ -3632,5 +4105,5 @@ type CreateHistoryOptions<T> = Omit<CreateSignalOptions<T[]>, 'equal'> & {
3632
4105
  */
3633
4106
  declare function withHistory<T>(sourceOrValue: WritableSignal<T> | T, opt?: CreateHistoryOptions<T>): SignalWithHistory<T>;
3634
4107
 
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 };
4108
+ 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 };
4109
+ 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 };