summaryrefslogtreecommitdiff
path: root/packages/sw
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-06-11 19:31:03 +0900
committerGitHub <noreply@github.com>2022-06-11 19:31:03 +0900
commit182a1bf653ecfbcf76e4530b3077c6252b0d4827 (patch)
tree45d1472747d4cac017e96616f844292f5785ccdd /packages/sw
parent12.110.1 (diff)
parent12.111.0 (diff)
downloadmisskey-182a1bf653ecfbcf76e4530b3077c6252b0d4827.tar.gz
misskey-182a1bf653ecfbcf76e4530b3077c6252b0d4827.tar.bz2
misskey-182a1bf653ecfbcf76e4530b3077c6252b0d4827.zip
Merge pull request #8783 from misskey-dev/develop
Release: 12.111.0
Diffstat (limited to 'packages/sw')
-rw-r--r--packages/sw/.eslintrc.js22
-rw-r--r--packages/sw/.npmrc2
-rw-r--r--packages/sw/.yarnrc1
-rw-r--r--packages/sw/build.js37
-rw-r--r--packages/sw/package.json17
-rw-r--r--packages/sw/src/filters/user.ts14
-rw-r--r--packages/sw/src/scripts/create-notification.ts237
-rw-r--r--packages/sw/src/scripts/get-account-from-id.ts7
-rw-r--r--packages/sw/src/scripts/get-user-name.ts3
-rw-r--r--packages/sw/src/scripts/i18n.ts29
-rw-r--r--packages/sw/src/scripts/lang.ts47
-rw-r--r--packages/sw/src/scripts/login-id.ts11
-rw-r--r--packages/sw/src/scripts/notification-read.ts50
-rw-r--r--packages/sw/src/scripts/operations.ts70
-rw-r--r--packages/sw/src/sw.ts200
-rw-r--r--packages/sw/src/types.ts31
-rw-r--r--packages/sw/tsconfig.json39
-rw-r--r--packages/sw/yarn.lock710
18 files changed, 1527 insertions, 0 deletions
diff --git a/packages/sw/.eslintrc.js b/packages/sw/.eslintrc.js
new file mode 100644
index 0000000000..9d56daca83
--- /dev/null
+++ b/packages/sw/.eslintrc.js
@@ -0,0 +1,22 @@
+module.exports = {
+ root: true,
+ env: {
+ "node": false
+ },
+ parserOptions: {
+ "parser": "@typescript-eslint/parser",
+ tsconfigRootDir: __dirname,
+ //project: ['./tsconfig.json'],
+ },
+ extends: [
+ //"../shared/.eslintrc.js",
+ ],
+ globals: {
+ "require": false,
+ "_DEV_": false,
+ "_LANGS_": false,
+ "_VERSION_": false,
+ "_ENV_": false,
+ "_PERF_PREFIX_": false,
+ }
+}
diff --git a/packages/sw/.npmrc b/packages/sw/.npmrc
new file mode 100644
index 0000000000..6b5f38e890
--- /dev/null
+++ b/packages/sw/.npmrc
@@ -0,0 +1,2 @@
+save-exact = true
+package-lock = false
diff --git a/packages/sw/.yarnrc b/packages/sw/.yarnrc
new file mode 100644
index 0000000000..788570fcd5
--- /dev/null
+++ b/packages/sw/.yarnrc
@@ -0,0 +1 @@
+network-timeout 600000
diff --git a/packages/sw/build.js b/packages/sw/build.js
new file mode 100644
index 0000000000..72d9db9c0f
--- /dev/null
+++ b/packages/sw/build.js
@@ -0,0 +1,37 @@
+const esbuild = require('esbuild');
+const locales = require('../../locales');
+const meta = require('../../package.json');
+const watch = process.argv[2]?.includes('watch');
+
+console.log('Starting SW building...');
+
+esbuild.build({
+ entryPoints: [ `${__dirname}/src/sw.ts` ],
+ bundle: true,
+ format: 'esm',
+ treeShaking: true,
+ minify: process.env.NODE_ENV === 'production',
+ absWorkingDir: __dirname,
+ outbase: `${__dirname}/src`,
+ outdir: `${__dirname}/../../built/_sw_dist_`,
+ loader: {
+ '.ts': 'ts'
+ },
+ tsconfig: `${__dirname}/tsconfig.json`,
+ define: {
+ _VERSION_: JSON.stringify(meta.version),
+ _LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]) => [k, v._lang_])),
+ _ENV_: JSON.stringify(process.env.NODE_ENV),
+ _DEV_: process.env.NODE_ENV !== 'production',
+ _PERF_PREFIX_: JSON.stringify('Misskey:'),
+ },
+ watch: watch ? {
+ onRebuild(error, result) {
+ if (error) console.error('SW: watch build failed:', error);
+ else console.log('SW: watch build succeeded:', result);
+ },
+ } : false,
+}).then(result => {
+ if (watch) console.log('watching...');
+ else console.log('done,', JSON.stringify(result));
+});
diff --git a/packages/sw/package.json b/packages/sw/package.json
new file mode 100644
index 0000000000..41dfe19b85
--- /dev/null
+++ b/packages/sw/package.json
@@ -0,0 +1,17 @@
+{
+ "private": true,
+ "scripts": {
+ "watch": "node build.js watch",
+ "build": "node build.js",
+ "lint": "eslint --quiet src/**/*.{ts}"
+ },
+ "resolutions": {},
+ "dependencies": {
+ "esbuild": "^0.14.13",
+ "idb-keyval": "^6.0.3",
+ "misskey-js": "0.0.14"
+ },
+ "devDependencies": {
+ "eslint": "^8.2.0"
+ }
+}
diff --git a/packages/sw/src/filters/user.ts b/packages/sw/src/filters/user.ts
new file mode 100644
index 0000000000..09437eb19a
--- /dev/null
+++ b/packages/sw/src/filters/user.ts
@@ -0,0 +1,14 @@
+import * as misskey from 'misskey-js';
+import * as Acct from 'misskey-js/built/acct';
+
+export const acct = (user: misskey.Acct) => {
+ return Acct.toString(user);
+};
+
+export const userName = (user: misskey.entities.User) => {
+ return user.name || user.username;
+};
+
+export const userPage = (user: misskey.Acct, path?, absolute = false) => {
+ return `${absolute ? origin : ''}/@${acct(user)}${(path ? `/${path}` : '')}`;
+};
diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts
new file mode 100644
index 0000000000..6d7ba7d524
--- /dev/null
+++ b/packages/sw/src/scripts/create-notification.ts
@@ -0,0 +1,237 @@
+/*
+ * Notification manager for SW
+ */
+declare var self: ServiceWorkerGlobalScope;
+
+import { swLang } from '@/scripts/lang';
+import { cli } from '@/scripts/operations';
+import { pushNotificationDataMap } from '@/types';
+import getUserName from '@/scripts/get-user-name';
+import { I18n } from '@/scripts/i18n';
+import { getAccountFromId } from '@/scripts/get-account-from-id';
+
+export async function createNotification<K extends keyof pushNotificationDataMap>(data: pushNotificationDataMap[K]) {
+ const n = await composeNotification(data);
+
+ if (n) {
+ return self.registration.showNotification(...n);
+ } else {
+ console.error('Could not compose notification', data);
+ return createEmptyNotification();
+ }
+}
+
+async function composeNotification<K extends keyof pushNotificationDataMap>(data: pushNotificationDataMap[K]): Promise<[string, NotificationOptions] | null> {
+ if (!swLang.i18n) swLang.fetchLocale();
+ const i18n = await swLang.i18n as I18n<any>;
+ const { t } = i18n;
+ switch (data.type) {
+ /*
+ case 'driveFileCreated': // TODO (Server Side)
+ return [t('_notification.fileUploaded'), {
+ body: body.name,
+ icon: body.url,
+ data
+ }];
+ */
+ case 'notification':
+ switch (data.body.type) {
+ case 'follow':
+ // users/showの型定義をswos.apiへ当てはめるのが困難なのでapiFetch.requestを直接使用
+ const account = await getAccountFromId(data.userId);
+ if (!account) return null;
+ const userDetail = await cli.request('users/show', { userId: data.body.userId }, account.token);
+ return [t('_notification.youWereFollowed'), {
+ body: getUserName(data.body.user),
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: userDetail.isFollowing ? [] : [
+ {
+ action: 'follow',
+ title: t('_notification._actions.followBack')
+ }
+ ],
+ }];
+
+ case 'mention':
+ return [t('_notification.youGotMention', { name: getUserName(data.body.user) }), {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'reply',
+ title: t('_notification._actions.reply')
+ }
+ ],
+ }];
+
+ case 'reply':
+ return [t('_notification.youGotReply', { name: getUserName(data.body.user) }), {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'reply',
+ title: t('_notification._actions.reply')
+ }
+ ],
+ }];
+
+ case 'renote':
+ return [t('_notification.youRenoted', { name: getUserName(data.body.user) }), {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'showUser',
+ title: getUserName(data.body.user)
+ }
+ ],
+ }];
+
+ case 'quote':
+ return [t('_notification.youGotQuote', { name: getUserName(data.body.user) }), {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'reply',
+ title: t('_notification._actions.reply')
+ },
+ ...((data.body.note.visibility === 'public' || data.body.note.visibility === 'home') ? [
+ {
+ action: 'renote',
+ title: t('_notification._actions.renote')
+ }
+ ] : [])
+ ],
+ }];
+
+ case 'reaction':
+ return [`${data.body.reaction} ${getUserName(data.body.user)}`, {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'showUser',
+ title: getUserName(data.body.user)
+ }
+ ],
+ }];
+
+ case 'pollVote':
+ return [t('_notification.youGotPoll', { name: getUserName(data.body.user) }), {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ }];
+
+ case 'pollEnded':
+ return [t('_notification.pollEnded'), {
+ body: data.body.note.text || '',
+ data,
+ }];
+
+ case 'receiveFollowRequest':
+ return [t('_notification.youReceivedFollowRequest'), {
+ body: getUserName(data.body.user),
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'accept',
+ title: t('accept')
+ },
+ {
+ action: 'reject',
+ title: t('reject')
+ }
+ ],
+ }];
+
+ case 'followRequestAccepted':
+ return [t('_notification.yourFollowRequestAccepted'), {
+ body: getUserName(data.body.user),
+ icon: data.body.user.avatarUrl,
+ data,
+ }];
+
+ case 'groupInvited':
+ return [t('_notification.youWereInvitedToGroup', { userName: getUserName(data.body.user) }), {
+ body: data.body.invitation.group.name,
+ data,
+ actions: [
+ {
+ action: 'accept',
+ title: t('accept')
+ },
+ {
+ action: 'reject',
+ title: t('reject')
+ }
+ ],
+ }];
+
+ case 'app':
+ return [data.body.header || data.body.body, {
+ body: data.body.header && data.body.body,
+ icon: data.body.icon,
+ data
+ }];
+
+ default:
+ return null;
+ }
+ case 'unreadMessagingMessage':
+ if (data.body.groupId === null) {
+ return [t('_notification.youGotMessagingMessageFromUser', { name: getUserName(data.body.user) }), {
+ icon: data.body.user.avatarUrl,
+ tag: `messaging:user:${data.body.userId}`,
+ data,
+ renotify: true,
+ }];
+ }
+ return [t('_notification.youGotMessagingMessageFromGroup', { name: data.body.group.name }), {
+ icon: data.body.user.avatarUrl,
+ tag: `messaging:group:${data.body.groupId}`,
+ data,
+ renotify: true,
+ }];
+ default:
+ return null;
+ }
+}
+
+export async function createEmptyNotification() {
+ return new Promise<void>(async res => {
+ if (!swLang.i18n) swLang.fetchLocale();
+ const i18n = await swLang.i18n as I18n<any>;
+ const { t } = i18n;
+
+ await self.registration.showNotification(
+ t('_notification.emptyPushNotificationMessage'),
+ {
+ silent: true,
+ tag: 'read_notification',
+ }
+ );
+
+ res();
+
+ setTimeout(async () => {
+ for (const n of
+ [
+ ...(await self.registration.getNotifications({ tag: 'user_visible_auto_notification' })),
+ ...(await self.registration.getNotifications({ tag: 'read_notification' }))
+ ]
+ ) {
+ n.close();
+ }
+ }, 1000);
+ });
+}
diff --git a/packages/sw/src/scripts/get-account-from-id.ts b/packages/sw/src/scripts/get-account-from-id.ts
new file mode 100644
index 0000000000..be4cfaeba4
--- /dev/null
+++ b/packages/sw/src/scripts/get-account-from-id.ts
@@ -0,0 +1,7 @@
+import { get } from 'idb-keyval';
+
+export async function getAccountFromId(id: string) {
+ const accounts = await get('accounts') as { token: string; id: string; }[];
+ if (!accounts) console.log('Accounts are not recorded');
+ return accounts.find(e => e.id === id);
+}
diff --git a/packages/sw/src/scripts/get-user-name.ts b/packages/sw/src/scripts/get-user-name.ts
new file mode 100644
index 0000000000..d499ea0203
--- /dev/null
+++ b/packages/sw/src/scripts/get-user-name.ts
@@ -0,0 +1,3 @@
+export default function(user: { name?: string | null, username: string }): string {
+ return user.name || user.username;
+}
diff --git a/packages/sw/src/scripts/i18n.ts b/packages/sw/src/scripts/i18n.ts
new file mode 100644
index 0000000000..3fe88e5514
--- /dev/null
+++ b/packages/sw/src/scripts/i18n.ts
@@ -0,0 +1,29 @@
+export class I18n<T extends Record<string, any>> {
+ public ts: T;
+
+ constructor(locale: T) {
+ this.ts = locale;
+
+ //#region BIND
+ this.t = this.t.bind(this);
+ //#endregion
+ }
+
+ // string にしているのは、ドット区切りでのパス指定を許可するため
+ // なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも
+ public t(key: string, args?: Record<string, string>): string {
+ try {
+ let str = key.split('.').reduce((o, i) => o[i], this.ts) as unknown as string;
+
+ if (args) {
+ for (const [k, v] of Object.entries(args)) {
+ str = str.replace(`{${k}}`, v);
+ }
+ }
+ return str;
+ } catch (err) {
+ console.warn(`missing localization '${key}'`);
+ return key;
+ }
+ }
+}
diff --git a/packages/sw/src/scripts/lang.ts b/packages/sw/src/scripts/lang.ts
new file mode 100644
index 0000000000..2d05404ef9
--- /dev/null
+++ b/packages/sw/src/scripts/lang.ts
@@ -0,0 +1,47 @@
+/*
+ * Language manager for SW
+ */
+declare var self: ServiceWorkerGlobalScope;
+
+import { get, set } from 'idb-keyval';
+import { I18n } from '@/scripts/i18n';
+
+class SwLang {
+ public cacheName = `mk-cache-${_VERSION_}`;
+
+ public lang: Promise<string> = get('lang').then(async prelang => {
+ if (!prelang) return 'en-US';
+ return prelang;
+ });
+
+ public setLang(newLang: string) {
+ this.lang = Promise.resolve(newLang);
+ set('lang', newLang);
+ return this.fetchLocale();
+ }
+
+ public i18n: Promise<I18n<any>> | null = null;
+
+ public fetchLocale() {
+ return this.i18n = this._fetch();
+ }
+
+ private async _fetch() {
+ // Service Workerは何度も起動しそのたびにlocaleを読み込むので、CacheStorageを使う
+ const localeUrl = `/assets/locales/${await this.lang}.${_VERSION_}.json`;
+ let localeRes = await caches.match(localeUrl);
+
+ // _DEV_がtrueの場合は常に最新化
+ if (!localeRes || _DEV_) {
+ localeRes = await fetch(localeUrl);
+ const clone = localeRes?.clone();
+ if (!clone?.clone().ok) Error('locale fetching error');
+
+ caches.open(this.cacheName).then(cache => cache.put(localeUrl, clone));
+ }
+
+ return new I18n(await localeRes.json());
+ }
+}
+
+export const swLang = new SwLang();
diff --git a/packages/sw/src/scripts/login-id.ts b/packages/sw/src/scripts/login-id.ts
new file mode 100644
index 0000000000..0f9c6be4a9
--- /dev/null
+++ b/packages/sw/src/scripts/login-id.ts
@@ -0,0 +1,11 @@
+export function getUrlWithLoginId(url: string, loginId: string) {
+ const u = new URL(url, origin);
+ u.searchParams.append('loginId', loginId);
+ return u.toString();
+}
+
+export function getUrlWithoutLoginId(url: string) {
+ const u = new URL(url);
+ u.searchParams.delete('loginId');
+ return u.toString();
+}
diff --git a/packages/sw/src/scripts/notification-read.ts b/packages/sw/src/scripts/notification-read.ts
new file mode 100644
index 0000000000..8433f902b4
--- /dev/null
+++ b/packages/sw/src/scripts/notification-read.ts
@@ -0,0 +1,50 @@
+declare var self: ServiceWorkerGlobalScope;
+
+import { get } from 'idb-keyval';
+import { pushNotificationDataMap } from '@/types';
+import { api } from '@/scripts/operations';
+
+type Accounts = {
+ [x: string]: {
+ queue: string[],
+ timeout: number | null
+ }
+};
+
+class SwNotificationReadManager {
+ private accounts: Accounts = {};
+
+ public async construct() {
+ const accounts = await get('accounts');
+ if (!accounts) Error('Accounts are not recorded');
+
+ this.accounts = accounts.reduce((acc, e) => {
+ acc[e.id] = {
+ queue: [],
+ timeout: null
+ };
+ return acc;
+ }, {} as Accounts);
+
+ return this;
+ }
+
+ // プッシュ通知の既読をサーバーに送信
+ public async read<K extends keyof pushNotificationDataMap>(data: pushNotificationDataMap[K]) {
+ if (data.type !== 'notification' || !(data.userId in this.accounts)) return;
+
+ const account = this.accounts[data.userId];
+
+ account.queue.push(data.body.id as string);
+
+ // 最後の呼び出しから200ms待ってまとめて処理する
+ if (account.timeout) clearTimeout(account.timeout);
+ account.timeout = setTimeout(() => {
+ account.timeout = null;
+
+ api('notifications/read', data.userId, { notificationIds: account.queue });
+ }, 200);
+ }
+}
+
+export const swNotificationRead = (new SwNotificationReadManager()).construct();
diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts
new file mode 100644
index 0000000000..02cf0d96cf
--- /dev/null
+++ b/packages/sw/src/scripts/operations.ts
@@ -0,0 +1,70 @@
+/*
+ * Operations
+ * 各種操作
+ */
+declare var self: ServiceWorkerGlobalScope;
+
+import * as Misskey from 'misskey-js';
+import { SwMessage, swMessageOrderType } from '@/types';
+import { acct as getAcct } from '@/filters/user';
+import { getAccountFromId } from '@/scripts/get-account-from-id';
+import { getUrlWithLoginId } from '@/scripts/login-id';
+
+export const cli = new Misskey.api.APIClient({ origin, fetch: (...args) => fetch(...args) });
+
+export async function api<E extends keyof Misskey.Endpoints>(endpoint: E, userId: string, options?: Misskey.Endpoints[E]['req']) {
+ const account = await getAccountFromId(userId);
+ if (!account) return;
+
+ return cli.request(endpoint, options, account.token);
+}
+
+// rendered acctからユーザーを開く
+export function openUser(acct: string, loginId: string) {
+ return openClient('push', `/@${acct}`, loginId, { acct });
+}
+
+// noteIdからノートを開く
+export function openNote(noteId: string, loginId: string) {
+ return openClient('push', `/notes/${noteId}`, loginId, { noteId });
+}
+
+export async function openChat(body: any, loginId: string) {
+ if (body.groupId === null) {
+ return openClient('push', `/my/messaging/${getAcct(body.user)}`, loginId, { body });
+ } else {
+ return openClient('push', `/my/messaging/group/${body.groupId}`, loginId, { body });
+ }
+}
+
+// post-formのオプションから投稿フォームを開く
+export async function openPost(options: any, loginId: string) {
+ // クエリを作成しておく
+ let url = `/share?`;
+ if (options.initialText) url += `text=${options.initialText}&`;
+ if (options.reply) url += `replyId=${options.reply.id}&`;
+ if (options.renote) url += `renoteId=${options.renote.id}&`;
+
+ return openClient('post', url, loginId, { options });
+}
+
+export async function openClient(order: swMessageOrderType, url: string, loginId: string, query: any = {}) {
+ const client = await findClient();
+
+ if (client) {
+ client.postMessage({ type: 'order', ...query, order, loginId, url } as SwMessage);
+ return client;
+ }
+
+ return self.clients.openWindow(getUrlWithLoginId(url, loginId));
+}
+
+export async function findClient() {
+ const clients = await self.clients.matchAll({
+ type: 'window'
+ });
+ for (const c of clients) {
+ if (c.url.indexOf('?zen') < 0) return c;
+ }
+ return null;
+}
diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts
new file mode 100644
index 0000000000..0ba6a6e4af
--- /dev/null
+++ b/packages/sw/src/sw.ts
@@ -0,0 +1,200 @@
+declare var self: ServiceWorkerGlobalScope;
+
+import { createEmptyNotification, createNotification } from '@/scripts/create-notification';
+import { swLang } from '@/scripts/lang';
+import { swNotificationRead } from '@/scripts/notification-read';
+import { pushNotificationDataMap } from '@/types';
+import * as swos from '@/scripts/operations';
+import { acct as getAcct } from '@/filters/user';
+
+self.addEventListener('install', ev => {
+ ev.waitUntil(self.skipWaiting());
+});
+
+self.addEventListener('activate', ev => {
+ ev.waitUntil(
+ caches.keys()
+ .then(cacheNames => Promise.all(
+ cacheNames
+ .filter((v) => v !== swLang.cacheName)
+ .map(name => caches.delete(name))
+ ))
+ .then(() => self.clients.claim())
+ );
+});
+
+self.addEventListener('fetch', ev => {
+ ev.respondWith(
+ fetch(ev.request)
+ .catch(() => new Response(`Offline. Service Worker @${_VERSION_}`, { status: 200 }))
+ );
+});
+
+self.addEventListener('push', ev => {
+ // クライアント取得
+ ev.waitUntil(self.clients.matchAll({
+ includeUncontrolled: true,
+ type: 'window'
+ }).then(async <K extends keyof pushNotificationDataMap>(clients: readonly WindowClient[]) => {
+ const data: pushNotificationDataMap[K] = ev.data?.json();
+
+ switch (data.type) {
+ // case 'driveFileCreated':
+ case 'notification':
+ case 'unreadMessagingMessage':
+ // クライアントがあったらストリームに接続しているということなので通知しない
+ if (clients.length != 0) return;
+ return createNotification(data);
+ case 'readAllNotifications':
+ for (const n of await self.registration.getNotifications()) {
+ if (n?.data?.type === 'notification') n.close();
+ }
+ break;
+ case 'readAllMessagingMessages':
+ for (const n of await self.registration.getNotifications()) {
+ if (n?.data?.type === 'unreadMessagingMessage') n.close();
+ }
+ break;
+ case 'readNotifications':
+ for (const n of await self.registration.getNotifications()) {
+ if (data.body?.notificationIds?.includes(n.data.body.id)) {
+ n.close();
+ }
+ }
+ break;
+ case 'readAllMessagingMessagesOfARoom':
+ for (const n of await self.registration.getNotifications()) {
+ if (n.data.type === 'unreadMessagingMessage'
+ && ('userId' in data.body
+ ? data.body.userId === n.data.body.userId
+ : data.body.groupId === n.data.body.groupId)
+ ) {
+ n.close();
+ }
+ }
+ break;
+ }
+
+ return createEmptyNotification();
+ }));
+});
+
+self.addEventListener('notificationclick', <K extends keyof pushNotificationDataMap>(ev: ServiceWorkerGlobalScopeEventMap['notificationclick']) => {
+ ev.waitUntil((async () => {
+ if (_DEV_) {
+ console.log('notificationclick', ev.action, ev.notification.data);
+ }
+
+ const { action, notification } = ev;
+ const data: pushNotificationDataMap[K] = notification.data;
+ const { userId: id } = data;
+ let client: WindowClient | null = null;
+
+ switch (data.type) {
+ case 'notification':
+ switch (action) {
+ case 'follow':
+ if ('userId' in data.body) await swos.api('following/create', id, { userId: data.body.userId });
+ break;
+ case 'showUser':
+ if ('user' in data.body) client = await swos.openUser(getAcct(data.body.user), id);
+ break;
+ case 'reply':
+ if ('note' in data.body) client = await swos.openPost({ reply: data.body.note }, id);
+ break;
+ case 'renote':
+ if ('note' in data.body) await swos.api('notes/create', id, { renoteId: data.body.note.id });
+ break;
+ case 'accept':
+ switch (data.body.type) {
+ case 'receiveFollowRequest':
+ await swos.api('following/requests/accept', id, { userId: data.body.userId });
+ break;
+ case 'groupInvited':
+ await swos.api('users/groups/invitations/accept', id, { invitationId: data.body.invitation.id });
+ break;
+ }
+ break;
+ case 'reject':
+ switch (data.body.type) {
+ case 'receiveFollowRequest':
+ await swos.api('following/requests/reject', id, { userId: data.body.userId });
+ break;
+ case 'groupInvited':
+ await swos.api('users/groups/invitations/reject', id, { invitationId: data.body.invitation.id });
+ break;
+ }
+ break;
+ case 'showFollowRequests':
+ client = await swos.openClient('push', '/my/follow-requests', id);
+ break;
+ default:
+ switch (data.body.type) {
+ case 'receiveFollowRequest':
+ client = await swos.openClient('push', '/my/follow-requests', id);
+ break;
+ case 'groupInvited':
+ client = await swos.openClient('push', '/my/groups', id);
+ break;
+ case 'reaction':
+ client = await swos.openNote(data.body.note.id, id);
+ break;
+ default:
+ if ('note' in data.body) {
+ client = await swos.openNote(data.body.note.id, id);
+ } else if ('user' in data.body) {
+ client = await swos.openUser(getAcct(data.body.user), id);
+ }
+ break;
+ }
+ }
+ break;
+ case 'unreadMessagingMessage':
+ client = await swos.openChat(data.body, id);
+ break;
+ }
+
+ if (client) {
+ client.focus();
+ }
+ if (data.type === 'notification') {
+ swNotificationRead.then(that => that.read(data));
+ }
+
+ notification.close();
+
+ })());
+});
+
+self.addEventListener('notificationclose', <K extends keyof pushNotificationDataMap>(ev: ServiceWorkerGlobalScopeEventMap['notificationclose']) => {
+ const data: pushNotificationDataMap[K] = ev.notification.data;
+
+ if (data.type === 'notification') {
+ swNotificationRead.then(that => that.read(data));
+ }
+});
+
+self.addEventListener('message', (ev: ServiceWorkerGlobalScopeEventMap['message']) => {
+ ev.waitUntil((async () => {
+ switch (ev.data) {
+ case 'clear':
+ // Cache Storage全削除
+ await caches.keys()
+ .then(cacheNames => Promise.all(
+ cacheNames.map(name => caches.delete(name))
+ ));
+ return; // TODO
+ }
+
+ if (typeof ev.data === 'object') {
+ // E.g. '[object Array]' → 'array'
+ const otype = Object.prototype.toString.call(ev.data).slice(8, -1).toLowerCase();
+
+ if (otype === 'object') {
+ if (ev.data.msg === 'initialize') {
+ swLang.setLang(ev.data.lang);
+ }
+ }
+ }
+ })());
+});
diff --git a/packages/sw/src/types.ts b/packages/sw/src/types.ts
new file mode 100644
index 0000000000..6aa3726eac
--- /dev/null
+++ b/packages/sw/src/types.ts
@@ -0,0 +1,31 @@
+import * as Misskey from 'misskey-js';
+
+export type swMessageOrderType = 'post' | 'push';
+
+export type SwMessage = {
+ type: 'order';
+ order: swMessageOrderType;
+ loginId: string;
+ url: string;
+ [x: string]: any;
+};
+
+// Defined also @/services/push-notification.ts#L7-L14
+type pushNotificationDataSourceMap = {
+ notification: Misskey.entities.Notification;
+ unreadMessagingMessage: Misskey.entities.MessagingMessage;
+ readNotifications: { notificationIds: string[] };
+ readAllNotifications: undefined;
+ readAllMessagingMessages: undefined;
+ readAllMessagingMessagesOfARoom: { userId: string } | { groupId: string };
+};
+
+export type pushNotificationData<K extends keyof pushNotificationDataSourceMap> = {
+ type: K;
+ body: pushNotificationDataSourceMap[K];
+ userId: string;
+};
+
+export type pushNotificationDataMap = {
+ [K in keyof pushNotificationDataSourceMap]: pushNotificationData<K>;
+};
diff --git a/packages/sw/tsconfig.json b/packages/sw/tsconfig.json
new file mode 100644
index 0000000000..c3a845f12a
--- /dev/null
+++ b/packages/sw/tsconfig.json
@@ -0,0 +1,39 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "noEmitOnError": false,
+ "noImplicitAny": false,
+ "noImplicitReturns": true,
+ "noUnusedParameters": false,
+ "noUnusedLocals": true,
+ "noFallthroughCasesInSwitch": true,
+ "declaration": false,
+ "sourceMap": false,
+ "target": "es2017",
+ "module": "esnext",
+ "moduleResolution": "node",
+ "removeComments": false,
+ "noLib": false,
+ "strict": true,
+ "strictNullChecks": true,
+ "experimentalDecorators": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"],
+ },
+ "typeRoots": [
+ "node_modules/@types",
+ "@types",
+ ],
+ "lib": [
+ "esnext",
+ "webworker"
+ ]
+ },
+ "compileOnSave": false,
+ "include": [
+ "./**/*.ts"
+ ]
+}
diff --git a/packages/sw/yarn.lock b/packages/sw/yarn.lock
new file mode 100644
index 0000000000..e6d683bc42
--- /dev/null
+++ b/packages/sw/yarn.lock
@@ -0,0 +1,710 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@eslint/eslintrc@^1.0.5":
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.5.tgz#33f1b838dbf1f923bfa517e008362b78ddbbf318"
+ integrity sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.3.2"
+ espree "^9.2.0"
+ globals "^13.9.0"
+ ignore "^4.0.6"
+ import-fresh "^3.2.1"
+ js-yaml "^4.1.0"
+ minimatch "^3.0.4"
+ strip-json-comments "^3.1.1"
+
+"@humanwhocodes/config-array@^0.9.2":
+ version "0.9.3"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.3.tgz#f2564c744b387775b436418491f15fce6601f63e"
+ integrity sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==
+ dependencies:
+ "@humanwhocodes/object-schema" "^1.2.1"
+ debug "^4.1.1"
+ minimatch "^3.0.4"
+
+"@humanwhocodes/object-schema@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+ integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+
+acorn-jsx@^5.3.1:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^8.7.0:
+ version "8.7.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
+ integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
+
+ajv@^6.10.0, ajv@^6.12.4:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+autobind-decorator@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/autobind-decorator/-/autobind-decorator-2.4.0.tgz#ea9e1c98708cf3b5b356f7cf9f10f265ff18239c"
+ integrity sha512-OGYhWUO72V6DafbF8PM8rm3EPbfuyMZcJhtm5/n26IDwO18pohE4eNazLoCGhPiXOCD0gEGmrbU3849QvM8bbw==
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+chalk@^4.0.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+cross-spawn@^7.0.2:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
+debug@^4.1.1, debug@^4.3.2:
+ version "4.3.3"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
+ integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
+ dependencies:
+ ms "2.1.2"
+
+deep-is@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+ integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+doctrine@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+ dependencies:
+ esutils "^2.0.2"
+
+esbuild-android-arm64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.17.tgz#7216810cb8d5b8cd03ce70bdc241dcdd90c34755"
+ integrity sha512-y7EJm8ADC9qKbo/dJ2zBXwNdIILJ76tTv7JDGvOkbLT8HJXIsgbpa0NJk7iFhyvP4GpsYvXTbvEQNn0DhyBhLA==
+
+esbuild-darwin-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.17.tgz#1419e020f41814f8a74ce92b2dcab29a6d47e510"
+ integrity sha512-V2JAP8yyVbW6qR4SVXsEDqRicYM0x5niUuB05IFiE5itPI45k8j2dA2l+DtirR2SGXr+LEqgX347+2VA6eyTiA==
+
+esbuild-darwin-arm64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.17.tgz#95acf1022066d48346a63ffc5e4d36a07b83c9b0"
+ integrity sha512-ENkSKpjF4SImyA2TdHhKiZqtYc1DkMykICe1KSBw0YNF1sentjFI6wu+CRiYMpC7REf/3TQXoems2XPqIqDMlQ==
+
+esbuild-freebsd-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.17.tgz#a3455199862110854937b05a0eecbed3e1aeec41"
+ integrity sha512-2i0nTNJM8ftNTvtR00vdqkru8XpHwAbkR2MBLoK2IDSzjsLStwCj+mxf6v83eVM9Abe3QA8xP+irqOdBlwDQ2g==
+
+esbuild-freebsd-arm64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.17.tgz#8a70f2a36f5b0da7d2efdd6fd02aa78611007fd0"
+ integrity sha512-QOmRi1n+uly2G7BbMbHb86YiFA5aM7B2T96A6OF1VG57LNwXwy8LPVM0PVjl7f9cV3pE3fy3VtXPJHJo8XggTA==
+
+esbuild-linux-32@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.17.tgz#b7123f6e4780687e017454604d909fbe558862e9"
+ integrity sha512-qG5NDk7FHHUVw01rjHESON0HvigF2X80b645TUlgTKsWRlrbzzHhMCmQguA01O5PiCimKnyoxti8aJIFNHpQnQ==
+
+esbuild-linux-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.17.tgz#47a6b510c2f7faef595a4d6257a629e65385fdc3"
+ integrity sha512-De8OcmNvfNyFfQRLWbfuZqau6NpYBJxNTLP7Ls/PqQcw0HAwfaYThutY8ozHpPbKFPa7wgqabXlIC4NVSWT0/A==
+
+esbuild-linux-arm64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.17.tgz#dfd9022b7215ca660d464fcb20597b88887c7e64"
+ integrity sha512-WDEOD/YRA4J1lxhETKZff3gRxGYqqZEiVwIOqNfvCh2YcwWU2y6UmNGZsxcuKk18wot4dAXCXQyNZgBkVUTCLw==
+
+esbuild-linux-arm@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.17.tgz#e6f6bb9fe52def5260d7d49b790fbec0e7c6d9cb"
+ integrity sha512-ZwsgFUk3gR2pEMJdh5z4Ds18fvGETgElPqmNdx1NtZTCOVlFMAwFB5u/tOR2FrXbMFv+LkGnNxPDh48PYPDz9A==
+
+esbuild-linux-mips64le@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.17.tgz#bceaad33ff18a822b6da0396c6497a231397b6c3"
+ integrity sha512-Lf4X9NB7r6imzp/11TaGs4kWL0DUn1JxI9gAAKotnKh6T8Y/0sLvZSvQS8WvSZcr0V8RRCrRZwiQqjOALUU/9g==
+
+esbuild-linux-ppc64le@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.17.tgz#9562f094d1e5e6c3b61b776b15a9bbd657042654"
+ integrity sha512-aExhxbrK7/Mh9FArdiC9MbvrQz2bGCDI8cBALKJbmhKg0h7LNt6y1E1S9GGBZ/ZXkHDvV9FFVrXXZKFVU5Qpiw==
+
+esbuild-linux-s390x@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.17.tgz#2963cfe62c227bbf1da64e36d4ca0b23db8008fe"
+ integrity sha512-b0T20rNcS7POi5YLw5dFlsiC+riobR5IfppQGn5NWer6QiIkdL1vOx9eC9CUD3z1itpkLboRAZYieZfKfhCA2Q==
+
+esbuild-netbsd-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.17.tgz#1d156023f9ae6be79b8627ab0cda2d7feb7f3a48"
+ integrity sha512-pFgTaAa2JF18nqNfCND9wOu1jbZ/mbDSaMxUp5fTkLlofyHhXeb5aChgXUkeipty2Pgq0OwOnxjHmiAxMI7N4g==
+
+esbuild-openbsd-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.17.tgz#3fc44102c9b65375385112f4ce5899ae5e38f349"
+ integrity sha512-K5+plb6gsAfBcFqB0EG4KvLbgBKslVAfEyJggicwt/QoDwQGJAzao4M6zOA4PG7LlXOwWSqv7VmSFbH+b6DyKw==
+
+esbuild-sunos-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.17.tgz#5bd24e7a7e863ea89d7e4eafd5364a155c9ea507"
+ integrity sha512-o1FINkbHRi9JB1YteOSXZdkDOmVUbmnCxRmTLkHvk8pfCFNpv/5/7ktt95teYKbEiJna2dEt3M4ckJ/+UVnW+w==
+
+esbuild-windows-32@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.17.tgz#8bda31c550fb6b425707114141d2c6ba034dab9b"
+ integrity sha512-Qutilz0I7OADWBtWrC/FD+2O/TNAkhwbZ+wIns7kF87lxIMtmqpBt3KnMk1e4F47aTrZRr0oH55Zhztd7m2PAA==
+
+esbuild-windows-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.17.tgz#50b42c06908d3ce9fab8f0f9673199de5d0f9cbc"
+ integrity sha512-b21/oRV+PHrav0HkRpKjbM2yNRVe34gAfbdMppbZFea416wa8SrjcmVfSd7n4jgqoTQG0xe+MGgOpwXtjiB3DQ==
+
+esbuild-windows-arm64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.17.tgz#62d3921a810b64a03fcace76dad4db51d2128b45"
+ integrity sha512-4HN9E1idllewYvptcrrdfTA6DIWgg11kK0Zrv6yjxstJZLJeKxfilGBEaksLGs4Pst2rAYMx3H2vbYq7AWLQNA==
+
+esbuild@^0.14.13:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.17.tgz#6a634e56447aa0e90b34c42091d472d802d399e5"
+ integrity sha512-JLgyC6Uv31mv9T9Mm2xF1LntUMCNBSzvg2n32d8cTKZMwFr1wmMFY2FkVum98TSoEsDff0cR+Aj49H2sbBcjKQ==
+ optionalDependencies:
+ esbuild-android-arm64 "0.14.17"
+ esbuild-darwin-64 "0.14.17"
+ esbuild-darwin-arm64 "0.14.17"
+ esbuild-freebsd-64 "0.14.17"
+ esbuild-freebsd-arm64 "0.14.17"
+ esbuild-linux-32 "0.14.17"
+ esbuild-linux-64 "0.14.17"
+ esbuild-linux-arm "0.14.17"
+ esbuild-linux-arm64 "0.14.17"
+ esbuild-linux-mips64le "0.14.17"
+ esbuild-linux-ppc64le "0.14.17"
+ esbuild-linux-s390x "0.14.17"
+ esbuild-netbsd-64 "0.14.17"
+ esbuild-openbsd-64 "0.14.17"
+ esbuild-sunos-64 "0.14.17"
+ esbuild-windows-32 "0.14.17"
+ esbuild-windows-64 "0.14.17"
+ esbuild-windows-arm64 "0.14.17"
+
+escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-scope@^7.1.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.0.tgz#c1f6ea30ac583031f203d65c73e723b01298f153"
+ integrity sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^5.2.0"
+
+eslint-utils@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
+ integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
+ dependencies:
+ eslint-visitor-keys "^2.0.0"
+
+eslint-visitor-keys@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+ integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+
+eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz#6fbb166a6798ee5991358bc2daa1ba76cc1254a1"
+ integrity sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==
+
+eslint@^8.2.0:
+ version "8.8.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.8.0.tgz#9762b49abad0cb4952539ffdb0a046392e571a2d"
+ integrity sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==
+ dependencies:
+ "@eslint/eslintrc" "^1.0.5"
+ "@humanwhocodes/config-array" "^0.9.2"
+ ajv "^6.10.0"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.2"
+ debug "^4.3.2"
+ doctrine "^3.0.0"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^7.1.0"
+ eslint-utils "^3.0.0"
+ eslint-visitor-keys "^3.2.0"
+ espree "^9.3.0"
+ esquery "^1.4.0"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^6.0.1"
+ functional-red-black-tree "^1.0.1"
+ glob-parent "^6.0.1"
+ globals "^13.6.0"
+ ignore "^5.2.0"
+ import-fresh "^3.0.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ js-yaml "^4.1.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.4.1"
+ lodash.merge "^4.6.2"
+ minimatch "^3.0.4"
+ natural-compare "^1.4.0"
+ optionator "^0.9.1"
+ regexpp "^3.2.0"
+ strip-ansi "^6.0.1"
+ strip-json-comments "^3.1.0"
+ text-table "^0.2.0"
+ v8-compile-cache "^2.0.3"
+
+espree@^9.2.0, espree@^9.3.0:
+ version "9.3.0"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.0.tgz#c1240d79183b72aaee6ccfa5a90bc9111df085a8"
+ integrity sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==
+ dependencies:
+ acorn "^8.7.0"
+ acorn-jsx "^5.3.1"
+ eslint-visitor-keys "^3.1.0"
+
+esquery@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
+ integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
+ dependencies:
+ estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+eventemitter3@^4.0.7:
+ version "4.0.7"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
+ integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+file-entry-cache@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+ dependencies:
+ flat-cache "^3.0.4"
+
+flat-cache@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
+ integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
+ dependencies:
+ flatted "^3.1.0"
+ rimraf "^3.0.2"
+
+flatted@^3.1.0:
+ version "3.2.5"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
+ integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+functional-red-black-tree@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+ integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+
+glob-parent@^6.0.1:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+ integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+ dependencies:
+ is-glob "^4.0.3"
+
+glob@^7.1.3:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
+ integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+globals@^13.6.0, globals@^13.9.0:
+ version "13.12.1"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.1.tgz#ec206be932e6c77236677127577aa8e50bf1c5cb"
+ integrity sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==
+ dependencies:
+ type-fest "^0.20.2"
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+idb-keyval@^6.0.3:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.1.0.tgz#e659cff41188e6097d7fadd69926f6adbbe70041"
+ integrity sha512-u/qHZ75rlD3gH+Zah8dAJVJcGW/RfCnfNrFkElC5RpRCnpsCXXhqjVk+6MoVKJ3WhmNbRYdI6IIVP88e+5sxGw==
+ dependencies:
+ safari-14-idb-fix "^3.0.0"
+
+ignore@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
+ integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
+
+ignore@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
+ integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
+
+import-fresh@^3.0.0, import-fresh@^3.2.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-glob@^4.0.0, is-glob@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
+
+levn@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ dependencies:
+ prelude-ls "^1.2.1"
+ type-check "~0.4.0"
+
+lodash.merge@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+misskey-js@0.0.14:
+ version "0.0.14"
+ resolved "https://registry.yarnpkg.com/misskey-js/-/misskey-js-0.0.14.tgz#1a616bdfbe81c6ee6900219eaf425bb5c714dd4d"
+ integrity sha512-bvLx6U3OwQwqHfp/WKwIVwdvNYAAPk0+YblXyxmSG3dwlzCgBRRLcB8o6bNruUDyJgh3t73pLDcOz3myxcUmww==
+ dependencies:
+ autobind-decorator "^2.4.0"
+ eventemitter3 "^4.0.7"
+ reconnecting-websocket "^4.4.0"
+
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+optionator@^0.9.1:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+ integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+ dependencies:
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+ word-wrap "^1.2.3"
+
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+prelude-ls@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+punycode@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+reconnecting-websocket@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783"
+ integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==
+
+regexpp@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+ integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
+
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+rimraf@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+ dependencies:
+ glob "^7.1.3"
+
+safari-14-idb-fix@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/safari-14-idb-fix/-/safari-14-idb-fix-3.0.0.tgz#450fc049b996ec7f3fd9ca2f89d32e0761583440"
+ integrity sha512-eBNFLob4PMq8JA1dGyFn6G97q3/WzNtFK4RnzT1fnLq+9RyrGknzYiM/9B12MnKAxuj1IXr7UKYtTNtjyKMBog==
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
+
+type-check@^0.4.0, type-check@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ dependencies:
+ prelude-ls "^1.2.1"
+
+type-fest@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+v8-compile-cache@^2.0.3:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
+ integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
+
+which@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+word-wrap@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+ integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=