@crowdedkingdoms/crowdyjs 13.0.2 → 13.1.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.
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Binary realtime relay transport (`crowdy-relay-v1`).
3
+ *
4
+ * Connects to the game-api's raw-WebSocket relay endpoint and exchanges
5
+ * complete Buddy wire datagrams as BINARY frames — no GraphQL, JSON, or
6
+ * base64 on the wire. Owned by {@link RealtimeClient}, which keeps the public
7
+ * handler/dispatch surface identical across transports.
8
+ *
9
+ * Auth: the app token rides as a `bearer.<base64url(token)>` subprotocol
10
+ * entry (browsers cannot set WebSocket headers). The server replies with one
11
+ * TEXT `ready` frame carrying the session's `gameTokenId`, which becomes the
12
+ * client-side HMAC signing context.
13
+ */
14
+ import type { CrowdyLogger } from './logger.js';
15
+ import { CrowdyRealtimeError } from './errors.js';
16
+ import type { UdpNotification } from './realtime.js';
17
+ import { type RelaySignContext } from './binary-wire.js';
18
+ export declare const RELAY_SUBPROTOCOL = "crowdy-relay-v1";
19
+ export interface BinaryRelayCallbacks {
20
+ getToken(): string | null;
21
+ onNotification(notification: UdpNotification): void;
22
+ onError(error: CrowdyRealtimeError): void;
23
+ onStatus(status: 'connecting' | 'connected' | 'reconnecting' | 'disconnected'): void;
24
+ /**
25
+ * The relay endpoint looks permanently unavailable (older server, blocked
26
+ * upgrade). The owner should fall back to the GraphQL transport.
27
+ */
28
+ onUnavailable(): void;
29
+ }
30
+ export interface BinaryRelayConfig {
31
+ /** Absolute ws(s) URL of the relay endpoint (e.g. `wss://host/realtime`). */
32
+ url: string;
33
+ retryAttempts?: number;
34
+ retryInitialDelayMs?: number;
35
+ retryMaxDelayMs?: number;
36
+ logger?: CrowdyLogger;
37
+ }
38
+ export declare class BinaryRelayTransport {
39
+ private readonly callbacks;
40
+ private readonly url;
41
+ private readonly logger;
42
+ private readonly retryAttempts;
43
+ private readonly retryInitialDelayMs;
44
+ private readonly retryMaxDelayMs;
45
+ private ws;
46
+ private signContext;
47
+ private desired;
48
+ private appId;
49
+ private retries;
50
+ private preReadyFailures;
51
+ private everReady;
52
+ private retryTimer;
53
+ private generation;
54
+ constructor(config: BinaryRelayConfig, callbacks: BinaryRelayCallbacks);
55
+ /** True when the socket is open and the `ready` handshake completed. */
56
+ isReady(): boolean;
57
+ /** The session signing context (gameTokenId + HMAC key), once ready. */
58
+ getSignContext(): RelaySignContext | null;
59
+ connect(appId: string): void;
60
+ disconnect(): void;
61
+ /** Restart (e.g. after a token refresh) while remaining desired. */
62
+ restart(): void;
63
+ /** Send one pre-serialized Buddy datagram as a BINARY frame. */
64
+ sendFrame(frame: Uint8Array): void;
65
+ private open;
66
+ private handleControlFrame;
67
+ private maybeRetry;
68
+ }
69
+ //# sourceMappingURL=binary-relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary-relay.d.ts","sourceRoot":"","sources":["../src/binary-relay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAE1B,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAMnD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,IAAI,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,CAAC,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACpD,OAAO,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC1C,QAAQ,CACN,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,GACnE,IAAI,CAAC;IACR;;;OAGG;IACH,aAAa,IAAI,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAMD,qBAAa,oBAAoB;IAmB7B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAlB5B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IAEzC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,UAAU,CAAK;gBAGrB,MAAM,EAAE,iBAAiB,EACR,SAAS,EAAE,oBAAoB;IASlD,wEAAwE;IACxE,OAAO,IAAI,OAAO;IAQlB,wEAAwE;IACxE,cAAc,IAAI,gBAAgB,GAAG,IAAI;IAIzC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAO5B,UAAU,IAAI,IAAI;IAoBlB,oEAAoE;IACpE,OAAO,IAAI,IAAI;IASf,gEAAgE;IAChE,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAUlC,OAAO,CAAC,IAAI;YA6FE,kBAAkB;IAwBhC,OAAO,CAAC,UAAU;CAyBnB"}
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Binary realtime relay transport (`crowdy-relay-v1`).
3
+ *
4
+ * Connects to the game-api's raw-WebSocket relay endpoint and exchanges
5
+ * complete Buddy wire datagrams as BINARY frames — no GraphQL, JSON, or
6
+ * base64 on the wire. Owned by {@link RealtimeClient}, which keeps the public
7
+ * handler/dispatch surface identical across transports.
8
+ *
9
+ * Auth: the app token rides as a `bearer.<base64url(token)>` subprotocol
10
+ * entry (browsers cannot set WebSocket headers). The server replies with one
11
+ * TEXT `ready` frame carrying the session's `gameTokenId`, which becomes the
12
+ * client-side HMAC signing context.
13
+ */
14
+ import { silentLogger } from './logger.js';
15
+ import { CrowdyRealtimeError } from './errors.js';
16
+ import { createSignContext, parseRelayFrame, } from './binary-wire.js';
17
+ export const RELAY_SUBPROTOCOL = 'crowdy-relay-v1';
18
+ const BEARER_PREFIX = 'bearer.';
19
+ /** Consecutive pre-ready failures before we declare the relay unavailable. */
20
+ const UNAVAILABLE_AFTER_FAILURES = 2;
21
+ function base64UrlEncode(value) {
22
+ return btoa(value).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
23
+ }
24
+ export class BinaryRelayTransport {
25
+ constructor(config, callbacks) {
26
+ this.callbacks = callbacks;
27
+ this.ws = null;
28
+ this.signContext = null;
29
+ this.desired = false;
30
+ this.appId = null;
31
+ this.retries = 0;
32
+ this.preReadyFailures = 0;
33
+ this.everReady = false;
34
+ this.retryTimer = null;
35
+ this.generation = 0;
36
+ this.url = config.url;
37
+ this.logger = config.logger ?? silentLogger;
38
+ this.retryAttempts = config.retryAttempts ?? 8;
39
+ this.retryInitialDelayMs = config.retryInitialDelayMs ?? 250;
40
+ this.retryMaxDelayMs = config.retryMaxDelayMs ?? 5000;
41
+ }
42
+ /** True when the socket is open and the `ready` handshake completed. */
43
+ isReady() {
44
+ return (this.ws != null &&
45
+ this.ws.readyState === WebSocket.OPEN &&
46
+ this.signContext != null);
47
+ }
48
+ /** The session signing context (gameTokenId + HMAC key), once ready. */
49
+ getSignContext() {
50
+ return this.signContext;
51
+ }
52
+ connect(appId) {
53
+ this.desired = true;
54
+ this.appId = appId;
55
+ if (this.ws)
56
+ return;
57
+ this.open();
58
+ }
59
+ disconnect() {
60
+ this.desired = false;
61
+ if (this.retryTimer) {
62
+ clearTimeout(this.retryTimer);
63
+ this.retryTimer = null;
64
+ }
65
+ this.generation += 1;
66
+ const ws = this.ws;
67
+ this.ws = null;
68
+ this.signContext = null;
69
+ if (ws) {
70
+ try {
71
+ ws.close(1000, 'client-disconnect');
72
+ }
73
+ catch {
74
+ /* already closed */
75
+ }
76
+ }
77
+ this.callbacks.onStatus('disconnected');
78
+ }
79
+ /** Restart (e.g. after a token refresh) while remaining desired. */
80
+ restart() {
81
+ if (!this.desired || this.appId == null)
82
+ return;
83
+ const appId = this.appId;
84
+ this.disconnect();
85
+ this.desired = true;
86
+ this.appId = appId;
87
+ this.open();
88
+ }
89
+ /** Send one pre-serialized Buddy datagram as a BINARY frame. */
90
+ sendFrame(frame) {
91
+ if (!this.isReady() || !this.ws) {
92
+ throw new CrowdyRealtimeError('Binary relay is not connected', {
93
+ code: 'BINARY_RELAY_UNAVAILABLE',
94
+ retryable: true,
95
+ });
96
+ }
97
+ this.ws.send(frame);
98
+ }
99
+ open() {
100
+ const token = this.callbacks.getToken();
101
+ if (!token) {
102
+ this.callbacks.onError(new CrowdyRealtimeError('Must be authenticated to open the binary relay', {
103
+ code: 'AUTH_REQUIRED',
104
+ retryable: false,
105
+ }));
106
+ return;
107
+ }
108
+ if (typeof WebSocket === 'undefined') {
109
+ this.logger.warn?.('No global WebSocket implementation; binary relay unavailable');
110
+ this.callbacks.onUnavailable();
111
+ return;
112
+ }
113
+ const generation = ++this.generation;
114
+ this.callbacks.onStatus(this.retries > 0 ? 'reconnecting' : 'connecting');
115
+ const separator = this.url.includes('?') ? '&' : '?';
116
+ const url = `${this.url}${separator}appId=${encodeURIComponent(this.appId ?? '')}`;
117
+ let ws;
118
+ try {
119
+ ws = new WebSocket(url, [
120
+ RELAY_SUBPROTOCOL,
121
+ `${BEARER_PREFIX}${base64UrlEncode(token)}`,
122
+ ]);
123
+ }
124
+ catch (error) {
125
+ this.logger.error?.('Binary relay socket construction failed', error);
126
+ this.preReadyFailures += 1;
127
+ this.maybeRetry(generation);
128
+ return;
129
+ }
130
+ ws.binaryType = 'arraybuffer';
131
+ this.ws = ws;
132
+ this.signContext = null;
133
+ let sawReady = false;
134
+ ws.onmessage = (event) => {
135
+ if (generation !== this.generation)
136
+ return;
137
+ const data = event.data;
138
+ if (typeof data === 'string') {
139
+ this.handleControlFrame(data, generation).catch((error) => {
140
+ this.logger.error?.('Binary relay handshake failed', error);
141
+ });
142
+ if (!sawReady)
143
+ sawReady = true;
144
+ return;
145
+ }
146
+ if (data instanceof ArrayBuffer) {
147
+ for (const notification of parseRelayFrame(new Uint8Array(data))) {
148
+ this.callbacks.onNotification(notification);
149
+ }
150
+ }
151
+ };
152
+ ws.onerror = () => {
153
+ if (generation !== this.generation)
154
+ return;
155
+ this.logger.warn?.('Binary relay socket error');
156
+ };
157
+ ws.onclose = (event) => {
158
+ if (generation !== this.generation)
159
+ return;
160
+ this.ws = null;
161
+ const hadReady = this.signContext != null || sawReady;
162
+ this.signContext = null;
163
+ if (!this.desired) {
164
+ this.callbacks.onStatus('disconnected');
165
+ return;
166
+ }
167
+ if (!hadReady) {
168
+ this.preReadyFailures += 1;
169
+ if (!this.everReady && this.preReadyFailures >= UNAVAILABLE_AFTER_FAILURES) {
170
+ this.logger.warn?.(`Binary relay unavailable after ${this.preReadyFailures} failed handshakes (close ${event.code}); falling back`);
171
+ this.desired = false;
172
+ this.callbacks.onUnavailable();
173
+ return;
174
+ }
175
+ }
176
+ else {
177
+ this.retries = 0;
178
+ }
179
+ this.maybeRetry(generation);
180
+ };
181
+ }
182
+ async handleControlFrame(data, generation) {
183
+ let frame;
184
+ try {
185
+ frame = JSON.parse(data);
186
+ }
187
+ catch {
188
+ return;
189
+ }
190
+ if (frame.type !== 'ready' || !frame.gameTokenId)
191
+ return;
192
+ const token = this.callbacks.getToken();
193
+ if (!token)
194
+ return;
195
+ const ctx = await createSignContext(BigInt(frame.gameTokenId), token);
196
+ if (generation !== this.generation)
197
+ return;
198
+ this.signContext = ctx;
199
+ this.everReady = true;
200
+ this.preReadyFailures = 0;
201
+ this.retries = 0;
202
+ this.callbacks.onStatus('connected');
203
+ }
204
+ maybeRetry(generation) {
205
+ if (!this.desired || generation !== this.generation)
206
+ return;
207
+ if (this.retries >= this.retryAttempts) {
208
+ this.desired = false;
209
+ this.callbacks.onError(new CrowdyRealtimeError('Binary relay reconnect attempts exhausted', {
210
+ code: 'BINARY_RELAY_RECONNECT_EXHAUSTED',
211
+ retryable: false,
212
+ }));
213
+ this.callbacks.onStatus('disconnected');
214
+ return;
215
+ }
216
+ const delay = Math.min(this.retryMaxDelayMs, this.retryInitialDelayMs * 2 ** this.retries);
217
+ const jitter = Math.floor(Math.random() * this.retryInitialDelayMs);
218
+ this.retries += 1;
219
+ this.callbacks.onStatus('reconnecting');
220
+ this.retryTimer = setTimeout(() => {
221
+ this.retryTimer = null;
222
+ if (this.desired && generation === this.generation)
223
+ this.open();
224
+ }, delay + jitter);
225
+ }
226
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Buddy client wire-format codec for the binary realtime relay
3
+ * (`crowdy-relay-v1`).
4
+ *
5
+ * Mirrors the game-api's `UdpMessageParserService` byte-for-byte: uplink
6
+ * serializers build the complete client→Buddy datagram (spatial header +
7
+ * payload + HMAC + gameTokenId + sequence) and downlink parsing produces the
8
+ * same `__typename`-tagged notification objects the GraphQL
9
+ * `udpNotifications` subscription delivers, so handler code is transport
10
+ * agnostic.
11
+ *
12
+ * The per-message HMAC key is the 64-octet game token string itself — the
13
+ * same bearer token the SDK already holds — computed with WebCrypto
14
+ * (HMAC-SHA256). Buddy verifies it server-side; the relay does not.
15
+ */
16
+ import type { UdpNotification } from './realtime.js';
17
+ export declare const RELAY_MAX_DATAGRAM_BYTES = 1232;
18
+ /** Wire opcodes (mirror of game-api / Buddy `UdpMessageType`). */
19
+ export declare const WireMessageType: {
20
+ readonly MESSAGE_BUNDLE: 2;
21
+ readonly GENERIC_ERROR_MESSAGE: 3;
22
+ readonly CHANNEL_MESSAGE_REQUEST: 17;
23
+ readonly CHANNEL_MESSAGE_NOTIFICATION: 18;
24
+ readonly ACTOR_UPDATE_REQUEST_2: 128;
25
+ readonly ACTOR_UPDATE_RESPONSE_2: 129;
26
+ readonly ACTOR_UPDATE_NOTIFICATION_2: 130;
27
+ readonly VOXEL_UPDATE_REQUEST_2: 131;
28
+ readonly VOXEL_UPDATE_RESPONSE_2: 132;
29
+ readonly VOXEL_UPDATE_NOTIFICATION_2: 133;
30
+ readonly CLIENT_AUDIO_PACKET_2: 134;
31
+ readonly CLIENT_AUDIO_NOTIFICATION_2: 135;
32
+ readonly CLIENT_TEXT_PACKET_2: 136;
33
+ readonly CLIENT_TEXT_NOTIFICATION_2: 137;
34
+ readonly CLIENT_EVENT_NOTIFICATION_2: 138;
35
+ readonly SERVER_EVENT_NOTIFICATION_2: 139;
36
+ readonly SINGLE_ACTOR_MESSAGE: 142;
37
+ };
38
+ /**
39
+ * Per-session signing context: the game token id (from the relay `ready`
40
+ * frame) plus the token bytes and imported WebCrypto HMAC key.
41
+ */
42
+ export interface RelaySignContext {
43
+ gameTokenId: bigint;
44
+ tokenBytes: Uint8Array;
45
+ key: CryptoKey;
46
+ }
47
+ /** Import the game token as an HMAC-SHA256 WebCrypto key (once per session). */
48
+ export declare function createSignContext(gameTokenId: bigint, gameTokenString: string): Promise<RelaySignContext>;
49
+ export interface SpatialSendBase {
50
+ appId: string;
51
+ chunk: {
52
+ x: string;
53
+ y: string;
54
+ z: string;
55
+ };
56
+ uuid: string;
57
+ distance?: number | null;
58
+ decayRate?: number | null;
59
+ sequenceNumber?: number | null;
60
+ }
61
+ export declare function serializeActorUpdate(ctx: RelaySignContext, input: SpatialSendBase & {
62
+ state: string;
63
+ }): Promise<Uint8Array>;
64
+ export declare function serializeVoxelUpdate(ctx: RelaySignContext, input: SpatialSendBase & {
65
+ voxel: {
66
+ x: number;
67
+ y: number;
68
+ z: number;
69
+ };
70
+ voxelType: number;
71
+ voxelState: string;
72
+ }): Promise<Uint8Array>;
73
+ export declare function serializeAudioPacket(ctx: RelaySignContext, input: SpatialSendBase & {
74
+ audioData: string;
75
+ }): Promise<Uint8Array>;
76
+ export declare function serializeTextPacket(ctx: RelaySignContext, input: SpatialSendBase & {
77
+ text: string;
78
+ }): Promise<Uint8Array>;
79
+ export declare function serializeClientEvent(ctx: RelaySignContext, input: SpatialSendBase & {
80
+ eventType: number;
81
+ state: string;
82
+ }): Promise<Uint8Array>;
83
+ export declare function serializeSingleActorMessage(ctx: RelaySignContext, input: {
84
+ appId: string;
85
+ chunk: {
86
+ x: string;
87
+ y: string;
88
+ z: string;
89
+ };
90
+ targetUuid: string;
91
+ payload?: string | null;
92
+ sequenceNumber?: number | null;
93
+ }): Promise<Uint8Array>;
94
+ /**
95
+ * CHANNEL_MESSAGE_REQUEST:
96
+ * `[1B type=17][8B channelId][32B uuid][2B payloadLen][payload][1B containsAuth]
97
+ * [32B HMAC][8B gameTokenId][1B seq]` — HMAC over everything before it.
98
+ */
99
+ export declare function serializeChannelMessage(ctx: RelaySignContext, input: {
100
+ channelId: string;
101
+ uuid: string;
102
+ payload?: string | null;
103
+ sequenceNumber?: number | null;
104
+ }): Promise<Uint8Array>;
105
+ /**
106
+ * Parse one relay BINARY frame (one Buddy datagram, possibly a
107
+ * MESSAGE_BUNDLE) into notification objects. Unparseable inner messages are
108
+ * skipped, mirroring the server-side bundle behavior.
109
+ */
110
+ export declare function parseRelayFrame(bytes: Uint8Array): UdpNotification[];
111
+ //# sourceMappingURL=binary-wire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary-wire.d.ts","sourceRoot":"","sources":["../src/binary-wire.ts"],"names":[],"mappings":"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"}