@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.
Files changed (110) hide show
  1. package/CHANGELOG.md +517 -0
  2. package/LICENSE-APACHE +190 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +666 -0
  5. package/dist/async-queue.d.ts +26 -0
  6. package/dist/async-queue.js +62 -0
  7. package/dist/credential.d.ts +70 -0
  8. package/dist/credential.js +95 -0
  9. package/dist/errors.d.ts +400 -0
  10. package/dist/errors.js +726 -0
  11. package/dist/events.d.ts +232 -0
  12. package/dist/events.js +298 -0
  13. package/dist/identity.d.ts +40 -0
  14. package/dist/identity.js +78 -0
  15. package/dist/index.bundle.js +2 -0
  16. package/dist/index.d.ts +38 -0
  17. package/dist/index.js +46 -0
  18. package/dist/leader/events.d.ts +111 -0
  19. package/dist/leader/events.js +116 -0
  20. package/dist/leader/index.d.ts +14 -0
  21. package/dist/leader/index.js +11 -0
  22. package/dist/leader/session.d.ts +539 -0
  23. package/dist/leader/session.js +770 -0
  24. package/dist/leader/wasm.d.ts +123 -0
  25. package/dist/leader/wasm.js +39 -0
  26. package/dist/lobby.d.ts +183 -0
  27. package/dist/lobby.js +504 -0
  28. package/dist/local.d.ts +80 -0
  29. package/dist/local.js +160 -0
  30. package/dist/net_leaf.d.ts +1494 -0
  31. package/dist/net_leaf.js +3753 -0
  32. package/dist/net_leaf_bg.wasm +0 -0
  33. package/dist/net_leaf_bg.wasm.d.ts +137 -0
  34. package/dist/netcode/client.d.ts +102 -0
  35. package/dist/netcode/client.js +296 -0
  36. package/dist/netcode/clock.d.ts +45 -0
  37. package/dist/netcode/clock.js +54 -0
  38. package/dist/netcode/host.d.ts +118 -0
  39. package/dist/netcode/host.js +276 -0
  40. package/dist/netcode/index.d.ts +19 -0
  41. package/dist/netcode/index.js +14 -0
  42. package/dist/netcode/interpolate.d.ts +50 -0
  43. package/dist/netcode/interpolate.js +100 -0
  44. package/dist/netcode/wire.d.ts +125 -0
  45. package/dist/netcode/wire.js +117 -0
  46. package/dist/node.d.ts +908 -0
  47. package/dist/node.js +1085 -0
  48. package/dist/org.d.ts +412 -0
  49. package/dist/org.js +567 -0
  50. package/dist/peer-driver.d.ts +117 -0
  51. package/dist/peer-driver.js +224 -0
  52. package/dist/store/assembly.d.ts +176 -0
  53. package/dist/store/assembly.js +325 -0
  54. package/dist/store/chunker.d.ts +83 -0
  55. package/dist/store/chunker.js +190 -0
  56. package/dist/store/core.d.ts +136 -0
  57. package/dist/store/core.js +477 -0
  58. package/dist/store/definition.d.ts +23 -0
  59. package/dist/store/definition.js +83 -0
  60. package/dist/store/errors.d.ts +77 -0
  61. package/dist/store/errors.js +94 -0
  62. package/dist/store/host.d.ts +247 -0
  63. package/dist/store/host.js +675 -0
  64. package/dist/store/index.d.ts +50 -0
  65. package/dist/store/index.js +40 -0
  66. package/dist/store/interest.d.ts +40 -0
  67. package/dist/store/interest.js +77 -0
  68. package/dist/store/inventory.d.ts +81 -0
  69. package/dist/store/inventory.js +140 -0
  70. package/dist/store/join.d.ts +118 -0
  71. package/dist/store/join.js +724 -0
  72. package/dist/store/json.d.ts +50 -0
  73. package/dist/store/json.js +283 -0
  74. package/dist/store/ledger.d.ts +187 -0
  75. package/dist/store/ledger.js +292 -0
  76. package/dist/store/owner.d.ts +616 -0
  77. package/dist/store/owner.js +1711 -0
  78. package/dist/store/patch.d.ts +70 -0
  79. package/dist/store/patch.js +166 -0
  80. package/dist/store/player.d.ts +65 -0
  81. package/dist/store/player.js +261 -0
  82. package/dist/store/replica.d.ts +187 -0
  83. package/dist/store/replica.js +740 -0
  84. package/dist/store/state.d.ts +42 -0
  85. package/dist/store/state.js +165 -0
  86. package/dist/store/types.d.ts +200 -0
  87. package/dist/store/types.js +25 -0
  88. package/dist/store/visibility.d.ts +118 -0
  89. package/dist/store/visibility.js +282 -0
  90. package/dist/store/wire.d.ts +309 -0
  91. package/dist/store/wire.js +834 -0
  92. package/dist/stream.d.ts +174 -0
  93. package/dist/stream.js +238 -0
  94. package/dist/three/index.d.ts +109 -0
  95. package/dist/three/index.js +130 -0
  96. package/dist/udp-probe.d.ts +159 -0
  97. package/dist/udp-probe.js +245 -0
  98. package/dist/wasm.d.ts +642 -0
  99. package/dist/wasm.js +98 -0
  100. package/dist/world/border.d.ts +84 -0
  101. package/dist/world/border.js +99 -0
  102. package/dist/world/handoff.d.ts +187 -0
  103. package/dist/world/handoff.js +222 -0
  104. package/dist/world/index.d.ts +13 -0
  105. package/dist/world/index.js +9 -0
  106. package/dist/world/link.d.ts +179 -0
  107. package/dist/world/link.js +476 -0
  108. package/dist/world/regions.d.ts +154 -0
  109. package/dist/world/regions.js +314 -0
  110. package/package.json +72 -0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * §8's lifecycle, as events.
