@net-mesh/browser 0.37.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/CHANGELOG.md +517 -0
- package/LICENSE-APACHE +190 -0
- package/LICENSE-MIT +21 -0
- package/README.md +666 -0
- package/dist/async-queue.d.ts +26 -0
- package/dist/async-queue.js +62 -0
- package/dist/credential.d.ts +70 -0
- package/dist/credential.js +95 -0
- package/dist/errors.d.ts +400 -0
- package/dist/errors.js +726 -0
- package/dist/events.d.ts +232 -0
- package/dist/events.js +298 -0
- package/dist/identity.d.ts +40 -0
- package/dist/identity.js +78 -0
- package/dist/index.bundle.js +2 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +46 -0
- package/dist/leader/events.d.ts +111 -0
- package/dist/leader/events.js +116 -0
- package/dist/leader/index.d.ts +14 -0
- package/dist/leader/index.js +11 -0
- package/dist/leader/session.d.ts +539 -0
- package/dist/leader/session.js +770 -0
- package/dist/leader/wasm.d.ts +123 -0
- package/dist/leader/wasm.js +39 -0
- package/dist/lobby.d.ts +183 -0
- package/dist/lobby.js +504 -0
- package/dist/local.d.ts +80 -0
- package/dist/local.js +160 -0
- package/dist/net_leaf.d.ts +1494 -0
- package/dist/net_leaf.js +3753 -0
- package/dist/net_leaf_bg.wasm +0 -0
- package/dist/net_leaf_bg.wasm.d.ts +137 -0
- package/dist/netcode/client.d.ts +102 -0
- package/dist/netcode/client.js +296 -0
- package/dist/netcode/clock.d.ts +45 -0
- package/dist/netcode/clock.js +54 -0
- package/dist/netcode/host.d.ts +118 -0
- package/dist/netcode/host.js +276 -0
- package/dist/netcode/index.d.ts +19 -0
- package/dist/netcode/index.js +14 -0
- package/dist/netcode/interpolate.d.ts +50 -0
- package/dist/netcode/interpolate.js +100 -0
- package/dist/netcode/wire.d.ts +125 -0
- package/dist/netcode/wire.js +117 -0
- package/dist/node.d.ts +908 -0
- package/dist/node.js +1085 -0
- package/dist/org.d.ts +412 -0
- package/dist/org.js +567 -0
- package/dist/peer-driver.d.ts +117 -0
- package/dist/peer-driver.js +224 -0
- package/dist/store/assembly.d.ts +176 -0
- package/dist/store/assembly.js +325 -0
- package/dist/store/chunker.d.ts +83 -0
- package/dist/store/chunker.js +190 -0
- package/dist/store/core.d.ts +136 -0
- package/dist/store/core.js +477 -0
- package/dist/store/definition.d.ts +23 -0
- package/dist/store/definition.js +83 -0
- package/dist/store/errors.d.ts +77 -0
- package/dist/store/errors.js +94 -0
- package/dist/store/host.d.ts +247 -0
- package/dist/store/host.js +675 -0
- package/dist/store/index.d.ts +50 -0
- package/dist/store/index.js +40 -0
- package/dist/store/interest.d.ts +40 -0
- package/dist/store/interest.js +77 -0
- package/dist/store/inventory.d.ts +81 -0
- package/dist/store/inventory.js +140 -0
- package/dist/store/join.d.ts +118 -0
- package/dist/store/join.js +724 -0
- package/dist/store/json.d.ts +50 -0
- package/dist/store/json.js +283 -0
- package/dist/store/ledger.d.ts +187 -0
- package/dist/store/ledger.js +292 -0
- package/dist/store/owner.d.ts +616 -0
- package/dist/store/owner.js +1711 -0
- package/dist/store/patch.d.ts +70 -0
- package/dist/store/patch.js +166 -0
- package/dist/store/player.d.ts +65 -0
- package/dist/store/player.js +261 -0
- package/dist/store/replica.d.ts +187 -0
- package/dist/store/replica.js +740 -0
- package/dist/store/state.d.ts +42 -0
- package/dist/store/state.js +165 -0
- package/dist/store/types.d.ts +200 -0
- package/dist/store/types.js +25 -0
- package/dist/store/visibility.d.ts +118 -0
- package/dist/store/visibility.js +282 -0
- package/dist/store/wire.d.ts +309 -0
- package/dist/store/wire.js +834 -0
- package/dist/stream.d.ts +174 -0
- package/dist/stream.js +238 -0
- package/dist/three/index.d.ts +109 -0
- package/dist/three/index.js +130 -0
- package/dist/udp-probe.d.ts +159 -0
- package/dist/udp-probe.js +245 -0
- package/dist/wasm.d.ts +642 -0
- package/dist/wasm.js +98 -0
- package/dist/world/border.d.ts +84 -0
- package/dist/world/border.js +99 -0
- package/dist/world/handoff.d.ts +187 -0
- package/dist/world/handoff.js +222 -0
- package/dist/world/index.d.ts +13 -0
- package/dist/world/index.js +9 -0
- package/dist/world/link.d.ts +179 -0
- package/dist/world/link.js +476 -0
- package/dist/world/regions.d.ts +154 -0
- package/dist/world/regions.js +314 -0
- package/package.json +72 -0
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner dispatch: join, admission, `authorize`, projection, `man`
|
|
3
|
+
* (brief §1.3, §1.5, §1.12, §2) — slice C.
|
|
4
|
+
*
|
|
5
|
+
* ## The authenticated origin is an argument, never a field
|
|
6
|
+
*
|
|
7
|
+
* {@link StoreOwner.receive} takes the **authenticated originating
|
|
8
|
+
* peer** as a parameter, and there is no way to name a caller inside a
|
|
9
|
+
* store frame at all (§2's shapes have no originator field). That is
|
|
10
|
+
* the whole of §1.3's rule expressed as a signature: the identity comes
|
|
11
|
+
* from the session the bytes arrived on, which the transport
|
|
12
|
+
* authenticated — `LeafEvent::StreamData.peer_node` on the Rust side,
|
|
13
|
+
* `LeafStream.peerNode` in the package — and a dispatcher cannot read
|
|
14
|
+
* it off the payload because the payload does not carry one.
|
|
15
|
+
*
|
|
16
|
+
* A caller could still *lie to this function*, so the contract is
|
|
17
|
+
* stated where it is enforceable: whoever calls `receive` must pass the
|
|
18
|
+
* peer the transport proved, and the store's own entry points will
|
|
19
|
+
* (slice G). Nothing here can make a wrong argument right, and
|
|
20
|
+
* pretending otherwise is the mistake this module refuses to make.
|
|
21
|
+
*
|
|
22
|
+
* ## What is here, and what is deliberately not
|
|
23
|
+
*
|
|
24
|
+
* Here: `join` admission, handle binding and re-checking, `authorize`
|
|
25
|
+
* for the read request, the audience projection, and the `man` + `snap`
|
|
26
|
+
* emission that follows. Refusals for the paths that reach those.
|
|
27
|
+
*
|
|
28
|
+
* Not here: `act`/`in`/`res` and the ledger (slice E), `aud`/`resume`
|
|
29
|
+
* transitions (slice F), `delta` emission (slice D). Those kinds are
|
|
30
|
+
* refused `not-ready` or `invalid-data` rather than half-implemented,
|
|
31
|
+
* and the refusal says which layer owns them.
|
|
32
|
+
*/
|
|
33
|
+
import { Assembly, AssemblyTable } from './assembly.js';
|
|
34
|
+
import type { JsonValue } from './json.js';
|
|
35
|
+
import { type Outcome } from './ledger.js';
|
|
36
|
+
import type { AccessRequest, ActionContext, Cancel, ActionSpec, InputSpec, Parse, StoreDefinition } from './types.js';
|
|
37
|
+
import { type Hex } from './wire.js';
|
|
38
|
+
/** Actions in flight per owner, against the pending bound (§2). */
|
|
39
|
+
export declare const MAX_PENDING_ACTIONS = 32;
|
|
40
|
+
/** Handles one owner will hold (brief §2). */
|
|
41
|
+
export declare const MAX_HANDLES = 256;
|
|
42
|
+
/** How long a handle lives without an accepted authenticated message. */
|
|
43
|
+
export declare const HANDLE_LEASE_MS = 60000;
|
|
44
|
+
/** What the owner needs from its host. */
|
|
45
|
+
export interface OwnerDeps<S extends object, A extends ActionSpec, I extends InputSpec> {
|
|
46
|
+
readonly definition: StoreDefinition<S, A, I>;
|
|
47
|
+
/**
|
|
48
|
+
* WHICH store of this definition this owner is — the address a
|
|
49
|
+
* joiner names in `join.store`.
|
|
50
|
+
*
|
|
51
|
+
* A node may host several stores of one definition (a lobby and a
|
|
52
|
+
* match; three worlds in a harness), every host store on that node
|
|
53
|
+
* receives every frame, and a `join` names no handle. So without
|
|
54
|
+
* this, the store that answers a join is whichever listener
|
|
55
|
+
* happened to be registered first — and a caller asking for B got
|
|
56
|
+
* A's document, A's handle and A's state mutated by its actions.
|
|
57
|
+
* Found by review: two COLD stores, join the second, and the first
|
|
58
|
+
* answers.
|
|
59
|
+
*/
|
|
60
|
+
readonly store: string;
|
|
61
|
+
/**
|
|
62
|
+
* The owner's policy. Receives the **authenticated** caller, because
|
|
63
|
+
* that is the only identity this module will ever hand it.
|
|
64
|
+
*/
|
|
65
|
+
authorize(request: AccessRequest<A, I>): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* The audience projection: what this caller may see. Returning the
|
|
68
|
+
* whole state is a decision, not a default — `empty()` is what
|
|
69
|
+
* absence looks like.
|
|
70
|
+
*
|
|
71
|
+
* Computed once per DISTINCT audience and shared by every handle
|
|
72
|
+
* carrying it. Exactly one of `project` and {@link projectFor} is
|
|
73
|
+
* given.
|
|
74
|
+
*/
|
|
75
|
+
project?(state: S, audience: readonly string[]): S;
|
|
76
|
+
/**
|
|
77
|
+
* The per-player projection: what THIS player may see — their own
|
|
78
|
+
* hand, their own fog of war. Handed the authenticated peer with the
|
|
79
|
+
* audience it reads.
|
|
80
|
+
*
|
|
81
|
+
* Computed once per distinct (peer, audience) pair, so its cost per
|
|
82
|
+
* change grows with the number of players, where `project`'s grows
|
|
83
|
+
* with the number of distinct audiences. That is inherent: a view
|
|
84
|
+
* that depends on who is looking has to be taken for each of them.
|
|
85
|
+
*/
|
|
86
|
+
projectFor?(state: S, viewer: Viewer): S;
|
|
87
|
+
/**
|
|
88
|
+
* The one hook for things that happen to players: `join`, `leave`,
|
|
89
|
+
* `area`. Runs on the host as a transaction with the player as
|
|
90
|
+
* `context.peer`, exactly like an action handler — so it can
|
|
91
|
+
* `setState` (a starting inventory on join) — and its changes reach
|
|
92
|
+
* every replica. Synchronous; a throw discards its writes.
|
|
93
|
+
*/
|
|
94
|
+
onEvent?(event: StoreEvent, context: ActionContext<S>): void;
|
|
95
|
+
/**
|
|
96
|
+
* Development warnings: a store that sends everything to everyone
|
|
97
|
+
* without saying so, a projection the state validator refuses. Off
|
|
98
|
+
* unless given; never on by default in production.
|
|
99
|
+
*/
|
|
100
|
+
warn?(message: string): void;
|
|
101
|
+
/**
|
|
102
|
+
* Which area a player is in, for `area` events: any string (a zone,
|
|
103
|
+
* a room, a grid cell), or `null` for none. Read-only; called after
|
|
104
|
+
* changes, for each present player.
|
|
105
|
+
*/
|
|
106
|
+
areaOf?(state: S, peer: string): string | null;
|
|
107
|
+
/** The admissible frame size, derived from the transport (§1.9). */
|
|
108
|
+
readonly maxEventBytes: number;
|
|
109
|
+
/** Monotone clock, milliseconds. */
|
|
110
|
+
now(): number;
|
|
111
|
+
/** 32 lowercase hex. Unguessable: a handle is a capability. */
|
|
112
|
+
newHandle(): Hex;
|
|
113
|
+
/** 16 lowercase hex, one per owner incarnation. */
|
|
114
|
+
newIncarnation(): Hex;
|
|
115
|
+
/**
|
|
116
|
+
* Whether a projection can be taken right now.
|
|
117
|
+
*
|
|
118
|
+
* A host that is mid-frame, or whose world is being rebuilt, answers
|
|
119
|
+
* `false` and the control is DEFERRED rather than refused (§1.8) — a
|
|
120
|
+
* control is never answered `not-ready`.
|
|
121
|
+
*/
|
|
122
|
+
canProject(): boolean;
|
|
123
|
+
/**
|
|
124
|
+
* The action handlers, one per declared action.
|
|
125
|
+
*
|
|
126
|
+
* Each runs inside ONE synchronous transaction: it stages writes
|
|
127
|
+
* through its context and returns the output, or throws to reject.
|
|
128
|
+
* A throw is an `action-rejected` outcome that is RETAINED (§1.10),
|
|
129
|
+
* not an absent one.
|
|
130
|
+
*/
|
|
131
|
+
readonly actions: ActionHandlers<S, A>;
|
|
132
|
+
/** The latest-value input handlers. Fire-and-forget, no reply. */
|
|
133
|
+
readonly inputs: InputHandlers<S, I>;
|
|
134
|
+
}
|
|
135
|
+
/** Why a player left, as `onEvent` hears it. */
|
|
136
|
+
export type LeaveReason =
|
|
137
|
+
/** The player closed their handle. */
|
|
138
|
+
'left'
|
|
139
|
+
/** Its lease ran out: a closed tab, a lost connection. */
|
|
140
|
+
| 'expired'
|
|
141
|
+
/** `authorize` stopped granting its read: a kick, a revoked permission. */
|
|
142
|
+
| 'refused'
|
|
143
|
+
/** Its view could not be delivered (too large, or `project` failed). */
|
|
144
|
+
| 'dropped';
|
|
145
|
+
/**
|
|
146
|
+
* What happened to a player, for the host's single `onEvent` hook.
|
|
147
|
+
*
|
|
148
|
+
* - `join`: a player arrived — their first subscription, or the host's
|
|
149
|
+
* own player (`hostPlayer`). Per player, not per connection.
|
|
150
|
+
* - `leave`: their last subscription ended, and why.
|
|
151
|
+
* - `area`: `areaOf(state, peer)` gave a different answer than it last
|
|
152
|
+
* did for them (`from` is `null` the first time).
|
|
153
|
+
*/
|
|
154
|
+
export type StoreEvent = {
|
|
155
|
+
readonly type: 'join';
|
|
156
|
+
readonly peer: string;
|
|
157
|
+
readonly audience: readonly string[];
|
|
158
|
+
} | {
|
|
159
|
+
readonly type: 'leave';
|
|
160
|
+
readonly peer: string;
|
|
161
|
+
readonly reason: LeaveReason;
|
|
162
|
+
} | {
|
|
163
|
+
readonly type: 'area';
|
|
164
|
+
readonly peer: string;
|
|
165
|
+
readonly from: string | null;
|
|
166
|
+
readonly to: string | null;
|
|
167
|
+
};
|
|
168
|
+
/** How many hook runs one drain may perform before it stops (see `drainEvents`). */
|
|
169
|
+
export declare const MAX_EVENT_ROUNDS = 256;
|
|
170
|
+
/** Who a per-player projection is for. */
|
|
171
|
+
export interface Viewer {
|
|
172
|
+
/** The authenticated peer, 16 lowercase hex — as `context.peer`. */
|
|
173
|
+
readonly peer: string;
|
|
174
|
+
/** The audience this handle reads. */
|
|
175
|
+
readonly audience: readonly string[];
|
|
176
|
+
}
|
|
177
|
+
/** One handler per declared action. */
|
|
178
|
+
export type ActionHandlers<S extends object, A extends ActionSpec> = {
|
|
179
|
+
readonly [K in keyof A]: (input: A[K]['input'], context: ActionContext<S>) => A[K]['output'];
|
|
180
|
+
};
|
|
181
|
+
/** One handler per declared input. */
|
|
182
|
+
export type InputHandlers<S extends object, I extends InputSpec> = {
|
|
183
|
+
readonly [K in keyof I]: (input: I[K], context: ActionContext<S>) => void;
|
|
184
|
+
};
|
|
185
|
+
/** One admitted subscription. */
|
|
186
|
+
export interface OwnerHandle {
|
|
187
|
+
readonly h: Hex;
|
|
188
|
+
readonly inc: Hex;
|
|
189
|
+
/** The authenticated peer this handle is bound to (§1.3). */
|
|
190
|
+
readonly peer: string;
|
|
191
|
+
readonly audience: readonly string[];
|
|
192
|
+
/** Highest generation allocated for this handle; monotone (§1.7a). */
|
|
193
|
+
readonly generation: number;
|
|
194
|
+
/**
|
|
195
|
+
* The revision this replica's view is at: its installation's, then
|
|
196
|
+
* each delta's `r`. A delta's `base` is THIS, not the owner's previous
|
|
197
|
+
* revision — a replica whose view did not change on a commit was sent
|
|
198
|
+
* nothing and is still here, and a `base` it never had is a gap.
|
|
199
|
+
*/
|
|
200
|
+
readonly revision: number;
|
|
201
|
+
/** Its interest set; `null` when it declared none (sees every entity). */
|
|
202
|
+
readonly interest: readonly string[] | null;
|
|
203
|
+
/** Last accepted authenticated message, for the lease. */
|
|
204
|
+
readonly lastSeen: number;
|
|
205
|
+
}
|
|
206
|
+
/** A frame the owner wants sent, and to whom. */
|
|
207
|
+
export interface Outbound {
|
|
208
|
+
readonly peer: string;
|
|
209
|
+
readonly h: Hex | null;
|
|
210
|
+
readonly frame: string;
|
|
211
|
+
}
|
|
212
|
+
/** What one inbound frame produced. */
|
|
213
|
+
export interface Dispatched {
|
|
214
|
+
readonly out: readonly Outbound[];
|
|
215
|
+
/** A stable, countable refusal reason, when the frame was refused. */
|
|
216
|
+
readonly refused: string | null;
|
|
217
|
+
/**
|
|
218
|
+
* The rest of an action whose request binding needed a digest.
|
|
219
|
+
*
|
|
220
|
+
* §1.10 puts one `await` in the ledger path, for inputs too large to
|
|
221
|
+
* retain verbatim. Rather than make every join and `alive` async to
|
|
222
|
+
* serve that one case, the synchronous answer comes back immediately
|
|
223
|
+
* and the remainder is handed over explicitly. A transport sends
|
|
224
|
+
* `out`, then sends the deferred result's `out` when it settles.
|
|
225
|
+
*
|
|
226
|
+
* `null` on every other path, which is all of them but one.
|
|
227
|
+
*/
|
|
228
|
+
readonly deferred: Promise<Dispatched> | null;
|
|
229
|
+
}
|
|
230
|
+
interface Counters {
|
|
231
|
+
[reason: string]: number;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* The owner half of the store protocol.
|
|
235
|
+
*
|
|
236
|
+
* Holds no transport: frames come in as text with an authenticated
|
|
237
|
+
* peer, and go out as text addressed to a peer. That is what lets the
|
|
238
|
+
* whole of slice C be exercised without a network.
|
|
239
|
+
*/
|
|
240
|
+
export declare class StoreOwner<S extends object, A extends ActionSpec, I extends InputSpec> {
|
|
241
|
+
#private;
|
|
242
|
+
private readonly deps;
|
|
243
|
+
private readonly handles;
|
|
244
|
+
private readonly counters;
|
|
245
|
+
private readonly chunkBytes;
|
|
246
|
+
private readonly incarnation;
|
|
247
|
+
/**
|
|
248
|
+
* The authoritative document.
|
|
249
|
+
*
|
|
250
|
+
* The owner's state IS a store: one place that validates, reconciles,
|
|
251
|
+
* counts revisions and runs a handler's synchronous transaction.
|
|
252
|
+
* Keeping a second copy beside it would give handler writes and
|
|
253
|
+
* `commit` two truths to disagree about.
|
|
254
|
+
*/
|
|
255
|
+
private readonly core;
|
|
256
|
+
/** Digest computations in flight, against the pending bound (§2). */
|
|
257
|
+
private pending;
|
|
258
|
+
private readonly ledgers;
|
|
259
|
+
/**
|
|
260
|
+
* One pending projection per handle (§1.8).
|
|
261
|
+
*
|
|
262
|
+
* A newer control REPLACES the pending one rather than queueing
|
|
263
|
+
* behind it, which is what keeps this a bound rather than a buffer.
|
|
264
|
+
*/
|
|
265
|
+
private readonly deferred;
|
|
266
|
+
/** The revision every installed view is at, before the commit. */
|
|
267
|
+
private revisionBeforeCommit;
|
|
268
|
+
constructor(deps: OwnerDeps<S, A, I>);
|
|
269
|
+
/** Whether projections depend on the peer (`projectFor`, or an `owner` rule). */
|
|
270
|
+
private readonly perPlayer;
|
|
271
|
+
/** The definition's declared rules, compiled; `null` when it declares none. */
|
|
272
|
+
private readonly visibility;
|
|
273
|
+
/** Development warnings already given, so each is said once. */
|
|
274
|
+
private readonly warned;
|
|
275
|
+
private warnOnce;
|
|
276
|
+
/** The key under which two handles are certain to receive the same projection. */
|
|
277
|
+
private viewKey;
|
|
278
|
+
/**
|
|
279
|
+
* Views whose change was projected from the changed entities alone
|
|
280
|
+
* (`sparseInterestDiff`). Not a refusal, so not in {@link snapshotCounters}.
|
|
281
|
+
*/
|
|
282
|
+
get sparseViews(): number;
|
|
283
|
+
private sparseViewCount;
|
|
284
|
+
/** Counters a host can report. Every refusal moves exactly one. */
|
|
285
|
+
snapshotCounters(): Readonly<Counters>;
|
|
286
|
+
/** Live handles, for a host's own bounds reporting. */
|
|
287
|
+
get handleCount(): number;
|
|
288
|
+
/** The handle record, for a test or a host's introspection. */
|
|
289
|
+
handle(h: Hex): OwnerHandle | undefined;
|
|
290
|
+
/**
|
|
291
|
+
* Replace the authoritative state, as `setState` does.
|
|
292
|
+
*
|
|
293
|
+
* Inside an open transaction the write JOINS it (`applySnapshot`
|
|
294
|
+
* stages) and the delta emission is DEFERRED to that transaction's
|
|
295
|
+
* commit (#5): `decide`/`input` diff the committed before/after at
|
|
296
|
+
* the transaction's end and ship exactly one delta, and a rollback
|
|
297
|
+
* ships nothing at all. Emitting here would put a staged-world
|
|
298
|
+
* delta on the wire that no rollback retracts.
|
|
299
|
+
*/
|
|
300
|
+
commit(next: S): Dispatched;
|
|
301
|
+
/**
|
|
302
|
+
* Write entities of one declared collection, as `setEntities` does:
|
|
303
|
+
* each changed entity validated by its per-entity parser, no
|
|
304
|
+
* whole-document validation. Same transaction and delta rules as
|
|
305
|
+
* {@link commit}.
|
|
306
|
+
*/
|
|
307
|
+
commitEntities(collection: string, changes: Readonly<Record<string, unknown>>): Dispatched;
|
|
308
|
+
/**
|
|
309
|
+
* Tell every installed handle what changed (§1.9).
|
|
310
|
+
*
|
|
311
|
+
* Per audience, because a delta of the raw state would ship what a
|
|
312
|
+
* projection exists to withhold: the previous and current
|
|
313
|
+
* projections are both taken and the difference between THOSE is
|
|
314
|
+
* what goes out. The previous one is recomputed from the previous
|
|
315
|
+
* state rather than retained per handle — one projection can be a
|
|
316
|
+
* megabyte, and keeping one per handle is 256 of them.
|
|
317
|
+
*
|
|
318
|
+
* When a delta would not fit the message budget the owner allocates
|
|
319
|
+
* a new generation instead and sends a manifest: an owner-initiated
|
|
320
|
+
* replacement, admissible at a `ready` replica (§1.8).
|
|
321
|
+
*/
|
|
322
|
+
private propagate;
|
|
323
|
+
/** The authoritative document, for a host's own reads. */
|
|
324
|
+
getState(): S;
|
|
325
|
+
/**
|
|
326
|
+
* Subscribe to the authoritative document.
|
|
327
|
+
*
|
|
328
|
+
* The owner's own code watches the state it serves — a host renders
|
|
329
|
+
* from this, and an action's commit is the same revision its
|
|
330
|
+
* subscribers see.
|
|
331
|
+
*/
|
|
332
|
+
subscribe(listener: (state: S, previous: S) => void): Cancel;
|
|
333
|
+
/** The current authoritative revision. */
|
|
334
|
+
get currentRevision(): number;
|
|
335
|
+
/**
|
|
336
|
+
* The host's own player: whether `peer` may read `audience`.
|
|
337
|
+
*
|
|
338
|
+
* The same `authorize` call a `join` makes. There is no handle, no
|
|
339
|
+
* lease and no ledger — the host's player has no wire to lose a
|
|
340
|
+
* frame on and no request to replay — but there is the policy.
|
|
341
|
+
*/
|
|
342
|
+
localRead(peer: string, audience: readonly string[]): boolean;
|
|
343
|
+
/** The host's own player: the projection a replica of `audience` receives. */
|
|
344
|
+
localView(peer: string, audience: readonly string[], interest?: readonly string[] | null): S | null;
|
|
345
|
+
/**
|
|
346
|
+
* The host's own player: one action, through the ladder a replica's
|
|
347
|
+
* `act` takes after the wire — known name, `authorize`, then one
|
|
348
|
+
* transaction that parses the input, runs the handler and validates
|
|
349
|
+
* the output, and a result that must fit the message budget a
|
|
350
|
+
* replica would have received it in.
|
|
351
|
+
*
|
|
352
|
+
* `input` is what `parseStoreJson` produced from the caller's value,
|
|
353
|
+
* so the handler sees exactly what it would see from a replica.
|
|
354
|
+
*/
|
|
355
|
+
localAct(name: string, input: JsonValue, peer: string): {
|
|
356
|
+
readonly outcome: Outcome;
|
|
357
|
+
readonly dispatched: Dispatched;
|
|
358
|
+
};
|
|
359
|
+
/**
|
|
360
|
+
* The host's own player: one latest-value input, through the same
|
|
361
|
+
* ladder as a replica's `in` after the wire. No sequence: a local
|
|
362
|
+
* call cannot arrive out of order.
|
|
363
|
+
*/
|
|
364
|
+
localInput(name: string, input: JsonValue, peer: string): Dispatched;
|
|
365
|
+
/**
|
|
366
|
+
* Dispatch one inbound frame.
|
|
367
|
+
*
|
|
368
|
+
* `peer` is the **authenticated** originating caller — see the module
|
|
369
|
+
* note. It is not read from `frame`, and no frame can carry one.
|
|
370
|
+
*/
|
|
371
|
+
receive(frame: string, peer: string, now?: number): Dispatched;
|
|
372
|
+
/**
|
|
373
|
+
* One action (§1.10).
|
|
374
|
+
*
|
|
375
|
+
* The ladder: gameplay readiness, then the sequence, then the
|
|
376
|
+
* ledger's disposition, then — only for a genuinely new request —
|
|
377
|
+
* `authorize` and one synchronous transaction.
|
|
378
|
+
*
|
|
379
|
+
* The digest path is the one place this module awaits. Everything
|
|
380
|
+
* decided before the await is discarded and decided again after it:
|
|
381
|
+
* the handle may have expired, the policy may have been revoked, and
|
|
382
|
+
* the ledger may have retained this very sequence while the digest
|
|
383
|
+
* was computing.
|
|
384
|
+
*/
|
|
385
|
+
private action;
|
|
386
|
+
/**
|
|
387
|
+
* The ledger's decision, and the execution it may authorize.
|
|
388
|
+
*
|
|
389
|
+
* Synchronous from here on: no `await` may appear below this line,
|
|
390
|
+
* because the transaction it enters is synchronous by construction
|
|
391
|
+
* and a permission checked before a wait can have been revoked
|
|
392
|
+
* during it.
|
|
393
|
+
*/
|
|
394
|
+
private decide;
|
|
395
|
+
/** One latest-value input (§1.11). Fire and forget: no reply, ever. */
|
|
396
|
+
private input;
|
|
397
|
+
/** A `res` for a result, a `no` for a retained refusal. */
|
|
398
|
+
private reply;
|
|
399
|
+
private permitsAction;
|
|
400
|
+
private permitsInput;
|
|
401
|
+
/** The lease is renewed by an accepted message, refusals excepted. */
|
|
402
|
+
private renew;
|
|
403
|
+
/**
|
|
404
|
+
* Everything that belongs to one handle, gone together.
|
|
405
|
+
*
|
|
406
|
+
* §2: the ledger goes with the handle. That is what makes a replay
|
|
407
|
+
* after eviction `closed` rather than `result-expired` — the owner
|
|
408
|
+
* no longer knows the sequence existed — and it is what keeps the
|
|
409
|
+
* ledger table bounded by live handles instead of by the owner's
|
|
410
|
+
* lifetime.
|
|
411
|
+
*/
|
|
412
|
+
private forget;
|
|
413
|
+
/** Peers present as players: a handle installed, or a local player. */
|
|
414
|
+
private readonly present;
|
|
415
|
+
private readonly localPlayers;
|
|
416
|
+
private readonly pendingEvents;
|
|
417
|
+
private readonly areas;
|
|
418
|
+
private areasAt;
|
|
419
|
+
/** Set by `farewell`: a closing store raises no more events. */
|
|
420
|
+
private ending;
|
|
421
|
+
private get wantsEvents();
|
|
422
|
+
private isPresent;
|
|
423
|
+
/** Queue `join` / `leave` when a peer's presence actually flips. */
|
|
424
|
+
private presenceChanged;
|
|
425
|
+
/**
|
|
426
|
+
* The host's own player arriving or going (`hostPlayer`). It has no
|
|
427
|
+
* handle, but it is a player: `onEvent` hears it like any other.
|
|
428
|
+
*/
|
|
429
|
+
localPresence(peer: string, joined: boolean, audience?: readonly string[], reason?: LeaveReason): void;
|
|
430
|
+
/** Queue `area` events for present players whose area moved. */
|
|
431
|
+
private detectAreas;
|
|
432
|
+
/**
|
|
433
|
+
* Run `onEvent` for everything queued, each as its own transaction,
|
|
434
|
+
* and return what those transactions changed for the replicas.
|
|
435
|
+
*
|
|
436
|
+
* Called by the host AFTER the frame that caused the events has been
|
|
437
|
+
* dispatched — never inside it — so a hook never runs in the middle
|
|
438
|
+
* of a projection pass. A hook that throws discards its writes, as a
|
|
439
|
+
* handler's do, and is counted. A hook whose writes cause more events
|
|
440
|
+
* (a teleport on join) is served in the same drain, up to a bound, so
|
|
441
|
+
* two hooks cannot ping-pong forever.
|
|
442
|
+
*/
|
|
443
|
+
drainEvents(): Dispatched;
|
|
444
|
+
/** Listeners told when the set of installed handles changes. */
|
|
445
|
+
private readonly membership;
|
|
446
|
+
private membershipChanged;
|
|
447
|
+
/**
|
|
448
|
+
* Be told when a handle is installed or forgotten — join, leave,
|
|
449
|
+
* expiry, a policy refusal, closure. Synchronous, inside the frame
|
|
450
|
+
* that caused it: a listener reads {@link peers} and must not call
|
|
451
|
+
* back into the owner.
|
|
452
|
+
*/
|
|
453
|
+
onMembership(listener: () => void): Cancel;
|
|
454
|
+
/** The distinct authenticated peers with an installed handle. */
|
|
455
|
+
peers(): readonly string[];
|
|
456
|
+
/**
|
|
457
|
+
* Re-ask `authorize` for every installed read, now.
|
|
458
|
+
*
|
|
459
|
+
* The delta feed re-authorizes before shipping, but only when there
|
|
460
|
+
* is a change to ship: a policy that revokes a peer (a kick) would
|
|
461
|
+
* otherwise leave it subscribed until the next commit. A refused
|
|
462
|
+
* handle is forgotten with the same unsolicited `closed` the feed
|
|
463
|
+
* sends, so the replica rejoins and its join is answered
|
|
464
|
+
* `forbidden`.
|
|
465
|
+
*/
|
|
466
|
+
reauthorize(): Dispatched;
|
|
467
|
+
/**
|
|
468
|
+
* Emit the projections that were deferred, now that one can be
|
|
469
|
+
* taken.
|
|
470
|
+
*
|
|
471
|
+
* Completion **revalidates**: a handle can die between the deferral
|
|
472
|
+
* and the availability, and an expired one never acquires a pending
|
|
473
|
+
* projection in the first place (§1.8).
|
|
474
|
+
*/
|
|
475
|
+
resumeDeferred(now?: number): Dispatched;
|
|
476
|
+
/** Projections waiting on availability, for a host's bounds report. */
|
|
477
|
+
get deferredCount(): number;
|
|
478
|
+
/** Ledgers held, for a host's own bounds reporting (§2). */
|
|
479
|
+
get ledgerCount(): number;
|
|
480
|
+
/** An accepted frame's reply. */
|
|
481
|
+
private accept;
|
|
482
|
+
/**
|
|
483
|
+
* Expire handles whose lease has run out (§2).
|
|
484
|
+
*
|
|
485
|
+
* Reports the PEER with the handle, because §1.6's expiry notice is
|
|
486
|
+
* addressed to a caller and the handle is gone by the time anyone
|
|
487
|
+
* could look it up. A host that cannot reach that peer sends
|
|
488
|
+
* nothing; no tombstone is retained to announce later.
|
|
489
|
+
*/
|
|
490
|
+
sweep(now: number): readonly {
|
|
491
|
+
readonly h: Hex;
|
|
492
|
+
readonly peer: string;
|
|
493
|
+
}[];
|
|
494
|
+
/**
|
|
495
|
+
* Say goodbye, once, to every replica this owner is serving.
|
|
496
|
+
*
|
|
497
|
+
* §1.6's doctrine is that a caller learns why rather than inferring
|
|
498
|
+
* it from silence, and it was applied to EXPIRY (`sweep`) and not
|
|
499
|
+
* to closure: a host that closed took its answer with it, so every
|
|
500
|
+
* bound replica's next write reached a store that no longer
|
|
501
|
+
* existed and the caller's only signal was a deadline — the
|
|
502
|
+
* `indeterminate` "the store did not answer before the deadline",
|
|
503
|
+
* measured over the real transport.
|
|
504
|
+
*
|
|
505
|
+
* The code is `owner-lost`, not `closed`, and the difference is
|
|
506
|
+
* the whole semantics: `closed` is the expiry notice and the
|
|
507
|
+
* replica REJOINS on it, which after a close would either hang or
|
|
508
|
+
* silently attach the caller to a SUCCESSOR's different document
|
|
509
|
+
* under the same handle. `owner-lost` is terminal — this
|
|
510
|
+
* incarnation held the document and is gone — so the replica's
|
|
511
|
+
* view is cleared, `ready()` rejects, and rejoining is the
|
|
512
|
+
* caller's decision to make explicitly.
|
|
513
|
+
*
|
|
514
|
+
* Every handle is forgotten here, so the frames are a one-shot:
|
|
515
|
+
* a second call has nothing to say.
|
|
516
|
+
*/
|
|
517
|
+
farewell(): readonly Outbound[];
|
|
518
|
+
/** This owner's incarnation, which every manifest carries. */
|
|
519
|
+
get incarnationHex(): Hex;
|
|
520
|
+
/**
|
|
521
|
+
* Admit a join.
|
|
522
|
+
*
|
|
523
|
+
* Order matters and is the ladder's: **the store address first**,
|
|
524
|
+
* because it decides whether this owner may answer at all; then
|
|
525
|
+
* definition identity and version, which are loud for the store
|
|
526
|
+
* they ARE addressed to; then capacity, `authorize`, and only then
|
|
527
|
+
* a handle. Nothing is allocated before the policy has ruled, so a
|
|
528
|
+
* refused caller costs one counter and no state.
|
|
529
|
+
*
|
|
530
|
+
* This summary is written out in full because it was wrong for one
|
|
531
|
+
* commit — it still listed the definition first, ten lines above
|
|
532
|
+
* the code that had just been reordered, in the rung whose
|
|
533
|
+
* position was the point of the change.
|
|
534
|
+
*/
|
|
535
|
+
private join;
|
|
536
|
+
/**
|
|
537
|
+
* Allocate the next generation and emit `man` + every `snap`.
|
|
538
|
+
*
|
|
539
|
+
* Returns `null` when the projection cannot be carried, so the caller
|
|
540
|
+
* decides what to do with the handle rather than this function
|
|
541
|
+
* guessing.
|
|
542
|
+
*/
|
|
543
|
+
private install;
|
|
544
|
+
/**
|
|
545
|
+
* One read decision, for the join and for every later read.
|
|
546
|
+
*
|
|
547
|
+
* A throwing policy is a refusal, never an admission.
|
|
548
|
+
*/
|
|
549
|
+
private permitsRead;
|
|
550
|
+
/** A handle from the application's allocator, or null if it threw. */
|
|
551
|
+
private newHandle;
|
|
552
|
+
/** The definition's interest collections and their key functions. */
|
|
553
|
+
private get interestFns();
|
|
554
|
+
/** An entity's interest key; a key function that throws or returns a non-string means "no key": always delivered. */
|
|
555
|
+
private interestKey;
|
|
556
|
+
/** A view narrowed to the entities an interest set covers. */
|
|
557
|
+
private filterInterest;
|
|
558
|
+
/**
|
|
559
|
+
* The commit's raw entity change, or `null` when this commit cannot be
|
|
560
|
+
* projected entity by entity.
|
|
561
|
+
*
|
|
562
|
+
* It can when the host projects only by DECLARED rules (no `project` /
|
|
563
|
+
* `projectFor`, which see the whole document), and every root key the
|
|
564
|
+
* commit changed is a collection declared in both `interest` and
|
|
565
|
+
* `entities`, and is an entity map on both sides. Declared rules are
|
|
566
|
+
* path patterns, so an entity's visibility is the same in a document
|
|
567
|
+
* holding only the changed entities; and `entities` is the contract that
|
|
568
|
+
* an entity is valid on its own, so its projection is validated by the
|
|
569
|
+
* per-entity parser instead of `state` over the whole view.
|
|
570
|
+
*/
|
|
571
|
+
private entityDelta;
|
|
572
|
+
/**
|
|
573
|
+
* One view's change from the changed entities alone: O(changed) per
|
|
574
|
+
* view instead of two whole-world projections, a whole-view validation
|
|
575
|
+
* and a scan of every id. Returns `undefined` for anything it cannot
|
|
576
|
+
* answer exactly as `interestDiff` over full projections would (a rule
|
|
577
|
+
* hiding a whole collection, a projected entity its parser refuses),
|
|
578
|
+
* and the caller takes the full path.
|
|
579
|
+
*/
|
|
580
|
+
private sparseInterestDiff;
|
|
581
|
+
/** One view's change, split for interest: the rest of the document, and per-entity changes with their keys. */
|
|
582
|
+
private interestDiff;
|
|
583
|
+
/** The ops one handle receives for a split diff. */
|
|
584
|
+
private interestOps;
|
|
585
|
+
/**
|
|
586
|
+
* A new interest set: the entities entering it arrive, the ones leaving
|
|
587
|
+
* go, in one delta — the view never blanks — and then `ok`.
|
|
588
|
+
*
|
|
589
|
+
* Interest is a filter, not a permission: nothing here asks `authorize`,
|
|
590
|
+
* because the audience (which is) is unchanged and every entity sent
|
|
591
|
+
* was already readable under it.
|
|
592
|
+
*/
|
|
593
|
+
private interestChange;
|
|
594
|
+
/**
|
|
595
|
+
* The audience projection, validated by the definition.
|
|
596
|
+
*
|
|
597
|
+
* A projection that does not satisfy `state()` is the owner's bug and
|
|
598
|
+
* must not be shipped as a snapshot, so it becomes `empty()` — the
|
|
599
|
+
* value that represents absence — rather than the full state.
|
|
600
|
+
*/
|
|
601
|
+
private project;
|
|
602
|
+
/**
|
|
603
|
+
* Re-check the binding for every message (§1.3).
|
|
604
|
+
*
|
|
605
|
+
* Returns a countable reason instead of a handle when the handle is
|
|
606
|
+
* unknown, expired, or bound to a different authenticated peer — and
|
|
607
|
+
* the three are **one** refusal on the wire, so a refusal cannot
|
|
608
|
+
* disclose that a handle exists (§2).
|
|
609
|
+
*/
|
|
610
|
+
private bind;
|
|
611
|
+
private no;
|
|
612
|
+
private refuse;
|
|
613
|
+
}
|
|
614
|
+
/** Re-exported so a host can hold assemblies without another import. */
|
|
615
|
+
export { Assembly, AssemblyTable };
|
|
616
|
+
export type { Parse };
|