@mmstack/primitives 20.10.1 → 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/README.md +83 -1
- package/fesm2022/mmstack-primitives.mjs +1061 -270
- package/fesm2022/mmstack-primitives.mjs.map +1 -1
- package/index.d.ts +477 -113
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,9 @@ npm install @mmstack/primitives
|
|
|
20
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
|
-
- [History & persistence](#history--persistence) — `withHistory`, `stored`, `tabSync`, `opLog`
|
|
23
|
+
- [History & persistence](#history--persistence) — `withHistory`, `storeHistory`, `stored`, `persistedStore`, `tabSync`, `opLog`
|
|
24
|
+
- [Sync & convergence](#sync--convergence) — `opSync`, `tabSync(store)`, merge policies (`lww`, `mergeThree`, `keyedArray`, `preserve`), `Conflicted`, `rebaseOps`, `policyStrategy`
|
|
25
|
+
- [Observability](#observability) — `provideConcurrencyInstrumentation`, `perfCustomTracks`
|
|
24
26
|
- [Performance helpers](#performance-helpers) — `chunked`, `pooled` / `pooledArray` / `pooledMap` / `pooledSet`
|
|
25
27
|
- [Sensors](#sensors) — `sensor()` facade + browser-state signals
|
|
26
28
|
- [Pipelines](#pipelines) — `piped` / `pipeable`, operators (`select`, `map`, `filter`, `filterWith`, `distinct`, `combineWith`, `tap`, `startWith`, `pairwise`, `scan`)
|
|
@@ -717,6 +719,86 @@ Batching is per tick (two writes to one leaf in a tick emit one composed op), `p
|
|
|
717
719
|
|
|
718
720
|
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
721
|
|
|
722
|
+
### `storeHistory`
|
|
723
|
+
|
|
724
|
+
Undo and redo for a store, over the op-log rather than value snapshots, so each entry costs only the diff. `undo()` applies one inverse batch; a new edit after an undo forks the timeline.
|
|
725
|
+
|
|
726
|
+
```typescript
|
|
727
|
+
import { store, storeHistory } from '@mmstack/primitives';
|
|
728
|
+
|
|
729
|
+
const doc = store({ title: 'Draft', body: '' });
|
|
730
|
+
const history = storeHistory(doc);
|
|
731
|
+
|
|
732
|
+
doc.title.set('Final');
|
|
733
|
+
history.undo(); // title back to 'Draft'
|
|
734
|
+
history.canRedo(); // Signal<boolean>
|
|
735
|
+
|
|
736
|
+
storeHistory(doc, { track: syncClient }); // collaborative: only your own writes are undoable
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
### `persistedStore`
|
|
740
|
+
|
|
741
|
+
Persists a whole store to an async backend (IndexedDB) and restores it on boot. It ships no IndexedDB code: you pass an `AsyncStore` adapter, which `idb-keyval` satisfies directly and a Dexie table satisfies with a few lines. Local durability, not sync (compose `tabSync` / `@mmstack/mesh` for that). Reads stay synchronous; because the backend is async, the store shows its initial value until the snapshot loads (`hydrated` is a signal you can gate on).
|
|
742
|
+
|
|
743
|
+
```typescript
|
|
744
|
+
import * as idbKeyval from 'idb-keyval';
|
|
745
|
+
import { persistedStore, providePersistedStoreOptions } from '@mmstack/primitives';
|
|
746
|
+
|
|
747
|
+
providePersistedStoreOptions({ store: idbKeyval }); // wire the backend once
|
|
748
|
+
|
|
749
|
+
const draft = persistedStore({ title: '', body: '' }, { key: 'draft' });
|
|
750
|
+
draft.store.title.set('Hi'); // persisted (debounced), restored on next load
|
|
751
|
+
draft.hydrated(); // Signal<boolean>
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
When the persisted shape changes between releases, pass `version` and a `migrate` hook. An older snapshot is brought forward on boot before it is adopted, then re-persisted in the new shape (a newer snapshot than the running build is left untouched). Boot is already async, so `migrate` can be async, so the migration ladder can be lazy-loaded.
|
|
755
|
+
|
|
756
|
+
```typescript
|
|
757
|
+
const profile = persistedStore({ first: '', last: '' }, {
|
|
758
|
+
key: 'profile',
|
|
759
|
+
version: 2,
|
|
760
|
+
migrate: async (data, from) => (await import('./migrations')).run(data, from),
|
|
761
|
+
});
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
`persistedStore` is `store()` + `persist()`. Reach for `persist(store, opt)` directly to add durability to a store you already have — one you also `meshSync`, or a worker-owned store's replica. Persistence is a reader over the op-log, so it composes with the other readers on the same store.
|
|
765
|
+
|
|
766
|
+
```typescript
|
|
767
|
+
import { store, persist, meshSync } from '@mmstack/primitives';
|
|
768
|
+
|
|
769
|
+
const doc = store({ title: '', body: '' });
|
|
770
|
+
persist(doc, { key: 'draft', store: idbKeyval }); // durable to IndexedDB
|
|
771
|
+
meshSync(doc, { room: 'doc-42', writer, transport }); // and synced to peers
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
## Sync & convergence
|
|
775
|
+
|
|
776
|
+
The op-log is the substrate; these keep two copies of a store in agreement across a boundary (tabs, a worker, a network). `opSync` wires a store to a transport: local writes emit stamped envelopes, received envelopes fold in through a per-path last-writer-wins register map, ordered by a hybrid logical clock so any arrival order converges to the same state. `tabSync(store, { id })` is `opSync` over `BroadcastChannel` with a join handshake.
|
|
777
|
+
|
|
778
|
+
```typescript
|
|
779
|
+
import { store, tabSync, keyedArray, preserve, isConflicted } from '@mmstack/primitives';
|
|
780
|
+
|
|
781
|
+
const board = tabSync(store({ title: 'Board', todos: [] }), {
|
|
782
|
+
id: 'board',
|
|
783
|
+
policies: [
|
|
784
|
+
{ path: 'todos', merge: keyedArray((t) => t.id) }, // reconcile a list by item identity
|
|
785
|
+
{ path: 'title', merge: preserve }, // keep both sides of a clash as data
|
|
786
|
+
],
|
|
787
|
+
});
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
A **merge policy** decides the result when two peers change one path at once: `lww` (default), `mergeThree` (three-way against the common ancestor), `keyedArray(idFn)` (list reconcile by identity), or `preserve` (both sides survive as a `Conflicted` value; `isConflicted(v)` narrows it, resolution is a later write). `rebaseOps(root, pending, remote, policies)` is the pure invert-apply-reapply routine behind optimistic updates and offline queues, and `policyStrategy(policies)` gives a `forkStore` the same per-path resolution. This is what [`@mmstack/mesh`](https://www.npmjs.com/package/@mmstack/mesh) wraps for multiplayer.
|
|
791
|
+
|
|
792
|
+
## Observability
|
|
793
|
+
|
|
794
|
+
An optional listener seam on the concurrency layer. `provideConcurrencyInstrumentation(listener)` receives events as transition scopes coordinate pending, suspense, and transaction windows; with no listener the taps are no-ops. `perfCustomTracks()` is a ready listener that writes each window to a Chrome DevTools Performance track, and the window hooks are span-shaped, so forwarding to [`@mmstack/telemetry-core`](https://www.npmjs.com/package/@mmstack/telemetry-core) is a direct mapping.
|
|
795
|
+
|
|
796
|
+
```typescript
|
|
797
|
+
import { provideConcurrencyInstrumentation, perfCustomTracks } from '@mmstack/primitives';
|
|
798
|
+
|
|
799
|
+
providers: [provideConcurrencyInstrumentation(perfCustomTracks())];
|
|
800
|
+
```
|
|
801
|
+
|
|
720
802
|
## Performance helpers
|
|
721
803
|
|
|
722
804
|
### `chunked`
|