3
+ *
4
+ * A session emits every {@link LeafEvent} the direct surface does,
5
+ * plus five tags of its own. They are here rather than in
6
+ * `src/events.ts` because they describe the *lifecycle*, not the node:
7
+ * a page using {@link BrowserNode} never sees them, and a page using a
8
+ * session cannot explain a leader change without them.
9
+ *
10
+ * `parseEvent` carries an unknown tag through intact, so a bundle
11
+ * whose leaf is newer than its wrapper stays legible; this module
12
+ * refines exactly the five it knows and leaves the rest as they
13
+ * arrived.
14
+ */
15
+ import { parseEvent } from '../events.js';
16
+ /**
17
+ * Parse one event JSON string from a session.
18
+ *
19
+ * Never throws: this runs inside a callback the wasm module invokes,
20
+ * and an exception there would unwind through Rust. Anything that is
21
+ * not one of the five lifecycle tags goes to `parseEvent`, so the node
22
+ * events and the unknown-tag passthrough behave exactly as they do on
23
+ * the direct surface.
24
+ */
25
+ export function parseSessionEvent(json) {
26
+ const base = parseEvent(json);
27
+ if (base.type !== 'unknown')
28
+ return base;
29
+ const refined = refine(base);
30
+ return refined ?? base;
31
+ }
32
+ /**
33
+ * `true` for the six tags a session adds, so a page can filter the
34
+ * lifecycle out of the event stream without listing them.
35
+ */
36
+ export function isLifecycleEvent(event) {
37
+ switch (event.type) {
38
+ case 'leader_changed':
39
+ case 'subscription_restored':
40
+ case 'leader_lost':
41
+ case 'generation_fenced':
42
+ case 'not_leader':
43
+ case 'promotion_failed':
44
+ return true;
45
+ default:
46
+ return false;
47
+ }
48
+ }
49
+ /**
50
+ * Refine an event `parseEvent` did not recognise.
51
+ *
52
+ * `raw` is whatever `parseJsonPreservingU64` produced, so the exact
53
+ * decimal strings for `generation`, `presented` and `current` are
54
+ * already intact — they are in `U64_EVENT_KEYS`, which is what keeps
55
+ * a large generation from being rounded into a fence that no longer
56
+ * fences.
57
+ */
58
+ function refine(event) {
59
+ const raw = event.raw;
60
+ if (raw === null || typeof raw !== 'object')
61
+ return null;
62
+ const fields = { ...raw };
63
+ switch (event.tag) {
64
+ case 'leader_changed':
65
+ return { type: 'leader_changed', generation: exact(fields.generation) };
66
+ case 'subscription_restored':
67
+ return {
68
+ type: 'subscription_restored',
69
+ channel: typeof fields.channel === 'string' ? fields.channel : '',
70
+ };
71
+ case 'leader_lost': {
72
+ // `failed` is a `usize` count, not a `u64` id: a number is
73
+ // exact for it, and it is deliberately not in `U64_EVENT_KEYS`.
74
+ const failed = Number(fields.failed);
75
+ return {
76
+ type: 'leader_lost',
77
+ generation: exact(fields.generation),
78
+ failed: Number.isFinite(failed) ? failed : 0,
79
+ };
80
+ }
81
+ case 'generation_fenced':
82
+ return {
83
+ type: 'generation_fenced',
84
+ presented: exact(fields.presented),
85
+ current: exact(fields.current),
86
+ };
87
+ case 'not_leader':
88
+ return {
89
+ type: 'not_leader',
90
+ presented: exact(fields.presented),
91
+ current: exact(fields.current),
92
+ };
93
+ case 'promotion_failed':
94
+ return {
95
+ type: 'promotion_failed',
96
+ generation: exact(fields.generation),
97
+ detail: typeof fields.detail === 'string' ? fields.detail : '',
98
+ };
99
+ default:
100
+ return null;
101
+ }
102
+ }
103
+ /**
104
+ * A `u64` field, kept exactly as it arrived.
105
+ *
106
+ * Five call sites that must agree: a generation read as a number
107
+ * anywhere is a generation that rounds above 2^53, and a rounded
108
+ * generation is a fence that silently stops fencing.
109
+ */
110
+ function exact(value) {
111
+ if (typeof value === 'string')
112
+ return value;
113
+ if (typeof value === 'number' && Number.isFinite(value))
114
+ return String(value);
115
+ return '0';
116
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * §8's session surface: one node per origin, leader-elected.
3
+ *
4
+ * `connect()` gives a page this tab's node; `openSession()` gives it
5
+ * the origin's node, whichever tab is running it. See
6
+ * {@link MeshSession} for the three methods that are promises here and
7
+ * synchronous on the direct surface, and why.
8
+ */
9
+ export { MeshSession, openSession } from './session.js';
10
+ export type { SessionOptions, SessionRole } from './session.js';
11
+ export { isLifecycleEvent, parseSessionEvent } from './events.js';
12
+ export type { GenerationFencedEvent, LeaderChangedEvent, LeaderLostEvent, NotLeaderEvent, SessionEvent, SessionEventOf, SessionLifecycleEvent, SubscriptionRestoredEvent, } from './events.js';
13
+ export { asSessionModule } from './wasm.js';
14
+ export type { LeafWasmSession, LeafWasmSessionModule, LeafWasmSessionOptions, } from './wasm.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * §8's session surface: one node per origin, leader-elected.
3
+ *
4
+ * `connect()` gives a page this tab's node; `openSession()` gives it
5
+ * the origin's node, whichever tab is running it. See
6
+ * {@link MeshSession} for the three methods that are promises here and
7
+ * synchronous on the direct surface, and why.
8
+ */
9
+ export { MeshSession, openSession } from './session.js';
10
+ export { isLifecycleEvent, parseSessionEvent } from './events.js';
11
+ export { asSessionModule } from './wasm.js';
@@ -0,0 +1,539 @@
1
+ /**
2
+ * `MeshSession` — one node per origin, whichever tab you opened.
3
+ *
4
+ * §8's decision is that a browser origin runs exactly one Net node:
5
+ * tabs contend for a Web Lock, the holder runs the node on the main
6
+ * thread, and the others attach over a `BroadcastChannel` and get the
7
+ * same API. This is that API in TypeScript.
8
+ *
9
+ * The difference from {@link BrowserNode} is one of scope, not of
10
+ * capability: `connect()` gives a page *this tab's* node, and
11
+ * `openSession()` gives it *the origin's* node. Everything a page can
12
+ * do with one it can do with the other — nRPC calls, channels,
13
+ * streams, announcements, queries, enrollment — and three methods are
14
+ * promises here that are synchronous there, because on a follower the
15
+ * answer lives in another tab:
16
+ *
17
+ * | | `BrowserNode` | `MeshSession` |
18
+ * |---|---|---|
19
+ * | `openStream` | synchronous | promise |
20
+ * | `counters` | synchronous | promise |
21
+ * | `isEnrolled` | synchronous | promise |
22
+ *
23
+ * Failures are the same taxonomy, re-typed by the same mapper, so a
24
+ * proxied refusal and a direct one arrive as the same class. The one
25
+ * addition is {@link NotLeaderError}: a tab that was superseded — a
26
+ * suspended tab that resumed, say — is refused rather than served, and
27
+ * that refusal is typed.
28
+ */
29
+ import { type OrgAccess, type OrgByteStream, type OrgCallOptions, type OrgClientStreamHandler, type OrgDuplexHandler, type OrgDuplexHandles, type OrgServeHandle, type OrgServeOptions, type OrgStreamingHandler, type OrgUnaryHandler, type OrgUploadCall } from '../org.js';
30
+ import { type ConnectOptions, type NodeDescriptor, type PeerConnectOutcome } from '../node.js';
31
+ import { LeafStream, type OpenStreamOptions } from '../stream.js';
32
+ import { type SessionEvent, type SessionEventOf, type SessionLifecycleEvent } from './events.js';
33
+ import { type Unsubscribe } from '../events.js';
34
+ /** Whether this tab runs the node or talks to the tab that does. */
35
+ export type SessionRole = 'leader' | 'follower';
36
+ /** {@link openSession}'s argument. */
37
+ export interface SessionOptions extends ConnectOptions {
38
+ /**
39
+ * Capabilities to announce, and to re-announce whenever this tab
40
+ * becomes the leader.
41
+ *
42
+ * On the session rather than on `announce()` because a new leader
43
+ * has to re-publish them without being asked — D2's restoration
44
+ * step. Calling `announce()` as well is fine and additive.
45
+ */
46
+ capabilities?: readonly string[];
47
+ /**
48
+ * Channels this tab depends on.
49
+ *
50
+ * Declared up front so a *new* leader can restore them: the leader
51
+ * keeps the union of every follower's declaration subscribed, and a
52
+ * tab that only called `subscribe()` after a handoff would have a
53
+ * window where its channel was not subscribed by anybody.
54
+ */
55
+ subscriptions?: readonly string[];
56
+ /** The IndexedDB database holding the identity. Defaults to `net-mesh-leaf`. */
57
+ dbName?: string;
58
+ /**
59
+ * Override the Web Lock and `BroadcastChannel` name. Defaults to
60
+ * `net-mesh/<origin>/<identity-fingerprint>`.
61
+ *
62
+ * For tests that need two independent elections inside one origin.
63
+ * A page should not set it: two scopes on one identity is two nodes
64
+ * on one identity, which is the eviction §8 exists to prevent.
65
+ */
66
+ lockScope?: string;
67
+ }
68
+ /** The origin's Net node, as this tab sees it. */
69
+ export declare class MeshSession {
70
+ private readonly inner;
71
+ /**
72
+ * The same fan-out the direct surface uses, instantiated over the
73
+ * wider event union. One implementation, so a throwing listener,
74
+ * the never-unwind-into-Rust rule and the iterator buffering behave
75
+ * identically on both surfaces.
76
+ */
77
+ private readonly hub;
78
+ /**
79
+ * Streams this session handed out, so the ones still open can be
80
+ * ended when leadership moves.
81
+ *
82
+ * A stream is session-scoped and is **not** restored across a
83
+ * leader change, which the Rust side enforces by binding each
84
+ * handle to its opening generation. That enforcement makes a stale
85
+ * `send` reject — but it says nothing about a consumer sitting in
86
+ * `await stream[Symbol.asyncIterator]().next()`, which would simply
87
+ * never settle: the wasm stream stops emitting and nothing tells
88
+ * the queue to end. So the session owns the set and closes it,
89
+ * which is what turns "no more data, ever" into the end of the
90
+ * iteration a page is already written to handle.
91
+ */
92
+ private readonly streams;
93
+ /**
94
+ * The generation the streams in that set were opened under.
95
+ *
96
+ * Tracked because the generation moving is the *only* signal an
97
+ * abrupt leader disappearance gives. A page that crashes or
98
+ * navigates away never sends its `leader_lost` announcement: the
99
+ * promoted follower fails its own pending requests and reports
100
+ * `leader_changed`, and so does every other surviving follower
101
+ * when the successor's Leadership arrives. The Rust handle then
102
+ * correctly suppresses the old stream's bytes by its opening
103
+ * generation — which is precisely what leaves a consumer sitting
104
+ * in `await iterator.next()` forever, waiting on a stream that is
105
+ * now guaranteed never to emit again.
106
+ */
107
+ private streamGeneration;
108
+ /**
109
+ * The live org handles (plan §4.5), retired when leadership moves
110
+ * or the session closes: pending calls fail typed and each dropped
111
+ * caller handle emits exactly one CANCEL.
112
+ */
113
+ private readonly orgHandles;
114
+ private closed;
115
+ /** `'leader'` if this tab runs the node, `'follower'` if another does. */
116
+ role(): SessionRole;
117
+ /**
118
+ * The generation in force for this tab, as an exact decimal string.
119
+ *
120
+ * A string because it is a `u64`: a number would round above 2^53,
121
+ * and a rounded generation is a fence that has stopped fencing.
122
+ */
123
+ generation(): string;
124
+ /** The node's id, 16 lowercase hex digits, or `null` before one is known. */
125
+ nodeIdHex(): string | null;
126
+ /** The identity fingerprint the Web Lock is named after. */
127
+ fingerprint(): string;
128
+ /** The Web Lock and `BroadcastChannel` name. */
129
+ scope(): string;
130
+ /**
131
+ * How long this tab took to take over, in milliseconds, or `null` if
132
+ * it was the origin's first leader.
133
+ *
134
+ * Measured from the lock coming free to the node being re-bootstrapped
135
+ * and the subscriptions and announcement restored. It is a
136
+ * measurement, not a promise: the interruption a *peer* observes is
137
+ * bounded by Net's own failure detection, which is a different and
138
+ * much larger number.
139
+ */
140
+ interruptionMs(): number | null;
141
+ /**
142
+ * Call an nRPC service.
143
+ *
144
+ * Rejects with {@link RpcError} — including `leader-lost` when the
145
+ * tab running the node was replaced while the call was in flight.
146
+ * Never a silent retry: §8's rule is that the caller is told and
147
+ * decides.
148
+ *
149
+ * On a follower the deadline is armed here as well as on the
150
+ * leader, and that is true whether or not `timeoutMs` was given:
151
+ * omitting it takes the leaf's own 30 s default rather than
152
+ * leaving the promise to a leader that may be frozen. Either way
153
+ * expiry is `rpc-indeterminate` — the work may already have been
154
+ * admitted in the other tab, and nothing here re-issues it.
155
+ */
156
+ call(service: string, payload: Uint8Array, timeoutMs?: number): Promise<Uint8Array>;
157
+ /**
158
+ * Subscribe to a channel.
159
+ *
160
+ * Also adds it to what a future leader restores, so a handoff does
161
+ * not silently drop it.
162
+ */
163
+ subscribe(channel: string): Promise<void>;
164
+ /**
165
+ * Release this tab's claim on a channel.
166
+ *
167
+ * Not `subscribe` run backwards. The origin runs one node and the
168
+ * leader holds **one** membership for the union of every tab's
169
+ * declarations, so this drops this tab's claim and the membership
170
+ * itself is given up only when no other tab still wants the
171
+ * channel. One tab closing a subscription therefore cannot stop
172
+ * another tab that is still reading the same channel.
173
+ *
174
+ * Also removes it from what a future leader restores, so a handoff
175
+ * does not silently bring back a channel this tab released.
176
+ */
177
+ unsubscribe(channel: string): Promise<void>;
178
+ /** Publish one payload to a channel. */
179
+ publish(channel: string, payload: Uint8Array): Promise<void>;
180
+ /** Publish this node's capability announcement. */
181
+ announce(capabilities: readonly string[]): Promise<void>;
182
+ /** Find the nodes offering a capability. */
183
+ query(capability: string): Promise<NodeDescriptor[]>;
184
+ /** Every leaf counter. Values stay strings because they are `u64`. */
185
+ counters(): Promise<Record<string, string>>;
186
+ /**
187
+ * Run the enrollment exchange if the node is not enrolled.
188
+ *
189
+ * Works on a follower too: one node per origin means one enrollment
190
+ * per origin, so a follower asking to enroll is asking the only
191
+ * session there is.
192
+ */
193
+ enroll(): Promise<void>;
194
+ /** Whether the anchor has admitted this node. */
195
+ isEnrolled(): Promise<boolean>;
196
+ /**
197
+ * Sign and send a `0x0D02` signalling envelope — the
198
+ * session-independent path, so no session with `peer` is needed.
199
+ */
200
+ signal(peerHex: string, dialogHex: string, kind: string, payload: Uint8Array): Promise<void>;
201
+ /**
202
+ * Connect directly to `nodeIdHex` (§9), wherever the node is.
203
+ *
204
+ * The same drive loop `BrowserNode.connectPeer` runs — literally
205
+ * the same function, from `peer-driver.ts`, with this session's
206
+ * primitives instead of the direct node's. One loop means the
207
+ * offerer and the answerer cannot disagree about what supersession
208
+ * or a terminal reading looks like, which is a disagreement neither
209
+ * side could detect.
210
+ *
211
+ * On a follower every step is a proxy round trip and the attempt
212
+ * runs on the leader tab's node. A step whose attempt was replaced
213
+ * while the request was in flight is refused by the leaf before it
214
+ * touches the replacement, and arrives here as `superseded`.
215
+ *
216
+ * **Idempotent on a healthy pair**, as `BrowserNode.connectPeer` is: an
217
+ * already-direct, open pair resolves `direct` with its live dialog and
218
+ * nothing is offered — a second offer would replace the working link
219
+ * and close it under the peer. A leader too old to answer that reading
220
+ * is asked for an offer, as before.
221
+ */
222
+ connectPeer(nodeIdHex: string): Promise<PeerConnectOutcome>;
223
+ /**
224
+ * Answer the offer `nodeIdHex` filed — {@link connectPeer}'s
225
+ * counterpart, on whichever tab holds the lock.
226
+ */
227
+ acceptPeer(nodeIdHex: string): Promise<PeerConnectOutcome>;
228
+ /**
229
+ * The proxied primitives, each re-typed by the same mapper every
230
+ * other method here uses, so a proxied refusal is the same class a
231
+ * direct one is — which is what lets one drive loop classify both.
232
+ */
233
+ private peerPrimitives;
234
+ /**
235
+ * Open a reliable or fire-and-forget stream.
236
+ *
237
+ * A promise where {@link BrowserNode.openStream} is synchronous: on
238
+ * a follower the stream is opened by the tab that owns the
239
+ * DataChannel. The object it resolves to is the same
240
+ * {@link LeafStream}, so `await stream.send(bytes)` and
241
+ * `for await (const payload of stream)` read the same either way.
242
+ *
243
+ * A stream is **not** restored across a leader change — it is
244
+ * session-scoped, and pretending otherwise would hide a real
245
+ * interruption. A page that wants one back opens one. What this
246
+ * session does guarantee is that the interruption is *observable*:
247
+ * the handle is retained here and ended when leadership moves, so
248
+ * `for await (const payload of stream)` finishes instead of hanging
249
+ * on a node that is gone.
250
+ *
251
+ * That guarantee has to cover the open itself. On a follower this
252
+ * is a proxy round trip, so a result can arrive *after* the
253
+ * leadership-loss notification that already drained the retained
254
+ * set — and the handle it carries was stamped by Rust with the
255
+ * generation the request was issued under, so it is stale on
256
+ * arrival. Registering it would put a permanently silent stream
257
+ * into a set nothing will drain again. The generation is therefore
258
+ * captured before the await and compared after it, and a crossing
259
+ * result is ended exactly as a consumer a microtask later would
260
+ * have seen it.
261
+ */
262
+ openStream(options: OpenStreamOptions): Promise<LeafStream>;
263
+ /**
264
+ * Call an org-protected unary service.
265
+ *
266
+ * Rejects with the §4.3 typed error: an **opening denial** is
267
+ * {@link OrgAdmissionDeniedError} carrying the coarse reason and
268
+ * nothing finer — a precise remote reason would be a credential
269
+ * oracle. Never a silent retry: the caller is told and decides.
270
+ *
271
+ * **Leader replacement.** A pending proxied call fails typed
272
+ * `LeaderLost` on a generation move and is NEVER resumed; a
273
+ * successor generation's calls carry fresh correlation and fresh
274
+ * proofs.
275
+ *
276
+ * **Suspension and closure (plan §4.5).** Deadlines are absolute: a
277
+ * frozen tab extends no lease and no window, so a call whose
278
+ * deadline passes while its tab is suspended retires with its
279
+ * deadline terminal on wake — {@link OrgTimeoutError}, or
280
+ * {@link OrgIndeterminateError} where the work may already be
281
+ * executing in another tab. Nothing resumes automatically: a
282
+ * retired call is never transparently re-opened, because re-opening
283
+ * is a fresh call with a fresh proof and MAY repeat effects. Node
284
+ * or tab close retires ownership — pending calls fail typed, and
285
+ * each dropped caller handle emits exactly one CANCEL.
286
+ */
287
+ callOrg(service: string, payload: Uint8Array, options: OrgCallOptions): Promise<Uint8Array>;
288
+ /**
289
+ * Call an org-protected server-streaming service; resolve to the
290
+ * response stream.
291
+ *
292
+ * An **opening denial** rejects here with
293
+ * {@link OrgAdmissionDeniedError} (coarse) — a denial is a
294
+ * rejection, never a terminal item. Midstream, the stream's FINAL
295
+ * error item carries the §4.3 outcome: revocation is
296
+ * `AdmissionDenied('denied')` ({@link OrgRevokedError} where the
297
+ * cause is known), deadline/cancel retirement is
298
+ * {@link OrgTimeoutError}/{@link OrgCancelledError}. The async
299
+ * iterator throws that typed error at the terminal error item.
300
+ *
301
+ * **Leader replacement.** A pending proxied call fails typed
302
+ * `LeaderLost` on a generation move and is NEVER resumed; a
303
+ * successor generation's calls carry fresh correlation and fresh
304
+ * proofs. An open that lands after the move is retired on arrival
305
+ * rather than handed out live.
306
+ *
307
+ * **Suspension and closure (plan §4.5).** Deadlines are absolute: a
308
+ * frozen tab extends no lease and no window, so a call whose
309
+ * deadline passes while its tab is suspended retires with its
310
+ * deadline terminal on wake — {@link OrgTimeoutError}, or
311
+ * {@link OrgIndeterminateError} where the work may already be
312
+ * executing in another tab. Nothing resumes automatically: a
313
+ * retired call is never transparently re-opened, because re-opening
314
+ * is a fresh call with a fresh proof and MAY repeat effects. Node
315
+ * or tab close retires ownership — pending calls fail typed, and
316
+ * each dropped caller handle emits exactly one CANCEL.
317
+ */
318
+ callOrgStreaming(service: string, payload: Uint8Array, options: OrgCallOptions): Promise<OrgByteStream>;
319
+ /**
320
+ * Open an org-protected client-streaming upload. `finish()` is the
321
+ * half-close and the reply await, and it rejects with the typed
322
+ * terminal error (§4.3).
323
+ *
324
+ * An **opening denial** rejects here with
325
+ * {@link OrgAdmissionDeniedError} (coarse).
326
+ *
327
+ * **Leader replacement.** A pending proxied call fails typed
328
+ * `LeaderLost` on a generation move and is NEVER resumed; a
329
+ * successor generation's calls carry fresh correlation and fresh
330
+ * proofs. An open that lands after the move is retired on arrival
331
+ * rather than handed out live.
332
+ *
333
+ * **Suspension and closure (plan §4.5).** Deadlines are absolute: a
334
+ * frozen tab extends no lease and no window, so a call whose
335
+ * deadline passes while its tab is suspended retires with its
336
+ * deadline terminal on wake — {@link OrgTimeoutError}, or
337
+ * {@link OrgIndeterminateError} where the work may already be
338
+ * executing in another tab. Nothing resumes automatically: a
339
+ * retired call is never transparently re-opened, because re-opening
340
+ * is a fresh call with a fresh proof and MAY repeat effects. Node
341
+ * or tab close retires ownership — pending calls fail typed, and
342
+ * each dropped caller handle emits exactly one CANCEL.
343
+ */
344
+ callOrgClientStream(service: string, options: OrgCallOptions): Promise<OrgUploadCall>;
345
+ /**
346
+ * Open an org-protected duplex call: an upload sink and an
347
+ * independent response stream.
348
+ *
349
+ * An **opening denial** rejects here with
350
+ * {@link OrgAdmissionDeniedError} (coarse). Midstream outcomes are
351
+ * the §4.3 terminals on each half.
352
+ *
353
+ * **Leader replacement.** A pending proxied call fails typed
354
+ * `LeaderLost` on a generation move and is NEVER resumed; a
355
+ * successor generation's calls carry fresh correlation and fresh
356
+ * proofs. An open that lands after the move is retired on arrival
357
+ * rather than handed out live.
358
+ *
359
+ * **Suspension and closure (plan §4.5).** Deadlines are absolute: a
360
+ * frozen tab extends no lease and no window, so a call whose
361
+ * deadline passes while its tab is suspended retires with its
362
+ * deadline terminal on wake — {@link OrgTimeoutError}, or
363
+ * {@link OrgIndeterminateError} where the work may already be
364
+ * executing in another tab. Nothing resumes automatically: a
365
+ * retired call is never transparently re-opened, because re-opening
366
+ * is a fresh call with a fresh proof and MAY repeat effects. Node
367
+ * or tab close retires ownership — pending calls fail typed, and
368
+ * each dropped caller handle emits exactly one CANCEL.
369
+ */
370
+ callOrgDuplex(service: string, options: OrgCallOptions): Promise<OrgDuplexHandles>;
371
+ /**
372
+ * Serve an org-protected unary service. `access` is `'same-org'`
373
+ * (owner-org traffic only) or `'granted'` (capability grants too);
374
+ * anything else is refused loudly at registration.
375
+ *
376
+ * On a follower the registration rides the proxy registry and
377
+ * inbound calls dispatch to the registering follower; teardown
378
+ * follows the retire discipline — a generation move fences the
379
+ * registration's calls and streams, never resurrected.
380
+ *
381
+ * **Handler drop (F-S3.1-2).** The retire supervisor may drop the
382
+ * handler future without a final poll — cancellation is observed
383
+ * through the retirement observables (the terminal item, the sink's
384
+ * typed closed refusal, and `retired`), never assumed as a
385
+ * handler-side event; a detached observer holding `retired`
386
+ * observes the signal. After retirement a handler sees typed
387
+ * refusals and any return value it produces is discarded.
388
+ *
389
+ * **`close()` is a retirement, not a graceful unregister (C9's
390
+ * protected split).** Closing the returned handle retires its live
391
+ * protected calls — each with its exact retirement terminal, the
392
+ * deadline/cancel/revocation vocabulary — and refuses new openings
393
+ * on the service. Deliberately NOT the public path's
394
+ * let-existing-calls-finish behavior.
395
+ */
396
+ serveOrg(service: string, access: OrgAccess, handler: OrgUnaryHandler, options: OrgServeOptions): OrgServeHandle;
397
+ /**
398
+ * Serve an org-protected server-streaming service: one request in,
399
+ * items out through the {@link OrgResponseSink}.
400
+ *
401
+ * On a follower the registration rides the proxy registry and
402
+ * inbound calls dispatch to the registering follower; teardown
403
+ * follows the retire discipline — a generation move fences the
404
+ * registration's calls and streams, never resurrected.
405
+ *
406
+ * **Handler drop (F-S3.1-2).** The retire supervisor may drop the
407
+ * handler future without a final poll — cancellation is observed
408
+ * through the retirement observables (the terminal item, the sink's
409
+ * typed closed refusal, and `retired`), never assumed as a
410
+ * handler-side event; a detached observer holding `retired`
411
+ * observes the signal. After retirement a handler sees typed
412
+ * refusals and any return value it produces is discarded.
413
+ *
414
+ * **`close()` is a retirement, not a graceful unregister (C9's
415
+ * protected split).** Closing the returned handle retires its live
416
+ * protected calls — each with its exact retirement terminal, the
417
+ * deadline/cancel/revocation vocabulary — and refuses new openings
418
+ * on the service. Deliberately NOT the public path's
419
+ * let-existing-calls-finish behavior.
420
+ */
421
+ serveOrgStreaming(service: string, access: OrgAccess, handler: OrgStreamingHandler, options: OrgServeOptions): OrgServeHandle;
422
+ /**
423
+ * Serve an org-protected client-streaming service: requests in
424
+ * through the {@link OrgRequestStream}, one reply out.
425
+ *
426
+ * On a follower the registration rides the proxy registry and
427
+ * inbound calls dispatch to the registering follower; teardown
428
+ * follows the retire discipline — a generation move fences the
429
+ * registration's calls and streams, never resurrected.
430
+ *
431
+ * **Handler drop (F-S3.1-2).** The retire supervisor may drop the
432
+ * handler future without a final poll — cancellation is observed
433
+ * through the retirement observables (the terminal item, the sink's
434
+ * typed closed refusal, and `retired`), never assumed as a
435
+ * handler-side event; a detached observer holding `retired`
436
+ * observes the signal. After retirement a handler sees typed
437
+ * refusals and any return value it produces is discarded.
438
+ *
439
+ * **`close()` is a retirement, not a graceful unregister (C9's
440
+ * protected split).** Closing the returned handle retires its live
441
+ * protected calls — each with its exact retirement terminal, the
442
+ * deadline/cancel/revocation vocabulary — and refuses new openings
443
+ * on the service. Deliberately NOT the public path's
444
+ * let-existing-calls-finish behavior.
445
+ */
446
+ serveOrgClientStream(service: string, access: OrgAccess, handler: OrgClientStreamHandler, options: OrgServeOptions): OrgServeHandle;
447
+ /**
448
+ * Serve an org-protected duplex service: requests in and items out,
449
+ * independently, through the request stream and the sink.
450
+ *
451
+ * On a follower the registration rides the proxy registry and
452
+ * inbound calls dispatch to the registering follower; teardown
453
+ * follows the retire discipline — a generation move fences the
454
+ * registration's calls and streams, never resurrected.
455
+ *
456
+ * **Handler drop (F-S3.1-2).** The retire supervisor may drop the
457
+ * handler future without a final poll — cancellation is observed
458
+ * through the retirement observables (the terminal item, the sink's
459
+ * typed closed refusal, and `retired`), never assumed as a
460
+ * handler-side event; a detached observer holding `retired`
461
+ * observes the signal. After retirement a handler sees typed
462
+ * refusals and any return value it produces is discarded.
463
+ *
464
+ * **`close()` is a retirement, not a graceful unregister (C9's
465
+ * protected split).** Closing the returned handle retires its live
466
+ * protected calls — each with its exact retirement terminal, the
467
+ * deadline/cancel/revocation vocabulary — and refuses new openings
468
+ * on the service. Deliberately NOT the public path's
469
+ * let-existing-calls-finish behavior.
470
+ */
471
+ serveOrgDuplex(service: string, access: OrgAccess, handler: OrgDuplexHandler, options: OrgServeOptions): OrgServeHandle;
472
+ /** Listen for one event tag. Returns a cancel handle. */
473
+ on<T extends SessionEvent['type']>(type: T, handler: (event: SessionEventOf<T>) => void): Unsubscribe;
474
+ /** Listen for every event. Returns a cancel handle. */
475
+ onEvent(handler: (event: SessionEvent) => void): Unsubscribe;
476
+ /**
477
+ * Listen for the five lifecycle tags only — `leader_changed`,
478
+ * `subscription_restored`, `leader_lost`, `generation_fenced`,
479
+ * `not_leader`.
480
+ *
481
+ * A page that wants to render "reconnecting" wants exactly these and
482
+ * none of the node traffic.
483
+ */
484
+ onLifecycle(handler: (event: SessionLifecycleEvent) => void): Unsubscribe;
485
+ /** Consume events as an async iterable. */
486
+ events(): AsyncIterableIterator<SessionEvent>;
487
+ /**
488
+ * Stand down and release the lock.
489
+ *
490
+ * On a leader this is what lets a follower promote, so a page that
491
+ * navigates away without calling it leaves the handoff to the
492
+ * browser's own lock release.
493
+ *
494
+ * Every stream this session handed out is ended first, in that
495
+ * order: a consumer awaiting the next payload has to be settled by
496
+ * something, and after `inner.close()` nothing will ever emit
497
+ * again.
498
+ */
499
+ close(): void;
500
+ /**
501
+ * End every stream this session handed out.
502
+ *
503
+ * `LeafStream.close` ends each waiting consumer and clears the
504
+ * listeners, which is precisely the disposition D2 specifies for a
505
+ * stream across a leader change: failed and observable, never
506
+ * resurrected and never left pending. Called on explicit
507
+ * leadership loss, on a generation that moved under this tab, and
508
+ * on close; idempotent, because a page may have closed a stream
509
+ * itself.
510
+ */
511
+ private endStreams;
512
+ /**
513
+ * Re-type whatever the boundary threw.
514
+ *
515
+ * One wrapper for every method because they all cross the same
516
+ * boundary and a proxied failure must arrive as the same class a
517
+ * direct one does — that is the whole point of the failure text
518
+ * being carried verbatim through the proxy instead of re-parsed on
519
+ * the Rust side.
520
+ */
521
+ private guard;
522
+ }
523
+ /**
524
+ * Join the origin's Net node, electing a leader if there is none.
525
+ *
526
+ * Resolves as soon as this tab knows its role: the leader once it has
527
+ * bootstrapped, a follower once it has attached. A follower keeps
528
+ * queueing for the lock in the background, so when the leader goes
529
+ * away it promotes itself, re-bootstraps with the same identity and
530
+ * restores the subscriptions — without the page doing anything.
531
+ *
532
+ * Rejects with a typed {@link LeafError}. {@link IdentityError} covers
533
+ * the two refusals worth knowing by name: a context with no Web Locks
534
+ * API (where one-node-per-origin cannot be enforced, so the leaf
535
+ * refuses rather than let two tabs evict each other) and a context
536
+ * with no IndexedDB (where an identity cannot be stored, so the page
537
+ * must inject one).
538
+ */
539
+ export declare function openSession(options: SessionOptions): Promise<MeshSession>;