@couch-kit/display 0.2.2 → 0.3.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 +42 -0
- package/dist/index.js +14 -5
- package/lib/relay-display-host.d.ts +26 -3
- package/lib/relay-display-host.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/relay-display-host.ts +47 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @couch-kit/display
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#158](https://github.com/faluciano/react-native-couch-kit/pull/158) [`509ea7c`](https://github.com/faluciano/react-native-couch-kit/commit/509ea7c02aa6f2e56ebf01e781d6de74f0ded021) Thanks [@faluciano](https://github.com/faluciano)! - Let the relay assign room codes
|
|
8
|
+
|
|
9
|
+
`RelayDisplayHost`'s `roomId` is now optional. Omit it and the relay mints an
|
|
10
|
+
unused six-character code, reported through the new `onRoomCode` callback and
|
|
11
|
+
the `roomCode` getter.
|
|
12
|
+
|
|
13
|
+
A display could never check its own code for collisions — only the relay knows
|
|
14
|
+
which codes are live — so a self-chosen code could land on a game already in
|
|
15
|
+
progress, and did so only after it was on screen. Minted codes are drawn from
|
|
16
|
+
the CSPRNG over a 32-character alphabet without `O`/`0` or `I`/`1`, giving about
|
|
17
|
+
1.07 billion codes.
|
|
18
|
+
|
|
19
|
+
Existing callers that pass `roomId` keep their current behaviour, including
|
|
20
|
+
`ROOM_EXISTS` when the code is taken. New callers should expect the code to
|
|
21
|
+
arrive one round trip after connecting rather than being known up front:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const [roomCode, setRoomCode] = useState<string | null>(null);
|
|
25
|
+
new RelayDisplayHost({ url, onRoomCode: setRoomCode, reducer, initialState });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Relays need a matching update to mint: both bundled implementations
|
|
29
|
+
(`services/relay`, `services/relay-worker`) support it. A display that omits
|
|
30
|
+
`roomId` against an older relay gets `MALFORMED`.
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- Updated dependencies [[`509ea7c`](https://github.com/faluciano/react-native-couch-kit/commit/509ea7c02aa6f2e56ebf01e781d6de74f0ded021)]:
|
|
35
|
+
- @couch-kit/client@0.13.0
|
|
36
|
+
|
|
37
|
+
## 0.2.3
|
|
38
|
+
|
|
39
|
+
### Patch Changes
|
|
40
|
+
|
|
41
|
+
- Updated dependencies [[`4f297a1`](https://github.com/faluciano/react-native-couch-kit/commit/4f297a19c442541703c3bee7bee26354ae3476a4)]:
|
|
42
|
+
- @couch-kit/runtime@0.2.0
|
|
43
|
+
- @couch-kit/client@0.12.0
|
|
44
|
+
|
|
3
45
|
## 0.2.2
|
|
4
46
|
|
|
5
47
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -12,17 +12,19 @@ import {
|
|
|
12
12
|
class RelayDisplayHost {
|
|
13
13
|
runtime;
|
|
14
14
|
ws;
|
|
15
|
-
|
|
15
|
+
assignedRoomId;
|
|
16
|
+
onRoomCode;
|
|
16
17
|
peers = new Set;
|
|
17
18
|
constructor(options) {
|
|
18
|
-
const { url, roomId, ...runtimeConfig } = options;
|
|
19
|
-
this.
|
|
19
|
+
const { url, roomId, onRoomCode, ...runtimeConfig } = options;
|
|
20
|
+
this.assignedRoomId = roomId ?? null;
|
|
21
|
+
this.onRoomCode = onRoomCode;
|
|
20
22
|
this.runtime = new GameHostRuntime(runtimeConfig);
|
|
21
23
|
this.ws = new WebSocket(relayRoomUrl(url, roomId));
|
|
22
24
|
this.ws.onopen = () => {
|
|
23
25
|
this.ws.send(JSON.stringify({
|
|
24
26
|
type: RelayMessageTypes.CREATE_ROOM,
|
|
25
|
-
roomId: this.
|
|
27
|
+
roomId: this.assignedRoomId ?? undefined
|
|
26
28
|
}));
|
|
27
29
|
};
|
|
28
30
|
this.ws.onmessage = (event) => {
|
|
@@ -41,6 +43,9 @@ class RelayDisplayHost {
|
|
|
41
43
|
};
|
|
42
44
|
this.runtime.setTransport(transport);
|
|
43
45
|
}
|
|
46
|
+
get roomCode() {
|
|
47
|
+
return this.assignedRoomId;
|
|
48
|
+
}
|
|
44
49
|
getState = () => this.runtime.getState();
|
|
45
50
|
subscribe = (listener) => this.runtime.subscribe(listener);
|
|
46
51
|
dispatch = (action) => this.runtime.dispatch(action);
|
|
@@ -52,7 +57,7 @@ class RelayDisplayHost {
|
|
|
52
57
|
sendEnvelope(message, to) {
|
|
53
58
|
const envelope = {
|
|
54
59
|
type: RelayMessageTypes.DATA,
|
|
55
|
-
roomId: this.
|
|
60
|
+
roomId: this.assignedRoomId ?? undefined,
|
|
56
61
|
data: JSON.stringify(message)
|
|
57
62
|
};
|
|
58
63
|
if (to !== undefined)
|
|
@@ -83,6 +88,10 @@ class RelayDisplayHost {
|
|
|
83
88
|
this.runtime.handleMessage(msg.from, parsed).catch((err) => this.runtime.handleError(err instanceof Error ? err : new Error(String(err))));
|
|
84
89
|
break;
|
|
85
90
|
}
|
|
91
|
+
case RelayMessageTypes.ROOM_CREATED:
|
|
92
|
+
this.assignedRoomId = msg.roomId;
|
|
93
|
+
this.onRoomCode?.(msg.roomId);
|
|
94
|
+
break;
|
|
86
95
|
case RelayMessageTypes.ERROR:
|
|
87
96
|
this.runtime.handleError(new Error(msg.message));
|
|
88
97
|
break;
|
|
@@ -10,8 +10,23 @@ import type { IGameState, IAction } from "@couch-kit/core";
|
|
|
10
10
|
export interface RelayDisplayHostOptions<S extends IGameState, A extends IAction> extends GameHostRuntimeConfig<S, A> {
|
|
11
11
|
/** WebSocket URL of the shared relay server. */
|
|
12
12
|
url: string;
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Room code phones will use to reach this display.
|
|
15
|
+
*
|
|
16
|
+
* Omit it — the normal case — and the relay allocates one, reporting it via
|
|
17
|
+
* {@link RelayDisplayHostOptions.onRoomCode} and {@link RelayDisplayHost.roomCode}.
|
|
18
|
+
* Only the relay can tell whether a code is already in use, so a code chosen
|
|
19
|
+
* here may be rejected as `ROOM_EXISTS`; supply one only when something
|
|
20
|
+
* outside the relay already fixed it.
|
|
21
|
+
*/
|
|
22
|
+
roomId?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Called once the room exists and its code is known.
|
|
25
|
+
*
|
|
26
|
+
* A minted code is not available synchronously, so a display renders a
|
|
27
|
+
* placeholder until this fires — roughly a round trip to the relay.
|
|
28
|
+
*/
|
|
29
|
+
onRoomCode?: (roomCode: string) => void;
|
|
15
30
|
}
|
|
16
31
|
/**
|
|
17
32
|
* Browser **display host** for the cross-network relay transport.
|
|
@@ -33,10 +48,18 @@ export interface RelayDisplayHostOptions<S extends IGameState, A extends IAction
|
|
|
33
48
|
export declare class RelayDisplayHost<S extends IGameState, A extends IAction> {
|
|
34
49
|
private readonly runtime;
|
|
35
50
|
private readonly ws;
|
|
36
|
-
|
|
51
|
+
/** Null until the relay confirms the room, when the code is relay-assigned. */
|
|
52
|
+
private assignedRoomId;
|
|
53
|
+
private readonly onRoomCode?;
|
|
37
54
|
/** Connected phone connection ids (relay peer ids). */
|
|
38
55
|
private readonly peers;
|
|
39
56
|
constructor(options: RelayDisplayHostOptions<S, A>);
|
|
57
|
+
/**
|
|
58
|
+
* The room code phones join with, or `null` before the relay has assigned
|
|
59
|
+
* one. See {@link RelayDisplayHostOptions.onRoomCode} to be told when it
|
|
60
|
+
* arrives.
|
|
61
|
+
*/
|
|
62
|
+
get roomCode(): string | null;
|
|
40
63
|
/** Current authoritative game state. */
|
|
41
64
|
getState: () => S;
|
|
42
65
|
/** Subscribe to state changes (for `useSyncExternalStore` or manual render). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-display-host.d.ts","sourceRoot":"","sources":["../src/relay-display-host.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,qBAAqB,EAE3B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAe,MAAM,iBAAiB,CAAC;AAOxE;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO,CAC9E,SAAQ,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC;IACnC,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ
|
|
1
|
+
{"version":3,"file":"relay-display-host.d.ts","sourceRoot":"","sources":["../src/relay-display-host.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,qBAAqB,EAE3B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAe,MAAM,iBAAiB,CAAC;AAOxE;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO,CAC9E,SAAQ,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC;IACnC,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,gBAAgB,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAC/B,+EAA+E;IAC/E,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAA6B;IACzD,uDAAuD;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAE3C,YAAY,OAAO,EAAE,uBAAuB,CAAC,CAAC,EAAE,CAAC,CAAC,EAwCjD;IAED;;;;OAIG;IACH,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED,wCAAwC;IACxC,QAAQ,QAAO,CAAC,CAA4B;IAE5C,gFAAgF;IAChF,SAAS,aAAc,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC,CACb;IAEnC,8CAA8C;IAC9C,QAAQ,WAAY,CAAC,KAAG,IAAI,CAAkC;IAE9D,8CAA8C;IAC9C,IAAI,IAAI,IAAI,CAIX;IAED,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,kBAAkB;CA2C3B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@couch-kit/display",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"provenance": true
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"clean": "rm -rf dist lib"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@couch-kit/client": "0.
|
|
54
|
+
"@couch-kit/client": "0.13.0",
|
|
55
55
|
"@couch-kit/core": "0.9.3",
|
|
56
|
-
"@couch-kit/runtime": "0.
|
|
56
|
+
"@couch-kit/runtime": "0.2.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"typescript": "^7.0.0"
|
|
@@ -23,8 +23,23 @@ export interface RelayDisplayHostOptions<S extends IGameState, A extends IAction
|
|
|
23
23
|
extends GameHostRuntimeConfig<S, A> {
|
|
24
24
|
/** WebSocket URL of the shared relay server. */
|
|
25
25
|
url: string;
|
|
26
|
-
/**
|
|
27
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Room code phones will use to reach this display.
|
|
28
|
+
*
|
|
29
|
+
* Omit it — the normal case — and the relay allocates one, reporting it via
|
|
30
|
+
* {@link RelayDisplayHostOptions.onRoomCode} and {@link RelayDisplayHost.roomCode}.
|
|
31
|
+
* Only the relay can tell whether a code is already in use, so a code chosen
|
|
32
|
+
* here may be rejected as `ROOM_EXISTS`; supply one only when something
|
|
33
|
+
* outside the relay already fixed it.
|
|
34
|
+
*/
|
|
35
|
+
roomId?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Called once the room exists and its code is known.
|
|
38
|
+
*
|
|
39
|
+
* A minted code is not available synchronously, so a display renders a
|
|
40
|
+
* placeholder until this fires — roughly a round trip to the relay.
|
|
41
|
+
*/
|
|
42
|
+
onRoomCode?: (roomCode: string) => void;
|
|
28
43
|
}
|
|
29
44
|
|
|
30
45
|
/**
|
|
@@ -47,21 +62,27 @@ export interface RelayDisplayHostOptions<S extends IGameState, A extends IAction
|
|
|
47
62
|
export class RelayDisplayHost<S extends IGameState, A extends IAction> {
|
|
48
63
|
private readonly runtime: GameHostRuntime<S, A>;
|
|
49
64
|
private readonly ws: WebSocket;
|
|
50
|
-
|
|
65
|
+
/** Null until the relay confirms the room, when the code is relay-assigned. */
|
|
66
|
+
private assignedRoomId: string | null;
|
|
67
|
+
private readonly onRoomCode?: (roomCode: string) => void;
|
|
51
68
|
/** Connected phone connection ids (relay peer ids). */
|
|
52
69
|
private readonly peers = new Set<string>();
|
|
53
70
|
|
|
54
71
|
constructor(options: RelayDisplayHostOptions<S, A>) {
|
|
55
|
-
const { url, roomId, ...runtimeConfig } = options;
|
|
56
|
-
this.
|
|
72
|
+
const { url, roomId, onRoomCode, ...runtimeConfig } = options;
|
|
73
|
+
this.assignedRoomId = roomId ?? null;
|
|
74
|
+
this.onRoomCode = onRoomCode;
|
|
57
75
|
this.runtime = new GameHostRuntime<S, A>(runtimeConfig);
|
|
58
76
|
this.ws = new WebSocket(relayRoomUrl(url, roomId));
|
|
59
77
|
|
|
60
78
|
this.ws.onopen = () => {
|
|
79
|
+
// No roomId asks the relay to allocate one. Sending the field as
|
|
80
|
+
// undefined omits it from the JSON, which is what the relay reads as
|
|
81
|
+
// "you pick".
|
|
61
82
|
this.ws.send(
|
|
62
83
|
JSON.stringify({
|
|
63
84
|
type: RelayMessageTypes.CREATE_ROOM,
|
|
64
|
-
roomId: this.
|
|
85
|
+
roomId: this.assignedRoomId ?? undefined,
|
|
65
86
|
}),
|
|
66
87
|
);
|
|
67
88
|
};
|
|
@@ -89,6 +110,15 @@ export class RelayDisplayHost<S extends IGameState, A extends IAction> {
|
|
|
89
110
|
this.runtime.setTransport(transport);
|
|
90
111
|
}
|
|
91
112
|
|
|
113
|
+
/**
|
|
114
|
+
* The room code phones join with, or `null` before the relay has assigned
|
|
115
|
+
* one. See {@link RelayDisplayHostOptions.onRoomCode} to be told when it
|
|
116
|
+
* arrives.
|
|
117
|
+
*/
|
|
118
|
+
get roomCode(): string | null {
|
|
119
|
+
return this.assignedRoomId;
|
|
120
|
+
}
|
|
121
|
+
|
|
92
122
|
/** Current authoritative game state. */
|
|
93
123
|
getState = (): S => this.runtime.getState();
|
|
94
124
|
|
|
@@ -109,7 +139,10 @@ export class RelayDisplayHost<S extends IGameState, A extends IAction> {
|
|
|
109
139
|
private sendEnvelope(message: HostMessage, to?: string): void {
|
|
110
140
|
const envelope: Record<string, unknown> = {
|
|
111
141
|
type: RelayMessageTypes.DATA,
|
|
112
|
-
|
|
142
|
+
// The relay routes by the sender's membership, not this field, so it is
|
|
143
|
+
// only ever informational — and nothing is sent before a peer joins,
|
|
144
|
+
// which cannot happen until the room exists.
|
|
145
|
+
roomId: this.assignedRoomId ?? undefined,
|
|
113
146
|
data: JSON.stringify(message),
|
|
114
147
|
};
|
|
115
148
|
if (to !== undefined) envelope.to = to;
|
|
@@ -147,10 +180,16 @@ export class RelayDisplayHost<S extends IGameState, A extends IAction> {
|
|
|
147
180
|
);
|
|
148
181
|
break;
|
|
149
182
|
}
|
|
183
|
+
case RelayMessageTypes.ROOM_CREATED:
|
|
184
|
+
// Carries the code when the relay chose it, and confirms the code when
|
|
185
|
+
// the caller supplied one.
|
|
186
|
+
this.assignedRoomId = msg.roomId;
|
|
187
|
+
this.onRoomCode?.(msg.roomId);
|
|
188
|
+
break;
|
|
150
189
|
case RelayMessageTypes.ERROR:
|
|
151
190
|
this.runtime.handleError(new Error(msg.message));
|
|
152
191
|
break;
|
|
153
|
-
//
|
|
192
|
+
// ROOM_JOINED is an acknowledgement; no action needed.
|
|
154
193
|
}
|
|
155
194
|
}
|
|
156
195
|
}
|