wenay-common2 1.0.63 → 1.0.65
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/CLAUDE.md +63 -0
- package/README.md +6 -322
- package/doc/NAMING_RENAMES.md +36 -0
- package/doc/RECOMMENDATIONS.md +56 -0
- package/doc/REPLAY-PLAN.md +85 -0
- package/doc/ROADMAP.md +131 -0
- package/doc/changes/1.0.63.md +8 -0
- package/doc/changes/1.0.64.md +10 -0
- package/doc/changes/1.0.65.md +7 -0
- package/doc/changes/README.md +8 -0
- package/doc/wenay-common2-rare.md +525 -0
- package/doc/wenay-common2.md +338 -0
- package/lib/Common/Observe/store-replay.d.ts +8 -0
- package/lib/Common/Observe/store-replay.js +5 -0
- package/lib/Common/events/replay-index.d.ts +1 -0
- package/lib/Common/events/replay-index.js +1 -0
- package/lib/Common/events/replay-route.d.ts +25 -0
- package/lib/Common/events/replay-route.js +174 -0
- package/package.json +6 -2
- package/rpc.md +293 -0
package/doc/ROADMAP.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# wenay-common2 — Roadmap (open / deferred)
|
|
2
|
+
|
|
3
|
+
> Forward-looking backlog of distributed-state / transport features that are **not fully built**.
|
|
4
|
+
> Everything here sits on top of the existing seams — `{emit,on}` transport, `exposeStore` /
|
|
5
|
+
> `createStoreMirror`, and the replay `seq` / `keyframe` / `frame` contract. None of it requires
|
|
6
|
+
> changing the store core; the store stays single-authority (one `seq` sequencer, last-writer-wins
|
|
7
|
+
> per path), and these features are layers or adapters above that.
|
|
8
|
+
> Status: 🔴 not started · 🟡 partial / ongoing · 🧊 deliberately shelved.
|
|
9
|
+
|
|
10
|
+
## 0. Distributed Runtime Model
|
|
11
|
+
|
|
12
|
+
The strategic direction is not "RPC plus store helpers"; it is a small distributed-state runtime:
|
|
13
|
+
application code keeps a stable typed API while lower layers can choose the transport, route, replay
|
|
14
|
+
source, and authority model.
|
|
15
|
+
|
|
16
|
+
Four concerns must stay explicit:
|
|
17
|
+
|
|
18
|
+
- **Transport** - how two endpoints exchange `{emit,on}` messages: socket.io, in-process loopback,
|
|
19
|
+
relay, WebRTC/direct channel, or a future adapter. Transport is replaceable infrastructure.
|
|
20
|
+
- **Routing** - where a logical call or stream goes: server, peer through relay, peer directly, or a
|
|
21
|
+
promoted relay <-> direct path. Routing must be allowed to change without changing the facade API.
|
|
22
|
+
- **Authority** - who is allowed to write truth: one server, partitioned peer-owned slices, server
|
|
23
|
+
authority with client prediction, or true multi-writer conflict resolution. This is a semantic
|
|
24
|
+
decision, not a transport decision.
|
|
25
|
+
- **Replay** - how live state survives reconnect, lag, transport swap, and history playback:
|
|
26
|
+
`seq` + keyframe/frame + deltas. Replay is the continuity layer that makes route changes boring.
|
|
27
|
+
|
|
28
|
+
Design rule: direct peer links and relay hand-offs are optimizations of transport/routing. They must
|
|
29
|
+
not silently change auth, ownership, validation, or conflict semantics. If a method is exposed as
|
|
30
|
+
`api.hit(playerId)`, it may travel through the server or a direct channel, but the authority rules
|
|
31
|
+
behind that method must remain the same.
|
|
32
|
+
|
|
33
|
+
This implies a useful split:
|
|
34
|
+
|
|
35
|
+
- **API surface** remains facade-shaped and typed.
|
|
36
|
+
- **Transport/routing layer** may substitute sockets and promote paths.
|
|
37
|
+
- **Replay layer** resumes streams and mirrors from the last known `seq`.
|
|
38
|
+
- **Authority layer** decides whether an incoming write is accepted, predicted, reconciled, or merged.
|
|
39
|
+
|
|
40
|
+
### 0.1 Optional account-to-account route plane ("wrapper over wrappers") 🔴
|
|
41
|
+
|
|
42
|
+
Some deployments need separate client/account identities to communicate directly when policy and
|
|
43
|
+
network conditions allow it, while still allowing the server/relay to step back into the path later.
|
|
44
|
+
This is not just a socket trick; it is an account-aware routing shell above the existing facade,
|
|
45
|
+
mirror, and replay primitives.
|
|
46
|
+
|
|
47
|
+
- **Account model:** every participant has its own account/session identity and a scoped facade/store
|
|
48
|
+
set. Runtime-account maps are a dynamic keyspace, so they should look like `noStrict(accountMap)`
|
|
49
|
+
rather than a fixed schema. Access checks stay in the facade/policy layer; `noStrict` is not an ACL.
|
|
50
|
+
- **Wrapper over wrappers:** an app-level coordinator owns the set of per-account clients, exposed
|
|
51
|
+
facades, mirrors, replay subscriptions, and route state. "State of other accounts" is represented as
|
|
52
|
+
selected `Observe` mirrors/replay/offline resources, ideally started and stopped through
|
|
53
|
+
`createStoreManager`, not as a new global store core.
|
|
54
|
+
- **Route states:** a pair of accounts may be `relay`, `direct`, `direct+shadowRelay` (audit/observe
|
|
55
|
+
copy), `blocked`, or `fallback`. Direct links are optional optimizations, useful for latency or
|
|
56
|
+
traffic cost, not a semantic change.
|
|
57
|
+
- **Re-interposition:** if NDA/privacy policy, audit, moderation, throttling, reauth, direct-link
|
|
58
|
+
failure, or group topology changes require it, the relay must be able to re-enter the data path.
|
|
59
|
+
This is the reverse of direct promotion: open the replacement route, resume from the last `seq`,
|
|
60
|
+
switch consumers after catch-up, then close or demote the old route.
|
|
61
|
+
- **Privacy rule:** direct account links are opt-in and policy-gated. Peers only receive the endpoint
|
|
62
|
+
and session material needed for that specific relationship; no implicit broad account discovery.
|
|
63
|
+
|
|
64
|
+
Open questions: account ACL/policy shape; signaling format; whether the relay sees payloads or only
|
|
65
|
+
coordinates encrypted direct streams; how route state is exposed to the app; backpressure across
|
|
66
|
+
multi-hop and direct paths; revocation in the middle of a live stream.
|
|
67
|
+
|
|
68
|
+
Status: 🔴 not started. This belongs to project-0 architecture: no store-core change, but it defines
|
|
69
|
+
the account/routing shell that later direct-connection work plugs into.
|
|
70
|
+
|
|
71
|
+
## 1. Connection hand-off — relay ↔ direct promotion ("port forwarding") 🟡
|
|
72
|
+
|
|
73
|
+
A relay/intermediary bootstraps a connection between two parties, then — on a signal — **steps out
|
|
74
|
+
of the middle**: both ends open a second, direct socket and migrate the live stream onto it,
|
|
75
|
+
bypassing the relay. The same mechanism must work in reverse: the relay can deliberately
|
|
76
|
+
**re-interpose** and become the middleman again. Under the hood the socket substitutes itself; data
|
|
77
|
+
starts flowing on the new path.
|
|
78
|
+
|
|
79
|
+
- Family: NAT hole-punching / WebRTC TURN→direct promotion, expressed over the existing `{emit,on}`
|
|
80
|
+
abstraction rather than a specific transport.
|
|
81
|
+
- **Design lead (reuse what exists):** the socket swap is a *stream resume*, which the replay layer
|
|
82
|
+
already solves. Migrate with `replaySubscribe(..., {since: prev.seq()})` /
|
|
83
|
+
`syncStoreReplay(..., {since})` on the new socket — the consumer fold is gap-free by contract, so a
|
|
84
|
+
mid-stream transport change is not a special case. The transport carries only `seq`; the semantics
|
|
85
|
+
never learn the socket changed.
|
|
86
|
+
- **Implemented foundation (2026-07-08):** `Replay.replayRouteSubscribe(...)` and
|
|
87
|
+
`Observe.syncStoreReplayRoute(...)` keep the old route alive, subscribe the replacement route,
|
|
88
|
+
catch it up from the last delivered `seq`, then close the old route. This covers relay → direct
|
|
89
|
+
promotion and direct → relay re-interposition for any ordered `ReplayRemote`; overlap is deduped by
|
|
90
|
+
`seq`, and a failed replacement leaves the old route active.
|
|
91
|
+
- Open questions: signaling channel that negotiates the direct endpoint; fallback if the direct path
|
|
92
|
+
never establishes (stay on relay); auth continuity across the swap; per-socketKey vs whole-connection
|
|
93
|
+
hand-off; policy trigger and catch-up boundary for direct → relay re-interposition.
|
|
94
|
+
- Status: 🟡 partial. Route hand-off/resume helper is implemented and covered by `replay/route-handoff.test.ts`; signaling, direct endpoint negotiation, NAT/WebRTC adapter, auth-continuity policy, and trigger rules remain open.
|
|
95
|
+
|
|
96
|
+
## 2. Coordinated fan-out send to a large group 🧊
|
|
97
|
+
|
|
98
|
+
Synchronized/batched send to a very large audience "at once", as opposed to the current model where
|
|
99
|
+
**each connection is paced independently** (per-connection lag gate + `frame` policy).
|
|
100
|
+
|
|
101
|
+
- Current reality: pacing is per-recipient by design — every client's link is unique. Even in video
|
|
102
|
+
fan-out no two receivers drain at the same rate, so the per-connection gate is usually the *correct*
|
|
103
|
+
answer. That is why this is shelved.
|
|
104
|
+
- When it would matter: true lockstep broadcast (all-or-nothing / same-instant delivery) — rare.
|
|
105
|
+
- Status: 🧊 deliberately shelved. Revisit only against a concrete lockstep requirement.
|
|
106
|
+
|
|
107
|
+
## 3. Multi-authority stores — two truths, one reconciliation (games) 🔴
|
|
108
|
+
|
|
109
|
+
Two stores, each **dynamically filled by its own authority ("truth")**, that must agree on a shared
|
|
110
|
+
source of truth. Decompose by write topology:
|
|
111
|
+
|
|
112
|
+
- **Partitioned authority — fits today.** Each peer authoritative over its own slice → two
|
|
113
|
+
`exposeStore` / `createStoreMirror` pairs crossed over one duplex `{emit,on}`. No new primitive.
|
|
114
|
+
"Confirmation" = the per-direction `seq` ack.
|
|
115
|
+
- **Authoritative server + client prediction — small layer on top.** Server store is the truth; the
|
|
116
|
+
client applies optimistically, then reconciles when the authoritative patch arrives. Build a
|
|
117
|
+
`predictedStore` = confirmed mirror + a pending-input list, rebased on each `mirror.each()` /
|
|
118
|
+
`changed` tick. Helper factory, not a core change.
|
|
119
|
+
- **Symmetric co-write of the same path — needs a different model.** LWW-per-path converges but can
|
|
120
|
+
silently drop a concurrent write. This is the one case that genuinely needs CRDT/OT. Design lead:
|
|
121
|
+
wrap a Yjs/Automerge doc as a `RemoteStore`-shaped source and mirror from it — the store↔transport
|
|
122
|
+
decoupling makes this a small adapter, not a rewrite.
|
|
123
|
+
- Status: 🔴 prediction layer + CRDT adapter not started; partitioned-authority already expressible.
|
|
124
|
+
|
|
125
|
+
## 4. Data-transfer optimization backlog (ongoing) 🟡
|
|
126
|
+
|
|
127
|
+
Open-ended transfer/perf work, especially for backend-heavy models. Never "done".
|
|
128
|
+
|
|
129
|
+
- Candidates: tighter `frame` condensation per line; delta/patch minimization; binary framing for hot
|
|
130
|
+
paths; batching heuristics beyond the current `pipe` / `space` modes.
|
|
131
|
+
- Status: 🟡 ongoing; pick items as real bottlenecks surface, not speculatively.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# 1.0.63
|
|
2
|
+
|
|
3
|
+
- Add `Observe.createStoreManager` and `Observe.managedStore` resource builders for declarative mirror/replay/offline store startup.
|
|
4
|
+
- Add manager planning with priority, tags, usage scoring, `large`, and `explicitOnly` gates.
|
|
5
|
+
- Add manager lifecycle helpers: `start`, `startPlanned`, `stop`, `stopAll`, `get`, `touch`, `usage`, `statusListen`, and typed `handles`.
|
|
6
|
+
- Rename public-facing Observe oracle folder from `observable2` to `observe` and make `Listen.ts` / `reactive.ts` the canonical source files, leaving numbered files as compatibility shims.
|
|
7
|
+
- Update brief and extended API docs for the store manager and current naming.
|
|
8
|
+
- Publish package `wenay-common2@1.0.63`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# 1.0.64
|
|
2
|
+
|
|
3
|
+
- Make `README.md` navigation-only and point it to the brief docs, rare docs, naming migrations, recent changes, and project rules.
|
|
4
|
+
- Add the `doc/changes/` release-note catalog rule to `CLAUDE.md`: one file per published version, commit-style summary, latest 10 versions only.
|
|
5
|
+
- Add the recent changes catalog README and keep the current release notes under `doc/changes/`.
|
|
6
|
+
- Package documentation, `CLAUDE.md`, and `rpc.md` into `dist` so README links work in the npm package.
|
|
7
|
+
- Restore the `noStrict` dynamic-map contract in the extended docs after removing a duplicated store-manager block.
|
|
8
|
+
- Move regression oracles to the canonical `observe/reactive` names after the `observable2/reactive2` rename.
|
|
9
|
+
- Normalize npm repository metadata.
|
|
10
|
+
- Publish package `wenay-common2@1.0.64`.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 1.0.65
|
|
2
|
+
|
|
3
|
+
- Add `Replay.replayRouteSubscribe` for transport-agnostic replay route hand-off: keep the old route live, catch up the replacement route by `seq`, then close the old route.
|
|
4
|
+
- Add `Observe.syncStoreReplayRoute` for store mirrors that can switch relay/direct routes without gaps or duplicate patch delivery.
|
|
5
|
+
- Add `replay/route-handoff.test.ts` covering relay -> direct promotion, direct -> relay re-interposition, failed replacement fallback, and store mirror convergence.
|
|
6
|
+
- Update roadmap/replay docs to mark connection hand-off as partially implemented and keep signaling/WebRTC/policy triggers explicitly open.
|
|
7
|
+
- Normalize package metadata for `wenay-common2@1.0.65`.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Recent changes
|
|
2
|
+
|
|
3
|
+
This directory keeps one file per published version.
|
|
4
|
+
|
|
5
|
+
Rules:
|
|
6
|
+
- file name: `<version>.md`, for example `1.0.63.md`;
|
|
7
|
+
- each file contains a short commit-style summary of what changed;
|
|
8
|
+
- keep only the latest 10 version files, deleting older version files when adding a new one.
|