Narrow linting rules and fix for eslintrc for js files

This commit is contained in:
hensm
2022-04-27 22:28:38 +01:00
parent 5e2d9a2fbc
commit dececa46c3
27 changed files with 298 additions and 246 deletions

View File

@@ -1,30 +1,48 @@
{ {
"root": true, "root": true,
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"plugins": [ "@typescript-eslint" ], "plugins": ["@typescript-eslint"],
"extends": [ "extends": ["eslint:recommended", "prettier"],
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": { "rules": {
"no-useless-escape": "off", "no-useless-escape": "off",
"no-prototype-builtins": "off", "no-prototype-builtins": "off",
"no-async-promise-executor": "off", "no-async-promise-executor": "off",
"semi": [ "error", "always"], "semi": ["error", "always"],
"no-multiple-empty-lines": [ "error", { "max": 2 }], "no-multiple-empty-lines": ["error", { "max": 2 }],
"no-console": [ "error", { "no-console": [
"allow": [ "info", "warn", "error" ] "error",
}], {
"allow": ["info", "warn", "error"]
}
]
},
"@typescript-eslint/no-empty-interface": "off", "overrides": [
"@typescript-eslint/no-explicit-any": "off", {
"@typescript-eslint/explicit-module-boundary-types": "off", "files": ["*/bin/**/*.js"],
"@typescript-eslint/no-unused-vars": "off", "env": {
"@typescript-eslint/ban-types": "off", "node": true
"@typescript-eslint/ban-ts-comment": "off", }
"@typescript-eslint/no-this-alias": [ "error", { },
"allowedNames": [ "this_" ] {
}] "files": ["**/*.ts", "**/*.tsx"],
} "extends": "plugin:@typescript-eslint/recommended",
"rules": {
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{ "argsIgnorePattern": "^_" }
],
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-this-alias": [
"error",
{
"allowedNames": ["this_"]
}
]
}
}
]
} }

View File

@@ -489,8 +489,7 @@ function packageLinuxRpm(
function packageWin32( function packageWin32(
arch, arch,
platformExecutableName, platformExecutableName,
platformExecutablePath, platformExecutablePath
platformManifestPath
) { ) {
const outputName = `${meta.__applicationName}-${meta.__applicationVersion}-${arch}.exe`; const outputName = `${meta.__applicationName}-${meta.__applicationVersion}-${arch}.exe`;
@@ -525,6 +524,6 @@ function packageWin32(
} }
build().catch(e => { build().catch(e => {
console.log("Build failed", e); console.error("Build failed", e);
process.exit(1); process.exit(1);
}); });

1
app/package-lock.json generated
View File

@@ -13,7 +13,6 @@
"mime-types": "^2.1.35", "mime-types": "^2.1.35",
"minimist": "^1.2.6", "minimist": "^1.2.6",
"node-fetch": "^3.2.3", "node-fetch": "^3.2.3",
"rage-edit": "*",
"tweetnacl": "^1.0.3", "tweetnacl": "^1.0.3",
"ws": "^8.5.0" "ws": "^8.5.0"
}, },

View File

