Skip to content

Commit

Permalink
Add TS decorator support and replace class arrow function fields with…
Browse files Browse the repository at this point in the history
… bound methods (#790)

* merge cherry-pick

* merge cherry-pick decorators

* clarify vite usage

* Create lovely-crabs-hide.md

* prettier decorators

* prettier

* improve types

* more field to method conversion

* use ts preset and preset-env for vite babel

* fix merge
  • Loading branch information
lukasIO authored Jul 20, 2023
1 parent 8fcabe7 commit 41a1df1
Show file tree
Hide file tree
Showing 19 changed files with 810 additions and 468 deletions.
5 changes: 5 additions & 0 deletions .changeset/lovely-crabs-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"livekit-client": patch
---

Add TS decorator support and replace class arrow function properties with bound methods
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"importOrder": ["<THIRD_PARTY_MODULES>", "^[./]"],
"importOrderSeparation": false,
"importOrderSortSpecifiers": true,
"importOrderParserPlugins": ["typescript"],
"importOrderParserPlugins": ["typescript", "decorators"],
"plugins": ["@trivago/prettier-plugin-sort-imports"]
}
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@
},
"devDependencies": {
"@babel/core": "7.22.1",
"@babel/plugin-proposal-decorators": "^7.22.7",
"@babel/preset-env": "7.22.4",
"@babel/preset-typescript": "^7.22.5",
"@bufbuild/protoc-gen-es": "^1.3.0",
"@changesets/cli": "2.26.1",
"@livekit/changesets-changelog-github": "^0.0.4",
Expand All @@ -73,9 +75,7 @@
"@size-limit/file": "^8.2.4",
"@size-limit/webpack": "^8.2.4",
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
"@types/events": "^3.0.0",
"@types/sdp-transform": "2.4.6",
"@types/ua-parser-js": "0.7.36",
"@typescript-eslint/eslint-plugin": "5.59.8",
"@typescript-eslint/parser": "5.59.8",
"downlevel-dts": "^0.11.0",
Expand All @@ -92,11 +92,11 @@
"rollup-plugin-re": "1.0.7",
"rollup-plugin-typescript2": "0.34.1",
"size-limit": "^8.2.4",
"ts-proto": "1.148.2",
"typedoc": "0.24.8",
"typedoc-plugin-no-inherit": "1.4.0",
"typescript": "5.1.3",
"vite": "4.3.9",
"vite": "4.4.2",
"vite-plugin-babel": "^1.1.3",
"vitest": "^0.32.0"
}
}
17 changes: 17 additions & 0 deletions src/decorators/autoBind.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* ensure that the method isn’t re-bound if it is called as a stand-alone function or passed as a callback.
* @see https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#decorators
*/
export function bound<T extends Object>(
_originalMethod: Function,
context: ClassMethodDecoratorContext<T>,
) {
const methodName = context.name as keyof T;
if (context.private) {
throw new Error(`'bound' cannot decorate private properties like ${methodName as string}.`);
}

context.addInitializer(function () {
this[methodName] = (this[methodName] as Function).bind(this);
});
}
6 changes: 4 additions & 2 deletions src/e2ee/E2eeManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import EventEmitter from 'eventemitter3';
import { bound } from '../decorators/autoBind';
import log from '../logger';
import { Encryption_Type, TrackInfo } from '../proto/livekit_models_pb';
import type RTCEngine from '../room/RTCEngine';
Expand Down Expand Up @@ -131,10 +132,11 @@ export class E2EEManager extends EventEmitter<E2EEManagerCallbacks> {
}
};

private onWorkerError = (ev: ErrorEvent) => {
@bound
private onWorkerError(ev: ErrorEvent) {
log.error('e2ee worker encountered an error:', { error: ev.error });
this.emit(EncryptionEvent.Error, ev.error);
};
}

