@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,62 @@
1
+ /**
2
+ * The one-consumer async queue behind every async iterable in this
3
+ * package (`node.events()`, `stream`).
4
+ *
5
+ * Items that arrive while the consumer is not awaiting are buffered in
6
+ * arrival order; buffering stops the moment the loop is left (`break`,
7
+ * `return`, or an exception), which is what calls the `detach` hook, so
8
+ * a page that stops consuming stops paying. A page that starts a loop
9
+ * and then never awaits it will grow the buffer — leave the loop
10
+ * instead of abandoning it.
11
+ */
12
+ export class AsyncQueue {
13
+ detach;
14
+ buffered = [];
15
+ waiting = null;
16
+ ended = false;
17
+ /** @param detach called once when the consumer stops iterating. */
18
+ constructor(detach = () => { }) {
19
+ this.detach = detach;
20
+ }
21
+ /** Hand an item to the consumer, or buffer it. */
22
+ push(item) {
23
+ if (this.ended)
24
+ return;
25
+ const waiting = this.waiting;
26
+ if (waiting) {
27
+ this.waiting = null;
28
+ waiting({ value: item, done: false });
29
+ return;
30
+ }
31
+ this.buffered.push(item);
32
+ }
33
+ /** End the iteration once the buffer drains. */
34
+ end() {
35
+ if (this.ended)
36
+ return;
37
+ this.ended = true;
38
+ const waiting = this.waiting;
39
+ if (waiting) {
40
+ this.waiting = null;
41
+ waiting({ value: undefined, done: true });
42
+ }
43
+ }
44
+ next() {
45
+ const buffered = this.buffered.shift();
46
+ if (buffered !== undefined)
47
+ return Promise.resolve({ value: buffered, done: false });
48
+ if (this.ended)
49
+ return Promise.resolve({ value: undefined, done: true });
50
+ const { promise, resolve } = Promise.withResolvers();
51
+ this.waiting = resolve;
52
+ return promise;
53
+ }
54
+ return() {
55
+ this.detach();
56
+ this.end();
57
+ return Promise.resolve({ value: undefined, done: true });
58
+ }
59
+ [Symbol.asyncIterator]() {
60
+ return this;
61
+ }
62
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * `requestCredential` — ask a game anchor for an anonymous visitor
3
+ * credential, so a page connects with no hand-minted secret.
4
+ *
5
+ * ```ts
6
+ * import { openSession, requestCredential } from '@net-mesh/browser';
7
+ *
8
+ * const { credentialB64, bootstrapUrl } = await requestCredential({
9
+ * anchorUrl: 'https://anchor.example.com',
10
+ * game: 'my-game',
11
+ * });
12
+ * const session = await openSession({ credentialB64, bootstrapUrl });
13
+ * ```
14
+ *
15
+ * The anchor must admit the game (`net-mesh anchor serve --game my-game`).
16
+ * Each call is a new visitor seat: the credential binds to the first
17
+ * browser identity that enrolls with it, and that identity may reconnect
18
+ * with it for the credential's lifetime (a promoted leader tab does).
19
+ *
20
+ * **Who the player is.** A game connects with `connect()` (stores, lobbies
21
+ * and the lossy channel need it) and keeps its player with
22
+ * `rememberedIdentity()`, which stores the identity in this origin's
23
+ * `localStorage`: one player per **browser profile**. Clearing site data
24
+ * makes a new player, and the same person on two devices is two players.
25
+ * A bare `connect()` without it makes a new identity every page load.
26
+ */
27
+ /** {@link requestCredential}'s argument. */
28
+ export interface RequestCredentialOptions {
29
+ /**
30
+ * The anchor's bootstrap URL — what the operator passed as
31
+ * `anchor serve --url`, e.g. `https://anchor.example.com`.
32
+ */
33
+ readonly anchorUrl: string;
34
+ /** The game id the anchor admits (`--game`). */
35
+ readonly game: string;
36
+ /** Cancels the request. */
37
+ readonly signal?: AbortSignal;
38
+ /** A `fetch` to use instead of the global one (tests, workers). */
39
+ readonly fetch?: typeof fetch;
40
+ }
41
+ /** What {@link requestCredential} returns — exactly what `connect()` / `openSession()` take. */
42
+ export interface AnchorCredential {
43
+ readonly credentialB64: string;
44
+ readonly bootstrapUrl: string;
45
+ readonly game: string;
46
+ }
47
+ /** Why {@link requestCredential} failed. */
48
+ export type CredentialRequestErrorKind =
49
+ /** The anchor does not admit that game. */
50
+ 'unknown-game'
51
+ /** Too many requests from this address, or for this game; retry shortly. */
52
+ | 'rate-limited'
53
+ /** The anchor did not understand the request. */
54
+ | 'malformed-request'
55
+ /** The anchor could not be reached, or it issues no credentials. */
56
+ | 'unreachable'
57
+ /** The anchor answered something this package does not recognise. */
58
+ | 'unexpected';
59
+ /** A typed {@link requestCredential} failure. */
60
+ export declare class CredentialRequestError extends Error {
61
+ readonly kind: CredentialRequestErrorKind;
62
+ /** The HTTP status, when the anchor answered. */
63
+ readonly status: number | null;
64
+ readonly name = "CredentialRequestError";
65
+ constructor(kind: CredentialRequestErrorKind, message: string,
66
+ /** The HTTP status, when the anchor answered. */
67
+ status?: number | null);
68
+ }
69
+ /** Ask `anchorUrl` for a fresh anonymous credential for `game`. */
70
+ export declare function requestCredential(options: RequestCredentialOptions): Promise<AnchorCredential>;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * `requestCredential` — ask a game anchor for an anonymous visitor
3
+ * credential, so a page connects with no hand-minted secret.
4
+ *
5
+ * ```ts
6
+ * import { openSession, requestCredential } from '@net-mesh/browser';
7
+ *
8
+ * const { credentialB64, bootstrapUrl } = await requestCredential({
9
+ * anchorUrl: 'https://anchor.example.com',
10
+ * game: 'my-game',
11
+ * });
12
+ * const session = await openSession({ credentialB64, bootstrapUrl });
13
+ * ```
14
+ *
15
+ * The anchor must admit the game (`net-mesh anchor serve --game my-game`).
16
+ * Each call is a new visitor seat: the credential binds to the first
17
+ * browser identity that enrolls with it, and that identity may reconnect
18
+ * with it for the credential's lifetime (a promoted leader tab does).
19
+ *
20
+ * **Who the player is.** A game connects with `connect()` (stores, lobbies
21
+ * and the lossy channel need it) and keeps its player with
22
+ * `rememberedIdentity()`, which stores the identity in this origin's
23
+ * `localStorage`: one player per **browser profile**. Clearing site data
24
+ * makes a new player, and the same person on two devices is two players.
25
+ * A bare `connect()` without it makes a new identity every page load.
26
+ */
27
+ /** A typed {@link requestCredential} failure. */
28
+ export class CredentialRequestError extends Error {
29
+ kind;
30
+ status;
31
+ name = 'CredentialRequestError';
32
+ constructor(kind, message,
33
+ /** The HTTP status, when the anchor answered. */
34
+ status = null) {
35
+ super(message);
36
+ this.kind = kind;
37
+ this.status = status;
38
+ }
39
+ }
40
+ const REFUSALS = {
41
+ unknown_game: 'unknown-game',
42
+ rate_limited: 'rate-limited',
43
+ malformed_request: 'malformed-request',
44
+ };
45
+ /** Ask `anchorUrl` for a fresh anonymous credential for `game`. */
46
+ export async function requestCredential(options) {
47
+ const doFetch = options.fetch ?? globalThis.fetch;
48
+ if (typeof doFetch !== 'function') {
49
+ throw new CredentialRequestError('unreachable', 'requestCredential needs fetch');
50
+ }
51
+ const endpoint = `${options.anchorUrl.replace(/\/+$/, '')}/credential`;
52
+ let response;
53
+ try {
54
+ response = await doFetch(endpoint, {
55
+ method: 'POST',
56
+ headers: { 'content-type': 'application/json' },
57
+ body: JSON.stringify({ game: options.game }),
58
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
59
+ });
60
+ }
61
+ catch (error) {
62
+ if (options.signal?.aborted)
63
+ throw error;
64
+ throw new CredentialRequestError('unreachable', `could not reach ${endpoint}: ${String(error)}`);
65
+ }
66
+ let body = null;
67
+ try {
68
+ body = await response.json();
69
+ }
70
+ catch {
71
+ // Not JSON: an anchor without `--game` answers its router's bare 404.
72
+ }
73
+ if (!response.ok) {
74
+ const refusal = body?.refusal;
75
+ const message = body?.message;
76
+ const kind = typeof refusal === 'string' && refusal in REFUSALS
77
+ ? REFUSALS[refusal]
78
+ : response.status === 404
79
+ ? 'unreachable'
80
+ : 'unexpected';
81
+ const detail = kind === 'unreachable'
82
+ ? `${endpoint} issues no credentials (is the anchor started with --game?)`
83
+ : typeof message === 'string'
84
+ ? message
85
+ : `the anchor answered ${response.status}`;
86
+ throw new CredentialRequestError(kind, detail, response.status);
87
+ }
88
+ const reply = body;
89
+ if (typeof reply?.credentialB64 !== 'string' ||
90
+ typeof reply.bootstrapUrl !== 'string' ||
91
+ typeof reply.game !== 'string') {
92
+ throw new CredentialRequestError('unexpected', 'the anchor answered without a credential', response.status);
93
+ }
94
+ return { credentialB64: reply.credentialB64, bootstrapUrl: reply.bootstrapUrl, game: reply.game };
95
+ }
@@ -0,0 +1,400 @@
1
+ /**
2
+ * The leaf's error taxonomy, mirrored in TypeScript.
3
+ *
4
+ * One class per variant of `net_leaf::error::LeafError`, with the two
5
+ * nested enums (`RtcError`, `RpcError`) flattened into `kind` so a
6
+ * caller — or a Playwright assertion — can branch on one stable
7
+ * string. `message` is verbatim the Rust `Display` text that crossed
8
+ * the wasm boundary, so nothing is lost in the re-typing;
9
+ * `errors.test.ts` pins that round-trip.
10
+ *
11
+ * **The failure-typing correction lives in this file's types.** There
12
+ * is no way to build a {@link RtcErrorFailure} of type `udpBlocked`
13
+ * without a {@link UdpBlockedEvidence}, and `udpBlockedEvidence()` —
14
+ * the mirror of Rust's `UdpBlockedEvidence::new` — returns `null`
15
+ * unless both observations hold. An ICE timeout on its own therefore
16
+ * cannot become `udp-blocked` anywhere in this package.
17
+ */
18
+ /** Every error kind the leaf can surface, one per Rust variant. */
19
+ export type LeafErrorKind = 'wire' | 'session' | 'control-plane' | 'identity' | 'not-leader' | 'ice-timeout' | 'udp-blocked' | 'channel-closed' | 'rtc-unsupported' | 'rpc-refused' | 'rpc-timeout' | 'session-lost' | 'leader-lost' | 'rpc-indeterminate' | 'rpc-malformed' | 'ice-server-conflict' | 'org-admission-denied' | 'org-revoked' | 'org-timeout' | 'org-cancelled' | 'org-leader-lost' | 'org-session-lost' | 'org-indeterminate' | 'org-refused' | 'org-internal' | 'org-malformed' | 'unknown';
20
+ /**
21
+ * The two observations that, together, distinguish blocked UDP from
22
+ * an anchor that simply is not there. Mirrors
23
+ * `net_leaf::error::UdpBlockedEvidence`.
24
+ *
25
+ * The field types are the literal `true`, not `boolean`: a value of
26
+ * this type is itself the proof, so a caller cannot hand one over
27
+ * with `bootstrapOk: false`.
28
+ */
29
+ export interface UdpBlockedEvidence {
30
+ /** The anchor answered over HTTPS — it is up and addressable. */
31
+ readonly bootstrapOk: true;
32
+ /** A STUN binding to the `rtc_addr` that anchor published went unanswered. */
33
+ readonly stunProbeFailed: true;
34
+ /** The address the probe was aimed at, so the claim names its subject. */
35
+ readonly probed: string;
36
+ }
37
+ /**
38
+ * `UdpBlockedEvidence` only when both observations hold; `null`
39
+ * otherwise, which is the caller's instruction to stay with
40
+ * `ice-timeout`. Mirror of `UdpBlockedEvidence::new`.
41
+ */
42
+ export declare function udpBlockedEvidence(bootstrapOk: boolean, stunProbeFailed: boolean, probed: string): UdpBlockedEvidence | null;
43
+ /** Why an RTC attempt did not produce a DataChannel. */
44
+ export type RtcErrorFailure = {
45
+ readonly type: 'iceTimeout';
46
+ } | {
47
+ readonly type: 'udpBlocked';
48
+ readonly evidence: UdpBlockedEvidence;
49
+ } | {
50
+ readonly type: 'channelClosed';
51
+ readonly detail: string;
52
+ } | {
53
+ readonly type: 'unsupported';
54
+ readonly detail: string;
55
+ };
56
+ /** How an nRPC call ended when it did not return a reply. */
57
+ export type RpcErrorFailure = {
58
+ readonly type: 'refused';
59
+ readonly status: number;
60
+ readonly message: string;
61
+ } | {
62
+ readonly type: 'timeout';
63
+ } | {
64
+ readonly type: 'sessionLost';
65
+ } | {
66
+ readonly type: 'leaderLost';
67
+ readonly generation: number;
68
+ } | {
69
+ readonly type: 'indeterminate';
70
+ readonly deadlineMs: number;
71
+ } | {
72
+ readonly type: 'malformed';
73
+ readonly detail: string;
74
+ };
75
+ /** The base class of everything this package rejects with. */
76
+ export declare abstract class LeafError extends Error {
77
+ /** The flat, stable discriminant. */
78
+ abstract readonly kind: LeafErrorKind;
79
+ protected constructor(message: string);
80
+ }
81
+ /** The wire layer refused a packet: framing, AEAD, replay window. */
82
+ export declare class WireError extends LeafError {
83
+ readonly detail: string;
84
+ readonly kind: "wire";
85
+ constructor(detail: string);
86
+ }
87
+ /** No session for a peer, an incomplete handshake, a replaced session. */
88
+ export declare class SessionError extends LeafError {
89
+ readonly detail: string;
90
+ readonly kind: "session";
91
+ constructor(detail: string);
92
+ }
93
+ /** The control plane could not carry what the leaf asked it to. */
94
+ export declare class ControlPlaneError extends LeafError {
95
+ readonly detail: string;
96
+ readonly kind: "control-plane";
97
+ constructor(detail: string);
98
+ }
99
+ /** Identity storage, generation or custody. */
100
+ export declare class IdentityError extends LeafError {
101
+ readonly detail: string;
102
+ readonly kind: "identity";
103
+ constructor(detail: string);
104
+ }
105
+ /**
106
+ * A supplied `iceServers` entry named **this connection's own peer**
107
+ * as its STUN server.
108
+ *
109
+ * The mirror of Rust's `LeafError::IceServerConflictsWithPeer`. An
110
+ * anchor's `rtc_addr` is the ICE peer of a connection with that
111
+ * anchor, not a STUN server for it: the connection gathers no
112
+ * server-reflexive candidate and times out. The leaf refuses this
113
+ * **before any ICE work**, and refuses rather than silently
114
+ * stripping the entry — stripping would turn an explicit
115
+ * NAT-traversal configuration into a host-candidate-only attempt
116
+ * while appearing to have accepted it.
117
+ *
118
+ * The fix is to omit `iceServers` and let `connect()` use the
119
+ * endpoint the anchor announces as `stun_addr` on
120
+ * `GET /rtc/anchor`, or to name a STUN server that is not this
121
+ * peer. {@link diagnosticStunUrl} exists for the throwaway UDP
122
+ * probe and is not a source of `iceServers`.
123
+ *
124
+ * **Detection is endpoint equality only**, after default-port
125
+ * normalisation. A URL naming a DNS alias that resolves to the peer
126
+ * is not detected: the leaf resolves no names. The announced
127
+ * endpoint is what makes detection unnecessary for the configuration
128
+ * Net supplies.
129
+ */
130
+ export declare class IceServerConflictError extends LeafError {
131
+ readonly entry: string;
132
+ readonly peerRtcAddr: string;
133
+ readonly kind: "ice-server-conflict";
134
+ constructor(entry: string, peerRtcAddr: string);
135
+ }
136
+ /**
137
+ * This tab is not the leader for the origin and the operation needs
138
+ * the node. Carries the generation the caller presented so a stale
139
+ * follower's failure is legible.
140
+ */
141
+ export declare class NotLeaderError extends LeafError {
142
+ readonly presented: number;
143
+ readonly current: number | null;
144
+ readonly kind: "not-leader";
145
+ constructor(presented: number, current?: number | null);
146
+ }
147
+ /** The RTC transport itself. `kind` is the flattened failure. */
148
+ export declare class RtcError extends LeafError {
149
+ readonly failure: RtcErrorFailure;
150
+ readonly kind: 'ice-timeout' | 'udp-blocked' | 'channel-closed' | 'rtc-unsupported';
151
+ constructor(failure: RtcErrorFailure);
152
+ }
153
+ /** An nRPC call failed or was disposed of. `kind` is the flattened failure. */
154
+ export declare class RpcError extends LeafError {
155
+ readonly failure: RpcErrorFailure;
156
+ readonly kind: 'rpc-refused' | 'rpc-timeout' | 'session-lost' | 'leader-lost' | 'rpc-indeterminate' | 'rpc-malformed';
157
+ constructor(failure: RpcErrorFailure);
158
+ }
159
+ /**
160
+ * Something crossed the boundary that this taxonomy does not
161
+ * recognise — a browser exception, or a leaf `Display` string this
162
+ * version of the package predates.
163
+ *
164
+ * Deliberately not folded into a neighbouring variant: mis-typing a
165
+ * failure is the exact mistake the Stage 5 correction exists to
166
+ * prevent, so an unrecognised message stays unrecognised.
167
+ */
168
+ export declare class UnknownLeafError extends LeafError {
169
+ /** The value as it crossed the boundary, for diagnosis. */
170
+ readonly cause?: unknown | undefined;
171
+ readonly kind: "unknown";
172
+ constructor(message: string,
173
+ /** The value as it crossed the boundary, for diagnosis. */
174
+ cause?: unknown | undefined);
175
+ }
176
+ /**
177
+ * The frozen coarse vocabulary of an admission denial — the whole of
178
+ * what a remote provider ever says about why. A precise remote reason
179
+ * would be a credential oracle, so the wire carries one of these three
180
+ * bytes and the detailed refusal stays provider-side audit only.
181
+ */
182
+ export type CoarseAdmissionReason = 'denied' | 'not-supported' | 'unavailable';
183
+ /** Every org terminal kind, one per the leaf's frozen terminal vocabulary. */
184
+ export type OrgErrorKind = 'org-admission-denied' | 'org-revoked' | 'org-timeout' | 'org-cancelled' | 'org-leader-lost' | 'org-session-lost' | 'org-indeterminate' | 'org-refused' | 'org-internal' | 'org-malformed';
185
+ /**
186
+ * The base class of the org terminal vocabulary — what
187
+ * {@link OrgByteStream}'s terminal error item carries and what the org
188
+ * verbs reject with. `message` is the boundary's own text, verbatim,
189
+ * exactly as the rest of this taxonomy keeps it.
190
+ */
191
+ export declare abstract class OrgStreamError extends LeafError {
192
+ abstract readonly kind: OrgErrorKind;
193
+ }
194
+ /**
195
+ * The provider's admission engine refused the call (`RpcStatus 0x0009`).
196
+ * `coarse` is the frozen three-bucket reason and nothing finer.
197
+ */
198
+ export declare class OrgAdmissionDeniedError extends OrgStreamError {
199
+ readonly coarse: CoarseAdmissionReason;
200
+ readonly kind: OrgErrorKind;
201
+ constructor(coarse: CoarseAdmissionReason, message?: string);
202
+ }
203
+ /**
204
+ * Credentials were revoked mid-call. **Is** an admission denial:
205
+ * `coarse` is fixed to `'denied'` (the frozen Revoked → Denied coarse
206
+ * map), so `instanceof OrgAdmissionDeniedError` and
207
+ * `coarse === 'denied'` hold — `instanceof OrgRevokedError` is how a
208
+ * page that wants the cause gets it.
209
+ */
210
+ export declare class OrgRevokedError extends OrgAdmissionDeniedError {
211
+ readonly kind: OrgErrorKind;
212
+ constructor(message?: string);
213
+ }
214
+ /** The call's deadline elapsed. Nothing is re-issued. */
215
+ export declare class OrgTimeoutError extends OrgStreamError {
216
+ readonly kind: "org-timeout";
217
+ constructor(message?: string);
218
+ }
219
+ /** The call was cancelled — by its owner, or by a teardown that owns it. */
220
+ export declare class OrgCancelledError extends OrgStreamError {
221
+ readonly kind: "org-cancelled";
222
+ constructor(message?: string);
223
+ }
224
+ /**
225
+ * The generation holding the call was replaced (the shared-session
226
+ * surface's typed `LeaderLost`). `generation` is the generation that
227
+ * owned the call, as an exact decimal string — the u64 never becomes a
228
+ * JS number here. Never resumed: a successor generation's calls are
229
+ * fresh calls with fresh correlation and fresh proofs.
230
+ */
231
+ export declare class OrgLeaderLostError extends OrgStreamError {
232
+ readonly generation: string;
233
+ readonly kind: "org-leader-lost";
234
+ constructor(generation: string, message?: string);
235
+ }
236
+ /** The session carrying the call went away. */
237
+ export declare class OrgSessionLostError extends OrgStreamError {
238
+ readonly kind: "org-session-lost";
239
+ constructor(message?: string);
240
+ }
241
+ /**
242
+ * The caller's own deadline elapsed before an answer arrived; the
243
+ * remote operation may still have executed and was not retried.
244
+ */
245
+ export declare class OrgIndeterminateError extends OrgStreamError {
246
+ readonly deadlineMs: number;
247
+ readonly kind: "org-indeterminate";
248
+ constructor(deadlineMs: number, message?: string);
249
+ }
250
+ /**
251
+ * The application refused the call. `status` is its status code.
252
+ *
253
+ * Also the frozen wire kinds `server_error` (a server-answered failure
254
+ * status — the vocabulary's second name for this same condition) and
255
+ * `capability_denied` (the capability gate's refusal of the call): a
256
+ * re-typed wire string carries no recoverable `status` (`0`) and its
257
+ * `message` keeps the detail verbatim.
258
+ */
259
+ export declare class OrgRefusedError extends OrgStreamError {
260
+ readonly status: number;
261
+ readonly kind: "org-refused";
262
+ constructor(status: number, message?: string);
263
+ }
264
+ /**
265
+ * The boundary failed internally. Also what a terminal item whose
266
+ * `kind` this build does not know becomes: mis-typing a failure is the
267
+ * exact mistake this taxonomy exists to prevent, so an unknown kind is
268
+ * named internal rather than guessed into a merits bucket. The frozen
269
+ * wire kinds `no_route` and `transport` land here for the same reason:
270
+ * they carry no merits verdict (nobody refused the call).
271
+ */
272
+ export declare class OrgInternalError extends OrgStreamError {
273
+ readonly kind: "org-internal";
274
+ constructor(message?: string);
275
+ }
276
+ /**
277
+ * The reply or item did not decode. Also the frozen wire kinds
278
+ * `codec_encode` / `codec_decode` — the one codec class, both
279
+ * directions: an item that did not survive the codec is malformed
280
+ * whichever side failed.
281
+ */
282
+ export declare class OrgMalformedError extends OrgStreamError {
283
+ readonly kind: "org-malformed";
284
+ constructor(message?: string);
285
+ }
286
+ /**
287
+ * Why a call, stream, sink or request stream retired — the exact
288
+ * string the retirement observables resolve with, kept verbatim.
289
+ *
290
+ * `node-closed` and `replaced` are teardown verdicts rather than call
291
+ * outcomes; both arrive at the terminal vocabulary as `cancelled`
292
+ * (the frozen retire → terminal map), and the raw string is what tells
293
+ * them apart.
294
+ *
295
+ * `resource-exhausted` is the leaf's byte-budget retirement
296
+ * (`RetireReason::ResourceExhausted`'s spelling). Its precise
297
+ * observable is the `admission-denied` / `unavailable` denial — the
298
+ * same class the byte-budget send refusal on that call re-types as
299
+ * ({@link ORG_SINK_BUDGET_REFUSAL}) — never a cancel and never a
300
+ * teardown verdict.
301
+ */
302
+ export type OrgRetireReason = 'timeout' | 'cancelled' | 'revoked' | 'session-lost' | 'leader-lost' | 'node-closed' | 'replaced' | 'resource-exhausted';
303
+ /**
304
+ * THE typed closed refusal of a handler-side sink after retirement —
305
+ * one text whatever the verdict, so the wrapper's belt (its own
306
+ * short-circuit) and the leaf's hardening (its rejection) are the
307
+ * same observable. The verdict itself is what `retired` reports.
308
+ */
309
+ export declare const ORG_SINK_CLOSED_REFUSAL = "org: the response sink is closed: the call was retired";
310
+ /**
311
+ * The upload sink's spelling of the SAME typed closed refusal —
312
+ * `sink_error(SinkError::Closed, "the upload sink")` at the leaf —
313
+ * pinned verbatim like {@link ORG_SINK_CLOSED_REFUSAL}. One text per
314
+ * sink, one text per verdict: what the leaf's `sink_error` spells is
315
+ * exactly this family, and anything outside it is not a closed refusal.
316
+ */
317
+ export declare const ORG_UPLOAD_SINK_CLOSED_REFUSAL = "org: the upload sink is closed: the call was retired";
318
+ /**
319
+ * The byte-budget closed refusals — `sink_error(SinkError::ResourceExhausted, ..)`
320
+ * for the response sink and the upload sink. The item overran the
321
+ * call's byte budget and the call retired as `ResourceExhausted`,
322
+ * whose precise observable is the `admission-denied` / `unavailable`
323
+ * denial (the leaf's frozen map; C4, coarse `Unavailable`) — so these
324
+ * re-type as exactly that, never as a cancel.
325
+ */
326
+ export declare const ORG_SINK_BUDGET_REFUSAL = "org: the response sink is closed: the call's byte budget refused the item and retired the call";
327
+ export declare const ORG_UPLOAD_SINK_BUDGET_REFUSAL = "org: the upload sink is closed: the call's byte budget refused the item and retired the call";
328
+ /**
329
+ * Parse one wire token — or the one-byte coarse body a `0x0009`
330
+ * refusal carries — into the coarse bucket. Anything undecodable is
331
+ * the least-informative bucket (`denied`), never an error about an
332
+ * error: the caller still learns it was denied.
333
+ */
334
+ export declare function coarseAdmissionReason(token: string | undefined): CoarseAdmissionReason;
335
+ /** The terminal error a retirement verdict maps to (the frozen map). */
336
+ export declare function orgRetireError(reason: OrgRetireReason): OrgStreamError;
337
+ /**
338
+ * Type one terminal item from the boundary — the frozen `kind`
339
+ * vocabulary plus its fields — as the class §4.3 names.
340
+ *
341
+ * `message` is kept verbatim as the error's message.
342
+ */
343
+ export declare function orgTerminalError(item: {
344
+ kind: string;
345
+ message?: string;
346
+ coarse?: string;
347
+ generation?: string;
348
+ status?: number;
349
+ deadlineMs?: number;
350
+ }): OrgStreamError;
351
+ /**
352
+ * Type one retirement verdict coming back from the boundary's
353
+ * `retired()`. A string this build does not know is a boundary
354
+ * disagreement — named `replaced`-as-cancelled would lie about which
355
+ * teardown ran, so it becomes the cancelled terminal and the raw
356
+ * string stays in `OrgRetireReason`'s consumers' hands unchanged.
357
+ */
358
+ export declare function orgRetireReason(raw: string): OrgRetireReason;
359
+ /**
360
+ * Parse one `org:` wire string (`org:<domain>:<kind>[:detail]`, the
361
+ * `OrgSdkError::to_wire` vocabulary) or a `0x0009` admission refusal,
362
+ * or `null` if the message is not one.
363
+ *
364
+ * The domains and kind tokens mirror `classifyOrgError` from the Node
365
+ * binding where names coincide; the `rpc` domain reuses the frozen
366
+ * nRPC kind vocabulary rather than minting second names for the same
367
+ * conditions. Every frozen `org:rpc:` kind classifies (the frozen
368
+ * wire-kind map at the `rpc` switch) — a binding that returned `null`
369
+ * there forced the caller into `UnknownLeafError`, which is the
370
+ * cross-language drift this closes.
371
+ */
372
+ export declare function parseOrgError(message: string): OrgStreamError | null;
373
+ /**
374
+ * Re-type whatever the wasm boundary threw.
375
+ *
376
+ * `wasm-bindgen` hands us a `JsError` whose `message` is the Rust
377
+ * `Display` of `LeafError`, so the taxonomy is recovered by parsing
378
+ * exactly those prefixes. Anything else — a `DOMException` from
379
+ * `RTCPeerConnection`, a string, a plain object — becomes
380
+ * {@link UnknownLeafError} with the original value as `cause`.
381
+ */
382
+ export declare function fromWasmError(thrown: unknown): LeafError;
383
+ /**
384
+ * Parse one `LeafError` `Display` string, or `null` if it is not one.
385
+ *
386
+ * Exported because it is the whole of the wasm→TS error contract and
387
+ * deserves to be unit-testable without a wasm module.
388
+ */
389
+ export declare function parseLeafError(message: string): LeafError | null;
390
+ /** Parse the nested `RtcError` `Display`, or `null`. */
391
+ export declare function parseRtcFailure(text: string): RtcErrorFailure | null;
392
+ /** Parse the nested `RpcError` `Display`, or `null`. */
393
+ export declare function parseRpcFailure(text: string): RpcErrorFailure | null;
394
+ /** `true` when this error carries the UDP-blocked evidence. */
395
+ export declare function isUdpBlocked(error: unknown): error is RtcError & {
396
+ failure: {
397
+ type: 'udpBlocked';
398
+ evidence: UdpBlockedEvidence;
399
+ };
400
+ };