@@ -72,8 +72,6 @@ export default class Session extends CastClient {
this.establishAppConnection(this.transportId); this.establishAppConnection(this.transportId);
this.onSessionCreated?.(this.sessionId); this.onSessionCreated?.(this.sessionId);
const { friendlyName } = this.receiverDevice;
messaging.sendMessage({ messaging.sendMessage({
subject: "cast:sessionCreated", subject: "cast:sessionCreated",
data: { data: {

View File

@@ -3,7 +3,7 @@
import messaging, { Message } from "../../messaging"; import messaging, { Message } from "../../messaging";
import Session from "./Session"; import Session from "./Session";
import CastClient, { NS_CONNECTION, NS_RECEIVER } from "./client"; import CastClient from "./client";
const sessions = new Map<string, Session>(); const sessions = new Map<string, Session>();

View File

@@ -125,7 +125,7 @@ interface BreakClip {
contentId?: string; contentId?: string;
contentType?: string; contentType?: string;
contentUrl?: string; contentUrl?: string;
customData?: {}; customData?: unknown;
duration?: number; duration?: number;
id: string; id: string;
hlsSegmentFormat?: HlsSegmentFormat; hlsSegmentFormat?: HlsSegmentFormat;
@@ -137,7 +137,7 @@ interface BreakClip {
interface TextTrackStyle { interface TextTrackStyle {
backgroundColor: Nullable<string>; backgroundColor: Nullable<string>;
customData: any; customData: unknown;
edgeColor: Nullable<string>; edgeColor: Nullable<string>;
edgeType: Nullable<string>; edgeType: Nullable<string>;
fontFamily: Nullable<string>; fontFamily: Nullable<string>;
@@ -151,7 +151,7 @@ interface TextTrackStyle {
} }
interface Track { interface Track {
customData: any; customData: unknown;
language: Nullable<string>; language: Nullable<string>;
name: Nullable<string>; name: Nullable<string>;
subtype: Nullable<string>; subtype: Nullable<string>;
@@ -162,7 +162,7 @@ interface Track {
} }
interface UserActionState { interface UserActionState {
customData: any; customData: unknown;
userAction: UserAction; userAction: UserAction;
} }
@@ -185,7 +185,7 @@ interface MediaInformation {
contentId: string; contentId: string;
contentType: string; contentType: string;
contentUrl?: string; contentUrl?: string;
customData: any; customData: unknown;
duration: Nullable<number>; duration: Nullable<number>;
entity?: string; entity?: string;
hlsSegmentFormat?: HlsSegmentFormat; hlsSegmentFormat?: HlsSegmentFormat;
@@ -269,7 +269,7 @@ interface PhotoMediaMetadata {
interface QueueItem { interface QueueItem {
activeTrackIds: Nullable<number[]>; activeTrackIds: Nullable<number[]>;
autoplay: boolean; autoplay: boolean;
customData: any; customData: unknown;
itemId: Nullable<number>; itemId: Nullable<number>;
media: MediaInformation; media: MediaInformation;
playbackDuration: Nullable<number>; playbackDuration: Nullable<number>;

View File

@@ -6,7 +6,7 @@ import { handleCastMessage } from "./components/cast";
import { startDiscovery, stopDiscovery } from "./components/discovery"; import { startDiscovery, stopDiscovery } from "./components/discovery";
import { startMediaServer, stopMediaServer } from "./components/mediaServer"; import { startMediaServer, stopMediaServer } from "./components/mediaServer";
import { __applicationName, __applicationVersion } from "../../package.json"; import { __applicationVersion } from "../../package.json";
process.on("SIGTERM", () => { process.on("SIGTERM", () => {
stopDiscovery(); stopDiscovery();

View File

@@ -37,7 +37,6 @@ export async function convertSrtToVtt(srtFilePath: string) {
* millisecond separator. * millisecond separator.
*/ */
for (const groups of fileContents.matchAll(REGEX_CAPTION)) { for (const groups of fileContents.matchAll(REGEX_CAPTION)) {
const captionSource = groups[0];
const captionIndex = groups[1]; const captionIndex = groups[1];
const captionTime = groups[2]; const captionTime = groups[2];
const captionText = groups[3]; const captionText = groups[3];

View File

@@ -163,12 +163,12 @@ type MessageDefinitions = {
/** /**
* Sent to bridge to stop HTTP media server. * Sent to bridge to stop HTTP media server.
*/ */
"bridge:stopMediaServer": {}; "bridge:stopMediaServer": undefined;
/** /**
* Sent to media sender from bridge when the media server has * Sent to media sender from bridge when the media server has
* stopped. * stopped.
*/ */
"mediaCast:mediaServerStopped": {}; "mediaCast:mediaServerStopped": undefined;
/** /**
* Sent to media sender from bridge when the media server has * Sent to media sender from bridge when the media server has
* encountered an error. * encountered an error.
@@ -190,8 +190,8 @@ type Messages = {
* all-optional keys. * all-optional keys.
*/ */
type NarrowedMessage<L extends MessageBase<keyof MessageDefinitions>> = type NarrowedMessage<L extends MessageBase<keyof MessageDefinitions>> =
L extends any L extends unknown
? {} extends L["data"] ? undefined extends L["data"]
? Omit<L, "data"> & Partial<L> ? Omit<L, "data"> & Partial<L>
: L : L
: never; : never;
@@ -231,7 +231,7 @@ class Messenger extends TypedEmitter<MessengerEvents> {
this.encodeTransform.write(message); this.encodeTransform.write(message);
} }
send(data: any) { send(data: unknown) {
this.encodeTransform.write(data); this.encodeTransform.write(data);
} }
} }

View File

@@ -1,9 +1,9 @@
"use strict"; "use strict";
import { Transform } from "stream"; import { Transform, TransformCallback } from "stream";
import { Message } from "./bridge/messaging"; import { Message } from "./bridge/messaging";
type ResponseHandlerFunction = (message: Message) => Promise<any>; type ResponseHandlerFunction = (message: Message) => Promise<unknown>;
/** /**
* Takes a handler function that implements the transform * Takes a handler function that implements the transform
@@ -20,8 +20,7 @@ export class ResponseTransform extends Transform {
public _transform( public _transform(
chunk: Message, chunk: Message,
_encoding: string, _encoding: string,
// tslint:disable-next-line:ban-types callback: TransformCallback
callback: Function
) { ) {
Promise.resolve(this._handler(chunk)).then(res => { Promise.resolve(this._handler(chunk)).then(res => {
if (res) { if (res) {
@@ -49,10 +48,10 @@ export class DecodeTransform extends Transform {
} }
public _transform( public _transform(
chunk: any, chunk: Uint8Array,
_encoding: string, _encoding: string,
// tslint:disable-next-line:ban-types // tslint:disable-next-line:ban-types
callback: Function callback: TransformCallback
) { ) {
// Append next chunk to buffer // Append next chunk to buffer
this._messageBuffer = Buffer.concat([this._messageBuffer, chunk]); this._messageBuffer = Buffer.concat([this._messageBuffer, chunk]);
@@ -108,10 +107,10 @@ export class EncodeTransform extends Transform {
} }
public _transform( public _transform(
chunk: any, chunk: Uint8Array,
_encoding: string, _encoding: string,
// tslint:disable-next-line:ban-types // tslint:disable-next-line:ban-types
callback: Function callback: TransformCallback
) { ) {
const messageLength = Buffer.alloc(4); const messageLength = Buffer.alloc(4);
const message = Buffer.from(JSON.stringify(chunk)); const message = Buffer.from(JSON.stringify(chunk));

View File

@@ -53,7 +53,7 @@ const preactCompatPlugin = {
"../node_modules/preact/compat/dist/compat.module.js" "../node_modules/preact/compat/dist/compat.module.js"
); );
build.onResolve({ filter: /^(react|react-dom)$/ }, args => ({ build.onResolve({ filter: /^(react|react-dom)$/ }, () => ({
path: preactPath path: preactPath
})); }));
} }

View File

@@ -5,7 +5,7 @@ import logger from "../lib/logger";
import { TypedEventTarget } from "../lib/TypedEventTarget"; import { TypedEventTarget } from "../lib/TypedEventTarget";
import { Message, Port } from "../messaging"; import { Message, Port } from "../messaging";
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../types"; import { ReceiverDevice } from "../types";
import { ReceiverStatus } from "../cast/sdk/types"; import { ReceiverStatus } from "../cast/sdk/types";
interface EventMap { interface EventMap {

View File

@@ -1,7 +1,6 @@
"use strict"; "use strict";
import logger from "../lib/logger"; import logger from "../lib/logger";
import { TypedEventTarget } from "../lib/TypedEventTarget";
import { Message } from "../messaging"; import { Message } from "../messaging";
type EventMessengerListener = (message: Message) => void; type EventMessengerListener = (message: Message) => void;

View File

@@ -1,6 +1,6 @@
"use strict"; "use strict";
import messaging, { Message } from "../messaging"; import { Message } from "../messaging";
import { BridgeInfo } from "../lib/bridge"; import { BridgeInfo } from "../lib/bridge";
import { TypedMessagePort } from "../lib/TypedMessagePort"; import { TypedMessagePort } from "../lib/TypedMessagePort";

View File

@@ -17,8 +17,7 @@ if (!_window.chrome) {
// Create page-accessible API object // Create page-accessible API object
_window.chrome.cast = new CastSDK(); _window.chrome.cast = new CastSDK();
let bridgeInfo: any; let frameworkScriptPromise: Promise<HTMLScriptElement> | undefined;
let frameworkScriptPromise: Promise<HTMLScriptElement>;
/** /**
* If loaded within a page via a <script> element, * If loaded within a page via a <script> element,
@@ -27,8 +26,9 @@ let frameworkScriptPromise: Promise<HTMLScriptElement>;
*/ */
if (document.currentScript) { if (document.currentScript) {
const currentScript = document.currentScript as HTMLScriptElement; const currentScript = document.currentScript as HTMLScriptElement;
const currentScriptUrl = new URL(currentScript.src); const currentScriptParams = new URLSearchParams(
const currentScriptParams = new URLSearchParams(currentScriptUrl.search); new URL(currentScript.src).search
);
// Load Framework API if requested // Load Framework API if requested
if (currentScriptParams.get("loadCastFramework") === "1") { if (currentScriptParams.get("loadCastFramework") === "1") {
@@ -43,17 +43,13 @@ if (document.currentScript) {
eventMessaging.page.addListener(async message => { eventMessaging.page.addListener(async message => {
switch (message.subject) { switch (message.subject) {
case "cast:initialized": { case "cast:initialized": {
bridgeInfo = message.data;
// If framework API is requested, ensure loaded // If framework API is requested, ensure loaded
if (frameworkScriptPromise) { await frameworkScriptPromise;
await frameworkScriptPromise;
}
// Call page script/framework API script's init function // Call page script/framework API script's init function
const initFn = _window.__onGCastApiAvailable; const initFn = _window.__onGCastApiAvailable;
if (initFn && typeof initFn === "function") { if (initFn && typeof initFn === "function") {
initFn(bridgeInfo && bridgeInfo.isVersionCompatible); initFn(message.data.isVersionCompatible);
} }
break; break;

View File

@@ -36,7 +36,7 @@ export class Error {
constructor( constructor(
public code: string, public code: string,
public description: Nullable<string> = null, public description: Nullable<string> = null,
public details: any = null public details: unknown = null
) {} ) {}
} }

View File

@@ -45,7 +45,7 @@ export default class Media {
activeTrackIds: Nullable<number[]> = null; activeTrackIds: Nullable<number[]> = null;
breakStatus?: BreakStatus; breakStatus?: BreakStatus;
currentTime = 0; currentTime = 0;
customData: any = null; customData: unknown = null;
idleReason: Nullable<string> = null; idleReason: Nullable<string> = null;
liveSeekableRange?: LiveSeekableRange; liveSeekableRange?: LiveSeekableRange;
media: Nullable<MediaInfo> = null; media: Nullable<MediaInfo> = null;

View File

@@ -49,7 +49,7 @@ export class BreakClip {
contentId?: string; contentId?: string;
contentType?: string; contentType?: string;
contentUrl?: string; contentUrl?: string;
customData?: {}; customData?: unknown;
duration?: number; duration?: number;
hlsSegmentFormat?: HlsSegmentFormat; hlsSegmentFormat?: HlsSegmentFormat;
posterUrl?: string; posterUrl?: string;
@@ -99,7 +99,7 @@ export class GenericMediaMetadata {
} }
export class GetStatusRequest { export class GetStatusRequest {
customData: any = null; customData: unknown = null;
} }
export class LiveSeekableRange { export class LiveSeekableRange {
@@ -117,7 +117,7 @@ export class LoadRequest {
atvCredentialsType?: string; atvCredentialsType?: string;
autoplay: Nullable<boolean> = true; autoplay: Nullable<boolean> = true;
currentTime: Nullable<number> = null; currentTime: Nullable<number> = null;
customData: any = null; customData: unknown = null;
media: MediaInfo; media: MediaInfo;
requestId = 0; requestId = 0;
sessionId: Nullable<string> = null; sessionId: Nullable<string> = null;
@@ -139,7 +139,7 @@ export class MediaInfo {
atvEntity?: string; atvEntity?: string;
breakClips?: BreakClip[]; breakClips?: BreakClip[];
breaks?: Break[]; breaks?: Break[];
customData: any = null; customData: unknown = null;
contentUrl?: string; contentUrl?: string;
duration: Nullable<number> = null; duration: Nullable<number> = null;
entity?: string; entity?: string;
@@ -200,7 +200,7 @@ export class MusicTrackMediaMetadata {
} }
export class PauseRequest { export class PauseRequest {
customData: any = null; customData: unknown = null;
} }
export class PhotoMediaMetadata { export class PhotoMediaMetadata {
@@ -218,7 +218,7 @@ export class PhotoMediaMetadata {
} }
export class PlayRequest { export class PlayRequest {
customData: any = null; customData: unknown = null;
} }
export class QueueData { export class QueueData {
@@ -236,7 +236,7 @@ export class QueueData {
} }
export class QueueInsertItemsRequest { export class QueueInsertItemsRequest {
customData: any = null; customData: unknown = null;
insertBefore: Nullable<number> = null; insertBefore: Nullable<number> = null;
requestId: Nullable<number> = null; requestId: Nullable<number> = null;
sessionId: Nullable<string> = null; sessionId: Nullable<string> = null;
@@ -248,7 +248,7 @@ export class QueueInsertItemsRequest {
export class QueueItem { export class QueueItem {
activeTrackIds: Nullable<number[]> = null; activeTrackIds: Nullable<number[]> = null;
autoplay = true; autoplay = true;
customData: any = null; customData: unknown = null;
itemId: Nullable<number> = null; itemId: Nullable<number> = null;
media: MediaInfo; media: MediaInfo;
playbackDuration: Nullable<number> = null; playbackDuration: Nullable<number> = null;
@@ -268,7 +268,7 @@ export class QueueJumpRequest {
export class QueueLoadRequest { export class QueueLoadRequest {
type = "QUEUE_LOAD"; type = "QUEUE_LOAD";
customData: any = null; customData: unknown = null;
repeatMode: string = RepeatMode.OFF; repeatMode: string = RepeatMode.OFF;
startIndex = 0; startIndex = 0;
@@ -277,13 +277,13 @@ export class QueueLoadRequest {
export class QueueRemoveItemsRequest { export class QueueRemoveItemsRequest {
type = "QUEUE_REMOVE"; type = "QUEUE_REMOVE";
customData: any = null; customData: unknown = null;
constructor(public itemIds: number[]) {} constructor(public itemIds: number[]) {}
} }
export class QueueReorderItemsRequest { export class QueueReorderItemsRequest {
customData: any = null; customData: unknown = null;
insertBefore: Nullable<number> = null; insertBefore: Nullable<number> = null;
type = "QUEUE_REORDER"; type = "QUEUE_REORDER";
@@ -292,30 +292,30 @@ export class QueueReorderItemsRequest {
export class QueueSetPropertiesRequest { export class QueueSetPropertiesRequest {
type = "QUEUE_UPDATE"; type = "QUEUE_UPDATE";
customData: any = null; customData: unknown = null;
repeatMode: Nullable<string> = null; repeatMode: Nullable<string> = null;
} }
export class QueueUpdateItemsRequest { export class QueueUpdateItemsRequest {
type = "QUEUE_UPDATE"; type = "QUEUE_UPDATE";
customData: any = null; customData: unknown = null;
constructor(public items: QueueItem[]) {} constructor(public items: QueueItem[]) {}
} }
export class SeekRequest { export class SeekRequest {
currentTime: Nullable<number> = null; currentTime: Nullable<number> = null;
customData: any = null; customData: unknown = null;
resumeState: Nullable<ResumeState> = null; resumeState: Nullable<ResumeState> = null;
} }
export class StopRequest { export class StopRequest {
customData: any = null; customData: unknown = null;
} }
export class TextTrackStyle { export class TextTrackStyle {
backgroundColor: Nullable<string> = null; backgroundColor: Nullable<string> = null;
customData: any = null; customData: unknown = null;
edgeColor: Nullable<string> = null; edgeColor: Nullable<string> = null;
edgeType: Nullable<string> = null; edgeType: Nullable<string> = null;
fontFamily: Nullable<string> = null; fontFamily: Nullable<string> = null;
@@ -329,7 +329,7 @@ export class TextTrackStyle {
} }
export class Track { export class Track {
customData: any = null; customData: unknown = null;
language: Nullable<string> = null; language: Nullable<string> = null;
name: Nullable<string> = null; name: Nullable<string> = null;
subtype: Nullable<string> = null; subtype: Nullable<string> = null;
@@ -355,7 +355,7 @@ export class TvShowMediaMetadata {
} }
export class UserActionState { export class UserActionState {
customData: any = null; customData: unknown = null;
constructor(public userAction: UserAction) {} constructor(public userAction: UserAction) {}
} }
@@ -374,7 +374,7 @@ export class VideoInformation {
} }
export class VolumeRequest { export class VolumeRequest {
customData: any = null; customData: unknown = null;
constructor(public volume: Volume) {} constructor(public volume: Volume) {}
} }

View File

@@ -7,6 +7,10 @@ import cast, { ensureInit } from "../../export";
import { Message } from "../../../messaging"; import { Message } from "../../../messaging";
import { ReceiverDevice } from "../../../types"; import { ReceiverDevice } from "../../../types";
import type Session from "../../sdk/Session";
import type Media from "../../sdk/media/Media";
import type { Error as Error_ } from "../../sdk/classes";
function startMediaServer( function startMediaServer(
filePath: string, filePath: string,
port: number port: number
@@ -49,12 +53,12 @@ function startMediaServer(
let backgroundPort: MessagePort; let backgroundPort: MessagePort;
let currentSession: cast.Session; let currentSession: Session;
let currentMedia: cast.media.Media; let currentMedia: Media;
let targetElement: HTMLElement; let targetElement: HTMLElement;
function getSession(opts: InitOptions): Promise<cast.Session> { function getSession(opts: InitOptions): Promise<Session> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
/** /**
* If a receiver is available, call requestSession. If a * If a receiver is available, call requestSession. If a
@@ -76,10 +80,10 @@ function getSession(opts: InitOptions): Promise<cast.Session> {
// TODO: Handle this // TODO: Handle this
} }
function onRequestSessionSuccess(session: cast.Session) { function onRequestSessionSuccess(session: Session) {
resolve(session); resolve(session);
} }
function onRequestSessionError(err: cast.Error) { function onRequestSessionError(err: Error_) {
reject(err.description); reject(err.description);
} }
@@ -97,7 +101,7 @@ function getSession(opts: InitOptions): Promise<cast.Session> {
}); });
} }
function getMedia(opts: InitOptions): Promise<cast.media.Media> { function getMedia(opts: InitOptions): Promise<Media> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
let mediaUrl = new URL(opts.mediaUrl); let mediaUrl = new URL(opts.mediaUrl);
const mediaTitle = mediaUrl.pathname.slice(1); const mediaTitle = mediaUrl.pathname.slice(1);

View File

@@ -6,6 +6,8 @@ import cast, { ensureInit } from "../export";
import { ReceiverSelectorMediaType } from "../../background/receiverSelector"; import { ReceiverSelectorMediaType } from "../../background/receiverSelector";
import { ReceiverDevice } from "../../types"; import { ReceiverDevice } from "../../types";
import type Session from "../sdk/Session";
// Variables passed from background // Variables passed from background
const { const {
selectedMedia, selectedMedia,
@@ -17,7 +19,7 @@ const {
const FX_CAST_RECEIVER_APP_NAMESPACE = "urn:x-cast:fx_cast"; const FX_CAST_RECEIVER_APP_NAMESPACE = "urn:x-cast:fx_cast";
let session: cast.Session; let session: Session;
let wasSessionRequested = false; let wasSessionRequested = false;
let peerConnection: RTCPeerConnection; let peerConnection: RTCPeerConnection;
@@ -26,7 +28,7 @@ let peerConnection: RTCPeerConnection;
* Sends a message to the fx_cast app running on the * Sends a message to the fx_cast app running on the
* receiver device. * receiver device.
*/ */
function sendAppMessage(subject: string, data: any) { function sendAppMessage(subject: string, data: unknown) {
if (!session) { if (!session) {
return; return;
} }
@@ -41,7 +43,7 @@ window.addEventListener("beforeunload", () => {
sendAppMessage("close", null); sendAppMessage("close", null);
}); });
async function onRequestSessionSuccess(newSession: cast.Session) { async function onRequestSessionSuccess(newSession: Session) {
cast.logMessage("onRequestSessionSuccess"); cast.logMessage("onRequestSessionSuccess");
session = newSession; session = newSession;

4
ext/src/global.d.ts vendored
View File

@@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
declare const BRIDGE_VERSION: string; declare const BRIDGE_VERSION: string;
declare const BRIDGE_NAME: string; declare const BRIDGE_NAME: string;
declare const MIRRORING_APP_ID: string; declare const MIRRORING_APP_ID: string;
@@ -68,7 +70,7 @@ type ExportFunctionFunc = (...args: any[]) => any;
declare function exportFunction( declare function exportFunction(
func: ExportFunctionFunc, func: ExportFunctionFunc,
targetScope: any, targetScope: Window,
options?: ExportFunctionOptions options?: ExportFunctionOptions
): ExportFunctionFunc; ): ExportFunctionFunc;

View File

@@ -3,7 +3,7 @@
export class Logger { export class Logger {
constructor(private prefix: string) {} constructor(private prefix: string) {}
public log(message: string, data?: any) { public log(message: string, data?: unknown) {
const formattedMessage = `${this.prefix} (Log): ${message}`; const formattedMessage = `${this.prefix} (Log): ${message}`;
if (data) { if (data) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@@ -13,7 +13,7 @@ export class Logger {
console.log(formattedMessage); console.log(formattedMessage);
} }
} }
public info(message: string, data?: any) { public info(message: string, data?: unknown) {
const formattedMessage = `${this.prefix} (Info): ${message}`; const formattedMessage = `${this.prefix} (Info): ${message}`;
if (data) { if (data) {
console.info(formattedMessage, data); console.info(formattedMessage, data);
@@ -21,7 +21,7 @@ export class Logger {
console.info(formattedMessage); console.info(formattedMessage);
} }
} }
public warn(message: string, data?: any) { public warn(message: string, data?: unknown) {
const formattedMessage = `${this.prefix} (Warning): ${message}`; const formattedMessage = `${this.prefix} (Warning): ${message}`;
if (data) { if (data) {
console.warn(formattedMessage, data); console.warn(formattedMessage, data);
@@ -29,7 +29,7 @@ export class Logger {
console.warn(formattedMessage); console.warn(formattedMessage);
} }
} }
public error(message: string, data?: any) { public error(message: string, data?: unknown) {
const formattedMessage = `${this.prefix} (Error): ${message}`; const formattedMessage = `${this.prefix} (Error): ${message}`;
if (data) { if (data) {
console.error(formattedMessage, data); console.error(formattedMessage, data);

View File

@@ -32,7 +32,6 @@ function connectNative(application: string): Port {
// Port proxy API // Port proxy API
const portObject: Port = { const portObject: Port = {
error: null as any,
name: "", name: "",
onDisconnect: { onDisconnect: {
@@ -150,7 +149,7 @@ function connectNative(application: string): Port {
} }
}); });
port.onMessage.addListener((message: any) => { port.onMessage.addListener((message: Message) => {
if (!isNativeHostStatusKnown) { if (!isNativeHostStatusKnown) {
isNativeHostStatusKnown = true; isNativeHostStatusKnown = true;
messageQueue = []; messageQueue = [];
@@ -177,8 +176,6 @@ async function sendNativeMessage(application: string, message: Message) {
); );
} }
const port = await options.get("bridgeBackupPort");
return await new Promise((resolve, reject) => { return await new Promise((resolve, reject) => {
const ws = new WebSocket( const ws = new WebSocket(
`ws://${bridgeBackupHost}:${bridgeBackupPort}` `ws://${bridgeBackupHost}:${bridgeBackupPort}`

View File

@@ -1,7 +1,5 @@
"use strict"; "use strict";
import logger from "./logger";
import { ReceiverSelectorMediaType } from "../background/receiverSelector"; import { ReceiverSelectorMediaType } from "../background/receiverSelector";
export function getNextEllipsis(ellipsis: string): string { export function getNextEllipsis(ellipsis: string): string {
@@ -18,7 +16,7 @@ export function getNextEllipsis(ellipsis: string): string {
*/ */
export function stringify( export function stringify(
templateStrings: TemplateStringsArray, templateStrings: TemplateStringsArray,
...substitutions: any[] ...substitutions: unknown[]
) { ) {
let formattedString = ""; let formattedString = "";

View File

@@ -52,7 +52,7 @@ type ExtMessageDefinitions = {
defaultMediaType?: ReceiverSelectorMediaType; defaultMediaType?: ReceiverSelectorMediaType;
availableMediaTypes?: ReceiverSelectorMediaType; availableMediaTypes?: ReceiverSelectorMediaType;
}; };
"popup:close": {}; "popup:close": undefined;
"receiverSelector:selected": ReceiverSelection; "receiverSelector:selected": ReceiverSelection;
"receiverSelector:stop": ReceiverSelection; "receiverSelector:stop": ReceiverSelection;
@@ -62,9 +62,9 @@ type ExtMessageDefinitions = {
}; };
"cast:selectReceiver/selected": ReceiverSelectionCast; "cast:selectReceiver/selected": ReceiverSelectionCast;
"cast:selectReceiver/stopped": ReceiverSelectionStop; "cast:selectReceiver/stopped": ReceiverSelectionStop;
"cast:selectReceiver/cancelled": {}; "cast:selectReceiver/cancelled": undefined;
"main:closeReceiverSelector": {}; "main:closeReceiverSelector": undefined;
"main:initializeCast": { appId: string }; "main:initializeCast": { appId: string };
"cast:initialized": BridgeInfo; "cast:initialized": BridgeInfo;
@@ -221,12 +221,12 @@ type AppMessageDefinitions = {
/** /**
* Sent to bridge to stop HTTP media server. * Sent to bridge to stop HTTP media server.
*/ */
"bridge:stopMediaServer": {}; "bridge:stopMediaServer": undefined;
/** /**
* Sent to media sender from bridge when the media server has * Sent to media sender from bridge when the media server has
* stopped. * stopped.
*/ */
"mediaCast:mediaServerStopped": {}; "mediaCast:mediaServerStopped": undefined;
/** /**
* Sent to media sender from bridge when the media server has * Sent to media sender from bridge when the media server has
* encountered an error. * encountered an error.
@@ -250,8 +250,8 @@ type Messages = {
* all-optional keys. * all-optional keys.
*/ */
type NarrowedMessage<L extends MessageBase<keyof MessageDefinitions>> = type NarrowedMessage<L extends MessageBase<keyof MessageDefinitions>> =
L extends any L extends unknown
? {} extends L["data"] ? undefined extends L["data"]
? Omit<L, "data"> & Partial<L> ? Omit<L, "data"> & Partial<L>
: L : L
: never; : never;
@@ -264,22 +264,28 @@ export type Message = NarrowedMessage<Messages[keyof Messages]>;
*/ */
export default new (class Messenger { export default new (class Messenger {
connect(connectInfo: { name: string }) { connect(connectInfo: { name: string }) {
return browser.runtime.connect(connectInfo) as unknown as Port; return browser.runtime.connect(connectInfo) as Port;
} }
connectTab(tabId: number, connectInfo: { name: string; frameId: number }) { connectTab(tabId: number, connectInfo: { name: string; frameId: number }) {
return browser.tabs.connect(tabId, connectInfo) as unknown as Port; return browser.tabs.connect(tabId, connectInfo) as Port;
} }
onConnect = { onConnect = {
addListener(cb: (port: Port) => void) { addListener(cb: (port: Port) => void) {
browser.runtime.onConnect.addListener(cb as any); browser.runtime.onConnect.addListener(
cb as (port: browser.runtime.Port) => void
);
}, },
removeListener(cb: (port: Port) => void) { removeListener(cb: (port: Port) => void) {
browser.runtime.onConnect.removeListener(cb as any); browser.runtime.onConnect.removeListener(
cb as (port: browser.runtime.Port) => void
);
}, },
hasListener(cb: (port: Port) => void) { hasListener(cb: (port: Port) => void) {
return browser.runtime.onConnect.hasListener(cb as any); return browser.runtime.onConnect.hasListener(
cb as (port: browser.runtime.Port) => void
);
} }
}; };
})(); })();

View File

@@ -4,14 +4,23 @@
import React, { Component } from "react"; import React, { Component } from "react";
import semver from "semver"; import semver from "semver";
import options, { Options } from "../../lib/options"; import { Options } from "../../lib/options";
import { BridgeInfo } from "../../lib/bridge"; import { BridgeInfo } from "../../lib/bridge";
import { getNextEllipsis } from "../../lib/utils"; import { getNextEllipsis } from "../../lib/utils";
import logger from "../../lib/logger";
const _ = browser.i18n.getMessage; const _ = browser.i18n.getMessage;
interface Release {
url: string;
tag_name: string;
html_url: string;
assets: Array<{
content_type: string;
html_url: string;
}>;
}
interface BridgeStatsProps { interface BridgeStatsProps {
info: BridgeInfo; info: BridgeInfo;
@@ -20,45 +29,42 @@ interface BridgeStatsProps {
const BridgeStats = (props: BridgeStatsProps) => ( const BridgeStats = (props: BridgeStatsProps) => (
<table className="bridge__stats"> <table className="bridge__stats">
<tr> <tr>
<th>{ _("optionsBridgeStatsName") }</th> <th>{_("optionsBridgeStatsName")}</th>
<td>{ props.info.name }</td> <td>{props.info.name}</td>
</tr> </tr>
<tr> <tr>
<th>{ _("optionsBridgeStatsVersion") }</th> <th>{_("optionsBridgeStatsVersion")}</th>
<td>{ props.info.version }</td> <td>{props.info.version}</td>
</tr> </tr>
<tr> <tr>
<th>{ _("optionsBridgeStatsExpectedVersion") }</th> <th>{_("optionsBridgeStatsExpectedVersion")}</th>
<td>{ props.info.expectedVersion }</td> <td>{props.info.expectedVersion}</td>
</tr> </tr>
<tr> <tr>
<th>{ _("optionsBridgeStatsCompatibility") }</th> <th>{_("optionsBridgeStatsCompatibility")}</th>
<td> <td>
{ props.info.isVersionCompatible {props.info.isVersionCompatible
? props.info.isVersionExact ? props.info.isVersionExact
? _("optionsBridgeCompatible") ? _("optionsBridgeCompatible")
: _("optionsBridgeLikelyCompatible") : _("optionsBridgeLikelyCompatible")
: _("optionsBridgeIncompatible") } : _("optionsBridgeIncompatible")}
</td> </td>
</tr> </tr>
<tr> <tr>
<th>{ _("optionsBridgeStatsRecommendedAction") }</th> <th>{_("optionsBridgeStatsRecommendedAction")}</th>
<td> <td>
{ {props.info.isVersionCompatible
props.info.isVersionCompatible ? _("optionsBridgeNoAction")
? _("optionsBridgeNoAction") : props.info.isVersionOlder
: props.info.isVersionOlder ? _("optionsBridgeOlderAction")
? _("optionsBridgeOlderAction") : props.info.isVersionNewer
: props.info.isVersionNewer ? _("optionsBridgeNewerAction")
? _("optionsBridgeNewerAction") : _("optionsBridgeNoAction")}
: _("optionsBridgeNoAction")
}
</td> </td>
</tr> </tr>
</table> </table>
); );
interface BridgeProps { interface BridgeProps {
info?: BridgeInfo; info?: BridgeInfo;
loading: boolean; loading: boolean;
@@ -76,17 +82,17 @@ interface BridgeState {
} }
export default class Bridge extends Component<BridgeProps, BridgeState> { export default class Bridge extends Component<BridgeProps, BridgeState> {
private updateData: any; private updateData?: Release;
private updateStatusTimeout?: number; private updateStatusTimeout?: number;
constructor(props: BridgeProps) { constructor(props: BridgeProps) {
super(props); super(props);
this.state = { this.state = {
isCheckingUpdates: false isCheckingUpdates: false,
, isUpdateAvailable: false isUpdateAvailable: false,
, wasErrorCheckingUpdates: false wasErrorCheckingUpdates: false,
, checkUpdatesEllipsis: "..." checkUpdatesEllipsis: "..."
}; };
this.onCheckUpdates = this.onCheckUpdates.bind(this); this.onCheckUpdates = this.onCheckUpdates.bind(this);
@@ -96,97 +102,123 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
} }
public render() { public render() {
const [ backupMessageStart, backupMessageEnd ] = const [backupMessageStart, backupMessageEnd] = _(
_("optionsBridgeBackupEnabled", "\0").split("\0"); "optionsBridgeBackupEnabled",
"\0"
).split("\0");
return ( return (
<div className="bridge"> <div className="bridge">
{ this.props.loading {this.props.loading ? (
? ( <div className="bridge__loading"> <div className="bridge__loading">
{ _("optionsBridgeLoading") } {_("optionsBridgeLoading")}
<progress></progress> <progress></progress>
</div> ) </div>
: this.renderStatus() } ) : (
this.renderStatus()
)}
{ !this.props.loading && this.props.options && {!this.props.loading && this.props.options && (
<div className="bridge__options"> <div className="bridge__options">
<label className="option option--inline"> <label className="option option--inline">
<div className="option__control"> <div className="option__control">
<input name="bridgeBackupEnabled" <input
type="checkbox" name="bridgeBackupEnabled"
checked={ this.props.options.bridgeBackupEnabled } type="checkbox"
onChange={ this.props.onChange } /> checked={
this.props.options.bridgeBackupEnabled
}
onChange={this.props.onChange}
/>
</div> </div>
<div className="option__label"> <div className="option__label">
{ backupMessageStart } {backupMessageStart}
<input className="bridge__backup-host" <input
name="bridgeBackupHost" className="bridge__backup-host"
type="text" name="bridgeBackupHost"
required type="text"
value={ this.props.options.bridgeBackupHost } required
onChange={ this.props.onChange } /> value={this.props.options.bridgeBackupHost}
onChange={this.props.onChange}
/>
: :
<input className="bridge__backup-port" <input
name="bridgeBackupPort" className="bridge__backup-port"
type="number" name="bridgeBackupPort"
required type="number"
min="1025" required
max="65535" min="1025"
value={ this.props.options.bridgeBackupPort } max="65535"
onChange={ this.props.onChange } /> value={this.props.options.bridgeBackupPort}
{ backupMessageEnd } onChange={this.props.onChange}
/>
{backupMessageEnd}
</div> </div>
<div className="option__description"> <div className="option__description">
{ _("optionsBridgeBackupEnabledDescription") } {_("optionsBridgeBackupEnabledDescription")}
</div> </div>
</label> </label>
</div> } </div>
)}
{ !this.props.loading && {!this.props.loading && (
<div className="bridge__update-info"> <div className="bridge__update-info">
{ this.state.isUpdateAvailable {this.state.isUpdateAvailable ? (
? ( <div className="bridge__update"> <div className="bridge__update">
<p className="bridge__update-label"> <p className="bridge__update-label">
{ _("optionsBridgeUpdateAvailable") } {_("optionsBridgeUpdateAvailable")}
</p> </p>
<div className="bridge__update-options"> <div className="bridge__update-options">
<button className="bridge__update-start" <button
type="button" className="bridge__update-start"
onClick={ this.onUpdate }>
{ _("optionsBridgeUpdate") }
</button>
</div>
</div> )
: ( <button className="bridge__update-check"
type="button" type="button"
disabled={ this.state.isCheckingUpdates } onClick={this.onUpdate}
onClick={ this.onCheckUpdates }> >
{_("optionsBridgeUpdate")}
{ this.state.isCheckingUpdates </button>
? _("optionsBridgeUpdateChecking" </div>
, getNextEllipsis(this.state.checkUpdatesEllipsis)) </div>
: _("optionsBridgeUpdateCheck") } ) : (
</button> )} <button
className="bridge__update-check"
type="button"
disabled={this.state.isCheckingUpdates}
onClick={this.onCheckUpdates}
>
{this.state.isCheckingUpdates
? _(
"optionsBridgeUpdateChecking",
getNextEllipsis(
this.state.checkUpdatesEllipsis
)
)
: _("optionsBridgeUpdateCheck")}
</button>
)}
<div className="bridge--update-status"> <div className="bridge--update-status">
{ this.state.updateStatus && !this.state.isUpdateAvailable {this.state.updateStatus &&
&& this.state.updateStatus } !this.state.isUpdateAvailable &&
this.state.updateStatus}
</div> </div>
</div> } </div>
)}
</div> </div>
); );
} }
private renderStatus() { private renderStatus() {
const infoClasses = `bridge__info ${!this.props.info const infoClasses = `bridge__info ${
? this.props.loadingTimedOut !this.props.info
? "bridge__info--timed-out" ? this.props.loadingTimedOut
: "bridge__info--not-found" ? "bridge__info--timed-out"
: "bridge__info--found"}`; : "bridge__info--not-found"
: "bridge__info--found"
}`;
let statusIcon: string; let statusIcon: string;
let statusTitle: string; let statusTitle: string;
let statusText: (string | null) = null; let statusText: string | null = null;
if (this.props.loadingTimedOut) { if (this.props.loadingTimedOut) {
statusIcon = "assets/icons8-warn-120.png"; statusIcon = "assets/icons8-warn-120.png";
@@ -208,22 +240,21 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
return ( return (
<div className={infoClasses}> <div className={infoClasses}>
<div className="bridge__status"> <div className="bridge__status">
<img className="bridge__status-icon" <img
width="60" height="60" className="bridge__status-icon"
src={ statusIcon } /> width="60"
height="60"
src={statusIcon}
/>
<h2 className="bridge__status-title"> <h2 className="bridge__status-title">{statusTitle}</h2>
{ statusTitle }
</h2>
{ statusText && {statusText && (
<p className="bridge__status-text"> <p className="bridge__status-text">{statusText}</p>
{ statusText } )}
</p> }
</div> </div>
{ this.props.info && {this.props.info && <BridgeStats info={this.props.info} />}
<BridgeStats info={ this.props.info }/> }
</div> </div>
); );
} }
@@ -236,7 +267,8 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
const timeout = window.setInterval(() => { const timeout = window.setInterval(() => {
this.setState(state => ({ this.setState(state => ({
checkUpdatesEllipsis: getNextEllipsis( checkUpdatesEllipsis: getNextEllipsis(
state.checkUpdatesEllipsis) state.checkUpdatesEllipsis
)
})); }));
}, 500); }, 500);
@@ -249,52 +281,58 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
.catch(this.onCheckUpdatesError); .catch(this.onCheckUpdatesError);
} }
private async onCheckUpdatesResponse(res: any) { private async onCheckUpdatesResponse(res: Release[]) {
if (!Array.isArray(res)) {
throw logger.error("Check update response is not array.", res);
}
let latestBridgeRelease; let latestBridgeRelease;
for (const release of res) { for (const release of res) {
if (release.assets.find((asset: any) => if (
asset.content_type !== "application/x-xpinstall")) { release.assets.find(
asset => asset.content_type !== "application/x-xpinstall"
)
) {
latestBridgeRelease = release; latestBridgeRelease = release;
break; break;
} }
} }
if (!latestBridgeRelease) { if (!latestBridgeRelease) {
this.setState({ throw logger.error(
isCheckingUpdates: false "Check update response does not contain release info."
, wasErrorCheckingUpdates: true );
, updateStatus: _("optionsBridgeUpdateStatusError")
});
return;
} }
const isUpdateAvailable = !this.props.info || semver.lt( /**
this.props.info.version, latestBridgeRelease.tag_name); * Update available if no bridge found or bridge version lower
* than fetched release version.
*/
const isUpdateAvailable =
!this.props.info ||
semver.lt(this.props.info.version, latestBridgeRelease.tag_name);
if (isUpdateAvailable) { if (isUpdateAvailable) {
this.updateData = latestBridgeRelease; this.updateData = latestBridgeRelease;
} } else {
this.setState({
isCheckingUpdates: false
, isUpdateAvailable
});
if (!isUpdateAvailable) {
this.setState({ this.setState({
updateStatus: _("optionsBridgeUpdateStatusNoUpdates") updateStatus: _("optionsBridgeUpdateStatusNoUpdates")
}); });
} }
this.setState({
isCheckingUpdates: false,
isUpdateAvailable
});
this.showUpdateStatus(); this.showUpdateStatus();
} }
private onCheckUpdatesError() { private onCheckUpdatesError() {
this.setState({ this.setState({
isCheckingUpdates: false isCheckingUpdates: false,
, wasErrorCheckingUpdates: true wasErrorCheckingUpdates: true,
, updateStatus: _("optionsBridgeUpdateStatusError") updateStatus: _("optionsBridgeUpdateStatusError")
}); });
this.showUpdateStatus(); this.showUpdateStatus();
@@ -313,7 +351,7 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
private async onUpdate() { private async onUpdate() {
// Open downloads page // Open downloads page
if (this.updateData.html_url) { if (this.updateData?.html_url) {
browser.tabs.create({ browser.tabs.create({
url: this.updateData.html_url url: this.updateData.html_url
}); });

View File

@@ -565,8 +565,6 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
return; return;
} }
const application = status.applications?.[0];
if (this.state.showAlternateAction) { if (this.state.showAlternateAction) {
this.props.onStop(this.props.receiverDevice); this.props.onStop(this.props.receiverDevice);
} else { } else {