public setupEngine(engine: RTCEngine) {
engine.on(EngineEvent.RTPVideoMapUpdate, (rtpMap) => {
Expand Down
46 changes: 28 additions & 18 deletions src/room/RTCEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import EventEmitter from 'eventemitter3';
import type { MediaAttributes } from 'sdp-transform';
import type { SignalOptions } from '../api/SignalClient';
import { SignalClient } from '../api/SignalClient';
import { bound } from '../decorators/autoBind';
import log from '../logger';
import type { InternalRoomOptions } from '../options';
import {
Expand Down Expand Up @@ -603,7 +604,8 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
this.reliableDC.onbufferedamountlow = this.handleBufferedAmountLow;
}

private handleDataChannel = async ({ channel }: RTCDataChannelEvent) => {
@bound
private async handleDataChannel({ channel }: RTCDataChannelEvent) {
if (!channel) {
return;
}
Expand All @@ -616,9 +618,10 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
}
log.debug(`on data channel ${channel.id}, ${channel.label}`);
channel.onmessage = this.handleDataMessage;
};
}

private handleDataMessage = async (message: MessageEvent) => {
@bound
private async handleDataMessage(message: MessageEvent) {
// make sure to respect incoming data message order by processing message events one after the other
const unlock = await this.dataProcessLock.lock();
try {
Expand All @@ -642,9 +645,10 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
} finally {
unlock();
}
};
}

private handleDataError = (event: Event) => {
@bound
private handleDataError(event: Event) {
const channel = event.currentTarget as RTCDataChannel;
const channelKind = channel.maxRetransmits === 0 ? 'lossy' : 'reliable';

Expand All @@ -654,15 +658,16 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
} else {
log.error(`Unknown DataChannel Error on ${channelKind}`, event);
}
};
}

private handleBufferedAmountLow = (event: Event) => {
@bound
private handleBufferedAmountLow(event: Event) {
const channel = event.currentTarget as RTCDataChannel;
const channelKind =
channel.maxRetransmits === 0 ? DataPacket_Kind.LOSSY : DataPacket_Kind.RELIABLE;

this.updateAndEmitDCBufferStatus(channelKind);
};
}

private setPreferredCodec(
transceiver: RTCRtpTransceiver,
Expand Down Expand Up @@ -811,7 +816,8 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
// websocket reconnect behavior. if websocket is interrupted, and the PeerConnection
// continues to work, we can reconnect to websocket to continue the session
// after a number of retries, we'll close and give up permanently
private handleDisconnect = (connection: string, disconnectReason?: ReconnectReason) => {
@bound
private handleDisconnect(connection: string, disconnectReason?: ReconnectReason) {
if (this._isClosed) {
return;
}
Expand Down Expand Up @@ -856,7 +862,7 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
() => this.attemptReconnect(disconnectReason),
delay,
);
};
}

private async attemptReconnect(reason?: ReconnectReason) {
if (this._isClosed) {
Expand Down Expand Up @@ -1105,7 +1111,8 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
throw new ConnectionError('could not establish PC connection');
}

waitForRestarted = () => {
@bound
waitForRestarted() {
return new Promise<void>((resolve, reject) => {
if (this.pcState === PCState.Connected) {
resolve();
Expand All @@ -1122,7 +1129,7 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
this.once(EngineEvent.Disconnected, onDisconnected);
this.once(EngineEvent.Closing, onDisconnected);
});
};
}

/* @internal */
async sendDataPacket(packet: DataPacket, kind: DataPacket_Kind) {
Expand All @@ -1139,20 +1146,22 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
this.updateAndEmitDCBufferStatus(kind);
}

private updateAndEmitDCBufferStatus = (kind: DataPacket_Kind) => {
@bound
private updateAndEmitDCBufferStatus(kind: DataPacket_Kind) {
const status = this.isBufferStatusLow(kind);
if (typeof status !== 'undefined' && status !== this.dcBufferStatus.get(kind)) {
this.dcBufferStatus.set(kind, status);
this.emit(EngineEvent.DCBufferStatusChanged, status, kind);
}
};
}

private isBufferStatusLow = (kind: DataPacket_Kind): boolean | undefined => {
@bound
private isBufferStatusLow(kind: DataPacket_Kind): boolean | undefined {
const dc = this.dataChannelForKind(kind);
if (dc) {
return dc.bufferedAmount <= dc.bufferedAmountLowThreshold;
}
};
}

/**
* @internal
Expand Down Expand Up @@ -1332,13 +1341,14 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
this.reconnectAttempts = 0;
}

private handleBrowserOnLine = () => {
@bound
private handleBrowserOnLine() {
// in case the engine is currently reconnecting, attempt a reconnect immediately after the browser state has changed to 'onLine'
if (this.client.isReconnecting) {
this.clearReconnectTimeout();
this.attemptReconnect(ReconnectReason.RR_SIGNAL_DISCONNECTED);
}
};
}

private registerOnLineListener() {
if (isWeb()) {
Expand Down
Loading

0 comments on commit 41a1df1

Please sign in to comment.