@mmstack/primitives 22.5.0 → 22.5.1

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/README.md CHANGED
@@ -17,7 +17,7 @@ npm install @mmstack/primitives
17
17
 
18
18
  - [Writable signal variants](#writable-signal-variants) — `mutable`, `derived`, `store` / `mutableStore`, `forkStore`, `toWritable`
19
19
  - [Timing & propagation](#timing--propagation) — `debounced`, `throttled`, `until`
20
- - [Reactive collections](#reactive-collections) — `indexArray`, `keyArray`, `mapObject`
20
+ - [Reactive collections](#reactive-collections) — `indexArray`, `keyArray`, `mapObject`, `projection`
21
21
  - [Effects](#effects) — `nestedEffect`
22
22
  - [Concurrency & transitions](#concurrency--transitions) — `keepPrevious`, keep-alive (`MmActivity`), `pausable*` / `providePausableOptions`, Suspense (`mm-suspense`), hold-and-swap (`*mmTransition`), per-element morphs (`mmViewTransitionName`), async derivations (`latest` / `use`), `deferredValue`, `startTransition` / `startTransaction`, `holdUntilReady`
23
23
  - [History & persistence](#history--persistence) — `withHistory`, `stored`, `tabSync`, `opLog`
@@ -303,6 +303,32 @@ const controls = mapObject(
303
303
  );
304
304
  ```
305
305
 
306
+ ### `projection`
307
+
308
+ A derived **store**, the store-shaped counterpart to `computed`. Where `derived` slices one value out of a source and `indexArray` / `keyArray` map a list, `projection` derives a whole store subtree from a computation. `fn` receives a mutable draft seeded with the current value and either mutates it or returns new data; the result is reconciled against the previous value so unchanged object subtrees keep their reference and keyed array items keep their identity across recomputes. Reading through the returned store is per-leaf, so a `computed` over one field only recomputes when that field actually changes, even though the whole projection re-ran.
309
+
310
+ ```typescript
311
+ import { projection } from '@mmstack/primitives';
312
+
313
+ const users = signal<User[]>([]);
314
+
315
+ // return form: derive a filtered collection, reconciled by id
316
+ const active = projection<User[]>(() => users().filter((u) => u.active), [], {
317
+ key: 'id',
318
+ });
319
+
320
+ // mutate form: update fields on the draft
321
+ const summary = projection<{ total: number; active: number }>(
322
+ (draft) => {
323
+ draft.total = users().length;
324
+ draft.active = users().filter((u) => u.active).length;
325
+ },
326
+ { total: 0, active: 0 },
327
+ );
328
+ ```
329
+
330
+ Recompute is pull-based, exactly like `computed`: memoized, re-run on the first read after a dependency changes, coherent immediately after a write (no waiting on an effect flush), and skipped entirely while nobody reads. `fn` must be pure since it runs inside the reactive computation. Prefer `computed` for a plain value, and reach for `projection` when you want the per-property tracking of a store on top of a derivation. The standalone `reconcile(prev, next, key)` is exported too, for producing a reference-stable value by hand. Values must be structured-clonable (the draft is a clone of the current value). With an explicit store context (`createStoreContext()`) a projection is injector-free, so it also runs on a worker host.
331
+
306
332
  ## Effects
307
333
 
308
334
  ### `nestedEffect`
@@ -681,6 +707,7 @@ log.latest(); // Signal<OpBatch | null> — lossy sampling (devtools-style)
681
707
  state.user.name.set('Bea');
682
708
  // → { origin, version, ops: [{ kind: 'set', path: ['user','name'], next: 'Bea', prev: 'Ann' }] }
683
709
 
710
+ log.flush(); // synchronously emit any pending change now, instead of waiting for the tick. idempotent, no-op when clean.
684
711
  log.apply(remoteBatch); // applies ops in ONE commit AND advances the diff baseline —
685
712
  // so applying a remote batch emits no echo batch (sync loops terminate by construction)
686
713
  invertBatch(batch); // prev-based inverse — undo is a data transform
@@ -688,6 +715,8 @@ invertBatch(batch); // prev-based inverse — undo is a data transform
688
715
 
689
716
  Batching is per tick (two writes to one leaf in a tick emit one composed op), `prev` is always carried in-memory (structural sharing makes it free — wire serializers decide whether to keep it), arrays diff per-index at equal lengths and as whole-array ops on length change, and a `forkStore`'s `commit()` lands as a single batch — fork *is* the transaction primitive. Mutable stores are unsupported (in-place mutation defeats ref-identity diffing; dev warn). This is the substrate for worker mirrors, tab/mesh sync, persistence journals, and undo — one protocol, many consumers.
690
717
 
718
+ An `opLog` can also run with no Angular injector, which is what lets the graph mirror into a Web Worker. Pass `driver: microtaskOpLogDriver()` to drive emission off the microtask queue instead of an `effect()`, and build the store with `createStoreContext()` (a self-contained proxy cache) so `store` and `opLog` work in a worker or a plain Node process. The pure helpers `applyOps(root, ops)` and `diffOps(prev, next)` apply and produce batches without owning a log. [`@mmstack/worker`](https://www.npmjs.com/package/@mmstack/worker) is built directly on these seams.
719
+
691
720
  ## Performance helpers
692
721
 
693
722
  ### `chunked`
@@ -4296,7 +4296,11 @@ function buildChildNode(target, prop, isMutableSource, options) {
4296
4296
  function toStore(source, { injector, vivify = false, noUnionLeaves = false, ...rest } = {}) {
4297
4297
  if (isStore(source))
4298
4298
  return source;
4299
- if (!injector)
4299
+ // injector is needed ONLY to resolve the two proxy-globals tokens; if a caller supplies the
4300
+ // globals directly (createStoreContext — the worker-side seam with no DI), skip inject entirely
4301
+ const sharedGlobals = rest[STORE_SHARED_GLOBALS];
4302
+ const hasSharedGlobals = !!(sharedGlobals?.cache && sharedGlobals?.registry);
4303
+ if (!injector && !hasSharedGlobals)
4300
4304
  injector = inject(Injector);
4301
4305
  const writableSource = isWritableSignal(source)
4302
4306
  ? source
@@ -4315,13 +4319,18 @@ function toStore(source, { injector, vivify = false, noUnionLeaves = false, ...r
4315
4319
  }, /* @ts-ignore */
4316
4320
  ...(ngDevMode ? [{ debugName: "kind" }] : /* istanbul ignore next */ []));
4317
4321
  const STORE_OPTIONS = {
4318
- injector,
4322
+ // may be undefined in worker/DI-less mode; unused downstream once globals are resolved
4323
+ // (children thread the resolved globals via STORE_SHARED_OPTIONS, derived needs no injector)
4324
+ injector: injector,
4319
4325
  vivify,
4320
4326
  noUnionLeaves,
4321
4327
  [STORE_SHARED_GLOBALS]: {
4322
- cache: rest[STORE_SHARED_GLOBALS]?.cache ?? injector.get(PROXY_CACHE_TOKEN),
4323
- registry: rest[STORE_SHARED_GLOBALS]?.registry ??
4324
- injector.get(PROXY_CLEANUP_TOKEN),
4328
+ // the `injector!` reads run only when a global is absent, which (per hasSharedGlobals) means
4329
+ // an injector was resolved above
4330
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4331
+ cache: sharedGlobals?.cache ?? injector.get(PROXY_CACHE_TOKEN),
4332
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4333
+ registry: sharedGlobals?.registry ?? injector.get(PROXY_CLEANUP_TOKEN),
4325
4334
  },
4326
4335
  };
4327
4336
  // built lazily so non-array nodes never allocate it
@@ -4393,7 +4402,14 @@ function toStore(source, { injector, vivify = false, noUnionLeaves = false, ...r
4393
4402
  return () => {
4394
4403
  if (!isWritableSource)
4395
4404
  return s;
4396
- return untracked(() => toStore(source.asReadonly(), { injector, vivify, noUnionLeaves }));
4405
+ return untracked(() => toStore(source.asReadonly(), {
4406
+ injector,
4407
+ vivify,
4408
+ noUnionLeaves,
4409
+ // forward the resolved globals — re-resolving from the injector both re-injects
4410
+ // needlessly and breaks in DI-less (worker) mode where injector is undefined
4411
+ [STORE_SHARED_GLOBALS]: STORE_OPTIONS[STORE_SHARED_GLOBALS],
4412
+ }));
4397
4413
  };
4398
4414
  const k = untracked(kind);
4399
4415
  if (prop === 'extend' && k !== 'array')
@@ -4559,6 +4575,40 @@ function mutableStore(value, opt) {
4559
4575
  ...opt,
4560
4576
  });
4561
4577
  }
4578
+ /**
4579
+ * Builds a DI-less store context — the shared proxy-cache and cleanup registry that {@link toStore}
4580
+ * normally resolves from the injector — so a `store`/`toStore`/`opLog` graph can run with NO Angular
4581
+ * injection context. Spread the result into the options:
4582
+ *
4583
+ * ```ts
4584
+ * import { microtaskOpLogDriver } from '@mmstack/worker/host';
4585
+ * const ctx = createStoreContext();
4586
+ * const s = store({ todos: [] }, ctx);
4587
+ * const log = opLog(s, { driver: microtaskOpLogDriver(), origin: 'worker' }); // no injector anywhere
4588
+ * ```
4589
+ *
4590
+ * **This is a worker-only fallback — do NOT use it on the main thread.** DI is the default and
4591
+ * correct path in an app: the injector scopes the proxy-cache/cleanup singletons per app instance,
4592
+ * which on the SERVER keeps one request's store identity from bleeding into another's (the exact
4593
+ * hazard a module-scope singleton would reintroduce). A Web Worker is safe because it is a single
4594
+ * store graph per thread and never runs during SSR (spawn is a `PLATFORM_ID === 'server'` no-op),
4595
+ * so there is no cross-request scope to contaminate. Never hoist a `createStoreContext()` to module
4596
+ * scope on a shared/main thread.
4597
+ *
4598
+ * **Share ONE context across every store in a worker** — the same way `providedIn: 'root'` shares
4599
+ * one cache across all of an app's stores. `@mmstack/worker/host` memoizes this per worker
4600
+ * (`workerStoreContext()`); reach for `createStoreContext()` directly only in a bare
4601
+ * (non-worker-host) DI-less setup, and hold the single instance yourself.
4602
+ */
4603
+ function createStoreContext() {
4604
+ const cache = new WeakMap();
4605
+ const registry = new FinalizationRegistry(({ target, prop }) => {
4606
+ const entry = cache.get(target);
4607
+ if (entry)
4608
+ entry.delete(prop);
4609
+ });
4610
+ return { [STORE_SHARED_GLOBALS]: { cache, registry } };
4611
+ }
4562
4612
 
4563
4613
  function isPlainRecord(value) {
4564
4614
  if (value === null || typeof value !== 'object')
@@ -4638,7 +4688,7 @@ function generateOrigin() {
4638
4688
  return globalThis.crypto.randomUUID();
4639
4689
  return Math.random().toString(36).substring(2);
4640
4690
  }
4641
- const isPlainArray = (v) => Array.isArray(v) && !isOpaque(v);
4691
+ const isPlainArray$1 = (v) => Array.isArray(v) && !isOpaque(v);
4642
4692
  /**
4643
4693
  * Reference-identity-pruned structural diff — the same short-circuit discipline as `merge3`:
4644
4694
  * an untouched subtree kept its reference (the store's copy-on-write contract), so the walk
@@ -4663,7 +4713,7 @@ function diffNode(prev, next, path, ops) {
4663
4713
  }
4664
4714
  return;
4665
4715
  }
4666
- if (isPlainArray(prev) && isPlainArray(next)) {
4716
+ if (isPlainArray$1(prev) && isPlainArray$1(next)) {
4667
4717
  // same length → per-index descent (matches `arr[i].x.set(...)` writes); a length
4668
4718
  // change is a whole unit — index attribution lies under insert/remove/reorder
4669
4719
  if (prev.length === next.length) {
@@ -4680,7 +4730,7 @@ function diffNode(prev, next, path, ops) {
4680
4730
  /** Immutably applies one op along its path, vivifying missing containers `'auto'`-style. */
4681
4731
  function applyAt(container, path, idx, op) {
4682
4732
  const seg = path[idx];
4683
- const base = isPlainArray(container)
4733
+ const base = isPlainArray$1(container)
4684
4734
  ? container.slice()
4685
4735
  : isRecord(container)
4686
4736
  ? { ...container }
@@ -4700,6 +4750,37 @@ function applyAt(container, path, idx, op) {
4700
4750
  base[seg] = applyAt(base[seg], path, idx + 1, op);
4701
4751
  return base;
4702
4752
  }
4753
+ /**
4754
+ * Pure, store-free application of ops onto a plain root value, returning the next immutable root
4755
+ * (structural-sharing along op paths, missing containers vivified `'auto'`-style). This is the
4756
+ * same transform {@link OpLog.apply} runs, extracted so a replica can fold a received batch into
4757
+ * a value WITHOUT owning a diffing {@link opLog} — e.g. the worker-graph read-replica seam.
4758
+ * Accepts a batch or a bare op list.
4759
+ */
4760
+ function applyOps(root, ops) {
4761
+ const list = Array.isArray(ops) ? ops : ops.ops;
4762
+ let next = root;
4763
+ for (const op of list) {
4764
+ if (op.path.length === 0) {
4765
+ if (op.kind === 'set')
4766
+ next = op.next;
4767
+ continue; // a root delete is meaningless — ignore (mirrors OpLog.apply)
4768
+ }
4769
+ next = applyAt(next, op.path, 0, op);
4770
+ }
4771
+ return next;
4772
+ }
4773
+ /**
4774
+ * Pure reference-pruned structural diff of two roots into minimal ops (the emission core of
4775
+ * {@link opLog}, exported so code outside a log can produce a batch — e.g. diffing a scratch
4776
+ * draft against a replica's current value to route a write to its owner). Trusts the
4777
+ * copy-on-write contract: an untouched subtree that kept its reference is skipped.
4778
+ */
4779
+ function diffOps(prev, next) {
4780
+ const ops = [];
4781
+ diffNode(prev, next, [], ops);
4782
+ return ops;
4783
+ }
4703
4784
  /**
4704
4785
  * Inverts a batch for undo: reversed order, `set`↔its own inverse (an add — a `set` with no
4705
4786
  * `prev` — inverts to a `delete`; a `delete` inverts to a `set` restoring `prev`). Feed the
@@ -4712,14 +4793,24 @@ function invertBatch(batch) {
4712
4793
  for (let i = ops.length - 1; i >= 0; i--) {
4713
4794
  const op = ops[i];
4714
4795
  if (op.kind === 'delete') {
4715
- inverted.push({ kind: 'set', path: op.path, next: op.prev, prev: undefined });
4796
+ inverted.push({
4797
+ kind: 'set',
4798
+ path: op.path,
4799
+ next: op.prev,
4800
+ prev: undefined,
4801
+ });
4716
4802
  continue;
4717
4803
  }
4718
4804
  if (!Object.hasOwn(op, 'prev')) {
4719
4805
  inverted.push({ kind: 'delete', path: op.path, prev: op.next });
4720
4806
  }
4721
4807
  else {
4722
- inverted.push({ kind: 'set', path: op.path, next: op.prev, prev: op.next });
4808
+ inverted.push({
4809
+ kind: 'set',
4810
+ path: op.path,
4811
+ next: op.prev,
4812
+ prev: op.next,
4813
+ });
4723
4814
  }
4724
4815
  }
4725
4816
  return inverted;
@@ -4748,7 +4839,6 @@ function invertBatch(batch) {
4748
4839
  * ```
4749
4840
  */
4750
4841
  function opLog(source, opt) {
4751
- const injector = opt?.injector ?? inject(Injector);
4752
4842
  const origin = opt?.origin ?? generateOrigin();
4753
4843
  // a store proxy's `has` trap answers for the VALUE's keys, so `isMutable`'s `'mutate' in`
4754
4844
  // probe can't see the brand — ask the store's own kind symbol first
@@ -4780,16 +4870,24 @@ function opLog(source, opt) {
4780
4870
  for (const cb of [...subscribers])
4781
4871
  cb(batch);
4782
4872
  };
4783
- const ref = effect(() => {
4873
+ const run = () => {
4784
4874
  source(); // track every commit…
4785
4875
  untracked(flush); // …and emit the delta since the last flush
4786
- }, { ...(ngDevMode ? { debugName: "ref" } : /* istanbul ignore next */ {}), injector: opt?.injector });
4876
+ };
4877
+ // default driver is an Angular effect (needs an injector); a supplied driver runs injector-free
4878
+ // (the worker-side seam, e.g. microtaskOpLogDriver from @mmstack/worker/host)
4879
+ const ref = opt?.driver
4880
+ ? opt.driver(run)
4881
+ : effect(run, { injector: opt?.injector ?? inject(Injector) });
4787
4882
  return {
4788
4883
  latest: latest.asReadonly(),
4789
4884
  subscribe: (cb) => {
4790
4885
  subscribers.add(cb);
4791
4886
  return () => subscribers.delete(cb);
4792
4887
  },
4888
+ // the emission core, callable on demand — reads the source untracked, so it never disturbs the
4889
+ // driver's subscription; a subsequent scheduled run just finds the baseline already advanced
4890
+ flush: () => flush(),
4793
4891
  apply: (batchOrOps) => {
4794
4892
  const ops = Array.isArray(batchOrOps)
4795
4893
  ? batchOrOps
@@ -4798,15 +4896,7 @@ function opLog(source, opt) {
4798
4896
  return;
4799
4897
  // pending local writes must emit BEFORE the baseline advances past them
4800
4898
  flush();
4801
- let root = untracked(source);
4802
- for (const op of ops) {
4803
- if (op.path.length === 0) {
4804
- if (op.kind === 'set')
4805
- root = op.next;
4806
- continue; // a root delete is meaningless — ignore
4807
- }
4808
- root = applyAt(root, op.path, 0, op);
4809
- }
4899
+ const root = applyOps(untracked(source), ops); // one atomic root, structural-shared
4810
4900
  source.set(root);
4811
4901
  prevRoot = root; // baseline advance: an applied batch never echoes
4812
4902
  },
@@ -4818,6 +4908,98 @@ function opLog(source, opt) {
4818
4908
  };
4819
4909
  }
4820
4910
 
4911
+ const isPlainArray = (v) => Array.isArray(v) && !isOpaque(v);
4912
+ function keyOf(item, key) {
4913
+ if (typeof key === 'function')
4914
+ return key(item);
4915
+ return isRecord(item) ? item[key] : item;
4916
+ }
4917
+ /**
4918
+ * Produces a value equal to `next` but sharing as much of `prev`'s reference structure as possible:
4919
+ * an object subtree that did not change keeps its `prev` reference, and array items are matched by
4920
+ * `key` so a surviving item keeps its identity across a reorder/insert/remove (only added items are
4921
+ * new, only removed items are dropped). This is what lets a derived store recompute without tearing
4922
+ * down every downstream `computed` that reads an unchanged part of it.
4923
+ */
4924
+ function reconcile(prev, next, key = 'id') {
4925
+ return reconcileValue(prev, next, key);
4926
+ }
4927
+ function reconcileValue(prev, next, key) {
4928
+ if (Object.is(prev, next))
4929
+ return prev;
4930
+ if (isPlainArray(prev) && isPlainArray(next)) {
4931
+ const byKey = new Map();
4932
+ for (const item of prev)
4933
+ byKey.set(keyOf(item, key), item);
4934
+ let changed = prev.length !== next.length;
4935
+ const out = next.map((item, i) => {
4936
+ const match = byKey.get(keyOf(item, key));
4937
+ const rv = match !== undefined ? reconcileValue(match, item, key) : item;
4938
+ if (rv !== prev[i])
4939
+ changed = true;
4940
+ return rv;
4941
+ });
4942
+ return changed ? out : prev;
4943
+ }
4944
+ if (isRecord(prev) && isRecord(next)) {
4945
+ const nextKeys = Object.keys(next);
4946
+ let changed = Object.keys(prev).length !== nextKeys.length;
4947
+ const out = {};
4948
+ for (const k of nextKeys) {
4949
+ const rv = Object.hasOwn(prev, k)
4950
+ ? reconcileValue(prev[k], next[k], key)
4951
+ : next[k];
4952
+ out[k] = rv;
4953
+ if (rv !== prev[k])
4954
+ changed = true;
4955
+ }
4956
+ return changed ? out : prev;
4957
+ }
4958
+ return next;
4959
+ }
4960
+ /**
4961
+ * A derived STORE, the store-shaped counterpart to `computed`. `fn` receives a mutable draft seeded
4962
+ * with the current value and either mutates it in place or returns a new value; whichever it does,
4963
+ * the result is reconciled against the previous value (see {@link reconcile}) so unchanged subtrees
4964
+ * keep reference identity and keyed array items keep their proxy identity. Reading through the
4965
+ * returned store is fine-grained: a `computed` over one field only recomputes when that field
4966
+ * actually changes, even though the whole projection re-ran.
4967
+ *
4968
+ * Recompute is pull-based, exactly like `computed`: the projection is memoized and re-runs on the
4969
+ * first read after a signal `fn` depends on changes, so reads are always coherent (no waiting on an
4970
+ * effect flush) and nothing recomputes while nobody reads. `fn` must be pure, it runs inside the
4971
+ * reactive computation. Prefer `computed` for a plain value; reach for `projection` when you want
4972
+ * the per-property tracking of a store on top of a derivation.
4973
+ *
4974
+ * ```ts
4975
+ * const active = projection<User[]>(() => users().filter((u) => u.active), [], { key: 'id' });
4976
+ * // active[0].name(); — surviving users keep identity across recomputes
4977
+ * ```
4978
+ *
4979
+ * Needs an injection context (or an explicit `injector`) for the store layer's cleanup on the main
4980
+ * thread; with an explicit store context (`createStoreContext()`) it is injector-free, so it also
4981
+ * runs on a worker host.
4982
+ *
4983
+ * @param fn receives the current draft; mutate it, or return new data.
4984
+ * @param seed the initial value, held before the first run.
4985
+ */
4986
+ function projection(fn, seed, opt) {
4987
+ const { key = 'id', ...storeOpt } = opt ?? {};
4988
+ // linkedSignal rather than an effect-driven signal: the computation runs in the tracked
4989
+ // context (fn's reads are dependencies) and `previous` hands back the last emitted value for
4990
+ // the reconcile, so the projection is glitch-free, lazy, and needs no effect scheduler.
4991
+ const root = linkedSignal({ ...(ngDevMode ? { debugName: "root" } : /* istanbul ignore next */ {}), source: () => undefined,
4992
+ computation: (_, previous) => {
4993
+ const base = previous ? previous.value : seed;
4994
+ // a plain mutable scratch seeded with the current value; fn mutates it or returns new data
4995
+ const draft = structuredClone(base);
4996
+ const returned = fn(draft);
4997
+ const next = (returned === undefined ? draft : returned);
4998
+ return reconcile(base, next, key);
4999
+ } });
5000
+ return toStore(root, storeOpt).asReadonlyStore();
5001
+ }
5002
+
4821
5003
  /**
4822
5004
  * @internal The plain-`effect` sibling of the public {@link pausableEffect} (which is built on
4823
5005
  * `nestedEffect`). For infra utilities that own a single top-level effect/subscription and don't
@@ -5346,5 +5528,5 @@ function withHistory(sourceOrValue, opt) {
5346
5528
  * Generated bundle index. Do not edit.
5347
5529
  */
5348
5530
 
5349
- 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 };
5531
+ export { MmActivity, MmTransition, MmViewTransitionName, PAUSABLE_OPTIONS, SuspenseBoundary, SuspenseBoundaryBase, UnscopedSuspenseBoundary, activeTransaction, applyOps, batteryStatus, bridgeScopeToPendingTasks, chunked, clipboard, combineWith, createAttributedPending, createForwardingScope, 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, 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, projection, provideForwardingTransitionScope, providePausableOptions, providePaused, provideTransitionScope, reconcile, registerResource, resolvePause, scan, scrollPosition, select, sensor, sensors, signalFromEvent, startWith, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, use, windowSize, withHistory };
5350
5532
  //# sourceMappingURL=mmstack-primitives.mjs.map