@lazily-hub/lazily-js 0.14.0 → 0.16.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 +47 -41
- package/package.json +11 -7
- package/src/async-reactive-family.d.ts +35 -58
- package/src/async-reactive-family.js +143 -179
- package/src/graph-view.d.ts +26 -0
- package/src/graph-view.js +115 -0
- package/src/instrumentation.js +4 -4
- package/src/merge.d.ts +50 -0
- package/src/merge.js +142 -0
- package/src/queue.js +48 -32
- package/src/reactive-family.d.ts +66 -80
- package/src/reactive-family.js +331 -182
- package/src/reactive.js +33 -11
- package/src/relay.d.ts +184 -0
- package/src/relay.js +622 -0
- package/src/thread-safe-reactive-family.d.ts +42 -51
- package/src/thread-safe-reactive-family.js +153 -125
- package/src/thread-safe.js +2 -2
- package/src/state-graph-mirror.d.ts +0 -58
- package/src/state-graph-mirror.js +0 -366
package/README.md
CHANGED
|
@@ -39,18 +39,20 @@ notes and platform carve-outs lives in
|
|
|
39
39
|
| Feature | Rust | Python | Kotlin | JS | Dart | Zig | Go | C++ |
|
|
40
40
|
| --------- | :----: | :------: | :------: | :--: | :----: | :---: | :--: | :---: |
|
|
41
41
|
| Reactive graph — `Cell` / `Slot` / `Signal` / `Effect` / memo / batch | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
42
|
-
|
|
|
43
|
-
| Thread-safe
|
|
44
|
-
| Async
|
|
45
|
-
|
|
|
42
|
+
| Keyed-map materialization (`SlotMap`) — mint-on-access derived slots: transparency + deferral (`#lzmatmode`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
43
|
+
| Thread-safe keyed map (`ThreadSafeSlotMap`) — `Send + Sync` + materialization confluence (`#lzmatmode`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
44
|
+
| Async keyed map (`AsyncSlotMap`) — eventual transparency (`#lzmatmode`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
45
|
+
| Keyed-map sync — membership propagation + materialize-on-ingest + derived-aggregate transparency (`#lzfamilysync`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
46
46
|
| Thread-safe context (lock-backed) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
47
47
|
| Async reactive context | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
48
48
|
| Flat state machine | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
49
49
|
| Harel state charts | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
50
|
-
| Keyed
|
|
50
|
+
| Keyed reactive maps (`ReactiveMap`: `CellMap` / `SlotMap`) + `CellTree` + reconcile | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
51
51
|
| Memoized semantic tree (`SemTree`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
52
52
|
| Stable-id alignment (manufactured identity) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
53
53
|
| Reactive queue (`QueueCell` SPSC/MPSC + `QueueStorage` adapter) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
54
|
+
| Merge algebra + `MergeCell` — associative `MergePolicy` (`KeepLatest`/`Sum`/`Max`/`SetUnion`/`RawFifo`), `Cell ≡ MergeCell<KeepLatest>`, `Reactive`/`Source` split (`#relaycell`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
55
|
+
| RelayCell — conflating relay + `BackpressurePolicy` + `SpillStore` + `Transport` + Inbox/Outbox + Rate/Window/Expiry/Priority/keyed policies (`#relaycell`) | ✅ | — | — | ✅ | — | — | — | — |
|
|
54
56
|
| Free-text character CRDT (`TextCrdt`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
55
57
|
| `TextCrdt` delta sync (`version_vector` / `delta_since` / `apply_delta`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
56
58
|
| Move-aware sequence CRDT (`SeqCrdt`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
@@ -62,8 +64,8 @@ notes and platform carve-outs lives in
|
|
|
62
64
|
| Shared-memory blob path (`ShmBlobArena`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
63
65
|
| Cross-process zero-copy transport (`BlobBackend` / shm / arrow) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
64
66
|
| Distributed CRDT plane (`CrdtPlaneRuntime` / anti-entropy) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
65
|
-
| Reliable sync — resync coordinator + at-least-once durable outbox + OR-set/LWW liveness (`#lzsync`) | ✅ |
|
|
66
|
-
| Reliable-sync transport seam + full-duplex `SyncDriver` loop (`IpcSink`/`IpcSource`, `#sync-driver`) | ✅ |
|
|
67
|
+
| Reliable sync — resync coordinator + at-least-once durable outbox + OR-set/LWW liveness (`#lzsync`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
68
|
+
| Reliable-sync transport seam + full-duplex `SyncDriver` loop (`IpcSink`/`IpcSource`, `#sync-driver`) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
67
69
|
| Distributed plane — WebRTC transport + signaling | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
68
70
|
| State projection / mirror | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
69
71
|
| Causal receipts (`CausalReceipts` outcome projection) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
@@ -76,7 +78,7 @@ notes and platform carve-outs lives in
|
|
|
76
78
|
|
|
77
79
|
Two JS ✅ marks are backed by runtime-specific mechanisms while keeping the core isomorphic:
|
|
78
80
|
|
|
79
|
-
- **Thread-safe** (`
|
|
81
|
+
- **Thread-safe** (`ThreadSafeReactiveMap`, `Thread-safe context`) — cross-realm mutual exclusion via a `SharedArrayBuffer` + `Atomics` reentrant mutex shared across Web Workers / `worker_threads`; degrades to a single-realm guard where shared memory is unavailable (e.g. a browser without cross-origin isolation), which is sound because no shared memory means no cross-realm concurrency.
|
|
80
82
|
- **C-ABI FFI** — the normative codec + in-process `FfiChannel` are pure-JS and run unchanged in the browser; the Node build additionally binds the real `lazily` shared library (`lazily_ffi_channel_*`) via koffi. Both speak the identical byte contract, so browser and native are drop-in interchangeable.
|
|
81
83
|
|
|
82
84
|
CRDT convergence and the wire protocol are pinned by the shared conformance fixtures
|
|
@@ -89,10 +91,10 @@ and JSON Schemas in `lazily-spec` and the Lean models in `lazily-formal`.
|
|
|
89
91
|
| `@lazily-hub/lazily-js/transport` | Cross-process zero-copy transport (`#lzzcpy`): `ShmBlobArena`, `InProcessBackend` / `ArrowBackend`, `BlobRouter`, `spillMessage` / `resolveValue`, and the FFI-gated `createShmBackend` (Node/Bun/Deno). Isomorphic — no FFI import; browser-safe |
|
|
90
92
|
| `@lazily-hub/lazily-js/reactive` | Reactive dependency graph: `Context`, `Cell`, `Slot`, `Signal`, `Effect` |
|
|
91
93
|
| `@lazily-hub/lazily-js/reactive-async` | Async reactive graph: `AsyncContext` — Promise-driven slots/effects with revision-guarded stale-completion discard, in-flight dedup, and cancellation |
|
|
92
|
-
| `@lazily-hub/lazily-js/reactive-family` | Unified keyed reactive
|
|
93
|
-
| `@lazily-hub/lazily-js/async-reactive-family` | Async keyed reactive
|
|
94
|
+
| `@lazily-hub/lazily-js/reactive-family` | Unified keyed reactive map: `ReactiveMap<K,V,H>` (reactive membership/order, `getOrInsertWith` mint-on-access, `remove`, `move`) + `CellMap` (adds cell-only `set` + eager `entry`/`entryWith`) and `SlotMap` (lazy `getOrInsertWith` + eager `materializeAll`; no `set`) specializations. No eager/lazy mode flag (`#reactivemap`) |
|
|
95
|
+
| `@lazily-hub/lazily-js/async-reactive-family` | Async keyed reactive map: `AsyncReactiveMap` + `AsyncCellMap` / `AsyncSlotMap` over `AsyncContext` — eventual transparency (a pending slot observes `undefined` and resolves to the canonical value; eager ≡ lazy once resolved) (`#reactivemap`) |
|
|
94
96
|
| `@lazily-hub/lazily-js/thread-safe` | Lock-backed reactive context: `ThreadSafeContext` (`Send + Sync` flavor of `Context`) + `AtomicMutex` — a real `SharedArrayBuffer` + `Atomics` reentrant mutex giving cross-worker mutual exclusion; degrades to a single-realm guard where shared memory is unavailable |
|
|
95
|
-
| `@lazily-hub/lazily-js/thread-safe-reactive-family` | Thread-safe keyed reactive
|
|
97
|
+
| `@lazily-hub/lazily-js/thread-safe-reactive-family` | Thread-safe keyed reactive map: `ThreadSafeReactiveMap` + `ThreadSafeCellMap` / `ThreadSafeSlotMap` — mutex-guarded present set with first-writer-wins materialization confluence (`#reactivemap`) |
|
|
96
98
|
| `@lazily-hub/lazily-js/ffi` | C-ABI FFI boundary (`schemas/ffi.json`): message codec (`encodeMessage` / `decodeMessage` / `validateMessage` / `kindOf`, `LazilyFfiStatus` / `LazilyFfiMessageKind`) + `FfiChannel` FIFO. Isomorphic core (browser shim); the Node build additionally exposes `NativeFfiChannel` / `loadNativeChannel` over the real `lazily_ffi_channel_*` C ABI via koffi |
|
|
97
99
|
| `@lazily-hub/lazily-js/instrumentation` | In-library instrumentation/benchmark API: `benchmark`, `runBenchmarkSuite`, `BenchmarkResult`, `withInstrumentation` — plus opt-in reactive-core counters via `new Context({ instrument: true })` / `instrumentationSnapshot()` |
|
|
98
100
|
| `@lazily-hub/lazily-js/state-machine` | Flat finite-state-machine kernel backed by a reactive `Cell` |
|
|
@@ -174,42 +176,46 @@ ctx.setCell(userId, 2); // supersedes any in-flight compute; slot re-resolves
|
|
|
174
176
|
await ctx.getAsync(profile); // the profile for user 2
|
|
175
177
|
```
|
|
176
178
|
|
|
177
|
-
##
|
|
179
|
+
## Keyed reactive maps (`ReactiveMap` / `CellMap` / `SlotMap`)
|
|
178
180
|
|
|
179
|
-
`
|
|
180
|
-
**keyed reactive
|
|
181
|
-
|
|
181
|
+
`ReactiveMap<K, V, H>` (from `@lazily-hub/lazily-js/reactive-family`) is the ONE
|
|
182
|
+
unified **keyed reactive collection** (`#reactivemap`): reactive membership +
|
|
183
|
+
order, `getOrInsertWith` mint-on-access, `remove`, and atomic `move`, generic
|
|
184
|
+
over the entry's handle kind. Its two specializations are the concrete types you
|
|
185
|
+
use:
|
|
182
186
|
|
|
183
|
-
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
- **`CellMap<K, V>`** — input-cell entries. Adds cell-only `set(key, value)` (an
|
|
188
|
+
input is settable) and eager value-minting (`entry` / `entryWith`).
|
|
189
|
+
- **`SlotMap<K, V>`** — derived-slot entries. `getOrInsertWith(key, factory)`
|
|
190
|
+
mints a slot on **first access** ("materialize on pull", **lazy**);
|
|
191
|
+
`materializeAll(keys, factory)` pre-mints the keyset up front (**eager**). A
|
|
192
|
+
slot's value is derived, so `SlotMap` has **no `set`**.
|
|
189
193
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
194
|
+
There is **no eager/lazy mode flag** — eager is the pre-mint loop, lazy is
|
|
195
|
+
mint-on-access, and they are **observationally transparent**: a read returns the
|
|
196
|
+
same value either way; only allocation timing and memory change. Lazy pays off
|
|
197
|
+
only for sparsely-touched large keyed address spaces.
|
|
193
198
|
|
|
194
199
|
```js
|
|
195
200
|
import { Context } from "@lazily-hub/lazily-js/reactive";
|
|
196
|
-
import {
|
|
201
|
+
import { SlotMap } from "@lazily-hub/lazily-js/reactive-family";
|
|
197
202
|
|
|
198
203
|
const ctx = new Context();
|
|
199
204
|
|
|
200
|
-
// A derived (slot)
|
|
205
|
+
// A derived (slot) map of key*3 over a large address space, built lazily:
|
|
201
206
|
// nothing is allocated until a key is read.
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
// Eager
|
|
211
|
-
const eager =
|
|
212
|
-
eager.
|
|
207
|
+
const map = new SlotMap(ctx);
|
|
208
|
+
map.presentCount(); // 0
|
|
209
|
+
|
|
210
|
+
map.getOrInsertWith(5, (k) => k * 3); // 15 — first read materializes just this entry
|
|
211
|
+
map.presentCount(); // 1
|
|
212
|
+
map.isPresent(5); // true
|
|
213
|
+
map.isPresent(6); // false
|
|
214
|
+
|
|
215
|
+
// Eager pre-mints the same values up front — observationally identical.
|
|
216
|
+
const eager = new SlotMap(ctx);
|
|
217
|
+
eager.materializeAll([0, 1, 2, 3], (k) => k * 3);
|
|
218
|
+
eager.get(2) === map.getOrInsertWith(2, (k) => k * 3); // true
|
|
213
219
|
```
|
|
214
220
|
|
|
215
221
|
## State machine and state charts
|
|
@@ -504,11 +510,11 @@ lazily-js replays the shared `lazily-spec` fixtures for IPC, agent-doc state,
|
|
|
504
510
|
keyed collections (`CellMap`, `CellTree`, LIS reconciliation), semantic tree,
|
|
505
511
|
sequence and text CRDTs (incl. `TextCrdt` delta sync, `#lztextsync`:
|
|
506
512
|
`textcrdt_convergence.json` + `textcrdt_delta_sync.json`), manufactured text
|
|
507
|
-
identity, the reactive
|
|
513
|
+
identity, the keyed reactive maps / materialization (`#reactivemap`:
|
|
508
514
|
`materialization/observational_transparency.json`,
|
|
509
515
|
`materialization/deferral_not_deallocation.json`,
|
|
510
516
|
`materialization/entry_kind_orthogonal_to_mode.json` — replayed through the
|
|
511
|
-
single-threaded, async, and thread-safe
|
|
517
|
+
single-threaded, async, and thread-safe maps), the C-ABI FFI boundary
|
|
512
518
|
(`schemas/ffi.json`: message codec + channel round-trip over `snapshot_*` /
|
|
513
519
|
`delta_*` wire), Harel state charts, the
|
|
514
520
|
signaling protocol (`signaling/frames.json`,
|
|
@@ -532,8 +538,8 @@ names the Lean theorems it mirrors:
|
|
|
532
538
|
| `Collection` | `collection-properties.test.js` | `setEntryValue_preserves_{membership,order,siblings}`, `moveKey_preserves_{membership,values}`, `moveKey_advances_order`, `addKey_advances_membership_and_order`, `Family.get_idempotent_after_first` |
|
|
533
539
|
| `Tree` | `tree-properties.test.js` | `setNodeValue_preserves_{other_nodes,node_signals}`, `moveChild_preserves_{non_parent,parent_value}`, `moveChild_advances_order_signal_only` |
|
|
534
540
|
| `Materialization` | `reactive-family.test.js` | `observe_canonical`, `eager_lazy_observationally_equivalent`, `eager_materializes_all`, `lazy_defers_slots`, `materialize_present_monotone`, `lazy_present_subset_eager`, `materialize_preserves_observe`, `cell_entries_materialized_in_every_mode`, `slot_entries_deferred_under_lazy` |
|
|
535
|
-
| `Materialization` (thread-safe) | `thread-safe-reactive-family.test.js` | `materialize_present_comm`, `materialize_observe_comm` (materialization confluence) + the base materialization laws replayed through `
|
|
536
|
-
| `AsyncMaterialization` | `async-reactive-family.test.js` | eventual transparency (a driven async slot resolves to the canonical value; eager ≡ lazy) + present-set monotonicity through `
|
|
541
|
+
| `Materialization` (thread-safe) | `thread-safe-reactive-family.test.js` | `materialize_present_comm`, `materialize_observe_comm` (materialization confluence) + the base materialization laws replayed through `ThreadSafeSlotMap` |
|
|
542
|
+
| `AsyncMaterialization` | `async-reactive-family.test.js` | eventual transparency (a driven async slot resolves to the canonical value; eager ≡ lazy) + present-set monotonicity through `AsyncSlotMap` |
|
|
537
543
|
| `ThreadSafe` | `thread-safe.test.js` | `flushBatch_empty`, `flushBatch_singleton_eq_setCell` (thread-safe batch refines `setCell`), `flushBatch_dependent_dirty`, `flushBatch_preserves_nondependent_dirty` |
|
|
538
544
|
| `Reconciliation` | `reconciliation-properties.test.js` | `lisBy_longest`, `reconcile_move_minimized`, `reconcile_stable_not_invalidated` |
|
|
539
545
|
| `AsyncSlotState` | `reactive-async.test.js` | `stale_completeOk_discarded`, `current_completeOk_publishes`, `current_completeErr_to_error` |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazily-hub/lazily-js",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Native JavaScript port of the lazily reactive core: a full reactive graph (Cell/Slot/Signal/Effect), sync/async/thread-safe keyed reactive
|
|
3
|
+
"version": "0.16.0",
|
|
4
|
+
"description": "Native JavaScript port of the lazily reactive core: a full reactive graph (Cell/Slot/Signal/Effect), sync/async/thread-safe keyed reactive maps (ReactiveMap/CellMap/SlotMap), the lazily-spec IPC wire types + C-ABI FFI boundary (isomorphic channel + Node native binding), keyed cell collections + LIS reconciliation, the memoized semantic tree, the move-aware sequence CRDT, the Fugue/RGA text CRDT, manufactured text identity, full-Harel state charts, an FFI state-projection consumer, and an in-library instrumentation/benchmark API.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -69,6 +69,14 @@
|
|
|
69
69
|
"types": "./src/queue.d.ts",
|
|
70
70
|
"default": "./src/queue.js"
|
|
71
71
|
},
|
|
72
|
+
"./merge": {
|
|
73
|
+
"types": "./src/merge.d.ts",
|
|
74
|
+
"default": "./src/merge.js"
|
|
75
|
+
},
|
|
76
|
+
"./relay": {
|
|
77
|
+
"types": "./src/relay.d.ts",
|
|
78
|
+
"default": "./src/relay.js"
|
|
79
|
+
},
|
|
72
80
|
"./sem-tree": {
|
|
73
81
|
"types": "./src/sem-tree.d.ts",
|
|
74
82
|
"default": "./src/sem-tree.js"
|
|
@@ -93,10 +101,6 @@
|
|
|
93
101
|
"types": "./src/state-projection.d.ts",
|
|
94
102
|
"default": "./src/state-projection.js"
|
|
95
103
|
},
|
|
96
|
-
"./state-graph-mirror": {
|
|
97
|
-
"types": "./src/state-graph-mirror.d.ts",
|
|
98
|
-
"default": "./src/state-graph-mirror.js"
|
|
99
|
-
},
|
|
100
104
|
"./signaling": {
|
|
101
105
|
"types": "./src/signaling.d.ts",
|
|
102
106
|
"default": "./src/signaling.js"
|
|
@@ -107,7 +111,7 @@
|
|
|
107
111
|
}
|
|
108
112
|
},
|
|
109
113
|
"scripts": {
|
|
110
|
-
"build": "node --check src/index.js && node --check src/transport.js && node --check src/shm-backend.js && node --check src/reactive.js && node --check src/reactive-async.js && node --check src/reactive-family.js && node --check src/async-reactive-family.js && node --check src/thread-safe.js && node --check src/thread-safe-reactive-family.js && node --check src/ffi.js && node --check src/ffi-node.js && node --check src/instrumentation.js && node --check src/state-machine.js && node --check src/statechart.js && node --check src/collections.js && node --check src/queue.js && node --check src/sem-tree.js && node --check src/stable-id.js && node --check src/seq-crdt.js && node --check src/text-crdt.js && node --check src/utf8-offsets.js && node --check src/lossless-tree-crdt.js && node --check src/state-projection.js && node --check src/
|
|
114
|
+
"build": "node --check src/index.js && node --check src/transport.js && node --check src/shm-backend.js && node --check src/reactive.js && node --check src/reactive-async.js && node --check src/reactive-family.js && node --check src/async-reactive-family.js && node --check src/thread-safe.js && node --check src/thread-safe-reactive-family.js && node --check src/ffi.js && node --check src/ffi-node.js && node --check src/instrumentation.js && node --check src/state-machine.js && node --check src/statechart.js && node --check src/collections.js && node --check src/queue.js && node --check src/merge.js && node --check src/relay.js && node --check src/sem-tree.js && node --check src/stable-id.js && node --check src/seq-crdt.js && node --check src/text-crdt.js && node --check src/utf8-offsets.js && node --check src/lossless-tree-crdt.js && node --check src/state-projection.js && node --check src/graph-view.js && node --check src/signaling.js && node --check src/distributed.js",
|
|
111
115
|
"test:formal": "node scripts/formal-check.mjs",
|
|
112
116
|
"bench": "node bench/context.bench.mjs",
|
|
113
117
|
"bench:scale": "node --max-old-space-size=8192 bench/scale.bench.mjs",
|
|
@@ -1,78 +1,55 @@
|
|
|
1
1
|
import type { AsyncCellHandle, AsyncContext, AsyncSlotHandle } from "./reactive-async.js";
|
|
2
|
-
import type { EntryKind
|
|
2
|
+
import type { EntryKind } from "./reactive-family.js";
|
|
3
3
|
|
|
4
|
-
export { EntryKind
|
|
4
|
+
export { EntryKind } from "./reactive-family.js";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
export type
|
|
6
|
+
/** The entry handle an {@link AsyncReactiveMap} holds. */
|
|
7
|
+
export type AsyncMapHandle = AsyncCellHandle | AsyncSlotHandle;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* The async keyed reactive
|
|
10
|
+
* The async keyed reactive collection (`#reactivemap`, async flavor): keys map to
|
|
11
11
|
* per-entry async reactive nodes (input cells resolved synchronously, or derived
|
|
12
|
-
* slots resolved asynchronously)
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* slots resolved asynchronously). The transparency law is EVENTUAL — a pending
|
|
13
|
+
* slot observes as `undefined` and resolves to the canonical value. Its two
|
|
14
|
+
* specializations are {@link AsyncCellMap} and {@link AsyncSlotMap}.
|
|
15
15
|
*/
|
|
16
|
-
export class
|
|
17
|
-
constructor(
|
|
18
|
-
ctx: AsyncContext,
|
|
19
|
-
mode: MaterializationMode,
|
|
20
|
-
keys: Iterable<K>,
|
|
21
|
-
factory: (key: K) => V,
|
|
22
|
-
entryKind?: EntryKindResolver<K>,
|
|
23
|
-
);
|
|
16
|
+
export class AsyncReactiveMap<K = unknown, V = unknown> {
|
|
17
|
+
constructor(ctx: AsyncContext, kind?: EntryKind);
|
|
24
18
|
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
ctx: AsyncContext,
|
|
28
|
-
keys: Iterable<K>,
|
|
29
|
-
factory: (key: K) => V,
|
|
30
|
-
entryKind?: EntryKindResolver<K>,
|
|
31
|
-
): AsyncReactiveFamily<K, V>;
|
|
32
|
-
|
|
33
|
-
/** Build a lazy async family: derived (slot) entries deferred to first read. */
|
|
34
|
-
static lazy<K, V>(
|
|
35
|
-
ctx: AsyncContext,
|
|
36
|
-
keys: Iterable<K>,
|
|
37
|
-
factory: (key: K) => V,
|
|
38
|
-
entryKind?: EntryKindResolver<K>,
|
|
39
|
-
): AsyncReactiveFamily<K, V>;
|
|
40
|
-
|
|
41
|
-
/** Build an async family in the default (eager) mode. */
|
|
42
|
-
static create<K, V>(
|
|
43
|
-
ctx: AsyncContext,
|
|
44
|
-
keys: Iterable<K>,
|
|
45
|
-
factory: (key: K) => V,
|
|
46
|
-
entryKind?: EntryKindResolver<K>,
|
|
47
|
-
): AsyncReactiveFamily<K, V>;
|
|
48
|
-
|
|
49
|
-
/** Materialize (lazy pull) and return the entry handle for `key`. */
|
|
50
|
-
get(key: K): AsyncFamilyHandle;
|
|
19
|
+
/** Get the entry handle for `key`, minting via `factory(key)` if absent. */
|
|
20
|
+
getOrInsertHandle(key: K, factory: (key: K) => V): AsyncMapHandle;
|
|
51
21
|
/** Non-blocking observe: value for a cell/resolved slot, `undefined` if pending. */
|
|
52
22
|
observe(key: K): V | undefined;
|
|
53
|
-
/** Drive `key` to resolution
|
|
54
|
-
resolve(key: K): Promise<V>;
|
|
55
|
-
/**
|
|
56
|
-
|
|
23
|
+
/** Drive `key` to resolution; mint via `factory(key)` if absent. */
|
|
24
|
+
resolve(key: K, factory?: (key: K) => V): Promise<V>;
|
|
25
|
+
/** The existing entry handle for `key`, or `undefined`. Non-minting. */
|
|
26
|
+
handle(key: K): AsyncMapHandle | undefined;
|
|
57
27
|
/** Whether `key` is currently materialized. Non-reactive. */
|
|
58
28
|
isPresent(key: K): boolean;
|
|
59
29
|
/** Currently-materialized keys, in first-materialization order. */
|
|
60
30
|
presentKeys(): K[];
|
|
61
31
|
/** Number of currently-materialized entries. */
|
|
62
32
|
presentCount(): number;
|
|
63
|
-
/** This
|
|
64
|
-
entryKind(
|
|
65
|
-
|
|
66
|
-
|
|
33
|
+
/** This map's entry kind. */
|
|
34
|
+
entryKind(): EntryKind;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The async input-cell map: every entry is an always-resolved input cell. Adds
|
|
39
|
+
* cell-only `set`.
|
|
40
|
+
*/
|
|
41
|
+
export class AsyncCellMap<K = unknown, V = unknown> extends AsyncReactiveMap<K, V> {
|
|
42
|
+
constructor(ctx: AsyncContext);
|
|
43
|
+
/** Set the value at `key`, inserting a new input cell if absent. Cell-only. */
|
|
44
|
+
set(key: K, value: V): void;
|
|
67
45
|
}
|
|
68
46
|
|
|
69
47
|
/**
|
|
70
|
-
* The
|
|
71
|
-
*
|
|
48
|
+
* The async derived-slot map: entries are derived slots minted lazily on access
|
|
49
|
+
* or eagerly via `materializeAll`, resolved via `ctx.getAsync`. NO `set`.
|
|
72
50
|
*/
|
|
73
|
-
export
|
|
74
|
-
ctx: AsyncContext
|
|
75
|
-
|
|
76
|
-
factory: (key: K) => V
|
|
77
|
-
|
|
78
|
-
): AsyncReactiveFamily<K, V>;
|
|
51
|
+
export class AsyncSlotMap<K = unknown, V = unknown> extends AsyncReactiveMap<K, V> {
|
|
52
|
+
constructor(ctx: AsyncContext);
|
|
53
|
+
/** Eager materialization: pre-mint a derived slot for every key in `keys`. */
|
|
54
|
+
materializeAll(keys: Iterable<K>, factory: (key: K) => V): void;
|
|
55
|
+
}
|