Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate substrate connect with Apps #5644

Merged
merged 67 commits into from Jul 12, 2021
Merged
Show file tree
Hide file tree
Changes from 66 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
7c6cc5e
integrate apps with smoldot init
wirednkod May 31, 2021
1a47f44
Add a new category for substrate connect light clients to be grouped …
wirednkod Jun 1, 2021
b418363
remove supportedLightClient array that was introduced. SubstrateConne…
wirednkod Jun 1, 2021
0e327a0
rollback 'hacky' changes in order to proceed with better implementation
wirednkod Jun 1, 2021
9ec926b
alter configuration of testing and production networks in order to in…
wirednkod Jun 3, 2021
322003b
upgrade substrate connect to 0.3.7
wirednkod Jun 10, 2021
92c5cbc
implementation for integrating substrate-connect
wirednkod Jun 19, 2021
2f3c482
merge from latest master
wirednkod Jun 19, 2021
dff740e
merge from latest master
wirednkod Jun 19, 2021
223590c
Add substrate/connect version to 0.3.10
wirednkod Jun 21, 2021
eb266c7
fix merge conflicts
wirednkod Jun 21, 2021
c01dec8
introduce function for correctly creating the provider URLs and updat…
wirednkod Jun 21, 2021
4fad0d4
Fix mistaken typos
wirednkod Jun 21, 2021
dad6da0
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jun 21, 2021
5d557ea
fix linter issue about rpc-provider@npm:^4.10.1 missing
wirednkod Jun 21, 2021
c10afae
move substrate-connect for react-api to dependencies
wirednkod Jun 21, 2021
2b54a74
Fix lint issues
wirednkod Jun 21, 2021
06a6796
Add tests for substrate-connect light clients urls and fix for ws
wirednkod Jun 21, 2021
e0770b7
Update packages/react-api/src/Api.tsx
wirednkod Jun 21, 2021
5a655f3
Remove duplicated code
wirednkod Jun 21, 2021
db9a98d
merge
wirednkod Jun 21, 2021
1183693
add exceptions in jest config (transformIgnorePatterns)
wirednkod Jun 21, 2021
92e27ed
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jun 21, 2021
c5dbb6e
minor fixes
wirednkod Jun 21, 2021
7aa6d18
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jun 22, 2021
dbe27b7
fix typo errors
wirednkod Jun 22, 2021
75961b9
minor yarn update
wirednkod Jun 22, 2021
07190d6
Alter urls to string | union type (revert urls for json-rpc)
wirednkod Jun 22, 2021
1e14155
fix erroneously changed url
wirednkod Jun 22, 2021
55d4d5a
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jun 22, 2021
ab1e746
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jun 25, 2021
7efc68a
merge conflicts
wirednkod Jun 25, 2021
64397ea
merge conflicts
wirednkod Jun 25, 2021
95f2581
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jun 25, 2021
9dba797
fix yarn.lock
wirednkod Jun 25, 2021
cc97ed0
Stub mock for tests of substrate connect
wirednkod Jun 26, 2021
3927bf1
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jun 26, 2021
1b99b51
fix yarn
wirednkod Jun 26, 2021
cdc9e7e
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jun 27, 2021
bcac17c
Update yarn.lock
wirednkod Jun 27, 2021
526f18f
Update yarn.lock
wirednkod Jun 27, 2021
6781d41
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jun 30, 2021
490879f
Update packages/apps/src/Endpoints/index.tsx
wirednkod Jun 30, 2021
eb513c1
Update packages/apps-config/src/endpoints/util.ts
wirednkod Jun 30, 2021
d142836
Fix Pr comments
wirednkod Jun 30, 2021
e8b5d4c
Fix conflicts
wirednkod Jun 30, 2021
0aad0d4
Fix function that was recreated in every rerender
wirednkod Jun 30, 2021
b5f2d2e
Update packages/apps/src/Endpoints/index.tsx
wirednkod Jun 30, 2021
8a1a7e2
Fix 2nd bundle of PR comments
wirednkod Jun 30, 2021
c654d81
Fix function on PR comment
wirednkod Jun 30, 2021
2013dcf
filter light clients out of auto-selected sidebar networks
wirednkod Jun 30, 2021
06debb4
Update package.json
wirednkod Jul 1, 2021
ff624cc
Update packages/react-api/package.json
wirednkod Jul 1, 2021
fa5825a
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jul 1, 2021
accf612
run install
wirednkod Jul 1, 2021
e9eb6ee
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jul 2, 2021
8b67a13
update yarn.lock from master
wirednkod Jul 2, 2021
f51d0e7
update yarn.lock from master
wirednkod Jul 2, 2021
a4b3900
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jul 2, 2021
6b01a99
sync yarn.lock from master
wirednkod Jul 3, 2021
6b87d0f
Merge branch 'nik-integrate-w-substrate-connect' of github.com:wiredn…
wirednkod Jul 3, 2021
286ae08
sync yarn.lock from master
wirednkod Jul 3, 2021
e8fa23d
Alter implementation to avoid white screen and make sure that API ret…
wirednkod Jul 9, 2021
0aab8f9
Merge branch 'master' into nik-integrate-w-substrate-connect
wirednkod Jul 9, 2021
f6ad764
Merge branch 'master' into nik-integrate-w-substrate-connect
jacogr Jul 12, 2021
b0d8aee
Update packages/page-accounts/src/CreateAccount.slow.spec.tsx
jacogr Jul 12, 2021
b3484d1
Update packages/page-bounties/src/Bounties.slow.spec.tsx
jacogr Jul 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion jest.config.cjs
Expand Up @@ -22,8 +22,9 @@ module.exports = {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'empty/object',
'\\.(md)$': '<rootDir>/jest/mocks/empty.js'
},
modulePathIgnorePatterns: ['<rootDir>/packages/apps-config/build'],
setupFilesAfterEnv: ['<rootDir>/jest/jest-setup.ts'],
testEnvironment: 'jsdom',
testTimeout: 90000,
transformIgnorePatterns: ['/node_modules/(?!@polkadot|@babel/runtime/helpers/esm/)']
transformIgnorePatterns: ['/node_modules/(?!@polkadot|@babel/runtime/helpers/esm/|@substrate|smoldot)']
};
28 changes: 26 additions & 2 deletions packages/apps-config/src/endpoints/index.spec.ts
Expand Up @@ -10,14 +10,20 @@ interface Endpoint {
ws: string;
}

