@4players/odin-plugin-web 1.0.0-alpha.10 → 1.0.0-alpha.12
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/lib/plugin/audioController.d.ts +6 -7
- package/lib/plugin/audioController.js +14 -3
- package/lib/plugin/connectionPool.d.ts +1 -1
- package/lib/plugin/connectionPool.js +1 -1
- package/lib/plugin/index.d.ts +7 -9
- package/lib/plugin/index.js +5 -5
- package/lib/plugin/mainConnection.d.ts +3 -5
- package/lib/plugin/mainConnection.js +4 -3
- package/lib/plugin/roomConnection.d.ts +2 -2
- package/lib/plugin/roomConnection.js +2 -2
- package/lib/plugin/rpcConnection.d.ts +12 -3
- package/lib/plugin/rpcConnection.js +25 -50
- package/lib/plugin/token.d.ts +2 -3
- package/lib/plugin/token.js +3 -4
- package/lib/plugin/websocket.d.ts +3 -0
- package/lib/plugin/websocket.js +56 -0
- package/lib/worker/index.js +1 -1
- package/package.json +8 -10
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { Backend, Strand } from '@4players/odin-common';
|
|
2
|
-
import { DeviceParameters } from '
|
|
2
|
+
import { DeviceParameters } from './index';
|
|
3
3
|
import { RoomConnection } from './roomConnection';
|
|
4
4
|
import { CaptureSink, PlaybackId, PlaybackWorker } from '../worker/common';
|
|
5
|
-
export type Volume = Backend.Volume;
|
|
6
|
-
export type Activity = Backend.Activity;
|
|
7
5
|
export type CreateAudioPlaybackParameters = Backend.CreateAudioPlaybackParameters;
|
|
8
6
|
export type CreateAudioCaptureParameters = Backend.CreateAudioCaptureParameters & {
|
|
9
7
|
readonly device?: Device;
|
|
@@ -38,7 +36,7 @@ export declare class AudioController {
|
|
|
38
36
|
globalVolume: number;
|
|
39
37
|
playbacks: Map<string, {
|
|
40
38
|
id: GlobalMediaId | undefined;
|
|
41
|
-
volume: Volume;
|
|
39
|
+
volume: Backend.Volume;
|
|
42
40
|
playback: AudioPlayback;
|
|
43
41
|
}>;
|
|
44
42
|
setPlaybackStrand: Strand;
|
|
@@ -58,9 +56,9 @@ export declare class AudioController {
|
|
|
58
56
|
playbackSetGlobalVolume(value: Backend.Volume): void;
|
|
59
57
|
playbackStart(uid: string, id: GlobalMediaId): void;
|
|
60
58
|
playbackStop(uid: string, id: GlobalMediaId): void;
|
|
61
|
-
playbackGetActivity(uid: string): Activity;
|
|
62
|
-
playbackSetVolume(uid: string, volume: Volume): void;
|
|
63
|
-
playbackGetVolume(uid: string): Volume;
|
|
59
|
+
playbackGetActivity(uid: string): Backend.Activity;
|
|
60
|
+
playbackSetVolume(uid: string, volume: Backend.Volume): void;
|
|
61
|
+
playbackGetVolume(uid: string): Backend.Volume;
|
|
64
62
|
closePlayback(uid: string): void;
|
|
65
63
|
close(): void;
|
|
66
64
|
}
|
|
@@ -95,6 +93,7 @@ export declare class AudioCapture implements Backend.AudioCapture {
|
|
|
95
93
|
get volume(): Backend.Volume;
|
|
96
94
|
get activity(): import("../worker/common").Activity;
|
|
97
95
|
get speechProbability(): number;
|
|
96
|
+
get deviceId(): string | undefined;
|
|
98
97
|
constructor(controller: AudioController, parameters: CreateAudioCaptureParameters);
|
|
99
98
|
start(room: RoomConnection, media: GlobalMediaId, uid: Backend.UID): void;
|
|
100
99
|
stop(room: RoomConnection, media: GlobalMediaId): void;
|
|
@@ -8,8 +8,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { Strand, assert, failure, sleep, success, } from '@4players/odin-common';
|
|
11
|
-
import { Silence, } from '../worker/common';
|
|
12
11
|
import { createCaptureWorker, createPlaybackWorker } from '../worker';
|
|
12
|
+
import { Silence, } from '../worker/common';
|
|
13
13
|
const defaultVad = {
|
|
14
14
|
voiceActivity: {
|
|
15
15
|
attackThreshold: 0.9,
|
|
@@ -175,6 +175,7 @@ export class AudioController {
|
|
|
175
175
|
const ms = yield navigator.mediaDevices.getUserMedia(constraints);
|
|
176
176
|
ms === null || ms === void 0 ? void 0 : ms.getTracks().forEach((track) => {
|
|
177
177
|
track.stop();
|
|
178
|
+
ms.removeTrack(track);
|
|
178
179
|
});
|
|
179
180
|
}
|
|
180
181
|
catch (e) {
|
|
@@ -336,6 +337,10 @@ export class AudioCapture {
|
|
|
336
337
|
var _a, _b;
|
|
337
338
|
return (_b = (_a = this._Capture) === null || _a === void 0 ? void 0 : _a.worker.speechProbability) !== null && _b !== void 0 ? _b : 0;
|
|
338
339
|
}
|
|
340
|
+
get deviceId() {
|
|
341
|
+
var _a, _b;
|
|
342
|
+
return (_b = (_a = this._deviceParameters) === null || _a === void 0 ? void 0 : _a.device) === null || _b === void 0 ? void 0 : _b.device.deviceId;
|
|
343
|
+
}
|
|
339
344
|
constructor(controller, parameters) {
|
|
340
345
|
this.controller = controller;
|
|
341
346
|
this._Medias = new Map();
|
|
@@ -400,13 +405,19 @@ export class AudioCapture {
|
|
|
400
405
|
setDevice(parameters) {
|
|
401
406
|
var _a;
|
|
402
407
|
return __awaiter(this, void 0, void 0, function* () {
|
|
408
|
+
const rollbackParams = this._deviceParameters;
|
|
409
|
+
this._deviceParameters = parameters;
|
|
410
|
+
if (this._Volume === 'muted') {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
403
413
|
const device = yield openCaptureDevice(this.controller, parameters, this._Apm);
|
|
404
414
|
if (device.type !== 'Success') {
|
|
405
415
|
console.error(`unable to open capture device; ${device.reason}`);
|
|
416
|
+
this._deviceParameters = rollbackParams;
|
|
406
417
|
return;
|
|
407
418
|
}
|
|
408
|
-
this._deviceParameters = parameters;
|
|
409
419
|
if (this._Capture !== undefined) {
|
|
420
|
+
stopMediaStream(this._Capture.source.mediaStream);
|
|
410
421
|
this._Capture.source.disconnect(this._Capture.worker.worklet);
|
|
411
422
|
}
|
|
412
423
|
if (this._Capture === undefined ||
|
|
@@ -461,7 +472,7 @@ export class AudioCapture {
|
|
|
461
472
|
}
|
|
462
473
|
}
|
|
463
474
|
function stopMediaStream(ms) {
|
|
464
|
-
ms.getTracks().forEach(track => {
|
|
475
|
+
ms.getTracks().forEach((track) => {
|
|
465
476
|
track.stop();
|
|
466
477
|
ms.removeTrack(track);
|
|
467
478
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DatagramHandler, MainConnection } from './mainConnection';
|
|
2
1
|
import { Result } from '@4players/odin-common';
|
|
2
|
+
import { DatagramHandler, MainConnection } from './mainConnection';
|
|
3
3
|
export declare class ConnectionPool {
|
|
4
4
|
readonly onDatagram: DatagramHandler;
|
|
5
5
|
private _LastConnectionId;
|
|
@@ -7,9 +7,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import { failure, nextTick, success } from '@4players/odin-common';
|
|
10
11
|
import { ClockJumpCheckInterval, ClockJumpThreshold } from './config';
|
|
11
12
|
import { MainConnection } from './mainConnection';
|
|
12
|
-
import { failure, nextTick, success } from '@4players/odin-common';
|
|
13
13
|
export class ConnectionPool {
|
|
14
14
|
get closed() {
|
|
15
15
|
return this._Close.signal.aborted;
|
package/lib/plugin/index.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { ConnectionPool } from './connectionPool';
|
|
2
1
|
import { Backend } from '@4players/odin-common';
|
|
3
|
-
import { AudioCapture, AudioContextFactory, AudioPlayback, CreateAudioCaptureParameters, CreateAudioPlaybackParameters, Device
|
|
2
|
+
import { AudioCapture, AudioContextFactory, AudioPlayback, CreateAudioCaptureParameters, CreateAudioPlaybackParameters, Device } from './audioController';
|
|
3
|
+
import { ConnectionPool } from './connectionPool';
|
|
4
4
|
import { MainConnection } from './mainConnection';
|
|
5
|
-
export type Media = Backend.Media;
|
|
6
|
-
export type JoinRoomParameters = Backend.JoinRoomParameters;
|
|
7
5
|
export interface DeviceParameters {
|
|
8
6
|
readonly device?: Device;
|
|
9
7
|
onStatusChanged?: Backend.OnStatusChanged;
|
|
@@ -17,11 +15,11 @@ export declare class WebPlugin implements Backend.Plugin {
|
|
|
17
15
|
get playbackVolume(): Backend.Volume;
|
|
18
16
|
static create(audioContextFactory: AudioContextFactory): Promise<WebPlugin>;
|
|
19
17
|
private constructor();
|
|
20
|
-
joinRoom(parameters: JoinRoomParameters): Room;
|
|
18
|
+
joinRoom(parameters: Backend.JoinRoomParameters): Room;
|
|
21
19
|
createAudioPlayback(parameters: CreateAudioPlaybackParameters): Promise<AudioPlayback>;
|
|
22
20
|
createAudioCapture(parameters: CreateAudioCaptureParameters): Promise<AudioCapture>;
|
|
23
21
|
enumerateDevices(): Promise<Device[]>;
|
|
24
|
-
setOutputVolume(volume: Volume): void;
|
|
22
|
+
setOutputVolume(volume: Backend.Volume): void;
|
|
25
23
|
setOutputDevice(device: DeviceParameters): Promise<void>;
|
|
26
24
|
close(): void;
|
|
27
25
|
}
|
|
@@ -34,10 +32,10 @@ export declare class Room implements Backend.Room {
|
|
|
34
32
|
private readonly _Peers;
|
|
35
33
|
private readonly _AvailableMediaIds;
|
|
36
34
|
get mainConnection(): MainConnection | undefined;
|
|
37
|
-
constructor(id: number, connectionPool: ConnectionPool, parameters: JoinRoomParameters);
|
|
35
|
+
constructor(id: number, connectionPool: ConnectionPool, parameters: Backend.JoinRoomParameters);
|
|
38
36
|
request(method: string, properties: unknown): Promise<unknown>;
|
|
39
|
-
link(media: Media): void;
|
|
40
|
-
unlink(media: Media): void;
|
|
37
|
+
link(media: Backend.Media): void;
|
|
38
|
+
unlink(media: Backend.Media): void;
|
|
41
39
|
close(): void;
|
|
42
40
|
private _HandleEvent;
|
|
43
41
|
private _HandleKnownEvent;
|
package/lib/plugin/index.js
CHANGED
|
@@ -7,12 +7,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import { assert, unwrap, RoomNotificationsRpc, generateUUID, } from '@4players/odin-common';
|
|
11
|
+
import { AudioCapture, AudioContextCollection, AudioController, AudioPlayback, } from './audioController';
|
|
10
12
|
import { ConnectionPool } from './connectionPool';
|
|
11
13
|
import { RoomConnection } from './roomConnection';
|
|
12
|
-
import {
|
|
14
|
+
import { parseRpcMessage } from './rpcConnection';
|
|
13
15
|
import { Token } from './token';
|
|
14
|
-
import { AudioCapture, AudioContextCollection, AudioController, AudioPlayback, } from './audioController';
|
|
15
|
-
import * as uuid from 'uuid';
|
|
16
16
|
export class WebPlugin {
|
|
17
17
|
get playbackVolume() {
|
|
18
18
|
return this._PlaybackVolume;
|
|
@@ -115,7 +115,7 @@ export class Room {
|
|
|
115
115
|
mediaId,
|
|
116
116
|
}
|
|
117
117
|
: undefined,
|
|
118
|
-
uid:
|
|
118
|
+
uid: generateUUID(),
|
|
119
119
|
};
|
|
120
120
|
this._Captures.set(media, entry);
|
|
121
121
|
if (entry.media !== undefined) {
|
|
@@ -256,7 +256,7 @@ export class Room {
|
|
|
256
256
|
const peer = this._Peers.get(peerId);
|
|
257
257
|
if (peer === undefined || media.properties.kind !== 'audio')
|
|
258
258
|
return;
|
|
259
|
-
const uid = String((_a = media.properties.uid) !== null && _a !== void 0 ? _a :
|
|
259
|
+
const uid = String((_a = media.properties.uid) !== null && _a !== void 0 ? _a : generateUUID());
|
|
260
260
|
console.log(`uuid ${media.properties.uid} ${uid}`);
|
|
261
261
|
media.properties.uid = uid; // feed the uid back, so it can be seen be seen down the chain.
|
|
262
262
|
const entry = (_b = this._Playbacks.get(uid)) !== null && _b !== void 0 ? _b : {
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { TypedRpcConnection } from './rpcConnection';
|
|
2
|
-
import { Token } from './token';
|
|
3
1
|
import { MainCommands, MainNotifications, Result } from '@4players/odin-common';
|
|
4
|
-
|
|
2
|
+
import { Token } from './token';
|
|
3
|
+
import { TypedRpcConnection } from './rpcConnection';
|
|
5
4
|
export type DatagramHandler = (connectionId: number, datagram: Uint8Array) => void;
|
|
6
5
|
export declare class MainConnection {
|
|
7
6
|
readonly id: number;
|
|
8
7
|
readonly url: URL;
|
|
9
8
|
readonly onDatagram: DatagramHandler;
|
|
10
|
-
readonly connection:
|
|
9
|
+
readonly connection: TypedRpcConnection<MainCommands, MainNotifications>;
|
|
11
10
|
readonly webRtc: RTCPeerConnection;
|
|
12
11
|
static create(id: number, url: URL, onDatagram: DatagramHandler, signal: AbortSignal): Promise<Result<MainConnection>>;
|
|
13
12
|
private readonly _AudioChannel;
|
|
@@ -36,4 +35,3 @@ export declare class MainConnection {
|
|
|
36
35
|
private _OnIceCandidate;
|
|
37
36
|
private _CleanUp;
|
|
38
37
|
}
|
|
39
|
-
export {};
|
|
@@ -8,10 +8,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
-
import { HealthCheckTimeout, MainConnectionPingInterval, WebRTCIceTimeout as WebRtcIceTimeout, WebRTCTimeout as WebRtcTimeout, } from './config';
|
|
12
|
-
import { TypedRpcConnection, connect } from './rpcConnection';
|
|
13
|
-
import { Token } from './token';
|
|
14
11
|
import { MainCommandsRpc, MainNotificationsRpc, assert, extendUrl, failure, success, } from '@4players/odin-common';
|
|
12
|
+
import { Token } from './token';
|
|
13
|
+
import { TypedRpcConnection } from './rpcConnection';
|
|
14
|
+
import { connect } from './websocket';
|
|
15
|
+
import { HealthCheckTimeout, MainConnectionPingInterval, WebRTCIceTimeout as WebRtcIceTimeout, WebRTCTimeout as WebRtcTimeout, } from './config';
|
|
15
16
|
export class MainConnection {
|
|
16
17
|
static create(id, url, onDatagram, signal) {
|
|
17
18
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { Backend, RoomCommands, Result } from '@4players/odin-common';
|
|
2
|
+
import { z } from 'zod';
|
|
1
3
|
import { ConnectionPool } from './connectionPool';
|
|
2
4
|
import { MainConnection } from './mainConnection';
|
|
3
5
|
import { Token } from './token';
|
|
4
|
-
import { Backend, RoomCommands, Result } from '@4players/odin-common';
|
|
5
|
-
import * as z from 'zod';
|
|
6
6
|
export declare class RoomConnection {
|
|
7
7
|
readonly connectionPool: ConnectionPool;
|
|
8
8
|
readonly onEvent: Backend.OnEvent;
|
|
@@ -7,10 +7,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import { Selector, failure, sleep, success, RoomCommandsRpc, } from '@4players/odin-common';
|
|
11
|
+
import { z } from 'zod';
|
|
10
12
|
import { RpcConnection } from './rpcConnection';
|
|
11
13
|
import { Token } from './token';
|
|
12
|
-
import { Selector, failure, sleep, success, RoomCommandsRpc, } from '@4players/odin-common';
|
|
13
|
-
import * as z from 'zod';
|
|
14
14
|
import { ConnectTimeout, MinTokenLifetime, MinTokenRefreshInterval, } from './config';
|
|
15
15
|
export class RoomConnection {
|
|
16
16
|
get token() {
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import * as z from 'zod';
|
|
2
|
-
import { Result,
|
|
3
|
-
export
|
|
4
|
-
|
|
2
|
+
import { Result, Accept, MessagePackRpc, Notifications } from '@4players/odin-common';
|
|
3
|
+
export interface RpcMessage {
|
|
4
|
+
name: string;
|
|
5
|
+
properties: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface RpcRequest {
|
|
8
|
+
accept: Accept<Result<unknown> | 'closed' | 'timeout'>;
|
|
9
|
+
timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
10
|
+
}
|
|
5
11
|
export declare class RpcConnection {
|
|
6
12
|
readonly webSocket: WebSocket;
|
|
7
13
|
private readonly _SendQueue;
|
|
@@ -46,3 +52,6 @@ export declare class TypedRpcConnection<C extends Commands, E extends Events> ex
|
|
|
46
52
|
notify<Name extends Extract<keyof C, string>>(name: Name, properties: z.infer<C[Name]['request']>): void;
|
|
47
53
|
recv(): Promise<RealizedEvents<E> | 'closed'>;
|
|
48
54
|
}
|
|
55
|
+
export declare function parseRpc(buffer: ArrayBuffer): Result<MessagePackRpc>;
|
|
56
|
+
export declare function parseRpcMessage<E extends Notifications>(events: E, rpc: RpcMessage): RealizedEvents<E> | undefined;
|
|
57
|
+
export declare function isKnownRpcMessage<E extends Notifications>(events: E, name: string): name is Extract<keyof E, string>;
|
|
@@ -8,55 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
-
import
|
|
12
|
-
import { failure, success, isKnownRpcMessage, parseRpc, parseRpcMessage, abortableSleep, } from '@4players/odin-common';
|
|
13
|
-
import { ConnectInterval } from './config';
|
|
14
|
-
export function connect(url, signal) {
|
|
15
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
16
|
-
console.debug(`connecting to ${url}`);
|
|
17
|
-
const start = performance.now();
|
|
18
|
-
let connection = failure('no connection was attempted');
|
|
19
|
-
for (let failures = 0; failures < ConnectInterval.length && signal.aborted === false; failures++) {
|
|
20
|
-
const nextReconnectAt = start + whenToConnect(failures);
|
|
21
|
-
if ((yield abortableSleep(nextReconnectAt - performance.now(), signal)) ===
|
|
22
|
-
'aborted') {
|
|
23
|
-
return failure('aborted');
|
|
24
|
-
}
|
|
25
|
-
connection = yield connectOnce(url, signal);
|
|
26
|
-
if (connection.type === 'Success') {
|
|
27
|
-
return connection;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return connection;
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
export function connectOnce(url, signal) {
|
|
34
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
-
let webSocket;
|
|
36
|
-
try {
|
|
37
|
-
webSocket = new WebSocket(url);
|
|
38
|
-
}
|
|
39
|
-
catch (reason) {
|
|
40
|
-
return failure(`unable to create websocket; ${reason}`);
|
|
41
|
-
}
|
|
42
|
-
const abort = new AbortController();
|
|
43
|
-
const onOpen = new Promise((accept) => webSocket.addEventListener('open', () => accept(success(webSocket)), {
|
|
44
|
-
signal: abort.signal,
|
|
45
|
-
}));
|
|
46
|
-
const onClose = new Promise((accept) => webSocket.addEventListener('close', (event) => accept(failure(event.reason)), { signal: abort.signal }));
|
|
47
|
-
const onAbort = new Promise((accept) => signal.addEventListener('abort', () => accept(failure('aborted')), {
|
|
48
|
-
signal: abort.signal,
|
|
49
|
-
}));
|
|
50
|
-
const result = yield Promise.race([onOpen, onClose, onAbort]);
|
|
51
|
-
abort.abort();
|
|
52
|
-
return result;
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
function whenToConnect(failures) {
|
|
56
|
-
const past_connects = ConnectInterval.slice(0, failures).reduce((a, b) => a + b, 0);
|
|
57
|
-
const interval = ConnectInterval[failures] * Math.random();
|
|
58
|
-
return past_connects + interval;
|
|
59
|
-
}
|
|
11
|
+
import { failure, success, msgpackEncode, msgpackDecode, MessagePackRpcSchema, } from '@4players/odin-common';
|
|
60
12
|
export class RpcConnection {
|
|
61
13
|
get closed() {
|
|
62
14
|
return this._Close.signal.aborted;
|
|
@@ -120,7 +72,7 @@ export class RpcConnection {
|
|
|
120
72
|
request.accept('timeout');
|
|
121
73
|
}
|
|
122
74
|
_Send(message) {
|
|
123
|
-
this.webSocket.send(
|
|
75
|
+
this.webSocket.send(msgpackEncode(message));
|
|
124
76
|
}
|
|
125
77
|
_OnWebSocketOpen(ev) {
|
|
126
78
|
for (const data in this._SendQueue) {
|
|
@@ -232,3 +184,26 @@ export class TypedRpcConnection extends RpcConnection {
|
|
|
232
184
|
});
|
|
233
185
|
}
|
|
234
186
|
}
|
|
187
|
+
export function parseRpc(buffer) {
|
|
188
|
+
try {
|
|
189
|
+
return success(MessagePackRpcSchema.parse(msgpackDecode(buffer)));
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
return failure(String(error));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
export function parseRpcMessage(events, rpc) {
|
|
196
|
+
if (!isKnownRpcMessage(events, rpc.name))
|
|
197
|
+
return undefined;
|
|
198
|
+
const parsed = events[rpc.name].safeParse(rpc.properties);
|
|
199
|
+
if (!parsed.success) {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
name: rpc.name,
|
|
204
|
+
properties: parsed.data,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
export function isKnownRpcMessage(events, name) {
|
|
208
|
+
return name in events;
|
|
209
|
+
}
|
package/lib/plugin/token.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { TokenAudienceSchema,
|
|
3
|
-
export type TokenClaims = z.infer<typeof TokenClaimsSchema>;
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { TokenAudienceSchema, TokenClaims, Result } from '@4players/odin-common';
|
|
4
3
|
export declare class Token {
|
|
5
4
|
readonly text: string;
|
|
6
5
|
readonly claims: TokenClaims;
|
package/lib/plugin/token.js
CHANGED
|
@@ -24,13 +24,12 @@ export class Token {
|
|
|
24
24
|
constructor(text, claims) {
|
|
25
25
|
this.text = text;
|
|
26
26
|
this.claims = claims;
|
|
27
|
-
|
|
28
|
-
if (address !== undefined) {
|
|
27
|
+
if (claims.adr !== undefined) {
|
|
29
28
|
try {
|
|
30
|
-
this._Address = new URL(`wss://${
|
|
29
|
+
this._Address = new URL(`wss://${claims.adr}`);
|
|
31
30
|
}
|
|
32
31
|
catch (error) {
|
|
33
|
-
console.error(`address in token(${
|
|
32
|
+
console.error(`address in token(${claims.adr}) can not be parsed; ${error}`);
|
|
34
33
|
}
|
|
35
34
|
}
|
|
36
35
|
if (claims.ups !== undefined) {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { ConnectInterval } from './config';
|
|
11
|
+
import { abortableSleep, failure, success, } from '@4players/odin-common';
|
|
12
|
+
export function connect(url, signal) {
|
|
13
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
14
|
+
const start = performance.now();
|
|
15
|
+
let connection = failure('no connection was attempted');
|
|
16
|
+
for (let failures = 0; failures < ConnectInterval.length && signal.aborted === false; failures++) {
|
|
17
|
+
const nextReconnectAt = start + whenToConnect(failures);
|
|
18
|
+
if ((yield abortableSleep(nextReconnectAt - performance.now(), signal)) ===
|
|
19
|
+
'aborted') {
|
|
20
|
+
return failure('aborted');
|
|
21
|
+
}
|
|
22
|
+
connection = yield connectOnce(url, signal);
|
|
23
|
+
if (connection.type === 'Success') {
|
|
24
|
+
return connection;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return connection;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export function connectOnce(url, signal) {
|
|
31
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
+
let webSocket;
|
|
33
|
+
try {
|
|
34
|
+
webSocket = new WebSocket(url);
|
|
35
|
+
}
|
|
36
|
+
catch (reason) {
|
|
37
|
+
return failure(`unable to create websocket; ${reason}`);
|
|
38
|
+
}
|
|
39
|
+
const abort = new AbortController();
|
|
40
|
+
const onOpen = new Promise((accept) => webSocket.addEventListener('open', () => accept(success(webSocket)), {
|
|
41
|
+
signal: abort.signal,
|
|
42
|
+
}));
|
|
43
|
+
const onClose = new Promise((accept) => webSocket.addEventListener('close', (event) => accept(failure(event.reason)), { signal: abort.signal }));
|
|
44
|
+
const onAbort = new Promise((accept) => signal.addEventListener('abort', () => accept(failure('aborted')), {
|
|
45
|
+
signal: abort.signal,
|
|
46
|
+
}));
|
|
47
|
+
const result = yield Promise.race([onOpen, onClose, onAbort]);
|
|
48
|
+
abort.abort();
|
|
49
|
+
return result;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function whenToConnect(failures) {
|
|
53
|
+
const past_connects = ConnectInterval.slice(0, failures).reduce((a, b) => a + b, 0);
|
|
54
|
+
const interval = ConnectInterval[failures] * Math.random();
|
|
55
|
+
return past_connects + interval;
|
|
56
|
+
}
|