port updater to ws, also small refactoring

This commit is contained in:
AAGaming
2023-12-30 21:32:23 -05:00
parent 70104065e2
commit 40c7c1b515
22 changed files with 86 additions and 145 deletions
+7 -31
View File
@@ -2,7 +2,6 @@ from __future__ import annotations
import os
import shutil
from asyncio import sleep
from json.decoder import JSONDecodeError
from logging import getLogger
from os import getcwd, path, remove
from typing import TYPE_CHECKING, List, TypedDict
@@ -10,7 +9,7 @@ if TYPE_CHECKING:
from .main import PluginManager
from .localplatform.localplatform import chmod, service_restart, ON_LINUX, get_keep_systemd_service, get_selinux
from aiohttp import ClientSession, web
from aiohttp import ClientSession
from . import helpers
from .injector import get_gamepadui_tab
@@ -30,14 +29,6 @@ class Updater:
def __init__(self, context: PluginManager) -> None:
self.context = context
self.settings = self.context.settings
# Exposes updater methods to frontend
self.updater_methods = {
"get_branch": self._get_branch,
"get_version": self.get_version,
"do_update": self.do_update,
"do_restart": self.do_restart,
"check_for_updates": self.check_for_updates
}
self.remoteVer: RemoteVer | None = None
self.allRemoteVers: List[RemoteVer] = []
self.localVer = helpers.get_loader_version()
@@ -49,27 +40,12 @@ class Updater:
logger.error("Current branch could not be determined, defaulting to \"Stable\"")
if context:
context.web_app.add_routes([
web.post("/updater/{method_name}", self._handle_server_method_call)
])
context.ws.add_route("updater/get_version_info", self.get_version_info);
context.ws.add_route("updater/check_for_updates", self.check_for_updates);
context.ws.add_route("updater/do_restart", self.do_restart);
context.ws.add_route("updater/do_update", self.do_update);
context.loop.create_task(self.version_reloader())
async def _handle_server_method_call(self, request: web.Request):
method_name = request.match_info["method_name"]
try:
args = await request.json()
except JSONDecodeError:
args = {}
res = {}
try:
r = await self.updater_methods[method_name](**args) # type: ignore
res["result"] = r
res["success"] = True
except Exception as e:
res["result"] = str(e)
res["success"] = False
return web.json_response(res)
def get_branch(self, manager: SettingsManager):
ver = manager.getSetting("branch", -1)
logger.debug("current branch: %i" % ver)
@@ -102,7 +78,7 @@ class Updater:
url = "https://raw.githubusercontent.com/SteamDeckHomebrew/decky-loader/main/dist/plugin_loader-prerelease.service"
return str(url)
async def get_version(self):
async def get_version_info(self):
return {
"current": self.localVer,
"remote": self.remoteVer,
@@ -139,7 +115,7 @@ class Updater:
logger.info("Updated remote version information")
tab = await get_gamepadui_tab()
await tab.evaluate_js(f"window.DeckyPluginLoader.notifyUpdates()", False, True, False)
return await self.get_version()
return await self.get_version_info()
async def version_reloader(self):
await sleep(30)
+1 -1
View File
@@ -16,7 +16,7 @@ from .helpers import get_csrf_token
class MessageType(IntEnum):
ERROR = -1
# Call-reply, Frontend -> Backend
# Call-reply, Frontend -> Backend -> Frontend
CALL = 0
REPLY = 1
# Pub/Sub, Backend -> Frontend
@@ -46,7 +46,7 @@ const MultiplePluginsInstallModal: FC<MultiplePluginsInstallModalProps> = ({
setLoading(true);
await onOK();
setTimeout(() => Navigation.OpenQuickAccessMenu(QuickAccessTab.Decky), 250);
setTimeout(() => window.DeckyPluginLoader.checkPluginUpdates(), 1000);
setTimeout(() => DeckyPluginLoader.checkPluginUpdates(), 1000);
}}
onCancel={async () => {
await onCancel();
@@ -34,7 +34,7 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({
setLoading(true);
await onOK();
setTimeout(() => Navigation.OpenQuickAccessMenu(QuickAccessTab.Decky), 250);
setTimeout(() => window.DeckyPluginLoader.checkPluginUpdates(), 1000);
setTimeout(() => DeckyPluginLoader.checkPluginUpdates(), 1000);
}}
onCancel={async () => {
await onCancel();
@@ -19,7 +19,7 @@ const PluginUninstallModal: FC<PluginUninstallModalProps> = ({ name, title, butt
await uninstallPlugin(name);
// uninstalling a plugin resets the hidden setting for it server-side
// we invalidate here so if you re-install it, you won't have an out-of-date hidden filter
await window.DeckyPluginLoader.hiddenPluginsService.invalidate();
await DeckyPluginLoader.hiddenPluginsService.invalidate();
}}
strTitle={title}
strOKButtonText={buttonText}
@@ -95,7 +95,7 @@ const sortOptions = [
},
];
const getList = window.DeckyBackend.callable<
const getList = DeckyBackend.callable<
[
path: string,
includeFiles?: boolean,
@@ -13,9 +13,7 @@ function rePatch() {
const details = window.appDetailsStore.GetAppDetails(appid);
logger.debug('game details', details);
// strShortcutStartDir
const file = await window.DeckyPluginLoader.openFilePicker(
details?.strShortcutStartDir.replaceAll('"', '') || '/',
);
const file = await DeckyPluginLoader.openFilePicker(details?.strShortcutStartDir.replaceAll('"', '') || '/');
logger.debug('user selected', file);
window.SteamClient.Apps.SetShortcutExe(appid, JSON.stringify(file.path));
const pathArr = file.path.split('/');
@@ -28,20 +28,13 @@ const installFromZip = async () => {
logger.error('The default path has not been found!');
return;
}
window.DeckyPluginLoader.openFilePickerV2(
FileSelectionType.FILE,
path,
true,
true,
undefined,
['zip'],
false,
false,
).then((val) => {
const url = `file://${val.path}`;
console.log(`Installing plugin locally from ${url}`);
installFromURL(url);
});
DeckyPluginLoader.openFilePickerV2(FileSelectionType.FILE, path, true, true, undefined, ['zip'], false, false).then(
(val) => {
const url = `file://${val.path}`;
console.log(`Installing plugin locally from ${url}`);
installFromURL(url);
},
);
};
export default function DeveloperSettings() {
@@ -92,10 +85,7 @@ export default function DeveloperSettings() {
<DialogButton
onClick={async () => {
try {
let tabId = await window.DeckyBackend.call<[name: string], string>(
'utilities/get_tab_id',
'SharedJSContext',
);
let tabId = await DeckyBackend.call<[name: string], string>('utilities/get_tab_id', 'SharedJSContext');
Navigation.NavigateToExternalWeb(
'localhost:8080/devtools/inspector.html?ws=localhost:8080/devtools/page/' + tabId,
);
@@ -3,7 +3,7 @@ import { FunctionComponent } from 'react';
import { useTranslation } from 'react-i18next';
import Logger from '../../../../logger';
import { callUpdaterMethod } from '../../../../updater';
import { checkForUpdates } from '../../../../updater';
import { useSetting } from '../../../../utils/hooks/useSetting';
const logger = new Logger('BranchSelect');
@@ -37,7 +37,7 @@ const BranchSelect: FunctionComponent<{}> = () => {
selectedOption={selectedBranch}
onChange={async (newVal) => {
await setSelectedBranch(newVal.data);
callUpdaterMethod('check_for_updates');
checkForUpdates();
logger.log('switching branches!');
}}
/>
@@ -6,7 +6,7 @@ import { useDeckyState } from '../../../DeckyState';
const NotificationSettings: FC = () => {
const { notificationSettings } = useDeckyState();
const notificationService = window.DeckyPluginLoader.notificationService;
const notificationService = DeckyPluginLoader.notificationService;
const { t } = useTranslation();
@@ -18,8 +18,8 @@ export default function RemoteDebuggingSettings() {
value={allowRemoteDebugging || false}
onChange={(toggleValue) => {
setAllowRemoteDebugging(toggleValue);
if (toggleValue) window.DeckyBackend.call('allow_remote_debugging');
else window.DeckyBackend.call('disallow_remote_debugging');
if (toggleValue) DeckyBackend.call('allow_remote_debugging');
else DeckyBackend.call('disallow_remote_debugging');
}}
/>
</Field>
@@ -15,7 +15,7 @@ import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaExclamation } from 'react-icons/fa';
import { VerInfo, callUpdaterMethod, finishUpdate } from '../../../../updater';
import { VerInfo, checkForUpdates, doRestart, doUpdate } from '../../../../updater';
import { useDeckyState } from '../../../DeckyState';
import InlinePatchNotes from '../../../patchnotes/InlinePatchNotes';
import WithSuspense from '../../../WithSuspense';
@@ -85,7 +85,7 @@ export default function UpdaterSettings() {
finish: async () => {
setUpdateProgress(0);
setReloading(true);
await finishUpdate();
await doRestart();
},
};
}, []);
@@ -122,13 +122,13 @@ export default function UpdaterSettings() {
!versionInfo?.remote || versionInfo?.remote?.tag_name == versionInfo?.current
? async () => {
setCheckingForUpdates(true);
const res = (await callUpdaterMethod('check_for_updates')) as { result: VerInfo };
setVersionInfo(res.result);
const verInfo = await checkForUpdates();
setVersionInfo(verInfo);
setCheckingForUpdates(false);
}
: async () => {
setUpdateProgress(0);
callUpdaterMethod('do_update');
doUpdate();
}
}
>
@@ -35,7 +35,7 @@ async function reinstallPlugin(pluginName: string, currentVersion?: string) {
type PluginTableData = PluginData & { name: string; hidden: boolean; onHide(): void; onShow(): void };
const reloadPluginBackend = window.DeckyBackend.callable<[pluginName: string], void>('loader/reload_plugin');
const reloadPluginBackend = DeckyBackend.callable<[pluginName: string], void>('loader/reload_plugin');
function PluginInteractables(props: { entry: ReorderableEntry<PluginTableData> }) {
const { t } = useTranslation();
@@ -58,14 +58,14 @@ function PluginInteractables(props: { entry: ReorderableEntry<PluginTableData> }
console.error('Error Reloading Plugin Backend', err);
}
window.DeckyPluginLoader.importPlugin(name, version);
DeckyPluginLoader.importPlugin(name, version);
}}
>
{t('PluginListIndex.reload')}
</MenuItem>
<MenuItem
onSelected={() =>
window.DeckyPluginLoader.uninstallPlugin(
DeckyPluginLoader.uninstallPlugin(
name,
t('PluginLoader.plugin_uninstall.title', { name }),
t('PluginLoader.plugin_uninstall.button'),
@@ -143,11 +143,11 @@ export default function PluginList() {
const { t } = useTranslation();
useEffect(() => {
window.DeckyPluginLoader.checkPluginUpdates();
DeckyPluginLoader.checkPluginUpdates();
}, []);
const [pluginEntries, setPluginEntries] = useState<ReorderableEntry<PluginTableData>[]>([]);
const hiddenPluginsService = window.DeckyPluginLoader.hiddenPluginsService;
const hiddenPluginsService = DeckyPluginLoader.hiddenPluginsService;
useEffect(() => {
setPluginEntries(
+2 -2
View File
@@ -38,7 +38,7 @@ export async function setShowValveInternal(show: boolean) {
}
export async function setShouldConnectToReactDevTools(enable: boolean) {
window.DeckyPluginLoader.toaster.toast({
DeckyPluginLoader.toaster.toast({
title: enable ? (
<TranslationHelper trans_class={TranslationClass.DEVELOPER} trans_text={'enabling'} />
) : (
@@ -48,7 +48,7 @@ export async function setShouldConnectToReactDevTools(enable: boolean) {
icon: <FaReact />,
});
await sleep(5000);
return enable ? window.DeckyBackend.call('utilities/enable_rdt') : window.DeckyBackend.call('utilities/disable_rdt');
return enable ? DeckyBackend.call('utilities/enable_rdt') : DeckyBackend.call('utilities/disable_rdt');
}
export async function startup() {
+1 -1
View File
@@ -6,7 +6,7 @@
window.deckyAuthToken = await fetch('http://127.0.0.1:1337/auth/token').then((r) => r.text());
console.debug('Connecting to Decky backend...');
window.DeckyBackend = new (await import('./wsrouter')).WSRouter();
await window.DeckyBackend.connect();
await DeckyBackend.connect();
console.debug('Starting Decky!');
await import('./start');
})();
+18 -22
View File
@@ -28,10 +28,10 @@ import { NotificationService } from './notification-service';
import { InstallType, Plugin } from './plugin';
import RouterHook from './router-hook';
import { deinitSteamFixes, initSteamFixes } from './steamfixes';
import { checkForUpdates } from './store';
import { checkForPluginUpdates } from './store';
import TabsHook from './tabs-hook';
import Toaster from './toaster';
import { VerInfo, callUpdaterMethod } from './updater';
import { getVersionInfo } from './updater';
import { getSetting, setSetting } from './utils/settings';
import TranslationHelper, { TranslationClass } from './utils/TranslationHelper';
@@ -106,9 +106,7 @@ class PluginLoader extends Logger {
.then(() => this.log('Initialized'));
}
private getPluginsFromBackend = window.DeckyBackend.callable<[], { name: string; version: string }[]>(
'loader/get_plugins',
);
private getPluginsFromBackend = DeckyBackend.callable<[], { name: string; version: string }[]>('loader/get_plugins');
private async loadPlugins() {
// wait for SP window to exist before loading plugins
@@ -129,13 +127,13 @@ class PluginLoader extends Logger {
}
public async getUserInfo() {
const userInfo = await window.DeckyBackend.call<[], UserInfo>('utilities/get_user_info');
const userInfo = await DeckyBackend.call<[], UserInfo>('utilities/get_user_info');
setSetting('user_info.user_name', userInfo.username);
setSetting('user_info.user_home', userInfo.path);
}
public async updateVersion() {
const versionInfo = (await callUpdaterMethod('get_version')).result as VerInfo;
const versionInfo = await getVersionInfo();
this.deckyState.setVersionInfo(versionInfo);
return versionInfo;
@@ -164,7 +162,7 @@ class PluginLoader extends Logger {
}
public async checkPluginUpdates() {
const updates = await checkForUpdates(this.plugins);
const updates = await checkForPluginUpdates(this.plugins);
this.deckyState.setUpdates(updates);
return updates;
}
@@ -199,8 +197,8 @@ class PluginLoader extends Logger {
version={version}
hash={hash}
installType={install_type}
onOK={() => window.DeckyBackend.call<[string]>('utilities/confirm_plugin_install', request_id)}
onCancel={() => window.DeckyBackend.call<[string]>('utilities/cancel_plugin_install', request_id)}
onOK={() => DeckyBackend.call<[string]>('utilities/confirm_plugin_install', request_id)}
onCancel={() => DeckyBackend.call<[string]>('utilities/cancel_plugin_install', request_id)}
/>,
);
}
@@ -212,8 +210,8 @@ class PluginLoader extends Logger {
showModal(
<MultiplePluginsInstallModal
requests={requests}
onOK={() => window.DeckyBackend.call<[string]>('utilities/confirm_plugin_install', request_id)}
onCancel={() => window.DeckyBackend.call<[string]>('utilities/cancel_plugin_install', request_id)}
onOK={() => DeckyBackend.call<[string]>('utilities/confirm_plugin_install', request_id)}
onCancel={() => DeckyBackend.call<[string]>('utilities/cancel_plugin_install', request_id)}
/>,
);
}
@@ -298,7 +296,7 @@ class PluginLoader extends Logger {
let res = await fetch(`http://127.0.0.1:1337/plugins/${name}/frontend_bundle`, {
credentials: 'include',
headers: {
Authentication: window.deckyAuthToken,
Authentication: deckyAuthToken,
},
});
@@ -364,7 +362,7 @@ class PluginLoader extends Logger {
this.warn(
`Calling ${methodName} via callServerMethod, which is deprecated and will be removed in a future release. Please switch to the backend API.`,
);
return await window.DeckyBackend.call<[methodName: string, kwargs: any], any>(
return await DeckyBackend.call<[methodName: string, kwargs: any], any>(
'utilities/_call_legacy_utility',
methodName,
args,
@@ -429,7 +427,7 @@ class PluginLoader extends Logger {
const pluginAPI = {
backend: {
call<Args extends any[] = any[], Return = void>(method: string, ...args: Args): Promise<Return> {
return window.DeckyBackend.call<[pluginName: string, method: string, ...args: Args], Return>(
return DeckyBackend.call<[pluginName: string, method: string, ...args: Args], Return>(
'loader/call_plugin_method',
pluginName,
method,
@@ -448,7 +446,7 @@ class PluginLoader extends Logger {
openFilePickerV2: this.openFilePickerV2,
// Legacy
async callPluginMethod(methodName: string, args = {}) {
return window.DeckyBackend.call<[pluginName: string, methodName: string, kwargs: any], any>(
return DeckyBackend.call<[pluginName: string, methodName: string, kwargs: any], any>(
'loader/call_legacy_plugin_method',
pluginName,
methodName,
@@ -472,7 +470,7 @@ class PluginLoader extends Logger {
}
// this is terrible but a. we're going to redo this entire method anyway and b. it was already terrible
try {
const ret = await window.DeckyBackend.call<
const ret = await DeckyBackend.call<
[method: string, url: string, extra_opts?: any],
{ status: number; headers: { [key: string]: string }; body: string }
>('utilities/http_request', method, url, req);
@@ -481,14 +479,12 @@ class PluginLoader extends Logger {
return { success: false, result: e?.toString() };
}
},
executeInTab: window.DeckyBackend.callable<
executeInTab: DeckyBackend.callable<
[tab: String, runAsync: Boolean, code: string],
{ success: boolean; result: any }
>('utilities/execute_in_tab'),
injectCssIntoTab: window.DeckyBackend.callable<[tab: string, style: string], string>(
'utilities/inject_css_into_tab',
),
removeCssFromTab: window.DeckyBackend.callable<[tab: string, cssId: string]>('utilities/remove_css_from_tab'),
injectCssIntoTab: DeckyBackend.callable<[tab: string, style: string], string>('utilities/inject_css_into_tab'),
removeCssFromTab: DeckyBackend.callable<[tab: string, cssId: string]>('utilities/remove_css_from_tab'),
};
return pluginAPI;
+3 -3
View File
@@ -22,7 +22,7 @@ type installPluginArgs = [
installType?: InstallType,
];
export let installPlugin = window.DeckyBackend.callable<installPluginArgs>('utilities/install_plugin');
export let installPlugin = DeckyBackend.callable<installPluginArgs>('utilities/install_plugin');
type installPluginsArgs = [
requests: {
@@ -34,6 +34,6 @@ type installPluginsArgs = [
}[],
];
export let installPlugins = window.DeckyBackend.callable<installPluginsArgs>('utilities/install_plugins');
export let installPlugins = DeckyBackend.callable<installPluginsArgs>('utilities/install_plugins');
export let uninstallPlugin = window.DeckyBackend.callable<[name: string]>('utilities/uninstall_plugin');
export let uninstallPlugin = DeckyBackend.callable<[name: string]>('utilities/uninstall_plugin');
+12 -14
View File
@@ -6,15 +6,13 @@ import PluginLoader from './plugin-loader';
import { DeckyUpdater } from './updater';
declare global {
interface Window {
DeckyPluginLoader: PluginLoader;
DeckyUpdater?: DeckyUpdater;
importDeckyPlugin: Function;
deckyHasLoaded: boolean;
deckyHasConnectedRDT?: boolean;
deckyAuthToken: string;
DFL?: any;
}
export var DeckyPluginLoader: PluginLoader;
export var DeckyUpdater: DeckyUpdater | undefined; // TODO get rid of this
export var importDeckyPlugin: Function;
export var deckyHasLoaded: boolean;
export var deckyHasConnectedRDT: boolean | undefined;
export var deckyAuthToken: string;
export var DFL: any | undefined;
}
(async () => {
@@ -37,7 +35,7 @@ declare global {
backend: {
loadPath: 'http://127.0.0.1:1337/locales/{{lng}}.json',
customHeaders: {
Authentication: window.deckyAuthToken,
Authentication: deckyAuthToken,
},
requestOptions: {
credentials: 'include',
@@ -45,12 +43,12 @@ declare global {
},
});
window.DeckyPluginLoader?.dismountAll();
window.DeckyPluginLoader?.deinit();
window?.DeckyPluginLoader?.dismountAll();
window?.DeckyPluginLoader?.deinit();
window.DeckyPluginLoader = new PluginLoader();
window.DeckyPluginLoader.init();
DeckyPluginLoader.init();
window.importDeckyPlugin = function (name: string, version: string) {
window.DeckyPluginLoader?.importPlugin(name, version);
DeckyPluginLoader?.importPlugin(name, version);
};
})();
+2 -2
View File
@@ -37,7 +37,7 @@ export async function getStore(): Promise<Store> {
}
export async function getPluginList(): Promise<StorePlugin[]> {
let version = await window.DeckyPluginLoader.updateVersion();
let version = await DeckyPluginLoader.updateVersion();
let store = await getSetting<Store | null>('store', null);
let customURL = await getSetting<string>('store-url', 'https://plugins.deckbrew.xyz/plugins');
let storeURL;
@@ -112,7 +112,7 @@ export async function requestMultiplePluginInstalls(requests: PluginInstallReque
);
}
export async function checkForUpdates(plugins: Plugin[]): Promise<PluginUpdateMapping> {
export async function checkForPluginUpdates(plugins: Plugin[]): Promise<PluginUpdateMapping> {
const serverData = await getPluginList();
const updateMap = new Map<string, StorePluginVersion>();
for (let plugin of plugins) {
+4 -17
View File
@@ -28,20 +28,7 @@ export interface VerInfo {
updatable: boolean;
}
export async function callUpdaterMethod(methodName: string, args = {}) {
const response = await fetch(`http://127.0.0.1:1337/updater/${methodName}`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Authentication: window.deckyAuthToken,
},
body: JSON.stringify(args),
});
return response.json();
}
export async function finishUpdate() {
callUpdaterMethod('do_restart');
}
export const doUpdate = DeckyBackend.callable('updater/do_update');
export const doRestart = DeckyBackend.callable('updater/do_restart');
export const getVersionInfo = DeckyBackend.callable<[], VerInfo>('updater/get_version_info');
export const checkForUpdates = DeckyBackend.callable<[], VerInfo>('updater/check_for_updates');
+2 -2
View File
@@ -1,8 +1,8 @@
export async function getSetting<T>(key: string, def: T): Promise<T> {
const res = await window.DeckyBackend.call<[string, T], T>('utilities/settings/get', key, def);
const res = await DeckyBackend.call<[string, T], T>('utilities/settings/get', key, def);
return res;
}
export async function setSetting<T>(key: string, value: T): Promise<void> {
await window.DeckyBackend.call<[string, T], void>('utilities/settings/set', key, value);
await DeckyBackend.call<[string, T], void>('utilities/settings/set', key, value);
}
+6 -10
View File
@@ -3,14 +3,12 @@ import { sleep } from 'decky-frontend-lib';
import Logger from './logger';
declare global {
interface Window {
DeckyBackend: WSRouter;
}
export var DeckyBackend: WSRouter;
}
enum MessageType {
ERROR = -1,
// Call-reply, Frontend -> Backend
// Call-reply, Frontend -> Backend -> Frontend
CALL = 0,
REPLY = 1,
// Pub/Sub, Backend -> Frontend
@@ -22,8 +20,6 @@ interface CallMessage {
args: any[];
route: string;
id: number;
// TODO implement this
// skipResponse?: boolean;
}
interface ReplyMessage {
@@ -61,7 +57,7 @@ export class WSRouter extends Logger {
connect() {
return (this.connectPromise = new Promise<void>((resolve) => {
// Auth is a query param as JS WebSocket doesn't support headers
this.ws = new WebSocket(`ws://127.0.0.1:1337/ws?auth=${window.deckyAuthToken}`);
this.ws = new WebSocket(`ws://127.0.0.1:1337/ws?auth=${deckyAuthToken}`);
this.ws.addEventListener('open', () => {
this.debug('WS Connected');
@@ -129,7 +125,7 @@ export class WSRouter extends Logger {
if (this.runningCalls.has(data.id)) {
this.runningCalls.get(data.id)!.reject(data.error);
this.runningCalls.delete(data.id);
this.debug(`Errored PY call ${data.id} with error`, data.error);
this.debug(`Rejected PY call ${data.id} with error`, data.error);
}
break;
@@ -143,7 +139,7 @@ export class WSRouter extends Logger {
}
// this.call<[number, number], string>('methodName', 1, 2);
call<Args extends any[] = any[], Return = void>(route: string, ...args: Args): Promise<Return> {
call<Args extends any[] = [], Return = void>(route: string, ...args: Args): Promise<Return> {
const resolver = this.createPromiseResolver<Return>();
const id = ++this.reqId;
@@ -157,7 +153,7 @@ export class WSRouter extends Logger {
return resolver.promise;
}
callable<Args extends any[] = any[], Return = void>(route: string): (...args: Args) => Promise<Return> {
callable<Args extends any[] = [], Return = void>(route: string): (...args: Args) => Promise<Return> {
return (...args) => this.call<Args, Return>(route, ...args);
}