interface LightClientEndpoint {
name: string;
param: string;
}

const allEndpoints = createWsEndpoints((k: string, v?: string) => v || k);

describe('urls are all valid', (): void => {
describe('WS urls are all valid', (): void => {
allEndpoints
.filter(({ value }) =>
value &&
isString(value) &&
!value.includes('127.0.0.1')
!value.includes('127.0.0.1') &&
!value.includes('substrate-connect')
)
.map(({ text, value }): Endpoint => ({
name: text as string,
Expand All @@ -30,6 +36,24 @@ describe('urls are all valid', (): void => {
);
});

describe('light client urls are all valid', (): void => {
allEndpoints
.filter(({ value }) =>
value &&
isString(value) &&
value.includes('substrate-connect')
)
.map(({ text, value }): LightClientEndpoint => ({
name: text as string,
param: value
}))
.forEach(({ name, param }) =>
it(`${name} @ ${param}`, (): void => {
assert(param.substr(param.indexOf('-')) === '-substrate-connect', `${name} @ ${param} should end with '-substrate-connect'`);
})
);
});

describe('urls are sorted', (): void => {
let hasDevelopment = false;
let lastHeader = '';
Expand Down
4 changes: 3 additions & 1 deletion packages/apps-config/src/endpoints/productionRelayKusama.ts
Expand Up @@ -5,6 +5,7 @@ import type { TFunction } from 'i18next';
import type { EndpointOption } from './types';

import { KUSAMA_GENESIS } from '../api/constants';
import { createProviderUrl } from './util';

/* eslint-disable sort-keys */

Expand All @@ -22,7 +23,8 @@ export function createKusama (t: TFunction): EndpointOption {
providers: {
Parity: 'wss://kusama-rpc.polkadot.io',
OnFinality: 'wss://kusama.api.onfinality.io/public-ws',
'Patract Elara': 'wss://kusama.elara.patract.io'
'Patract Elara': 'wss://kusama.elara.patract.io',
'light client': createProviderUrl('kusama-substrate-connect', 'substrate-connect')
jacogr marked this conversation as resolved.
Show resolved Hide resolved
wirednkod marked this conversation as resolved.
Show resolved Hide resolved
// Pinknode: 'wss://rpc.pinknode.io/kusama/explorer' // https://github.com/polkadot-js/apps/issues/5721
},
teleport: [1000],
Expand Down
Expand Up @@ -5,6 +5,7 @@ import type { TFunction } from 'i18next';
import type { EndpointOption } from './types';

import { POLKADOT_GENESIS } from '../api/constants';
import { createProviderUrl } from './util';

/* eslint-disable sort-keys */

Expand All @@ -22,7 +23,8 @@ export function createPolkadot (t: TFunction): EndpointOption {
providers: {
Parity: 'wss://rpc.polkadot.io',
OnFinality: 'wss://polkadot.api.onfinality.io/public-ws',
'Patract Elara': 'wss://polkadot.elara.patract.io'
'Patract Elara': 'wss://polkadot.elara.patract.io',
'light client': createProviderUrl('polkadot-substrate-connect', 'substrate-connect')
// Pinknode: 'wss://rpc.pinknode.io/polkadot/explorer' // https://github.com/polkadot-js/apps/issues/5721
},
linked: [
Expand Down
4 changes: 3 additions & 1 deletion packages/apps-config/src/endpoints/testingRelayWestend.ts
Expand Up @@ -5,6 +5,7 @@ import type { TFunction } from 'i18next';
import type { EndpointOption } from './types';

import { WESTEND_GENESIS } from '../api/constants';
import { createProviderUrl } from './util';

/* eslint-disable sort-keys */

Expand All @@ -24,7 +25,8 @@ export function createWestend (t: TFunction): EndpointOption {
providers: {
Parity: 'wss://westend-rpc.polkadot.io',
'Patract Elara': 'wss://westend.elara.patract.io',
OnFinality: 'wss://westend.api.onfinality.io/public-ws'
OnFinality: 'wss://westend.api.onfinality.io/public-ws',
'light client': createProviderUrl('westend-substrate-connect', 'substrate-connect')
// 'NodeFactory(Vedran)': 'wss://westend.vedran.nodefactory.io/ws', // https://github.com/polkadot-js/apps/issues/5580
// Pinknode: 'wss://rpc.pinknode.io/westend/explorer' // https://github.com/polkadot-js/apps/issues/5721
},
Expand Down
5 changes: 4 additions & 1 deletion packages/apps-config/src/endpoints/types.ts
Expand Up @@ -3,6 +3,8 @@

import type { Option } from '../settings/types';

import { Endpoint } from '@polkadot/ui-settings/types';

export interface EndpointOption {
dnslink?: string;
genesisHash?: string;
Expand All @@ -14,7 +16,7 @@ export interface EndpointOption {
linked?: EndpointOption[];
info?: string;
paraId?: number;
providers: Record<string, string>;
providers: Record<string, string | Endpoint>;
summary?: string;
teleport?: number[];
text: React.ReactNode;
Expand All @@ -27,6 +29,7 @@ export interface LinkOption extends Option {
homepage?: string;
isChild?: boolean;
isDevelopment?: boolean;
isLightClient?: boolean;
isRelay?: boolean;
isUnreachable?: boolean;
isSpaced?: boolean;
Expand Down
14 changes: 12 additions & 2 deletions packages/apps-config/src/endpoints/util.ts
Expand Up @@ -4,6 +4,9 @@
import type { TFunction } from 'i18next';
import type { EndpointOption, LinkOption } from './types';

import { Endpoint, EndpointType } from '@polkadot/ui-settings/types';
import { isString } from '@polkadot/util';

interface SortOption {
isUnreachable?: boolean;
}
Expand Down Expand Up @@ -52,9 +55,12 @@ export function expandEndpoint (t: TFunction, { dnslink, genesisHash, homepage,
.map(([host, value], index): LinkOption => ({
...base,
dnslink: index === 0 ? dnslink : undefined,
isLightClient: isString(value) ? false : value.type === 'substrate-connect',
isRelay: false,
textBy: t('rpc.hosted.by', 'via {{host}}', { ns: 'apps-config', replace: { host } }),
value
textBy: isString(value)
? t('rpc.hosted.by', 'hosted by {{host}}', { ns: 'apps-config', replace: { host } })
: t('lightclient.experimental', 'light client (experimental)', { ns: 'apps-config' }),
value: isString(value) ? value : value.param
}));

if (linked) {
Expand All @@ -78,3 +84,7 @@ export function expandEndpoint (t: TFunction, { dnslink, genesisHash, homepage,
export function expandEndpoints (t: TFunction, input: EndpointOption[], firstOnly?: boolean): LinkOption[] {
return input.sort(sortLinks).reduce((result: LinkOption[], input) => result.concat(expandEndpoint(t, input, firstOnly)), []);
}

export function createProviderUrl (param: string, type: EndpointType): Endpoint {
return { param, type };
}
13 changes: 13 additions & 0 deletions packages/apps-config/src/settings/types.ts
Expand Up @@ -8,3 +8,16 @@ export interface Option {
text: React.ReactNode;
value: string | number;
}

export interface LinkOption extends Option {
dnslink?: string;
genesisHash?: string;
genesisHashRelay?: string;
isChild?: boolean;
isDevelopment?: boolean;
isSpaced?: boolean;
isLightClient?: boolean;
linked?: LinkOption[];
paraId?: number;
textBy: string;
}
1 change: 1 addition & 0 deletions packages/apps/public/locales/en/apps-config.json
@@ -1,4 +1,5 @@
{
"lightclient.experimental": "light client (experimental)",
"lng.detect": "Default browser language (auto-detect)",
"rpc.KlugDossier": "Klug Dossier",
"rpc.dev.custom": "Custom environment",
Expand Down
6 changes: 5 additions & 1 deletion packages/apps/src/Endpoints/Network.tsx
Expand Up @@ -25,7 +25,11 @@ function NetworkDisplay ({ apiUrl, className = '', setApiUrl, value: { icon, isC
);

const _selectUrl = useCallback(
() => setApiUrl(name, providers[Math.floor(Math.random() * providers.length)].url),
() => {
const filteredProviders = providers.filter((p) => !p.name.startsWith('light client'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not complaining, but we should pass through the isLightClient thing here. Can be done later... but probably jut need a comment to make sure we pass that through the chain somehow.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just realised that isLightningClient is placed in props in wrong place. That willl take some fixing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not complaining, but we should pass through the isLightClient thing here. Can be done later... but probably jut need a comment to make sure we pass that through the chain somehow.

You want to add it in this PR or open an issue for later on?

Copy link
Member

@jacogr jacogr Jul 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can just open an issue. I don't want to rework all this again here, it will be cleaner in a new PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened this issue.

Is there anything else pending for merging this?


return setApiUrl(name, filteredProviders[Math.floor(Math.random() * filteredProviders.length)].url);
},
[name, providers, setApiUrl]
);

Expand Down
36 changes: 30 additions & 6 deletions packages/apps/src/Endpoints/index.tsx
Expand Up @@ -14,6 +14,7 @@ import styled from 'styled-components';
import { createWsEndpoints, CUSTOM_ENDPOINT_KEY } from '@polkadot/apps-config';
import { Button, Input, Sidebar } from '@polkadot/react-components';
import { settings } from '@polkadot/ui-settings';
import { Endpoint, EndpointType } from '@polkadot/ui-settings/types';
import { isAscii } from '@polkadot/util';

import { useTranslation } from '../translate';
Expand Down Expand Up @@ -43,13 +44,21 @@ function isValidUrl (url: string): boolean {
);
}

function getApiType (param: string): Endpoint {
if (param.includes('-substrate-connect')) {
return { param, type: 'substrate-connect' };
}

return { param, type: 'json-rpc' };
}

function combineEndpoints (endpoints: LinkOption[]): Group[] {
return endpoints.reduce((result: Group[], e): Group[] => {
if (e.isHeader) {
result.push({ header: e.text, isDevelopment: e.isDevelopment, isSpaced: e.isSpaced, networks: [] });
} else {
const prev = result[result.length - 1];
const prov = { name: e.textBy, url: e.value };
const prov = { isLightClient: e.isLightClient, name: e.textBy, url: e.value };

if (prev.networks[prev.networks.length - 1] && e.text === prev.networks[prev.networks.length - 1].name) {
prev.networks[prev.networks.length - 1].providers.push(prov);
Expand Down Expand Up @@ -118,6 +127,18 @@ function loadAffinities (groups: Group[]): Record<string, string> {
}), {});
}

function isSwitchDisabled (hasUrlChanged: boolean, apiType: EndpointType, isUrlValid: boolean): boolean {
if (!hasUrlChanged) {
return true;
} else if (apiType === 'substrate-connect') {
return false;
} else if (isUrlValid) {
return false;
}

return true;
}

function Endpoints ({ className = '', offset, onClose }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const linkOptions = createWsEndpoints(t);
Expand Down Expand Up @@ -216,21 +237,24 @@ function Endpoints ({ className = '', offset, onClose }: Props): React.ReactElem
const _onApply = useCallback(
(): void => {
settings.set({ ...(settings.get()), apiUrl });

window.location.assign(`${window.location.origin}${window.location.pathname}?rpc=${encodeURIComponent(apiUrl)}${window.location.hash}`);
window.location.assign(`${window.location.origin}${window.location.pathname}${getApiType(apiUrl).type === 'substrate-connect' ? '?sc=' : '?rpc='}${encodeURIComponent(apiUrl)}${window.location.hash}`);
// window.location.reload();

onClose();
},
[apiUrl, onClose]
);

const canSwitch = useMemo(
() => isSwitchDisabled(hasUrlChanged, getApiType(apiUrl).type, isUrlValid),
[hasUrlChanged, apiUrl, isUrlValid]
);

return (
<Sidebar
button={
<Button
icon='sync'
isDisabled={!(hasUrlChanged && isUrlValid)}
isDisabled={canSwitch}
label={t<string>('Switch')}
onClick={_onApply}
/>
Expand All @@ -251,7 +275,7 @@ function Endpoints ({ className = '', offset, onClose }: Props): React.ReactElem
setGroup={_changeGroup}
value={group}
>
{group.isDevelopment && (
{group.isDevelopment && getApiType(apiUrl).type === 'json-rpc' && (
<div className='endpointCustomWrapper'>
<Input
className='endpointCustom'
Expand Down
1 change: 1 addition & 0 deletions packages/apps/src/Endpoints/types.ts
Expand Up @@ -6,6 +6,7 @@ import React from 'react';
export interface Network {
icon?: string;
isChild?: boolean;
isLightClient?: boolean;
isUnreachable?: boolean;
name: string;
providers: {
Expand Down
2 changes: 1 addition & 1 deletion packages/apps/src/Root.tsx
Expand Up @@ -44,8 +44,8 @@ function Root ({ store }: Props): React.ReactElement<Props> {
<ThemeProvider theme={theme}>
<Queue>
<Api
apiType={settings.apiType}
store={store}
url={settings.apiUrl}
>
<BlockAuthors>
<Events>
Expand Down
38 changes: 28 additions & 10 deletions packages/apps/src/initSettings.ts
Expand Up @@ -7,9 +7,18 @@ import store from 'store';
import { createWsEndpoints } from '@polkadot/apps-config';
import { extractIpfsDetails } from '@polkadot/react-hooks/useIpfs';
import { settings } from '@polkadot/ui-settings';
import { Endpoint } from '@polkadot/ui-settings/types';
import { assert } from '@polkadot/util';

function getApiUrl (): string {
function networkOrUrl (apiType: Endpoint): void {
if (apiType.type === 'json-rpc') {
console.log('WS endpoint=', apiType.param);
} else if (apiType.type === 'substrate-connect') {
console.log('Chain of light client is =', apiType.param);
}
}

function getApiType (): Endpoint {
// we split here so that both these forms are allowed
// - http://localhost:3000/?rpc=wss://substrate-rpc.parity.io/#/explorer
// - http://localhost:3000/#/explorer?rpc=wss://substrate-rpc.parity.io
Expand All @@ -24,7 +33,15 @@ function getApiUrl (): string {

assert(url.startsWith('ws://') || url.startsWith('wss://'), 'Non-prefixed ws/wss url');

return url;
return { param: url, type: 'json-rpc' };
} else if (urlOptions.sc) {
assert(!Array.isArray(urlOptions.sc), 'Invalid network specified');

// https://polkadot.js.org/apps/?sc=kusama#/explorer;
const network = decodeURIComponent(urlOptions.sc.split('#')[0]);
const chain = network.split('-')[0];

return { param: chain, type: 'substrate-connect' };
}

const endpoints = createWsEndpoints(<T = string>(): T => ('' as unknown as T));
Expand All @@ -35,24 +52,25 @@ function getApiUrl (): string {
const option = endpoints.find(({ dnslink }) => dnslink === ipnsChain);

if (option) {
return option.value;
return { param: option.value, type: 'json-rpc' };
}
}

const stored = store.get('settings') as Record<string, unknown> || {};
const fallbackUrl = endpoints.find(({ value }) => !!value);

// via settings, or the default chain
return [stored.apiUrl, process.env.WS_URL].includes(settings.apiUrl)
? settings.apiUrl // keep as-is
return [stored.apiType, process.env.WS_URL].includes(settings.apiType)
? settings.apiType // keep as-is
: fallbackUrl
? fallbackUrl.value // grab the fallback
: 'ws://127.0.0.1:9944'; // nothing found, go local
? { param: fallbackUrl.value, type: 'json-rpc' } // grab the fallback
: { param: 'ws://127.0.0.1:9944', type: 'json-rpc' }; // nothing found, go local
}

const apiUrl = getApiUrl();
// There cannot be a Substrate Connect light client default (expect only jrpc EndpointType)
const apiType = getApiType();

// set the default as retrieved here
settings.set({ apiUrl });
settings.set({ apiType });

console.log('WS endpoint=', apiUrl);
networkOrUrl(apiType);
4 changes: 2 additions & 2 deletions packages/apps/src/overlays/Connecting.tsx
Expand Up @@ -9,8 +9,8 @@ import { settings } from '@polkadot/ui-settings';
import { useTranslation } from '../translate';
import BaseOverlay from './Base';

const wsUrl = settings.apiUrl;
const isWs = typeof wsUrl === 'string' && wsUrl.startsWith('ws://');
const wsUrl = settings.apiType.param;
const isWs = settings.apiType.type === 'json-rpc' && typeof wsUrl === 'string' && wsUrl.startsWith('ws://');
const isWsLocal = typeof wsUrl === 'string' && wsUrl.includes('127.0.0.1');
const isHttps = window.location.protocol.startsWith('https:');

Expand Down