1
0
mirror of https://github.com/h3poteto/whalebird-desktop synced 2025-02-02 18:36:56 +01:00

Support Firefish and Friendica

This commit is contained in:
AkiraFukushima 2023-10-01 00:49:10 +09:00
parent fa205c40b1
commit e19b69499f
No known key found for this signature in database
GPG Key ID: B6E51BAC4DE1A957
18 changed files with 94 additions and 96 deletions

View File

@ -92,7 +92,7 @@
"emoji-mart-vue-fast": "^15.0.0",
"i18next": "^22.4.15",
"i18next-vue": "^2.1.1",
"megalodon": "6.1.0",
"megalodon": "8.1.1",
"minimist": "^1.2.8",
"mitt": "^3.0.0",
"moment": "^2.29.4",

View File

@ -5,7 +5,8 @@ import HeaderMenu, { HeaderMenuState } from '~/src/renderer/store/TimelineSpace/
const list: Entity.List = {
id: '1',
title: 'example'
title: 'example',
replies_policy: null
}
const mockClient = {

View File

@ -54,7 +54,10 @@ const account: Entity.Account = {
emojis: [],
moved: null,
fields: [],
bot: false
bot: false,
noindex: null,
suspended: null,
limited: null
}
const state = (): AddListMemberState => {

View File

@ -84,22 +84,28 @@ const account: Entity.Account = {
emojis: [],
moved: null,
fields: [],
bot: false
bot: false,
noindex: null,
suspended: null,
limited: null
}
const list1: Entity.List = {
id: '1',
title: 'list1'
title: 'list1',
replies_policy: null
}
const list2: Entity.List = {
id: '2',
title: 'list2'
title: 'list2',
replies_policy: null
}
const list3: Entity.List = {
id: '3',
title: 'list3'
title: 'list3',
replies_policy: null
}
let state = (): ListMembershipState => {

View File

@ -5,7 +5,7 @@ export const insertServer = (
db: Database,
baseURL: string,
domain: string,
sns: 'mastodon' | 'pleroma',
sns: 'mastodon' | 'pleroma' | 'firefish' | 'friendica',
accountId: number | null
): Promise<LocalServer> => {
return new Promise((resolve, reject) => {

View File

@ -442,10 +442,7 @@ app.on('activate', () => {
ipcMain.handle('add-server', async (_: IpcMainInvokeEvent, domain: string) => {
const proxy = await proxyConfiguration.forMastodon()
const sns = await detector(`https://${domain}`, proxy)
if (sns === 'friendica') {
return new Promise((_resolve, reject) => reject('friendica is not supported yet'))
}
if (sns === 'misskey') {
if ((sns as string) === 'misskey') {
return new Promise((_resolve, reject) => reject('misskey is not supported yet'))
}
const server = await insertServer(db, `https://${domain}`, domain, sns, null)
@ -1126,6 +1123,7 @@ const publicStreamings: { [key: number]: DirectStreaming } = {}
const startUserStreaming = async (account: LocalAccount, server: LocalServer, preferences: Preferences) => {
const proxy = await proxyConfiguration.forMastodon()
if (server.sns === 'friendica') return
const url = await StreamingURL(server.sns, account, server, proxy)
userStreamings[account.id] = new UserStreaming(server.sns, account, url, proxy)
userStreamings[account.id].start(
@ -1153,6 +1151,7 @@ const startUserStreaming = async (account: LocalAccount, server: LocalServer, pr
const startDirectStreaming = async (account: LocalAccount, server: LocalServer) => {
const proxy = await proxyConfiguration.forMastodon()
if (server.sns === 'friendica') return
const url = await StreamingURL(server.sns, account, server, proxy)
directStreamings[account.id] = new DirectStreaming(server.sns, account, url, proxy)
directStreamings[account.id].start(
@ -1174,6 +1173,7 @@ const startDirectStreaming = async (account: LocalAccount, server: LocalServer)
const startLocalStreaming = async (account: LocalAccount, server: LocalServer) => {
const proxy = await proxyConfiguration.forMastodon()
if (server.sns === 'friendica') return
const url = await StreamingURL(server.sns, account, server, proxy)
localStreamings[account.id] = new LocalStreaming(server.sns, account, url, proxy)
localStreamings[account.id].start(
@ -1195,6 +1195,7 @@ const startLocalStreaming = async (account: LocalAccount, server: LocalServer) =
const startPublicStreaming = async (account: LocalAccount, server: LocalServer) => {
const proxy = await proxyConfiguration.forMastodon()
if (server.sns === 'friendica') return
const url = await StreamingURL(server.sns, account, server, proxy)
publicStreamings[account.id] = new PublicStreaming(server.sns, account, url, proxy)
publicStreamings[account.id].start(
@ -1271,6 +1272,7 @@ const publishNotification = async (notification: Entity.Notification, accountId:
}
const createNotification = (notification: Entity.Notification, notifyConfig: Notify): NotificationConstructorOptions | null => {
if (!notification.account) return null
switch (notification.type) {
case NotificationType.Favourite:
if (notifyConfig.favourite) {
@ -1398,6 +1400,7 @@ ipcMain.on('start-list-streaming', async (event: IpcMainEvent, obj: ListStreamin
listStreamings[accountId].stop()
}
const proxy = await proxyConfiguration.forMastodon()
if (server.sns === 'friendica') return
const url = await StreamingURL(server.sns, account, server, proxy)
listStreamings[accountId] = new ListStreaming(server.sns, account, url, proxy)
listStreamings[accountId].start(
@ -1441,6 +1444,7 @@ ipcMain.on('start-tag-streaming', async (event: IpcMainEvent, obj: TagStreamingO
tagStreamings[accountId].stop()
}
const proxy = await proxyConfiguration.forMastodon()
if (server.sns === 'friendica') return
const url = await StreamingURL(server.sns, account, server, proxy)
tagStreamings[accountId] = new TagStreaming(server.sns, account, url, proxy)
tagStreamings[accountId].start(

View File

@ -4,7 +4,7 @@ import { LocalAccount } from '~/src/types/localAccount'
import { LocalServer } from '~src/types/localServer'
const StreamingURL = async (
sns: 'mastodon' | 'pleroma',
sns: 'mastodon' | 'pleroma' | 'firefish',
account: LocalAccount,
server: LocalServer,
proxy: ProxyConfig | false
@ -26,7 +26,7 @@ class WebSocket {
public client: MegalodonInterface
public listener: WebSocketInterface | null
constructor(sns: 'mastodon' | 'pleroma', account: LocalAccount, streamingURL: string, proxy: ProxyConfig | false) {
constructor(sns: 'mastodon' | 'pleroma' | 'firefish', account: LocalAccount, streamingURL: string, proxy: ProxyConfig | false) {
const url = streamingURL.replace(/^https:\/\//, 'wss://')
this.client = generator(sns, url, account.accessToken, 'Whalebird', proxy)
this.listener = null

View File

@ -60,7 +60,7 @@ export default defineComponent({
const domain = ref<string>('')
const searching = ref<boolean>(false)
const allowLogin = computed(() => domain.value && form.domainName == domain.value)
const sns = ref<'mastodon' | 'pleroma'>('mastodon')
const sns = ref<'mastodon' | 'pleroma' | 'firefish' | 'friendica'>('mastodon')
const rules = reactive<FormRules>({
domainName: [
@ -117,10 +117,7 @@ export default defineComponent({
try {
const cleanDomain = form.domainName.trim()
const res = await detector(`https://${cleanDomain}`)
if (res === 'friendica') {
throw new Error('Friendica is not supported')
}
if (res === 'misskey') {
if ((res as string) === 'misskey') {
throw new Error('Misskey is not supported')
}
sns.value = res

View File

@ -652,7 +652,7 @@ export default defineComponent({
return
}
try {
const result = await client.value.search(word, 'hashtags')
const result = await client.value.search(word, { type: 'hashtags' })
startIndex.value = start
matchWord.value = word
filteredSuggestion.value = result.data.hashtags.map(tag => ({ name: `#${tag.name}` }))

View File

@ -101,7 +101,7 @@ export default defineComponent({
break
case 'tag':
client.value
?.search(`#${query.value}`, 'hashtags', { resolve: true })
?.search(`#${query.value}`, { type: 'hashtags', resolve: true })
.then(res => {
tags.value = res.data.hashtags
})
@ -115,7 +115,7 @@ export default defineComponent({
break
case 'toot':
client.value
?.search(query.value, 'statuses', { resolve: true })
?.search(query.value, { type: 'statuses', resolve: true })
.then(res => {
statuses.value = res.data.statuses
})

View File

@ -6,11 +6,11 @@
<font-awesome-icon icon="user-plus" size="sm" />
</div>
<div class="action-detail">
<span class="bold" @click="openUser(message.account)">
<span class="bold" @click="openUser(message.account!)">
<bdi
v-html="
$t('notification.follow.body', {
username: username(message.account),
username: username(message.account!),
interpolation: { escapeValue: false }
})
"
@ -18,7 +18,7 @@
</span>
</div>
<div class="action-icon" role="presentation">
<FailoverImg :src="message.account.avatar" />
<FailoverImg :src="message.account!.avatar" />
</div>
</div>
<div class="clearfix"></div>

View File

@ -6,11 +6,11 @@
<font-awesome-icon icon="user-plus" size="sm" />
</div>
<div class="action-detail">
<span class="bold" @click="openUser(message.account)">
<span class="bold" @click="openUser(message.account!)">
<bdi
v-html="
$t('notification.follow_request.body', {
username: username(message.account),
username: username(message.account!),
interpolation: { escapeValue: false }
})
"
@ -18,7 +18,7 @@
</span>
</div>
<div class="action-icon" role="presentation">
<FailoverImg :src="message.account.avatar" />
<FailoverImg :src="message.account!.avatar" />
</div>
</div>
<div class="clearfix"></div>

View File

@ -5,11 +5,11 @@
<font-awesome-icon icon="home" size="sm" />
</div>
<div class="action-detail">
<span class="bold" @click="openUser(message.account)">
<span class="bold" @click="openUser(message.account!)">
<bdi
v-html="
$t('notification.status.body', {
username: username(message.account),
username: username(message.account!),
interpolation: { escapeValue: false }
})
"
@ -17,7 +17,7 @@
</span>
</div>
<div class="action-icon" role="presentation">
<FailoverImg :src="message.account.avatar" :alt="`Avatar of ${message.account.username}`" />
<FailoverImg :src="message.account!.avatar" :alt="`Avatar of ${message.account!.username}`" />
</div>
</div>
<div class="clearfix"></div>

View File

@ -12,11 +12,11 @@
</template>
</div>
<div class="action-detail">
<span class="bold" @click="openUser(message.account)">
<span class="bold" @click="openUser(message.account!)">
<bdi
v-html="
$t(reactionMessage, {
username: username(message.account),
username: username(message.account!),
interpolation: { escapeValue: false }
})
"
@ -24,7 +24,7 @@
</span>
</div>
<div class="action-icon" role="presentation">
<FailoverImg :src="message.account.avatar" :alt="`Avatar of ${message.account.username}`" />
<FailoverImg :src="message.account!.avatar" :alt="`Avatar of ${message.account!.username}`" />
</div>
</div>
<div class="clearfix"></div>
@ -80,7 +80,7 @@
</div>
<LinkPreview
v-if="status.card && status.card.type === 'link'"
:icon="status.card.image"
:icon="status.card.image!"
:title="status.card.title"
:description="status.card.description"
:url="status.card.url"

View File

@ -85,7 +85,7 @@
/>
<LinkPreview
v-if="originalMessage.card && originalMessage.card.type === 'link' && isShowContent"
:icon="originalMessage.card.image"
:icon="originalMessage.card.image!"
:title="originalMessage.card.title"
:description="originalMessage.card.description"
:url="originalMessage.card.url"

View File

@ -1,9 +1,12 @@
import { QuoteSupportMastodon } from '~/src/constants/servers/quote'
const quoteSupported = (_sns: 'mastodon' | 'pleroma', domain: string): boolean => {
const quoteSupported = (sns: 'mastodon' | 'pleroma' | 'firefish' | 'friendica', domain: string): boolean => {
if (QuoteSupportMastodon.includes(domain)) {
return true
}
if (sns === 'firefish') {
return true
}
return false
}

View File

@ -2,6 +2,6 @@ export type LocalServer = {
id: number
baseURL: string
domain: string
sns: 'mastodon' | 'pleroma'
sns: 'mastodon' | 'pleroma' | 'firefish' | 'friendica'
accountId: number | null
}

100
yarn.lock
View File

@ -2063,7 +2063,7 @@
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.2.tgz#9b0e3e8533fe5024ad32d6637eb9589988b6fdca"
integrity sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==
"@types/oauth@^0.9.0":
"@types/oauth@^0.9.2":
version "0.9.2"
resolved "https://registry.yarnpkg.com/@types/oauth/-/oauth-0.9.2.tgz#846f11d732deadff4303228d81f07a7b377df287"
integrity sha512-Nu3/abQ6yR9VlsCdX3aiGsWFkj6OJvJqDvg/36t8Gwf2mFXdBZXPDN3K+2yfeA6Lo2m1Q12F8Qil9TZ48nWhOQ==
@ -2176,13 +2176,6 @@
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.17.tgz#5c9f3c617f64a9735d7b72a7cc671e166d900c40"
integrity sha512-4p9vcSmxAayx72yn70joFoL44c9MO/0+iVEBIQXe3v2h2SiAsEIo/G5v6ObFWvNKRFjbrVadNf9LqEEZeQPzdA==
"@types/ws@^8.5.4":
version "8.5.4"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5"
integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==
dependencies:
"@types/node" "*"
"@types/ws@^8.5.5":
version "8.5.6"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.6.tgz#e9ad51f0ab79b9110c50916c9fcbddc36d373065"
@ -2818,7 +2811,7 @@ agent-base@6, agent-base@^6.0.2:
dependencies:
debug "4"
agent-base@^7.0.1, agent-base@^7.0.2:
agent-base@^7.0.2:
version "7.1.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434"
integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==
@ -3212,15 +3205,6 @@ available-typed-arrays@^1.0.5:
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
axios@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f"
integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
axios@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.1.tgz#11fbaa11fc35f431193a9564109c88c1f27b585f"
@ -4582,7 +4566,12 @@ date-fns@^1.27.2:
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
dayjs@^1.11.3, dayjs@^1.11.8:
dayjs@^1.11.10:
version "1.11.10"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
dayjs@^1.11.3:
version "1.11.8"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.8.tgz#4282f139c8c19dd6d0c7bd571e30c2d0ba7698ea"
integrity sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==
@ -6707,10 +6696,10 @@ https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1:
agent-base "6"
debug "4"
https-proxy-agent@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.0.tgz#75cb70d04811685667183b31ab158d006750418a"
integrity sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw==
https-proxy-agent@^7.0.2:
version "7.0.2"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b"
integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==
dependencies:
agent-base "^7.0.2"
debug "4"
@ -8344,24 +8333,24 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
megalodon@6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/megalodon/-/megalodon-6.1.0.tgz#42a1008c0e702a528f07fcbe143969a82d1d3a56"
integrity sha512-LHmy9sU6BMSRaNlMaomCLLDBYhouYNU99P6nRw6LdMYcWt2Pgwd8q+5Y0isCEZIlhDg3sJbCKRRN1L0d4raOnQ==
megalodon@8.1.1:
version "8.1.1"
resolved "https://registry.yarnpkg.com/megalodon/-/megalodon-8.1.1.tgz#36c254aeca980aa4147cdec68aaae79e25e31056"
integrity sha512-K7YjGmRbNkJao2E0hadJCW3IDloufVPUbYA/3+RFDFZvZO5v1MBz3rU4OixIgrHHY74PVTkSU8YHzyv7KA4rhA==
dependencies:
"@types/oauth" "^0.9.0"
"@types/ws" "^8.5.4"
axios "1.4.0"
dayjs "^1.11.8"
"@types/oauth" "^0.9.2"
"@types/ws" "^8.5.5"
axios "1.5.1"
dayjs "^1.11.10"
form-data "^4.0.0"
https-proxy-agent "^7.0.0"
https-proxy-agent "^7.0.2"
oauth "^0.10.0"
object-assign-deep "^0.4.0"
parse-link-header "^2.0.0"
socks-proxy-agent "^8.0.1"
typescript "5.0.4"
uuid "^9.0.0"
ws "8.13.0"
socks-proxy-agent "^8.0.2"
typescript "5.2.2"
uuid "^9.0.1"
ws "8.14.2"
memfs@^3.4.3:
version "3.6.0"
@ -10651,12 +10640,12 @@ socks-proxy-agent@^7.0.0:
debug "^4.3.3"
socks "^2.6.2"
socks-proxy-agent@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.1.tgz#ffc5859a66dac89b0c4dab90253b96705f3e7120"
integrity sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ==
socks-proxy-agent@^8.0.2:
version "8.0.2"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz#5acbd7be7baf18c46a3f293a840109a430a640ad"
integrity sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==
dependencies:
agent-base "^7.0.1"
agent-base "^7.0.2"
debug "^4.3.4"
socks "^2.7.1"
@ -11537,10 +11526,10 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
typescript@5.0.4:
version "5.0.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
typescript@5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78"
integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==
typescript@^4.9.5:
version "4.9.5"
@ -11783,10 +11772,10 @@ uuid@^8.3.0, uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
uuid@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
uuid@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
v8-compile-cache@^2.3.0:
version "2.3.0"
@ -12330,21 +12319,16 @@ write-file-atomic@^4.0.2:
imurmurhash "^0.1.4"
signal-exit "^3.0.7"
ws@8.13.0:
version "8.13.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
ws@8.14.2, ws@^8.13.0:
version "8.14.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f"
integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==
ws@^7.4.6:
version "7.5.9"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
ws@^8.13.0:
version "8.14.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f"
integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==
xml-char-classes@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d"