diff --git a/.autogen/check_pr.jq b/.autogen/check_pr.jq deleted file mode 100644 index 0adb0b503dcd4a7fbec55de156d99dcb91287e88..0000000000000000000000000000000000000000 --- a/.autogen/check_pr.jq +++ /dev/null @@ -1,3 +0,0 @@ -.[] -.head -.label diff --git a/.autogen/next_url.jq b/.autogen/next_url.jq deleted file mode 100644 index b4c3b819a5bee52ae8e5067c742127e273dfe69e..0000000000000000000000000000000000000000 --- a/.autogen/next_url.jq +++ /dev/null @@ -1,2 +0,0 @@ -.links -.next diff --git a/.autogen/patreon.jq b/.autogen/patreon.jq deleted file mode 100644 index c761d587b87d1dd93198d396d8b21fe12551c63d..0000000000000000000000000000000000000000 --- a/.autogen/patreon.jq +++ /dev/null @@ -1,39 +0,0 @@ -( - .data | - map( - select( - .relationships - .currently_entitled_tiers - .data[] - ) - ) | - map( - .relationships - .user - .data - .id - ) -) as $data | -.included | -map( - select( - .id as $id | - $data | - contains( - [ - $id - ] - ) - ) -) | -map( - .attributes | - [ - .full_name, - .thumb_url, - .url - ] | - @tsv -) | -.[] | -@text diff --git a/.autogen/update_readme_patreon.sh b/.autogen/update_readme_patreon.sh deleted file mode 100755 index 7a108c2b6746cdffa5bae7cbc1e579d6d4702380..0000000000000000000000000000000000000000 --- a/.autogen/update_readme_patreon.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env bash -# __MISSKEY_BEARER_TOKEN= -# __MISSKEY_CAMPAIGN_ID= -# __MISSKEY_GITHUB_TOKEN= -# __MISSKEY_HEAD=syuilo:patch-autogen -# __MISSKEY_REPO=syuilo/misskey -# __MISSKEY_BRANCH=develop -test "$(curl -LSs -w '\n' -- "https://api.github.com/repos/$REPO/pulls?access_token=$__MISSKEY_GITHUB_TOKEN" | jq -r -f check_pr.jq | grep $__MISSKEY_HEAD)" && exit 1 -cd "$(dirname $0)/.." && \ -touch null.cache && \ -rm *.cache && \ -git checkout $__MISSKEY_BRANCH && \ -git pull origin $__MISSKEY_BRANCH && \ -git pull upstream $__MISSKEY_BRANCH && \ -git stash && \ -git rebase -f upstream/$__MISSKEY_BRANCH && \ -git branch patch-autogen && \ -git checkout patch-autogen && \ -git reset --hard HEAD || \ -exit 1 -touch patreon.md.cache && \ -rm patreon.md.cache && \ -echo '<!-- PATREON_START -->' > patreon.md.cache && \ -url="https://www.patreon.com/api/oauth2/v2/campaigns/$__MISSKEY_CAMPAIGN_ID/members?include=currently_entitled_tiers,user&fields%5Btier%5D=title&fields%5Buser%5D=full_name,thumb_url,url,hide_pledges" -while : - do - touch patreon.raw.cache && \ - rm patreon.raw.cache && \ - curl -LSs -w '\n' -H "Authorization: Bearer $__MISSKEY_BEARER_TOKEN" -- $url > patreon.raw.cache && \ - touch patreon.cache && \ - rm patreon.cache && \ - cat patreon.raw.cache | \ - jq -r -f patreon.jq >> patreon.cache && \ - echo '<table><tr>' >> patreon.md.cache && \ - cat patreon.cache | \ - awk -F'\t' '{print $2,$1}' | \ - sed -e 's/ /\\" alt=\\"/' | \ - xargs -I% echo '<td><img src="%" width="100"></td>' >> patreon.md.cache && \ - echo '</tr><tr>' >> patreon.md.cache && \ - cat patreon.cache | \ - awk -F'\t' '{print $3,$1}' | \ - sed -e 's/ /\\">/' | \ - xargs -I% echo '<td><a href="%</a></td>' >> patreon.md.cache && \ - echo '</tr></table>' >> patreon.md.cache || \ - exit 1 - new_url="$(cat patreon.raw.cache | jq -r -f next_url.jq)" - test "$new_url" = 'null' && \ - break || \ - URL="$url" -done -ignore= && \ -echo -e "\n**Last updated:** $(date -uR | sed 's/\+0000/UTC/')\n<!-- PATREON_END -->" >> patreon.md.cache && \ -touch README.md && \ -touch .autogen/README.md && \ -rm .autogen/README.md && \ -mv README.md .autogen/README.md && \ -cat .autogen/README.md | while IFS= read line; - do - if [[ -z "$ignore" ]] - then - if [[ "$line" = '<!-- PATREON_START -->' ]] - then - ignore='PATREON_INSIDE' - else - echo "$line" >> README.md - fi - else - if [[ "$LINE" = '<!-- PATREON_END -->' ]] - then - ignore= - cat patreon.md.cache >> README.md - fi - fi -done -cat patreon.md.cache -touch null.cache && \ -rm *.cache && \ -diff .autogen/README.md README.md > diff.cache -cat diff.cache && \ -test 4 -lt $(cat diff.cache | wc -l) && \ -git add README.md && \ -git commit -m 'Update README.md [AUTOGEN]' && \ -git push -f origin patch-autogen && \ -curl -LSs -w '\n' -X POST -d '{"title":"[AUTOMATED] Update README.md","body":"*This pull request was created by a tool.*","head":"'$__MISSKEY_HEAD'","base":"'$__MISSKEY_BRANCH'"}' -- "https://api.github.com/repos/$__MISSKEY_REPO/pulls?access_token=$__MISSKEY_GITHUB_TOKEN" -git stash -git checkout $__MISSKEY_BRANCH -git branch -D patch-autogen diff --git a/.eslintrc b/.eslintrc index 3a220319e5fb8bb1653379bf2a4f15a0bbbc59f9..3e5b59cb048a849557963774729f0ba640d63884 100644 --- a/.eslintrc +++ b/.eslintrc @@ -3,29 +3,11 @@ "parser": "@typescript-eslint/parser" }, "extends": [ - "eslint:recommended", - "plugin:vue/recommended" + "eslint:recommended" ], "rules": { - "vue/require-v-for-key": 0, - "vue/max-attributes-per-line": 0, - "vue/html-indent": 0, - "vue/html-self-closing": 0, - "vue/no-unused-vars": 0, - "vue/attributes-order": 0, - "vue/require-prop-types": 0, - "vue/require-default-prop": 0, - "vue/html-closing-bracket-spacing": 0, - "vue/singleline-html-element-content-newline": 0, - "vue/no-v-html": 0, "no-console": 0, "no-unused-vars": 0, "no-empty": 0 - }, - "globals": { - "ENV": true, - "VERSION": true, - "API": true, - "LANGS": true } } diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..1fd048e8eaf23c34f69442b2bcb30da67f1d252e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +see [releases](https://github.com/syuilo/misskey/releases) diff --git a/assets/favicon.ico b/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9be1ff62956cd5d1e21540bee043a3c6c144d453 Binary files /dev/null and b/assets/favicon.ico differ diff --git a/assets/redoc.html b/assets/redoc.html index 4d2360fb20a7d8d037856dc4cc902d13e7edee7e..9ee5a95c05e9455d406655d8fd53a300274ccf34 100644 --- a/assets/redoc.html +++ b/assets/redoc.html @@ -19,6 +19,6 @@ </head> <body> <redoc spec-url="/api.json" expand-responses="200" expand-single-schema-field="true"></redoc> - <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script> + <script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.50/bundles/redoc.standalone.js" integrity="sha256-WJbngBWN9vp6vkEuzeoSj5tE5saW9Hfj6/SinkzhL2s=" crossorigin="anonymous"></script> </body> </html> diff --git a/gulpfile.ts b/gulpfile.ts index 95b2394887ddeb324e1a579ff32012045c84da83..771a5c0e322d98da6554e2c15033ff0eb411aadd 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -14,7 +14,7 @@ const locales: { [x: string]: any } = require('./locales'); const meta = require('./package.json'); gulp.task('build:ts', () => { - const tsProject = ts.createProject('./tsconfig.json'); + const tsProject = ts.createProject('./src/tsconfig.json'); return tsProject .src() @@ -64,7 +64,6 @@ gulp.task('build:client:style', () => { gulp.task('build:copy', gulp.parallel('build:copy:locales', 'build:copy:views', 'build:client:script', 'build:client:style', 'build:copy:fonts', () => gulp.src([ './src/emojilist.json', - './src/server/web/views/**/*', './src/**/assets/**/*', '!./src/client/assets/**/*' ]).pipe(gulp.dest('./built/')) @@ -78,17 +77,16 @@ gulp.task('cleanall', gulp.parallel('clean', cb => rimraf('./node_modules', cb) )); -gulp.task('copy:docs', () => - gulp.src([ - './src/docs/**/*', - ]) - .pipe(gulp.dest('./built/assets/docs/')) -); - gulp.task('build', gulp.parallel( 'build:ts', 'build:copy', - 'copy:docs', )); gulp.task('default', gulp.task('build')); + +gulp.task('watch', () => { + gulp.watch([ + './src/**/*', + '!./src/client/**/*' + ], { ignoreInitial: false }, gulp.task('build')); +}); diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 16815ab25d81977783ace4c82c6a196fb9add54d..1b3ccbbb0cc042740ec67a3bb24bc6c5fe29244a 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -110,7 +110,7 @@ attachCancel: "Supprimer le fichier attaché" markAsSensitive: "Marquer comme sensible" unmarkAsSensitive: "Supprimer le marquage comme sensible" enterFileName: "Entrer le nom du fichier" -mute: "Mettre en sourdine" +mute: "Masquer" unmute: "Ne plus masquer" block: "Bloquer" unblock: "Débloquer" @@ -206,7 +206,7 @@ all: "Tous" subscribing: "Abonné" publishing: "Publié" notResponding: "Ne répond pas" -instanceFollowing: "Suivre une instance" +instanceFollowing: "Abonnements de l'instance" instanceFollowers: "Abonné·e·s de l’instance" instanceUsers: "Utilisateur·rice·s de cette l’instance" changePassword: "Modifier votre mot de passe" @@ -317,12 +317,12 @@ disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur registration: "S’inscrire" enableRegistration: "Autoriser les nouvelles inscriptions" invite: "Inviter" -proxyRemoteFiles: "Proxy fichiers distants" +proxyRemoteFiles: "Utiliser les fichiers distants comme proxy" proxyRemoteFilesDescription: "Si vous activez ce paramètre, les fichiers distants non stockés ou supprimés en raison d'une capacité excédentaire seront affichés via un proxy local et généreront une miniature. Cela n'affectera pas le stockage du serveur." driveCapacityPerLocalAccount: "Volume du Drive par utilisateur local" driveCapacityPerRemoteAccount: "Volume du Drive par utilisateur distant" inMb: "en mégaoctets" -iconUrl: "URL de l’image de l’avatar" +iconUrl: "URL de l'icône" bannerUrl: "URL de l’image de la bannière" basicInfo: "Informations basiques" pinnedUsers: "Utilisateur·rice épinglé·e" @@ -491,7 +491,7 @@ objectStorageUseProxyDesc: "Désactivez cette option si vous n'utilisez pas Prox objectStorageSetPublicRead: "Régler sur « public » lors de l'envoi" serverLogs: "Journal du serveur" deleteAll: "Supprimer tout" -showFixedPostForm: "Afficher le formulaire en haut du fil d'actualité" +showFixedPostForm: "Afficher le formulaire de publication en haut du fil d'actualité" newNoteRecived: "Vous avez reçu une nouvelle note" sounds: "Sons" listen: "Écouter" @@ -616,12 +616,14 @@ openInNewTab: "Ouvrir dans un nouvel onglet" openInSideView: "Ouvrir en vue latérale" defaultNavigationBehaviour: "Navigation par défaut" editTheseSettingsMayBreakAccount: "La modification de ces paramètres peut endommager votre compte." +instanceTicker: "Nom de l'instance d'origine des notes" waitingFor: "En attente de {x}" random: "Aléatoire" system: "Système" switchUi: "Modifier l'interface utilisateur" desktop: "Bureau" clip: "Clip" +createNew: "Créer nouveau" optional: "Facultatif" createNewClip: "Créer un nouveau clip" public: "Public" @@ -806,6 +808,7 @@ _reversi: canPutEverywhere: "Les pions peuvent être placés partout " _instanceTicker: none: "Cacher " + remote: "Montrer pour les utilisateur·ice·s distant·e·s" always: "Toujours afficher" _serverDisconnectedBehavior: reload: "Rechargement automatique" @@ -823,11 +826,12 @@ _channel: notesCount: "{n} Notes" _sidebar: full: "Complet" - icon: "Avatar" + icon: "Icônes" hide: "Masquer" _wordMute: muteWords: "Mots à filtrer" muteWordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec un saut de ligne pour une condition OR." + muteWordsDescription2: "Pour utiliser des expressions régulières (regex), mettez les mots-clés entre barres obliques." softDescription: "Masquez les notes de votre fil selon les paramètres que vous définissez." hardDescription: "Empêchez votre fil de charger les notes selon les paramètres que vous définissez. Cette action est irréversible : si vous modifiez ces paramètres plus tard, les notes précédemment filtrées ne seront pas récupérées." soft: "Doux" @@ -902,6 +906,8 @@ _sfx: chatBg: "Discuter (De fond)" antenna: "Réception de l’antenne" channel: "Notifications de canal" + reversiPutBlack: "Reversi : les pions noirs ont joué" + reversiPutWhite: "Reversi : les pions blancs ont joué" _ago: unknown: "Inconnu" future: "Futur" @@ -953,12 +959,12 @@ _2fa: _permissions: "read:account": "Afficher les informations du compte" "write:account": "Mettre à jour les informations de votre compte" - "read:blocks": "Voir les blocs" - "write:blocks": "Écrire des blocs" + "read:blocks": "Voir les comptes bloqués" + "write:blocks": "Gérer les comptes bloqués" "read:drive": "Parcourir le Drive" "write:drive": "Écrire sur le Drive" "read:favorites": "Afficher les favoris" - "write:favorites": "Écrire des favoris" + "write:favorites": "Gérer les favoris" "read:following": "Voir les informations de vos abonnements" "write:following": "Abonnements/Se désabonner" "read:messaging": "Cherche à discuter" @@ -1012,7 +1018,7 @@ _widgets: photos: "Photos" digitalClock: "Horloge numérique" federation: "Fédération" - postForm: "Formulaire à publier" + postForm: "Formulaire de publication" slideshow: "Diaporama" button: "Bouton" onlineUsers: "Utilisateurs en ligne" @@ -1083,8 +1089,8 @@ _profile: _exportOrImport: allNotes: "Toutes les notes" followingList: "Abonnements" - muteList: "Liste des comptes maqués" - blockingList: "Bloquer" + muteList: "Comptes masqués" + blockingList: "Comptes bloqués" userLists: "Listes" _charts: federationInstancesIncDec: "Variation du nombre des instances fédérées" @@ -1228,17 +1234,17 @@ _pages: if: "Si" _if: variable: "Variables" - post: "Formulaire à publier" + post: "Formulaire de publication" _post: text: "Contenu" attachCanvasImage: "Publier avec Toile comme image" canvasId: "Toile ID" - textInput: "Entrée de textuelle" + textInput: "Entrée textuelle" _textInput: name: "Nom de la variable" text: "Titre" default: "Valeur par défaut" - textareaInput: "Entrée de textuelle multiligne" + textareaInput: "Entrée textuelle multi-ligne" _textareaInput: name: "Nom de la variable" text: "Titre" @@ -1253,10 +1259,12 @@ _pages: id: "Toile ID" width: "Largeur" height: "Hauteur" + note: "Note intégrée" _note: id: "Identifiant de la note" + idDescription: "Pour configurer la note, vous pouvez aussi coller ici l'URL correspondante." detailed: "Afficher les détails" - switch: "Basculer" + switch: "Interrupteur" _switch: name: "Nom de la variable" text: "Titre" @@ -1265,16 +1273,16 @@ _pages: _counter: name: "Nom de la variable" text: "Titre" - inc: "Augmenter le chiffre" + inc: "Augmenter de" _button: text: "Titre" colored: "Coloré" - action: "L'opération lorsque le bouton sera pressé" + action: "Opération à effectuer lorsque le bouton est pressé" _action: dialog: "Afficher une fenêtre de dialogue" _dialog: content: "Contenu" - resetRandom: "Réinitialiser le nombre aléatoire" + resetRandom: "Réinitialiser un nombre aléatoire" pushEvent: "Envoyer un évènement" _pushEvent: event: "Nom de l’évènement" @@ -1288,7 +1296,7 @@ _pages: _radioButton: name: "Nom de la variable" title: "Titre" - values: "Choix séparés par des sauts de ligne" + values: "Liste des choix (un par ligne)" default: "Valeur par défaut" script: categories: @@ -1304,7 +1312,7 @@ _pages: list: "Listes" blocks: text: "Texte" - multiLineText: "Texte (Multi-lignes)" + multiLineText: "Texte (multi-ligne)" textList: "Liste de texte" _textList: info: "Veuillez séparer chaque entrée avec un saut de ligne" @@ -1347,10 +1355,10 @@ _pages: _mod: arg1: "A" arg2: "B" - round: "Décimal rond" + round: "Arrondir les décimales" _round: arg1: "Numérique" - eq: "A et B sont équivalents" + eq: "A et B sont égaux" _eq: arg1: "A" arg2: "B" @@ -1366,7 +1374,7 @@ _pages: _or: arg1: "A" arg2: "B" - lt: "A est plus petit que B" + lt: "A est inférieur à B" _lt: arg1: "A" arg2: "B" @@ -1374,7 +1382,7 @@ _pages: _gt: arg1: "A" arg2: "B" - ltEq: "A est plus petit ou égal à B" + ltEq: "A est inférieur ou égal à B" _ltEq: arg1: "A" arg2: "B" @@ -1440,7 +1448,7 @@ _pages: numberToString: "Convertir du numérique en texte" _numberToString: arg1: "Numérique" - splitStrByLine: "Séparer le texte par lignes" + splitStrByLine: "Séparer le texte par des sauts de lignes" _splitStrByLine: arg1: "Texte" ref: "Variables" @@ -1448,7 +1456,7 @@ _pages: fn: "Fonction" _fn: slots: "Slots" - slots-info: "Veuillez délimiter chaque slot par un saut de ligne" + slots-info: "Veuillez insérer un seul slot par ligne" arg1: "Sortie" for: "Répéter" _for: @@ -1482,18 +1490,19 @@ _notification: youWereFollowed: "Vous suit" youReceivedFollowRequest: "Vous avez reçu une demande d’abonnement" yourFollowRequestAccepted: "Votre demande d’abonnement a été accepté" - youWereInvitedToGroup: "Invité au groupe" + youWereInvitedToGroup: "Invité·e au groupe" _types: all: "Toutes" follow: "Abonnements" - mention: "Mentionner" + mention: "Mentions" reply: "Réponses" renote: "Partager" quote: "Citer" reaction: "Réactions" + pollVote: "Votes dans des sondages" receiveFollowRequest: "Demande d'abonnement reçue" followRequestAccepted: "Demande d'abonnement acceptée" - groupInvited: "Invité aux groupes" + groupInvited: "Invitation à un groupe" app: "Notifications provenant des apps" _deck: alwaysShowMainColumn: "Toujours afficher la colonne principale" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 4e2e4a5353b9d837aa4aa406b23075b5ced4bb9a..25ee8c6f6b5bacfa7268b043a01ad057dc2bbfdc 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -1,6 +1,7 @@ --- _lang_: "Italiano" headlineMisskey: "Rete collegata tramite note" +introMisskey: "Benvenut@! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \nScrivi \"note\" per condividere ciò che sta succedendo adesso o per dire a tutti qualcosa di te. 📡\nGrazie alla funzione \"reazioni\" puoi anche mandare reazioni rapide alle note delle altre persone del Fediverso. ðŸ‘\nEsplora un nuovo mondo! 🚀" monthAndDay: "{day}/{month}" search: "Cerca" notifications: "Notifiche" @@ -11,7 +12,7 @@ ok: "OK" gotIt: "Capito!" cancel: "Annulla" enterUsername: "Inserisci un nome utente" -renotedBy: "Condiviso da {user}" +renotedBy: "Rinotato da {user}" noNotes: "Nessuna nota!" noNotifications: "Nessuna notifica" instance: "Istanza" @@ -21,6 +22,7 @@ otherSettings: "Altre impostazioni" openInWindow: "Apri in una finestra" profile: "Profilo" timeline: "Timeline" +noAccountDescription: "L'utente non ha ancora scritto niente nella biografia di profilo." login: "Accedi" loggingIn: "Accesso in corso..." logout: "Esci" @@ -32,15 +34,16 @@ addUser: "Aggiungi utente" favorite: "Preferiti" favorites: "Preferiti" unfavorite: "Rimuovi nota dai preferiti" -favorited: "Aggiunta ai preferiti." -alreadyFavorited: "Già tra i preferiti." -cantFavorite: "Impossibile aggiungere ai Preferiti." +favorited: "Aggiunta ai tuoi preferiti." +alreadyFavorited: "Già tra i tuoi preferiti." +cantFavorite: "Impossibile aggiungere la nota ai preferiti." pin: "Fissa sul profilo" unpin: "Non fissare sul profilo" copyContent: "Copia il contenuto" -copyLink: "Copia link" +copyLink: "Copia il link" delete: "Elimina" -deleteAndEdit: "Elimina & Modifica" +deleteAndEdit: "Elimina e modifica" +deleteAndEditConfirm: "Vuoi davvero cancellare questa nota e scriverla di nuovo? Verrano eliminate anche tutte le reazioni, Rinote e risposte collegate." addToList: "Aggiungi alla lista" sendMessage: "Invia messaggio" copyUsername: "Copia nome utente" @@ -49,65 +52,104 @@ reply: "Rispondi" loadMore: "Mostra di più" showMore: "Mostra di più" youGotNewFollower: "Ha iniziato a seguirti" -receiveFollowRequest: "Nuova richiesta di essere seguito" +receiveFollowRequest: "Hai ricevuto una richiesta di follow." +followRequestAccepted: "Richiesta di follow accettata" mention: "Menzioni" mentions: "Menzioni" directNotes: "Note dirette" -importAndExport: "Importa ed Esporta" +importAndExport: "Importa ed esporta" import: "Importa" export: "Esporta" -files: "Allegato" +files: "Allegati" download: "Scarica" +driveFileDeleteConfirm: "Vuoi davvero eliminare il file「{name}? Anche gli allegati verranno eliminati." +unfollowConfirm: "Vuoi davvero smettere di seguire {name}?" +exportRequested: "Hai richiesto un'esportazione, e potrebbe volerci tempo. Quando sarà compiuta, il file verrà aggiunto direttamente al Drive." +importRequested: "Hai richiesto un'importazione. Può volerci tempo. " lists: "Liste" -noLists: "Qui non c'è ancora niente" +noLists: "Nessuna lista" note: "Nota" notes: "Note" -following: "Seiguiti" -followers: "Seguaci" +following: "Follows" +followers: "Followers" followsYou: "Ti segue" -createList: "Crea una nuova lista" -manageLists: "Modifica lista" +createList: "Aggiungi una nuova lista" +manageLists: "Gestisci liste" error: "Errore" -somethingHappened: "Qualcosa è andato storto." +somethingHappened: "Si è verificato un problema" retry: "Riprova" -enterListName: "Inserisci il nome della lista" +pageLoadError: "Caricamento pagina non riuscito. " +enterListName: "Nome della lista" privacy: "Privacy" +makeFollowManuallyApprove: "Richiedi di approvare i follower manualmente" +defaultNoteVisibility: "Privacy predefinita delle note" follow: "Segui" -followRequest: "Richiesta di seguire" -followRequests: "Richiesta di seguire" +followRequest: "Richiesta di follow" +followRequests: "Richieste di follow" unfollow: "Smetti di seguire" -followRequestPending: "In sospeso" +followRequestPending: "La richiesta di follow deve essere approvata" enterEmoji: "Inserisci emoji" renote: "Rinota" unrenote: "Annulla rinota" -renoted: "Condiviso!" -cantReRenote: "È impossibile rinota una condivisione." +renoted: "Rinotato!" +cantRenote: "È impossibile rinotare questa nota." +cantReRenote: "È impossibile rinotare una Rinota." quote: "Cita" +pinnedNote: "Nota fissata" pinned: "Fissa sul profilo" you: "Tu" clickToShow: "Clicca per visualizzare" sensitive: "Contenuto sensibile" add: "Aggiungi" reaction: "Reazione" +reactionSettingDescription: "Scegli le reazioni che preferisci e fissale nel pannello di reazioni." +reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere." +rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note" attachCancel: "Rimuovi allegato" markAsSensitive: "Segna come sensibile" unmarkAsSensitive: "Segna come non sensibile" +enterFileName: "Nome del file" mute: "Silenzia" unmute: "Riattiva" block: "Blocca" unblock: "Sblocca" suspend: "Sospendi" unsuspend: "Annulla la sospensione dell'account" -blockConfirm: "Vuoi bloccare?" -unblockConfirm: "Vuoi sbloccare?" +blockConfirm: "Vuoi davvero bloccare l'account?" +unblockConfirm: "Vuoi davvero sbloccare l'account?" +suspendConfirm: "Vuoi davvero sospendere questo account?" +unsuspendConfirm: "Vuoi annullare la sospensione dell'account?" +selectList: "Seleziona una lista" +selectAntenna: "Scegli un'antenna" +selectWidget: "Seleziona widget" +editWidgets: "Modifica i widget" editWidgetsExit: "Modifica fine" +customEmojis: "Emoji personalizzati" emoji: "Emoji" -addAcount: "Aggiungi un account esistente" +emojiName: "Nome dell'emoji" +emojiUrl: "URL dell'emoji" +addEmoji: "Aggiungi un emoji" +settingGuide: "Configurazione suggerita" +cacheRemoteFiles: "Memorizzazione nella cache dei file remoti" +flagAsBot: "Io sono un robot" +flagAsBotDescription: "Se l'account esegue principalmente operazioni automatiche, attiva quest'opzione. Quando attivata, opera come un segnalatore per gli altri sviluppatori allo scopo di prevenire catene d’interazione senza fine con altri bot, e di adeguare i sistemi interni di Misskey perché trattino questo account come un bot." +flagAsCat: "Io sono un gatto" +flagAsCatDescription: "Abilita l'opzione \"Io sono un gatto\" per l'account." +autoAcceptFollowed: "Accetta automaticamente le richieste di follow da utenti che già segui" +addAcount: "Aggiungi account" +loginFailed: "Accesso non riuscito" +showOnRemote: "Sfoglia sull'istanza remota" general: "Generali" wallpaper: "Sfondo" setWallpaper: "Imposta sfondo" removeWallpaper: "Elimina lo sfondo" searchWith: "Cerca: {q}" +youHaveNoLists: "Non hai ancora creato nessuna lista" +followConfirm: "Sei sicur@ di voler seguire {name}?" +proxyAccount: "Account proxy" +host: "Server remoto" +selectUser: "Seleziona utente" +recipient: "Destinatario" annotation: "Descrizione" federation: "Federazione" instances: "Istanza" @@ -115,36 +157,62 @@ storageUsage: "Volume di dischi" charts: "Grafici" perHour: "All'ora" perDay: "al giorno" +blockThisInstance: "Blocca l'istanza" +operations: "Operazioni" software: "Software" version: "Versione" metadata: "Metadato" +withNFiles: "{n} file in allegato" +monitor: "Monitorare" +jobQueue: "Coda di lavoro" cpuAndMemory: "CPU e Memoria" network: "Rete" disk: "Disco" -instanceInfo: "Informazioni di istanza" +instanceInfo: "Informazioni sull'istanza" statistics: "Statistiche" -clearQueue: "Cancella coda" -clearQueueConfirmTitle: "Cancella coda?" -blockedInstances: "Istanza bloccati" -muteAndBlock: "Silenziamento e blocco" +clearQueue: "Svuota coda" +clearQueueConfirmTitle: "Vuoi davvero svuotare la coda?" +clearCachedFiles: "Svuota cache" +clearCachedFilesConfirm: "Vuoi davvero svuotare la cache da tutti i file remoti?" +blockedInstances: "Istanze bloccate" +blockedInstancesDescription: "Elenca le istanze che vuoi bloccare, una per riga. Esse non potranno più interagire con la tua istanza." +muteAndBlock: "Silenziati / Bloccati" mutedUsers: "Account silenziati" blockedUsers: "Account bloccati" +noUsers: "Nessun utente trovato" editProfile: "Modifica profilo" noteDeleteConfirm: "Eliminare questo Nota?" +pinLimitExceeded: "Non puoi fissare altre note " +intro: "L'installazione di Misskey è finita! Si prega di creare un account amministratore." done: "Fine" processing: "In elaborazione" -blocked: "Bloccati" +preview: "Anteprima" +default: "Predefinito" +noCustomEmojis: "Nessun emoji" +noJobs: "Nessun lavoro" +federating: "Federando" +blocked: "Bloccato" +suspended: "Sospes@" all: "Tutti" +subscribing: "Iscrivendo" +publishing: "Pubblicando" notResponding: "Nessuna risposta" +instanceFollowing: "Seguiti dall'istanza" +instanceFollowers: "Followers dell'istanza" +instanceUsers: "Utenti dell'istanza" changePassword: "Aggiorna Password" security: "Sicurezza" retypedNotMatch: "Le password non corrispondono." currentPassword: "Password attuale" newPassword: "Nuova Password" -newPasswordRetype: "Conferma nuova password" +newPasswordRetype: "Conferma password" +attachFile: "Allega file" more: "Altri!" +featured: "Tendenze" +usernameOrUserId: "Nome utente o ID utente" +noSuchUser: "Nessun utente trovato" lookup: "Cercare" -announcements: "Annuncio" +announcements: "Annunci" imageUrl: "URL dell'immagine" remove: "Elimina" removed: "Il tuo Tweet è stato eliminato" @@ -154,31 +222,66 @@ resetAreYouSure: "Reimposta" saved: "Salvato" messaging: "Messaggi" upload: "Carica" +fromDrive: "Dal Drive" +fromUrl: "Dall'URL" uploadFromUrl: "Incolla URL immagine" +uploadFromUrlDescription: "URL del file che vuoi caricare" +uploadFromUrlRequested: "Caricamento richiesto" +uploadFromUrlMayTakeTime: "Il caricamento del file può richiedere tempo." explore: "Esplora" games: "Misskey Giochi" messageRead: "Visualizzato" +noMoreHistory: "Non c'è più cronologia da visualizzare" startMessaging: "Nuovo messaggio" +nUsersRead: "Letto da {n} persone" +agreeTo: "Sono d'accordo con {0}" tos: "Termini di servizio" +start: "Inizia!" home: "Home" +remoteUserCaution: "Può darsi che le informazioni siano incomplete perché questo è un utente remoto." +activity: "Attività " images: "Immagini" birthday: "Compleanno" yearsOld: "{age}Anni" registeredDate: "Iscrizione a.." location: "Posizione" theme: "Tema" +themeForLightMode: "Tema da utilizzare per il modo chiaro" +themeForDarkMode: "Tema da utilizzare per il modo scuro" light: "Chiaro" dark: "Scuro" lightThemes: "Tema Chiaro" darkThemes: "Tema Scuro" +syncDeviceDarkMode: "Sincronizza il tema scuro con le impostazioni del dispositivo" drive: "Drive" fileName: "Nome dell'allegato" +selectFile: "Scelta allegato" +selectFiles: "Scelta allegato" +selectFolder: "Seleziona cartella" +selectFolders: "Seleziona cartella" +renameFile: "Rinomina file" +folderName: "Nome della cartella" +createFolder: "Nuova cartella" +renameFolder: "Rinominare cartella" +deleteFolder: "Elimina cartella" +addFile: "Allega" +emptyDrive: "Il Drive è vuoto" +emptyFolder: "La cartella è vuota" +unableToDelete: "Eliminazione impossibile" +inputNewFileName: "Inserisci nome del nuovo file" +inputNewFolderName: "Inserisci nome della nuova cartella" +circularReferenceFolder: "La cartella di destinazione è una sottocartella della cartella che vuoi spostare." +hasChildFilesOrFolders: "Impossibile eliminare la cartella perché non è vuota" copyUrl: "Copia URL" rename: "Modifica nome" avatar: "Foto del profilo" -banner: "Foto d'intestazione" +banner: "Intestazione" nsfw: "Contenuti sensibili" +whenServerDisconnected: "Quando la connessione col server è persa" +disconnectedFromServer: "Disconness@ dal server" reload: "Ricarica" +doNothing: "Nessun'azione" +reloadConfirm: "Vuoi ricaricare?" watch: "Osserva" unwatch: "Smetti di Osserva" accept: "Accetta" @@ -195,21 +298,64 @@ today: "Oggi" dayX: "{day}" monthX: "{month}" yearX: "{year}" +pages: "Pagine" integration: "App collegate" connectSerice: "Connetti" disconnectSerice: "Disconnetti" +enableLocalTimeline: "Abilita Timeline locale" +enableGlobalTimeline: "Abilita Timeline federata" +disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e i moderatori potranno sempre accederci." registration: "Iscriviti" +enableRegistration: "Permettere nuove registrazioni" invite: "Invita" -bannerUrl: "indirizzo Foto d'intestazione" +proxyRemoteFiles: "Usare file remoti come proxy" +driveCapacityPerLocalAccount: "Volume del Drive per utente locale" +driveCapacityPerRemoteAccount: "Volume del Drive per utente remoto" +inMb: "in Megabytes" +iconUrl: "URL di icona (favicon, ecc.)" +bannerUrl: "URL dell'immagine d'intestazione" basicInfo: "Informazioni fondamentali" +pinnedUsers: "Utenti in evidenza" +pinnedUsersDescription: "Elenca gli/le utenti che vuoi fissare in cima alla pagina \"Esplora\", un@ per riga." +pinnedPages: "Pagine in evidenza" +pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima alla pagina dell'istanza. Una pagina per riga." +pinnedClipId: "ID della clip in evidenza" +pinnedNotes: "Nota fissata" hcaptcha: "hCaptcha" enableHcaptcha: "Abilita hCaptcha" +hcaptchaSiteKey: "Chiave del sito" +hcaptchaSecretKey: "Chiave segreta" recaptcha: "reCAPTCHA" enableRecaptcha: "Abilita reCAPTCHA" +recaptchaSiteKey: "Chiave del sito" +recaptchaSecretKey: "Chiave segreta" +antennas: "Antenne" +manageAntennas: "Gestore delle antenne" name: "Nome" +antennaSource: "Fonte dell'antenna" +antennaKeywords: "Parole chiavi da ricevere" +antennaExcludeKeywords: "Parole chiavi da escludere" +antennaKeywordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con un'interruzzione riga indica la condizione \"O\"." +notifyAntenna: "Invia notifiche delle nuove note" +withFileAntenna: "Solo note con file in allegato" serviceworker: "ServiceWorker" +enableServiceworker: "Abilita ServiceWorker" +antennaUsersDescription: "Inserisci solo un nome utente per riga" +caseSensitive: "Sensibile alla distinzione tra maiuscole e minuscole" +withReplies: "Includere le risposte" +connectedTo: "Sei conness@ agli account qui sotto:" notesAndReplies: "Note e risposte" -exploreFediverse: "Esplora Fediverse" +withFiles: "Con file in allegato" +silence: "Silenzia" +silenceConfirm: "Vuoi davvero silenziare l'utente?" +unsilence: "Riattiva" +unsilenceConfirm: "Vuoi davvero riattivare l'utente?" +popularUsers: "Utenti popolari" +recentlyUpdatedUsers: "Utenti attivi di recente" +recentlyRegisteredUsers: "Utenti registrati di recente" +recentlyDiscoveredUsers: "Utenti scoperti di recente" +exploreUsersCount: "Ci sono {count} utenti" +exploreFediverse: "Esplora il Fediverso" popularTags: "Tag di tendenza" userList: "Liste" about: "Informazioni" @@ -218,96 +364,243 @@ administrator: "Amministratore" token: "Token" twoStepAuthentication: "Autenticazione a due fattori" moderator: "Moderatore" +nUsersMentioned: "{n} utenti menzionatÉ™" +securityKey: "Chiave di sicurezza" +securityKeyName: "Nome della chiave" +registerSecurityKey: "Registra una chiave di sicurezza" lastUsed: "Ultima attività " -unregister: "Disattiva account" +unregister: "Annulla l'iscrizione" +passwordLessLogin: "Accedi senza password" resetPassword: "Reimposta password" -share: "Renota" +newPasswordIs: "La tua nuova password è「{password}ã€" +reduceUiAnimation: "Ridurre le animazioni dell'interfaccia" +share: "Condividi" +notFound: "Non trovato" +notFoundDescription: "Nessuna pagina corrisponde all'URL indicata." +uploadFolder: "Destinazione caricamento predefinita" cacheClear: "Svuota cache" +markAsReadAllNotifications: "Segna tutte le notifiche come lette" +markAsReadAllUnreadNotes: "Segna tutte le note come lette" +markAsReadAllTalkMessages: "Segna tutte le chat come lette" help: "Guida" +inputMessageHere: "Scrivi messaggio qui" close: "Chiudi" group: "Gruppo" groups: "Gruppi" createGroup: "Nuovo gruppo" -invites: "Invita" +ownedGroups: "I miei gruppi" +joinedGroups: "Gruppi a cui mi sono unit@" +invites: "Inviti" +groupName: "Nome del gruppo" +members: "Membri" transfer: "Trasferisci" +messagingWithUser: "Iniziare una chat con un altr@ utente" +messagingWithGroup: "Chattare in gruppo" title: "Titolo" +text: "Testo" +enable: "Abilita" next: "Avanti" +retype: "Conferma" noteOf: "Note di {user}" +inviteToGroup: "Invitare al gruppo" +maxNoteTextLength: "Lunghezza massima delle note" +quoteAttached: "Citazione allegata" +quoteQuestion: "Vuoi aggiungere una citazione?" +noMessagesYet: "Ancora nessuna chat" +newMessageExists: "Hai ricevuto un nuovo messaggio" +onlyOneFileCanBeAttached: "È possibile allegare al messaggio soltanto uno file" +signinRequired: "Devi essere registrat@ nel tuo account" invitations: "Invita" invitationCode: "Codice di invito" +checking: "Confermando" available: "Consigliati" unavailable: "Il nome utente è già in uso" usernameInvalidFormat: "Il nome utente può contenere solo lettere, numeri e '_'" tooShort: "Troppo breve" tooLong: "Troppo lungo" +weakPassword: "Password debole" +normalPassword: "Password buona" +strongPassword: "Password forte" +passwordMatched: "Corretta" passwordNotMatched: "Le password non corrispondono." +signinWith: "Accedi con {x}" +signinFailed: "Autenticazione non riuscita. Controlla la tua password e nome utente." +tapSecurityKey: "Premi la chiave di sicurezza" +or: "oppure" +language: "Lingua" +uiLanguage: "Lingua di visualizzazione dell'interfaccia" +groupInvited: "Invitat@ al gruppo" +aboutX: "Informazioni su {x}" +useOsNativeEmojis: "Usare le emoji native del sistema operativo" +youHaveNoGroups: "Nessun gruppo" +joinOrCreateGroup: "Puoi creare il tuo gruppo o essere invitat@ a gruppi che già esistono." +noHistory: "Nessuna cronologia" signinHistory: "Cronologia di accesso all'account" +category: "Categoria" tags: "Tag" +docSource: "Sorgente della scheda" createAccount: "Crea il tuo account" existingAcount: "Account esistente" +regenerate: "Generare di nuovo" +fontSize: "Dimensione carattere" +noFollowRequests: "Non hai alcuna richiesta di follow" +openImageInNewTab: "Aprire immagini in una nuova scheda" +dashboard: "Pannello di controllo" local: "Locale" remote: "Remoto" -accountSettings: "Impostazioni Account" +total: "Totale" +weekOverWeekChanges: "Settimanale" +dayOverDayChanges: "Giornaliero" +appearance: "Aspetto" +clientSettings: "Impostazioni client" +accountSettings: "Impostazioni account" +promotion: "Promossa" promote: "Pubblicizza" +numberOfDays: "Numero di giorni" +hideThisNote: "Nasconda la nota" +showFeaturedNotesInTimeline: "Mostrare le note di tendenza nella tua timeline" +objectStorage: "Stoccaggio oggetti" +useObjectStorage: "Utilizza stoccaggio oggetti" objectStorageBaseUrl: "Base URL" objectStorageBucket: "Bucket" +objectStoragePrefix: "Prefix" +objectStoragePrefixDesc: "I file saranno conservati sotto la directory di questo prefisso." objectStorageEndpoint: "Endpoint" objectStorageRegion: "Region" objectStorageUseSSL: "Usare SSL" serverLogs: "Log del server" deleteAll: "Cancella cronologia" -sounds: "Effetti sonori" +showFixedPostForm: "Visualizzare la finestra di pubblicazione in cima alla timeline" +newNoteRecived: "Nuova nota ricevuta" +sounds: "Impostazioni suoni" listen: "Ascolta" none: "Niente" +showInPage: "Visualizza in pagina" +popout: "Finestra pop-out" volume: "Volume" +masterVolume: "Volume principale" details: "Dettagli" +chooseEmoji: "Scegli emoji" +unableToProcess: "Impossibile compiere l'operazione" +recentUsed: "Usato di recente" install: "Installa" uninstall: "Disinstalla" +installedApps: "Applicazioni installate" +nothing: "Niente da visualizzare" installedDate: "Data installazione" +lastUsedDate: "Data di ultimo uso" state: "Stato" sort: "Ordina per" -visibility: "Privacy dei post" +ascendingOrder: "Ascendente" +descendingOrder: "Discendente" +scratchpad: "ScratchPad" +output: "Uscita" +script: "Script" +disablePagesScript: "Disabilita AiScript nelle pagine" +updateRemoteUser: "Aggiornare le informazioni di utente remoto" +deleteAllFiles: "Elimina tutti i file" +deleteAllFilesConfirm: "Vuoi davvero eliminare tutti i file?" +removeAllFollowing: "Cancella tutti i follows" +removeAllFollowingDescription: "Cancella tutti i follows del server {host}. Per favore, esegui se, ad esempio, l'istanza non esiste più." +userSuspended: "L'utente è sospes@." +userSilenced: "L'utente è silenziat@." +sidebar: "Barra laterale" +divider: "Linea di separazione" +addItem: "Aggiungi elemento" +rooms: "Camera" +serviceworkerInfo: "Deve essere abilitato per le notifiche push. " +deletedNote: "Nota eliminata" +invisibleNote: "Nota invisibile" +enableInfiniteScroll: "Abilita scorrimento infinito" +visibility: "Visibilità " poll: "Sondaggio" useCw: "Nascondere media" +expandTweet: "Espandi tweet" +themeEditor: "Editor di temi" description: "Descrizione" author: "Autore" +leaveConfirm: "Ci sono delle modifiche ancora non salvate. Vuoi cancellarle?" +manage: "Gestione" +useFullReactionPicker: "Usa la totalità del pannello di reazioni" width: "Larghezza" height: "Altezza" large: "Grande" medium: "Predefinito" small: "Piccolo" +enableAll: "Abilita tutto" +disableAll: "Disabilita tutto" +tokenRequested: "Autorizza accesso all'account" +notificationType: "Tipo di notifiche" edit: "Modifica" +useStarForReactionFallback: "Se è sconosciuto l'emoji di reazione, usare la ★ come alternativa." +emailConfig: "Impostazioni server email" email: "Email" +smtpHost: "Server remoto" smtpUser: "Nome utente" smtpPass: "Password" -wordMute: "Parole silenziate" +wordMute: "Filtri parole" +userSaysSomething: "{name} ha detto qualcosa" display: "Visualizza" copy: "Copia" logs: "Log" database: "Base di dati" channel: "Canale" -notificationSetting: "impostazioni delle notifiche" +create: "Crea" +notificationSetting: "Impostazioni notifiche" +notificationSettingDesc: "Seleziona il tipo di notifiche da visualizzare." other: "Avanzate" +fileIdOrUrl: "ID o URL del file" abuseReports: "Segnala" reportAbuse: "Segnala" reportAbuseOf: "Segnala {name}" send: "Inviare" openInNewTab: "Apri in una nuova scheda" +editTheseSettingsMayBreakAccount: "Modificare queste impostazioni può danneggiare l'account." +waitingFor: "Aspettando {x}" random: "Casuale" system: "Sistema" +switchUi: "Cambiare interfaccia utente" desktop: "Desktop" +clip: "Clip" +createNew: "Crea nuov@" optional: "Opzionale" -public: "Pubblico" +createNewClip: "Nuova clip" +public: "Pubblica" +i18nInfo: "Misskey è tradotto in diverse lingue da volontari. Anche tu puoi contribuire su {link}." +notesCount: "Conteggio note" +repliesCount: "Numero di risposte inviate" +renotesCount: "Numero di note che hai ricondiviso" +repliedCount: "Numero di risposte ricevute" +renotedCount: "Numero delle tue note ricondivise" +followingCount: "Numero di account seguiti" +followersCount: "Numero di account che ti seguono" +sentReactionsCount: "Numero di reazioni inviate" +receivedReactionsCount: "Numero di reazioni ricevute" +pollVotesCount: "Numero di voti inviati" +pollVotedCount: "Numero di voti ricevuti" yes: "Sì" no: "No" +driveFilesCount: "Numero di file nel Drive" +noteFavoritesCount: "Conteggio note tra i preferiti" +pageLikesCount: "Numero di pagine che ti piacciono" +pageLikedCount: "Numero delle tue pagine che hanno ricevuto \"Mi piace\"" +reversiCount: "Numero di partite a Reversi" contact: "Contatti" +clips: "Clip" +experimentalFeatures: "Funzioni sperimentali" developer: "Sviluppatore" +showGapBetweenNotesInTimeline: "Mostrare un intervallo tra le note sulla timeline" duplicate: "Duplica" left: "Sinistra" center: "Centro" wide: "Largo" +clearCache: "Svuota cache" +onlineUsersCount: "{n} utenti online" +nUsers: "{n} utenti" nNotes: "{n}Note" +myTheme: "I miei temi" backgroundColor: "Sfondo" +textColor: "Testo" value: "Valore" saveConfirm: "Vuoi salvare le modifiche?" deleteConfirm: "Rimuovere?" @@ -317,45 +610,103 @@ currentVersion: "Versione attuale" latestVersion: "Ultima versione" editCode: "Modifica codice" apply: "Applica" +emailNotification: "Eventi per notifiche via mail" +inChannelSearch: "Cerca in canale" +useReactionPickerForContextMenu: "Cliccare sul tasto destro per aprire il pannello di reazioni" +typingUsers: "{users} sta(nno) scrivendo" +showingPastTimeline: "Stai visualizzando una vecchia timeline" _email: _follow: title: "Ha iniziato a seguirti" + _receiveFollowRequest: + title: "Hai ricevuto una richiesta di follow" _registry: key: "Dati" keys: "Dati" + createKey: "Crea chiave" _aboutMisskey: + source: "Codice sorgente" morePatrons: "Ci sono molti altri che ci sostengono. Grazie 🥰" _mfm: mention: "Menzioni" + mentionDescription: "Si può menzionare un utente specifico digitando il suo nome utente subito dopo il segno @." + hashtag: "Hashtag" url: "URL" link: "Link" bold: "Grassetto" - blockCode: "Codice(blocco)" + blockCode: "Codice (blocco)" inlineMath: "Espressione matematica(Immersione)" - blockMath: "Espressione matematica(blocco)" + blockMath: "Formula matematica (blocco)" quote: "Cita il nota" + emoji: "Emoji personalizzati" search: "Cerca" blur: "Sfocatura" font: "Tipo di carattere" _reversi: + gameSettings: "Impostazioni di gioco" + botSettings: "Opzioni del bot" black: "Nero" white: "Bianco" + total: "Totale" ended: "Esci" +_instanceTicker: + none: "Nascondi" + remote: "Mostra solo per gli/le utenti remotÉ™" + always: "Mostra sempre" _channel: + create: "Nuovo canale" + edit: "Gerisci canale" + setBanner: "Scegli intestazione" + removeBanner: "Rimuovi intestazione" featured: "Tendenze" + owned: "I miei canali" + following: "Seguiti" + usersCount: "{n} partecipanti" + notesCount: "{n} note" _sidebar: - icon: "Foto del profilo" + icon: "Icone" hide: "Nascondere" +_wordMute: + muteWords: "Parole da silenziare" + muteWordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con un'interruzzione riga indica la condizione \"O\"." + muteWordsDescription2: "Metti le parole chiavi tra slash per usare espressioni regolari (regexp)." + mutedNotes: "Note silenziate" _theme: + explore: "Esplora temi" + install: "Installa un tema" + manage: "Gerisci temi" + code: "Codice tema" + installed: "{name} è installato" + installedThemes: "Temi installati" + builtinThemes: "Temi integrati" + alreadyInstalled: "Questo tema è già installato" + invalid: "Il formato tema non è valido" + make: "Crea un tema" + base: "Base" + addConstant: "Aggiungi costante" constant: "Costante" defaultValue: "Valore predefinito" color: "Colore" + key: "Chiave" func: "Funzione" + argument: "Argomento" darken: "Scuro" lighten: "Chiaro" keys: bg: "Sfondo" + fg: "Testo" + focus: "Focalizzazione" + indicator: "Indicatore" + panel: "Pannello" shadow: "Ombra" + header: "Intestazione" + navBg: "Sfondo della barra laterale" + navFg: "Testo della barra laterale" + navHoverFg: "Testo della barra laterale (al passaggio del mouse)" + navActive: "Testo della barra laterale (attivo)" + navIndicator: "Indicatore di barra laterale" + link: "Link" + hashtag: "Hashtag" mention: "Menzioni" renote: "Rinota" divider: "Interruzione di linea" @@ -363,6 +714,8 @@ _sfx: note: "Nota" notification: "Notifiche" chat: "Messaggi" + antenna: "Ricezione dell'antenna" + channel: "Notifiche di canale" _ago: unknown: "Sconosciuto" future: "Futuro" @@ -381,14 +734,51 @@ _time: day: "giorni" _tutorial: title: "Come usare Misskey" - step1_1: "Benvenuto" + step1_1: "Benvenuto/a!" + step1_2: "Questa pagina si chiama una \" Timeline \". Mostra in ordine cronologico le \" note \" delle persone che segui." + step1_3: "Attualmente la tua Timeline è vuota perché non segui alcun account e non hai pubblicato alcuna nota ancora." + step2_1: "Prima di scrivere una nota o di seguire un account, imposta il tuo profilo!" + step2_2: "Aggiungere qualche informazione su di te aumenterà le tue possibilità di essere seguit@ da altre persone. " + step3_1: "Hai finito di impostare il tuo profilo?" + step3_2: "Ora, puoi pubblicare una nota. Facciamo una prova! Premi il pulsante a forma di penna in cima allo schermo per aprire una finestra di dialogo. " + step3_3: "Scritto il testo della nota, puoi pubblicarla premendo il pulsante nella parte superiore destra della finestra di dialogo." + step3_4: "Non ti viene niente in mente? Perché non scrivi semplicemente \"Ho appena cominciato a usare Misskey\"?" + step4_1: "Hai pubblicato qualcosa?" + step4_2: "Se puoi visualizzare la tua nota sulla timeline, ce l'hai fatta!" + step5_1: "Adesso, cerca di seguire altre persone per vivacizzare la tua timeline. " + step5_2: "La pagina {featured} mostra le note di tendenza su questa istanza e, sfogliandole, magari toverai degli account che ti piacciono e che vorrai seguire. Oppure, potrai trovare utenti popolari usando {explore}." + step5_3: "Per seguire altrÉ™ utenti, clicca sul loro avatar per aprire la pagina di profilo dove puoi premere il pulsante \"Seguire\". " + step5_4: "AlcunÉ™ utenti scelgono di confermare manualmente le richieste di follow che ricevono, quindi a seconda delle persone potrebbe volerci un pò prima che la tua richiesta sia accolta." + step6_1: "Ora, se puoi visualizzare le note di altrÉ™ utenti sulla tua timeline, ce l'hai fatta!" + step6_2: "Puoi inviare una risposta rapida alle note di altrÉ™ utenti mandando loro \"reazioni\"." + step6_3: "Per inviare una reazione, premi l'icona + della nota e scegli l'emoji che vuoi mandare." + step7_1: "Complimenti! Sei arrivat@ alla fine dell'esercitazione di base su come usare Misskey. " + step7_2: "Se vuoi saperne di più su Misskey, puoi dare un'occhiata alla sezione {help}." + step7_3: "Da ultimo, buon divertimento su Misskey! 🚀" _permissions: - "read:blocks": "Visualizza gli account che hai bloccato." - "write:blocks": "Gestisci gli account che hai bloccato." - "read:favorites": "Visualizza Preferiti" - "write:favorites": "Gestisci Preferiti" + "read:blocks": "Visualizza gli account bloccati" + "write:blocks": "Gestisci gli account bloccati" + "read:favorites": "Visualizza i tuoi preferiti" + "write:favorites": "Gestisci i tuoi preferiti" + "read:following": "Vedi le informazioni di follow" "write:following": "Seguiti/ Smetti di seguire" + "read:mutes": "Vedi account silenziati" + "write:mutes": "Gerisci account silenziati" + "write:notes": "Creare / Eliminare note" "read:notifications": "Visualizza notifiche" + "write:notifications": "Gerisci notifiche" + "read:reactions": "Vedi reazioni" + "write:reactions": "Gerisci reazioni" + "read:user-groups": "Vedi gruppi di utenti" + "write:user-groups": "Gestisci gruppi di utenti" + "read:channels": "Visualizza canali" + "write:channels": "Gerisci canali" +_antennaSources: + all: "Tutte le note" + homeTimeline: "Note dagli utenti che segui" + users: "Note dagli utenti selezionati" + userList: "Note dagli utenti della lista selezionata" + userGroup: "Note dagli utenti del gruppo selezionato" _weekday: sunday: "Domenica" monday: "Lunedì" @@ -409,6 +799,9 @@ _widgets: photos: "Foto" digitalClock: "Orologio digitale" federation: "Federazione" + button: "Pulsante" + onlineUsers: "Utenti online" + jobQueue: "Coda di lavoro" _cw: hide: "Nascondere" show: "Mostra di più" @@ -422,14 +815,20 @@ _poll: voted: "Votato" closed: "Terminato" _visibility: - public: "Pubblico" + public: "Pubblica" + publicDescription: "Visibile per tutti sul Fediverso" home: "Home" - followers: "Seguaci" - localOnly: "Solo Locale" - localOnlyDescription: "Solo locale" + homeDescription: "Visibile solo sulla timeline \"Home\"" + followers: "Followers" + followersDescription: "Visibile solo per i tuoi followers" + specified: "Diretta" + specifiedDescription: "Visibile solo per gli/le utenti menzionatÉ™" + localOnly: "Soltanto locale" + localOnlyDescription: "Nascosta per gli/le utenti remotÉ™" _postForm: replyPlaceholder: "Nota la tua risposta.." quotePlaceholder: "Cita Nota..." + channelPlaceholder: "Pubblica in canale" _profile: name: "Nome" username: "Nome utente" @@ -437,16 +836,29 @@ _profile: metadata: "Metadati" metadataLabel: "Etichetta" metadataContent: "Contenuto" + changeBanner: "Cambia intestazione" _exportOrImport: - followingList: "Seiguiti" - muteList: "Silenzia" - blockingList: "Blocca" + allNotes: "Tutte le note" + followingList: "Follows" + muteList: "Account silenziati" + blockingList: "Account bloccati" userLists: "Liste" +_charts: + usersIncDec: "Variazione del numero di utenti" + usersTotal: "Numero totale di utenti" + activeUsers: "Numero di utenti attivi" + notesTotal: "Conteggio totale di note" +_instanceCharts: + users: "Variazione del numero di utenti" + usersTotal: "Totale cumulativo di utenti" _timelines: home: "Home" local: "Locale" + social: "Sociale" _rooms: + roomOf: "Camera di {user}" _roomType: + default: "Predefinito" washitsu: "Washitsu" _furnitures: milk: "Cartone del latte" @@ -477,31 +889,59 @@ _rooms: photoframe: "Cornice" cube: "Cubo" tv: "Televisore" - pinguin: "Pinguini" + pinguin: "Pinguino" bin: "Cestino" cup-noodle: "Noodle istantanei" _pages: + created: "Pagina creata!" + pageSetting: "Impostazioni pagina" + viewSource: "Visualizza sorgente" like: "Mi piace" unlike: "Togli Mi piace" + featured: "Popolari" + content: "Blocco di pagina" variables: "Variabili" title: "Titolo" + hideTitleWhenPinned: "Nascondere il titolo pagina quando è fissata in cima al profilo." font: "Tipo di carattere" + chooseBlock: "Aggiungi blocco" blocks: + text: "Testo" + textarea: "Area di testo" + section: "Sezione" image: "Immagini" + button: "Pulsante" if: "Se" _if: variable: "Variabili" _post: text: "Contenuto" _textInput: + name: "Nome della variabile" text: "Titolo" + default: "Valore predefinito" _textareaInput: + name: "Nome della variabile" text: "Titolo" + default: "Valore predefinito" _numberInput: + name: "Nome della variabile" text: "Titolo" + default: "Valore predefinito" + _canvas: + width: "Larghezza" + height: "Altezza" + note: "Nota integrata" + _note: + id: "ID nota" + idDescription: "Qui puoi anche incollare l'URL della nota che vuoi impostare." + detailed: "Visualizzazione dettagliata" _switch: + name: "Nome della variabile" text: "Titolo" + default: "Valore predefinito" _counter: + name: "Nome della variabile" text: "Titolo" _button: text: "Titolo" @@ -509,7 +949,9 @@ _pages: _dialog: content: "Contenuto" _radioButton: + name: "Nome della variabile" title: "Titolo" + default: "Valore predefinito" script: categories: comparison: "Metodo comparativo" @@ -518,6 +960,15 @@ _pages: fn: "Funzione" list: "Liste" blocks: + text: "Testo" + _strLen: + arg1: "Testo" + _strPick: + arg1: "Testo" + _strReplace: + arg1: "Testo" + _strReverse: + arg1: "Testo" _join: arg1: "Liste" _add: @@ -575,26 +1026,46 @@ _pages: arg1: "Liste" _listLen: arg1: "Liste" + _stringToNumber: + arg1: "Testo" + _splitStrByLine: + arg1: "Testo" ref: "Variabili" fn: "Funzione" types: + string: "Testo" array: "Liste" _notification: + fileUploaded: "File caricato correttamente" + youGotMention: "{name} ti ha menzionato" + youGotReply: "{name} ti ha risposto" youGotQuote: "{name} ha citato il tuo Nota e ha detto" - youRenoted: "{name} ha rinota" + youRenoted: "{name} ha rinotato" youGotPoll: "{name} ha volluto." + youGotMessagingMessageFromUser: "{name} ti ha mandato un messaggio" + youGotMessagingMessageFromGroup: "{name} ti ha mandato un messaggio nella chat" youWereFollowed: "Ha iniziato a seguirti" + youReceivedFollowRequest: "Hai ricevuto una richiesta di follow" + yourFollowRequestAccepted: "La tua richiesta di follow è stata accettata" + youWereInvitedToGroup: "Invitat@ al gruppo" _types: all: "Tutto" - follow: "Seiguiti" + follow: "Follows" mention: "Menzioni" reply: "Rispondi" renote: "Rinota" quote: "Cita" reaction: "Reazione" + pollVote: "Voti ricevuti" + receiveFollowRequest: "Richiesta di follow ricevuta" + followRequestAccepted: "Richiesta di follow accettata" + groupInvited: "Invito a un gruppo" + app: "Notifiche da applicazioni" _deck: _columns: notifications: "Notifiche" tl: "Timeline" + antenna: "Antenne" list: "Liste" mentions: "Menzioni" + direct: "Diretta" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e5700fe05984235880ec7d9d0a4c5e74ef5623ca..5729da8da899d471a078f673987abec4c49dc82d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -322,7 +322,7 @@ proxyRemoteFilesDescription: "ã“ã®è¨å®šã‚’有効ã«ã™ã‚‹ã¨ã€æœªä¿å˜ã¾ driveCapacityPerLocalAccount: "ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã²ã¨ã‚Šã‚ãŸã‚Šã®ãƒ‰ãƒ©ã‚¤ãƒ–容é‡" driveCapacityPerRemoteAccount: "リモートユーザーã²ã¨ã‚Šã‚ãŸã‚Šã®ãƒ‰ãƒ©ã‚¤ãƒ–容é‡" inMb: "メガãƒã‚¤ãƒˆå˜ä½" -iconUrl: "アイコン画åƒã®URL" +iconUrl: "アイコン画åƒã®URL (faviconãªã©)" bannerUrl: "ãƒãƒŠãƒ¼ç”»åƒã®URL" basicInfo: "åŸºæœ¬æƒ…å ±" pinnedUsers: "ピン留ã‚ユーザー" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 0015db07b9cbc4cde0d963e54695a70472ed08a1..1a7001269af7b6700c416d7717e7d3b6ddfb88cd 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -81,7 +81,7 @@ pageLoadError: "ページã®èªã¿è¾¼ã¿ã«å¤±æ•—ã—ã¦ã—ã‚‚ã†ãŸã§â€¦" pageLoadErrorDescription: "ã“ã‚Œã¯æ™®é€šã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‹ãƒ–ラウザã‚ャッシュãŒåŽŸå› ã‚„ã‹ã‚‰ã。ã‚ャッシュをクリアã™ã‚‹ã‹ã€ã‚‚ã†ã¡ã£ã¨ã ã‘å¾…ã£ã¦ãã‚Œã¸ã‚“ã‹ï¼Ÿ" enterListName: "リストåを入れã¦ã‚„" privacy: "プライãƒã‚·ãƒ¼" -makeFollowManuallyApprove: "ãˆãˆã£ã¦è¨€ã‚ãªãƒ•ã‚©ãƒãƒ¼ã§ãã¸ã‚“よã†ã«ã™ã‚‹" +makeFollowManuallyApprove: "自分ãŒèªã‚ãŸäººã ã‘ãŒã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’フォãƒãƒ¼ã§ãるよã†ã«ã™ã‚‹" defaultNoteVisibility: "ã‚‚ã¨ã‹ã‚‰ã®å…¬é–‹ç¯„囲" follow: "フォãƒãƒ¼" followRequest: "フォãƒãƒ¼ã‚’é ¼ã‚€" @@ -308,7 +308,7 @@ monthX: "{month}月" yearX: "{year}å¹´" pages: "ページ" integration: "連æº" -connectSerice: "ã¤ãªã’ã‚‹" +connectSerice: "ã¤ãªã" disconnectSerice: "切ã£ã¦ã¾ã†" enableLocalTimeline: "ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインを使ãˆã‚‹ã‚ˆã†ã«ã™ã‚‹" enableGlobalTimeline: "ã‚°ãƒãƒ¼ãƒãƒ«ã‚¿ã‚¤ãƒ ラインを使ãˆã‚‹ã‚ˆã†ã«ã™ã‚‹" @@ -392,7 +392,7 @@ markAsReadAllUnreadNotes: "投稿ã¯å…¨ã¦èªã‚“ã ã‚ã£" markAsReadAllTalkMessages: "ãƒãƒ£ãƒƒãƒˆã¯ã‚‚ã†ãœã‚“ã¶èªã‚“ã ã‚ã£" help: "ヘルプ" inputMessageHere: "ã“ã“ã«ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸æ›¸ã„ã¦ã‚„" -close: "ã•ã„ãªã‚‰" +close: "é–‰ã˜ã‚‹" group: "グループ" groups: "グループ" createGroup: "グループを作るã§" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index eb392a27bbafb0270f2813e08dc3b4f065f88b24..689321340903b5545d8ab200ef523511d5c30fa2 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -241,7 +241,7 @@ explore: "Обзор" games: "Игры Misskey" messageRead: "Прочитали" noMoreHistory: "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð·Ð°ÐºÐ¾Ð½Ñ‡Ð¸Ð»Ð°ÑÑŒ" -startMessaging: "Отправить Ñообщение" +startMessaging: "Ðачать общение" nUsersRead: "Прочитали {n}" agreeTo: "Я ÑоглашаюÑÑŒ Ñ {0}" tos: "ПользовательÑкое Ñоглашение" @@ -329,7 +329,7 @@ pinnedUsers: "Прикреплённый пользователь" pinnedUsersDescription: "ПеречиÑлите по одному имени Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð² Ñтроке. Пользователи, перечиÑленные здеÑÑŒ, будут привÑзаны к закладке \"Изучение\"." pinnedPages: "Закрепленные Ñтраницы" pinnedPagesDescription: "ЕÑли хотите закрепить Ñтраницы на главной Ñайта, Ñюда можно добавить пути к ним, каждый в отдельной Ñтроке." -pinnedClipId: "Идентификатор закреплённой памÑтки" +pinnedClipId: "Идентификатор закреплённой подборки" pinnedNotes: "Ð—Ð°ÐºÑ€ÐµÐ¿Ð»Ñ‘Ð½Ð½Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ°" hcaptcha: "hCaptcha" enableHcaptcha: "Включить hCaptcha" @@ -405,8 +405,8 @@ invites: "ПриглашениÑ" groupName: "Ðазвание группы" members: "УчаÑтники" transfer: "Отдать" -messagingWithUser: "Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÐµÐ¹" -messagingWithGroup: "Чат в группе" +messagingWithUser: "Общение Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼ пользователем" +messagingWithGroup: "Общение в группе" title: "Заголовок" text: "ТекÑÑ‚" enable: "Включить" @@ -471,7 +471,7 @@ promotion: "Продвинуто" promote: "Продвинуть" numberOfDays: "КоличеÑтво дней" hideThisNote: "СпрÑтать Ñту запиÑÑŒ" -showFeaturedNotesInTimeline: "Показывать в ленте заметки из подборки Ñайта" +showFeaturedNotesInTimeline: "Показывать в ленте заметки из «ГорÑчего»" objectStorage: "Хранилище" useObjectStorage: "ЗанÑто в хранилище" objectStorageBaseUrl: "Базовый адреÑ" @@ -524,7 +524,7 @@ deleteAllFiles: "Удалить вÑе файлы" deleteAllFilesConfirm: "Ð’Ñ‹ хотите удалить вÑе файлы?" removeAllFollowing: "Удалить вÑех подпиÑчиков" removeAllFollowingDescription: "Отменить вÑе подпиÑки Ñ Ð´Ð¾Ð¼ÐµÐ½Ð° {host}? ПожалуйÑта, применÑйте Ñто дейÑтвие, еÑли инÑÑ‚Ð°Ð½Ñ Ð±Ð¾Ð»ÑŒÑˆÐµ не ÑущеÑтвует." -userSuspended: "Ðтот пользователь был заморожен" +userSuspended: "Ðта ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ заморожена" userSilenced: "Ðтот пользователь был заглушен" sidebar: "Ð‘Ð¾ÐºÐ¾Ð²Ð°Ñ Ð¿Ð°Ð½ÐµÐ»ÑŒ" divider: "ЛиниÑ-разделитель" @@ -623,30 +623,30 @@ random: "Случайные" system: "СиÑтема" switchUi: "Выбор вида" desktop: "Стол" -clip: "Ð’ памÑтку" +clip: "Ð’ подборку" createNew: "Ðовый документ" optional: "ÐеобÑзательно" -createNewClip: "ÐÐ¾Ð²Ð°Ñ Ð¿Ð°Ð¼Ñтка" +createNewClip: "ÐÐ¾Ð²Ð°Ñ Ð¿Ð¾Ð´Ð±Ð¾Ñ€ÐºÐ°" public: "ОбщедоÑтупно" i18nInfo: "Misskey переводÑÑ‚ на разные Ñзыки добровольцы Ñо вÑего Ñвета. Ваша помощь тоже пригодитÑÑ Ð·Ð´ÐµÑÑŒ: {link}." manageAccessTokens: "Управление токенами доÑтупа" accountInfo: "Ð¡Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾Ð± учётной запиÑи" notesCount: "КоличеÑтво заметок" repliesCount: "Сколько раз пользователь кому-то ответил" -renotesCount: "Сколько раз пользователь передал чужие заметки" +renotesCount: "Сколько раз пользователь делилÑÑ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ°Ð¼Ð¸" repliedCount: "Сколько раз ответили пользователю" -renotedCount: "Сколько раз передавали заметки пользователÑ" +renotedCount: "Сколько раз делилиÑÑŒ заметками пользователÑ" followingCount: "КоличеÑтво подпиÑок" followersCount: "КоличеÑтво подпиÑавшихÑÑ" -sentReactionsCount: "Сколько раз пользователь отреагировал" -receivedReactionsCount: "Сколько раз отреагировали на заметки пользователÑ" -pollVotesCount: "Сколько раз учаÑтвовал в опроÑах" +sentReactionsCount: "КоличеÑтво реакций пользователÑ" +receivedReactionsCount: "КоличеÑтво реакций на заметки пользователÑ" +pollVotesCount: "Сколько раз пользователь учаÑтвовал в опроÑах" pollVotedCount: "Сколько раз учаÑтвовали в опроÑах пользователÑ" yes: "Да" no: "Ðет" driveFilesCount: "КоличеÑтво файлов на диÑке" -driveUsage: "Сколько меÑта занÑто на диÑке" -noCrawle: "Паукам вход воÑпрещён" +driveUsage: "ЗанÑто меÑта на диÑке" +noCrawle: "Запретить паукам индекÑировать Ñайт" noCrawleDescription: "ПроÑьба поиÑковым ÑиÑтемам не ходить по вашему профилю, по заметкам, Ñтраницам и не индекÑировать их." lockedAccountInfo: "Даже еÑли вы вручную подтверждаете подпиÑки, кто угодно может читать ваши заметки, еÑли вы не отмечаете их Â«Ð´Ð»Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñчиков»." alwaysMarkSensitive: "Отмечать файлы как «Ñодержимое не Ð´Ð»Ñ Ð²Ñех» по умолчанию" @@ -661,7 +661,7 @@ pageLikedCount: "КоличеÑтво Ñтраниц, понравившихÑÑ reversiCount: "КоличеÑтво Ñыгранных игр в реверÑи" contact: "Как ÑвÑзатьÑÑ" useSystemFont: "ИÑпользовать шрифт, предлагаемый ÑиÑтемой" -clips: "ПамÑтки" +clips: "Подборки" experimentalFeatures: "ÐкÑпериментальные функции" developer: "Разработчик" makeExplorable: "Опубликовать профиль в «Обзоре»." @@ -983,7 +983,7 @@ _tutorial: step7_2: "Хотите изучить Misskey глубже — добро пожаловать в раздел «{help}»." step7_3: "ПриÑтно вам провеÑти Ð²Ñ€ÐµÐ¼Ñ Ñ Misskey🚀" _2fa: - alreadyRegistered: "ÐаÑтройка завершена" + alreadyRegistered: "Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ ÑƒÐ¶Ðµ наÑтроена." registerDevice: "ЗарегиÑтрируйте ваше уÑтройÑтво" registerKey: "ЗарегиÑтрировать ключ" step1: "Прежде вÑего, уÑтановите на уÑтройÑтво приложение Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸, например, {a} или {b}." @@ -1240,7 +1240,7 @@ _pages: liked: "ПонравившиеÑÑ Ñтраницы" featured: "ПопулÑрные" inspector: "ИнÑпектор" - contents: "Содержательные" + contents: "Содержимое" content: "Содержимое" variables: "Переменные" title: "Заголовок" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index c13cf12220b6e76df11109196052a575b1610d21..d6da577982913fef2273a9c01041a6edca370552 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -579,7 +579,7 @@ smtpPort: "端å£" smtpUser: "用户å" smtpPass: "密ç " emptyToDisableSmtpAuth: "用户å和密ç 留空å¯ä»¥ç¦ç”¨SMTP验è¯" -smtpSecure: "在 SMTP 连接ä¸é»˜è®¤ä½¿ç”¨ SSL / TLS" +smtpSecure: "在 SMTP 连接ä¸ä½¿ç”¨éšå¼ SSL / TLS" smtpSecureInfo: "使用STARTTLS时关é—。" testEmail: "邮件å‘é€æµ‹è¯•" wordMute: "æ–‡å—å±è”½" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 9d95c958d1f39801f899bbadab5808ca8f94f01a..dfae988f1b29162364c0d53edd70e4d91821a8e2 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -438,6 +438,7 @@ signinWith: "以{x}登錄" signinFailed: "登入失敗。 請檢查用戶å和密碼。" tapSecurityKey: "點擊安全密鑰" or: "或者" +language: "語言" uiLanguage: "介é¢èªžè¨€" groupInvited: "您有新的群組邀請" aboutX: "關於{x}" @@ -677,9 +678,12 @@ newVersionOfClientAvailable: "新版本的用戶端å¯ç”¨ã€‚" usageAmount: "使用é‡" capacity: "容é‡" inUse: "已使用" +clear: "清除" _email: _follow: title: "您有新的追隨者" +_plugin: + manage: "管ç†æ’件" _registry: scope: "範åœ" key: "機碼" @@ -702,7 +706,9 @@ _nsfw: _mfm: cheatSheet: "MFM代碼å°æŠ„" intro: "MFM是Misskey專用的標記語言,å¯ä»¥åœ¨Misskeyä¸çš„å„個ä½ç½®ä½¿ç”¨ã€‚ 您å¯ä»¥é€™è£çœ‹åˆ°MFMå¯ç”¨èªžæ³•åˆ—表。" + dummy: "通éŽMisskey擴展Fediverse的世界" mention: "æåŠ" + mentionDescription: "é€éŽ @+用戶å 來標示特定使用者。" hashtag: "#tag" url: "URL" link: "éˆæŽ¥" diff --git a/migration/1615965918224-chart-v2.ts b/migration/1615965918224-chart-v2.ts new file mode 100644 index 0000000000000000000000000000000000000000..cacbd1945b9c2665ab10b1cfbda0bc6dc3985a04 --- /dev/null +++ b/migration/1615965918224-chart-v2.ts @@ -0,0 +1,218 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class chartV21615965918224 implements MigrationInterface { + name = 'chartV21615965918224' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`DELETE FROM "__chart__active_users" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__drive" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__federation" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__hashtag" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__instance" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__network" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__notes" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__per_user_drive" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__per_user_following" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__per_user_notes" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__per_user_reaction" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__test" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__test_grouped" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__test_unique" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__users" WHERE "span" = 'day'`); + + await queryRunner.query(`DROP INDEX "IDX_15e91a03aeeac9dbccdf43fc06"`); + await queryRunner.query(`DROP INDEX "IDX_20f57cc8f142c131340ee16742"`); + await queryRunner.query(`DROP INDEX "IDX_c26e2c1cbb6e911e0554b27416"`); + await queryRunner.query(`DROP INDEX "IDX_3fa0d0f17ca72e3dc80999a032"`); + await queryRunner.query(`DROP INDEX "IDX_6e1df243476e20cbf86572ecc0"`); + await queryRunner.query(`DROP INDEX "IDX_06690fc959f1c9fdaf21928222"`); + await queryRunner.query(`DROP INDEX "IDX_e447064455928cf627590ef527"`); + await queryRunner.query(`DROP INDEX "IDX_2d416e6af791a82e338c79d480"`); + await queryRunner.query(`DROP INDEX "IDX_e9cd07672b37d8966cf3709283"`); + await queryRunner.query(`DROP INDEX "IDX_fcc181fb8283009c61cc4083ef"`); + await queryRunner.query(`DROP INDEX "IDX_49975586f50ed7b800fdd88fbd"`); + await queryRunner.query(`DROP INDEX "IDX_6d6f156ceefc6bc5f273a0e370"`); + await queryRunner.query(`DROP INDEX "IDX_c12f0af4a66cdd30c2287ce8aa"`); + await queryRunner.query(`DROP INDEX "IDX_d0a4f79af5a97b08f37b547197"`); + await queryRunner.query(`DROP INDEX "IDX_f5448d9633cff74208d850aabe"`); + await queryRunner.query(`DROP INDEX "IDX_f8dd01baeded2ffa833e0a610a"`); + await queryRunner.query(`DROP INDEX "IDX_08fac0eb3b11f04c200c0b40dd"`); + await queryRunner.query(`DROP INDEX "IDX_9ff6944f01acb756fdc92d7563"`); + await queryRunner.query(`DROP INDEX "IDX_e69096589f11e3baa98ddd64d0"`); + await queryRunner.query(`DROP INDEX "IDX_0c9a159c5082cbeef3ca6706b5"`); + await queryRunner.query(`DROP INDEX "IDX_924fc196c80ca24bae01dd37e4"`); + await queryRunner.query(`DROP INDEX "IDX_328f259961e60c4fa0bfcf55ca"`); + await queryRunner.query(`DROP INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53"`); + await queryRunner.query(`DROP INDEX "IDX_f2aeafde2ae6fbad38e857631b"`); + await queryRunner.query(`DROP INDEX "IDX_f92dd6d03f8d994f29987f6214"`); + await queryRunner.query(`DROP INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f"`); + await queryRunner.query(`DROP INDEX "IDX_4db3b84c7be0d3464714f3e0b1"`); + await queryRunner.query(`DROP INDEX "IDX_8d2cbbc8114d90d19b44d626b6"`); + await queryRunner.query(`DROP INDEX "IDX_046feeb12e9ef5f783f409866a"`); + await queryRunner.query(`DROP INDEX "IDX_f68a5ab958f9f5fa17a32ac23b"`); + await queryRunner.query(`DROP INDEX "IDX_65633a106bce43fc7c5c30a5c7"`); + await queryRunner.query(`DROP INDEX "IDX_edeb73c09c3143a81bcb34d569"`); + await queryRunner.query(`DROP INDEX "IDX_e316f01a6d24eb31db27f88262"`); + await queryRunner.query(`DROP INDEX "IDX_2be7ec6cebddc14dc11e206686"`); + await queryRunner.query(`DROP INDEX "IDX_a5133470f4825902e170328ca5"`); + await queryRunner.query(`DROP INDEX "IDX_84e661abb7bd1e51b690d4b017"`); + await queryRunner.query(`DROP INDEX "IDX_5c73bf61da4f6e6f15bae88ed1"`); + await queryRunner.query(`DROP INDEX "IDX_d70c86baedc68326be11f9c0ce"`); + await queryRunner.query(`DROP INDEX "IDX_66e1e1ecd2f29e57778af35b59"`); + await queryRunner.query(`DROP INDEX "IDX_92255988735563f0fe4aba1f05"`); + await queryRunner.query(`DROP INDEX "IDX_c5870993e25c3d5771f91f5003"`); + await queryRunner.query(`DROP INDEX "IDX_f170de677ea75ad4533de2723e"`); + await queryRunner.query(`DROP INDEX "IDX_7c184198ecf66a8d3ecb253ab3"`); + await queryRunner.query(`DROP INDEX "IDX_f091abb24193d50c653c6b77fc"`); + await queryRunner.query(`DROP INDEX "IDX_a770a57c70e668cc61590c9161"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__active_users_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___local_count"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_count"`); + await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__drive_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__federation_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__hashtag_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_count"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_count"`); + await queryRunner.query(`ALTER TABLE "__chart__instance" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__instance_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__instance" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__network" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__network_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__network" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__notes_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__per_user_drive_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_following" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__per_user_following_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_following" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__per_user_notes_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__per_user_reaction_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__test_grouped" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__test_grouped_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__test_grouped" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__test_unique_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "___foo"`); + await queryRunner.query(`ALTER TABLE "__chart__test" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__test_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__test" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__users" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__users_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__users" DROP COLUMN "unique"`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "__chart__users" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__users_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__users" ADD "span" "__chart__users_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__test" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__test_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__test" ADD "span" "__chart__test_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "___foo" bigint NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__test_unique_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "span" "__chart__test_unique_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__test_grouped" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__test_grouped_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__test_grouped" ADD "span" "__chart__test_grouped_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_reaction_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ADD "span" "__chart__per_user_reaction_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_notes_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ADD "span" "__chart__per_user_notes_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_following_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ADD "span" "__chart__per_user_following_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_drive_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ADD "span" "__chart__per_user_drive_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__notes_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "span" "__chart__notes_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__network" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__network_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__network" ADD "span" "__chart__network_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__instance" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__instance_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__instance" ADD "span" "__chart__instance_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___remote_count" bigint NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___local_count" bigint NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__hashtag_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "span" "__chart__hashtag_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__federation_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "span" "__chart__federation_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__drive_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "span" "__chart__drive_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___remote_count" bigint NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___local_count" bigint NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__active_users_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "span" "__chart__active_users_span_enum" NOT NULL`); + await queryRunner.query(`CREATE INDEX "IDX_a770a57c70e668cc61590c9161" ON "__chart__users" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_f091abb24193d50c653c6b77fc" ON "__chart__users" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_7c184198ecf66a8d3ecb253ab3" ON "__chart__users" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_f170de677ea75ad4533de2723e" ON "__chart__test" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_c5870993e25c3d5771f91f5003" ON "__chart__test" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_92255988735563f0fe4aba1f05" ON "__chart__test" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_66e1e1ecd2f29e57778af35b59" ON "__chart__test_unique" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_d70c86baedc68326be11f9c0ce" ON "__chart__test_unique" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_5c73bf61da4f6e6f15bae88ed1" ON "__chart__test_unique" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_84e661abb7bd1e51b690d4b017" ON "__chart__test_grouped" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_a5133470f4825902e170328ca5" ON "__chart__test_grouped" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_2be7ec6cebddc14dc11e206686" ON "__chart__test_grouped" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_e316f01a6d24eb31db27f88262" ON "__chart__per_user_reaction" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_edeb73c09c3143a81bcb34d569" ON "__chart__per_user_reaction" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_65633a106bce43fc7c5c30a5c7" ON "__chart__per_user_reaction" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_f68a5ab958f9f5fa17a32ac23b" ON "__chart__per_user_notes" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_046feeb12e9ef5f783f409866a" ON "__chart__per_user_notes" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_8d2cbbc8114d90d19b44d626b6" ON "__chart__per_user_notes" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_4db3b84c7be0d3464714f3e0b1" ON "__chart__per_user_following" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f" ON "__chart__per_user_following" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_f92dd6d03f8d994f29987f6214" ON "__chart__per_user_following" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_f2aeafde2ae6fbad38e857631b" ON "__chart__per_user_drive" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53" ON "__chart__per_user_drive" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_328f259961e60c4fa0bfcf55ca" ON "__chart__per_user_drive" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_924fc196c80ca24bae01dd37e4" ON "__chart__notes" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_0c9a159c5082cbeef3ca6706b5" ON "__chart__notes" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_e69096589f11e3baa98ddd64d0" ON "__chart__notes" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_9ff6944f01acb756fdc92d7563" ON "__chart__network" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_08fac0eb3b11f04c200c0b40dd" ON "__chart__network" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_f8dd01baeded2ffa833e0a610a" ON "__chart__network" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_f5448d9633cff74208d850aabe" ON "__chart__instance" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_d0a4f79af5a97b08f37b547197" ON "__chart__instance" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_c12f0af4a66cdd30c2287ce8aa" ON "__chart__instance" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_6d6f156ceefc6bc5f273a0e370" ON "__chart__hashtag" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_49975586f50ed7b800fdd88fbd" ON "__chart__hashtag" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_fcc181fb8283009c61cc4083ef" ON "__chart__hashtag" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_e9cd07672b37d8966cf3709283" ON "__chart__federation" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_2d416e6af791a82e338c79d480" ON "__chart__federation" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_e447064455928cf627590ef527" ON "__chart__federation" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_06690fc959f1c9fdaf21928222" ON "__chart__drive" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_6e1df243476e20cbf86572ecc0" ON "__chart__drive" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_3fa0d0f17ca72e3dc80999a032" ON "__chart__drive" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_c26e2c1cbb6e911e0554b27416" ON "__chart__active_users" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_20f57cc8f142c131340ee16742" ON "__chart__active_users" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_15e91a03aeeac9dbccdf43fc06" ON "__chart__active_users" ("span") `); + } + +} diff --git a/migration/1615966519402-chart-v2-2.ts b/migration/1615966519402-chart-v2-2.ts new file mode 100644 index 0000000000000000000000000000000000000000..a694f9542aa39cf583c92a2ce3fe930e668468bc --- /dev/null +++ b/migration/1615966519402-chart-v2-2.ts @@ -0,0 +1,22 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class chartV221615966519402 implements MigrationInterface { + name = 'chartV221615966519402' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___local_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___remote_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___local_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___remote_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "___foo" character varying array NOT NULL DEFAULT '{}'::varchar[]`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "___foo"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_users"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`); + } + +} diff --git a/package.json b/package.json index 2c0525b6059f27d1ee4aaec77c2d7019321030d6..0f40837a6fd6fcea61a2ae5f624d43dc940d8b3b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo <syuilotan@yahoo.co.jp>", - "version": "12.74.1", + "version": "12.75.0", "codename": "indigo", "repository": { "type": "git", @@ -11,20 +11,20 @@ "private": true, "scripts": { "start": "node ./index.js", - "start-product": "cross-env NODE_ENV=production node ./index.js", "init": "npm run migrate", "ormconfig": "node ./built/ormconfig.js", "migrate": "ts-node ./node_modules/typeorm/cli.js migration:run", "migrateandstart": "npm run migrate && npm run start", - "build": "webpack && gulp build", - "build-product": "cross-env NODE_ENV=production webpack && gulp build", - "webpack": "webpack", - "watch": "webpack --watch", - "gulp": "gulp build", + "build": "npm run build-webpack && npm run build-gulp", + "build-webpack": "webpack", + "build-gulp": "gulp build", + "watch": "concurrently \"npm:watch-*\"", + "watch-webpack": "webpack --watch", + "watch-gulp": "gulp watch", "clean": "gulp clean", "cleanall": "gulp cleanall", "lint": "tslint 'src/**/*.ts'", - "test": "cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_COMPILER_OPTIONS=\"{\\\"target\\\":\\\"es2017\\\",\\\"module\\\":\\\"commonjs\\\",\\\"typeRoots\\\":[\\\"node_modules/@types\\\",\\\"src/@types\\\"]}\" mocha", + "test": "cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha", "format": "gulp format" }, "resolutions": { @@ -35,25 +35,24 @@ "lodash": "^4.17.20" }, "dependencies": { - "@babel/plugin-transform-runtime": "7.13.9", + "@babel/plugin-transform-runtime": "7.13.10", "@elastic/elasticsearch": "7.11.0", - "@fortawesome/fontawesome-svg-core": "1.2.34", - "@fortawesome/free-brands-svg-icons": "5.15.2", - "@fortawesome/free-regular-svg-icons": "5.15.2", - "@fortawesome/free-solid-svg-icons": "5.15.2", + "@fortawesome/fontawesome-svg-core": "1.2.35", + "@fortawesome/free-brands-svg-icons": "5.15.3", + "@fortawesome/free-regular-svg-icons": "5.15.3", + "@fortawesome/free-solid-svg-icons": "5.15.3", "@fortawesome/vue-fontawesome": "3.0.0-3", "@koa/cors": "3.1.0", "@koa/multer": "3.0.0", "@koa/router": "9.0.1", "@sentry/browser": "5.29.2", "@sentry/tracing": "5.29.2", - "@sinonjs/fake-timers": "6.0.1", + "@sinonjs/fake-timers": "7.0.2", "@syuilo/aiscript": "0.11.1", "@types/bcryptjs": "2.4.2", "@types/bull": "3.15.0", "@types/cbor": "5.0.1", "@types/dateformat": "3.0.1", - "@types/double-ended-queue": "2.1.1", "@types/escape-regexp": "0.0.0", "@types/glob": "7.1.3", "@types/gulp": "4.0.8", @@ -61,10 +60,10 @@ "@types/gulp-replace": "0.0.31", "@types/is-url": "1.2.28", "@types/js-yaml": "4.0.0", - "@types/jsdom": "16.2.6", - "@types/jsonld": "1.5.4", + "@types/jsdom": "16.2.7", + "@types/jsonld": "1.5.5", "@types/katex": "0.11.0", - "@types/koa": "2.13.0", + "@types/koa": "2.13.1", "@types/koa-bodyparser": "4.3.0", "@types/koa-cors": "0.0.0", "@types/koa-favicon": "2.0.19", @@ -78,9 +77,9 @@ "@types/markdown-it": "12.0.1", "@types/matter-js": "0.14.10", "@types/mocha": "8.2.1", - "@types/node": "14.14.31", + "@types/node": "14.14.35", "@types/node-fetch": "2.5.8", - "@types/nodemailer": "6.4.0", + "@types/nodemailer": "6.4.1", "@types/nprogress": "0.2.0", "@types/oauth": "0.9.1", "@types/parse5": "6.0.0", @@ -105,44 +104,44 @@ "@types/web-push": "3.3.0", "@types/webpack": "4.41.26", "@types/webpack-stream": "3.2.11", - "@types/websocket": "1.0.1", + "@types/websocket": "1.0.2", "@types/ws": "7.4.0", - "@typescript-eslint/parser": "4.16.1", - "@vue/compiler-sfc": "3.0.5", + "@typescript-eslint/parser": "4.18.0", + "@vue/compiler-sfc": "3.0.7", "abort-controller": "3.0.0", - "apexcharts": "3.25.0", + "apexcharts": "3.26.0", "autobind-decorator": "2.4.0", "autosize": "4.0.2", "autwh": "0.1.0", - "aws-sdk": "2.848.0", + "aws-sdk": "2.867.0", "bcryptjs": "2.4.3", "blurhash": "1.1.3", - "broadcast-channel": "3.4.1", - "bull": "3.20.1", + "broadcast-channel": "3.5.3", + "bull": "3.21.1", "cafy": "15.2.1", - "cbor": "7.0.3", + "cbor": "7.0.4", "chalk": "4.1.0", "chart.js": "2.9.4", "cli-highlight": "2.1.10", "commander": "4.1.1", + "concurrently": "6.0.0", "content-disposition": "0.5.3", - "core-js": "3.9.0", + "core-js": "3.9.1", "crc-32": "1.2.0", - "css-loader": "5.0.2", + "css-loader": "5.1.3", "cssnano": "4.1.10", "dateformat": "4.5.1", "diskusage": "1.1.3", - "double-ended-queue": "2.1.0-0", "escape-regexp": "0.0.1", - "eslint": "7.21.0", - "eslint-plugin-vue": "7.6.0", + "eslint": "7.22.0", + "eslint-plugin-vue": "7.7.0", "eventemitter3": "4.0.7", "feed": "4.2.2", "fibers": "5.0.0", - "file-type": "16.2.0", + "file-type": "16.3.0", "fluent-ffmpeg": "2.1.2", "glob": "7.1.6", - "got": "11.8.1", + "got": "11.8.2", "gulp": "4.0.2", "gulp-cssnano": "2.1.3", "gulp-rename": "2.0.0", @@ -155,17 +154,17 @@ "http-proxy-agent": "4.0.1", "http-signature": "1.3.5", "https-proxy-agent": "5.0.0", - "idb-keyval": "5.0.2", + "idb-keyval": "5.0.4", "insert-text-at-cursor": "0.3.0", "is-root": "2.1.0", - "is-svg": "4.2.1", + "is-svg": "4.3.1", "js-yaml": "4.0.0", - "jsdom": "16.4.0", + "jsdom": "16.5.1", "json5": "2.2.0", "json5-loader": "4.0.1", "jsonld": "4.0.1", "jsrsasign": "8.0.20", - "katex": "0.12.0", + "katex": "0.13.0", "koa": "2.13.1", "koa-bodyparser": "4.3.0", "koa-favicon": "2.1.0", @@ -174,13 +173,13 @@ "koa-mount": "4.0.0", "koa-send": "5.0.1", "koa-slow": "2.1.0", - "koa-views": "6.3.1", + "koa-views": "7.0.1", "langmap": "0.0.16", "lookup-dns-cache": "2.1.0", "markdown-it": "12.0.4", - "markdown-it-anchor": "7.0.2", + "markdown-it-anchor": "7.1.0", "matter-js": "0.16.1", - "mocha": "8.3.0", + "mocha": "8.3.2", "moji": "0.5.1", "ms": "2.1.3", "multer": "1.4.2", @@ -189,20 +188,19 @@ "nodemailer": "6.5.0", "object-assign-deep": "0.4.0", "os-utils": "0.0.14", - "p-cancelable": "2.0.0", "parse5": "6.0.1", "parsimmon": "1.16.0", "pg": "8.5.1", "portscanner": "2.2.0", - "postcss": "8.2.7", - "postcss-loader": "5.0.0", + "postcss": "8.2.8", + "postcss-loader": "5.2.0", "prismjs": "1.23.0", - "probe-image-size": "6.0.0", + "probe-image-size": "7.0.1", "promise-limit": "2.7.0", "promise-sequential": "1.1.1", - "pug": "2.0.4", + "pug": "3.0.2", "punycode": "2.1.1", - "pureimage": "0.2.5", + "pureimage": "0.2.7", "qrcode": "1.4.4", "random-seed": "0.3.0", "ratelimiter": "3.4.1", @@ -221,49 +219,49 @@ "sass": "1.32.8", "sass-loader": "11.0.1", "seedrandom": "3.0.5", - "sharp": "0.27.1", + "sharp": "0.27.2", "speakeasy": "2.0.0", "stringz": "2.1.0", "style-loader": "2.0.0", "summaly": "2.4.0", "syslog-pro": "1.0.0", - "systeminformation": "5.6.1", + "systeminformation": "5.6.7", "syuilo-password-strength": "0.0.1", "textarea-caret": "3.1.0", "three": "0.117.1", "throttle-debounce": "3.0.1", "tinycolor2": "1.4.2", "tmp": "0.2.1", - "ts-loader": "8.0.17", + "ts-loader": "8.0.18", "ts-node": "9.1.1", "tslint": "6.1.3", "tslint-sonarts": "1.9.0", "typeorm": "0.2.31", - "typescript": "4.1.5", + "typescript": "4.2.3", "ulid": "2.3.0", "url-loader": "4.1.1", "uuid": "8.3.2", "v-debounce": "0.1.2", "vanilla-tilt": "1.7.0", - "vue": "3.0.5", + "vue": "3.0.7", "vue-color": "2.8.1", "vue-json-pretty": "1.7.1", "vue-loader": "16.1.2", "vue-prism-editor": "2.0.0-alpha.2", - "vue-router": "4.0.4", + "vue-router": "4.0.5", "vue-style-loader": "4.1.3", "vuedraggable": "4.0.1", "web-push": "3.4.4", - "webpack": "5.24.2", + "webpack": "5.26.3", "webpack-cli": "4.5.0", "websocket": "1.0.33", - "ws": "7.4.3", + "ws": "7.4.4", "xev": "2.0.1" }, "devDependencies": { "@types/chai": "4.2.15", "@types/fluent-ffmpeg": "2.1.16", - "chai": "4.3.0", + "chai": "4.3.4", "cross-env": "7.0.3" } } diff --git a/src/.eslintrc b/src/.eslintrc new file mode 100644 index 0000000000000000000000000000000000000000..d54e20f6b69c9d85306c33f456f45721ba0c748d --- /dev/null +++ b/src/.eslintrc @@ -0,0 +1,6 @@ +{ + "env": { + "node": true, + "commonjs": true + } +} diff --git a/src/client/.eslintrc b/src/client/.eslintrc index 8829472b49ec8a61856f854b371547e1d23bddb7..fffa28d9e4c68fa347a2837f43996d7ced0073f1 100644 --- a/src/client/.eslintrc +++ b/src/client/.eslintrc @@ -1,4 +1,24 @@ { + "env": { + "node": false, + }, + "extends": [ + "eslint:recommended", + "plugin:vue/recommended" + ], + "rules": { + "vue/require-v-for-key": 0, + "vue/max-attributes-per-line": 0, + "vue/html-indent": 0, + "vue/html-self-closing": 0, + "vue/no-unused-vars": 0, + "vue/attributes-order": 0, + "vue/require-prop-types": 0, + "vue/require-default-prop": 0, + "vue/html-closing-bracket-spacing": 0, + "vue/singleline-html-element-content-newline": 0, + "vue/no-v-html": 0 + }, "globals": { "_DEV_": false, "_LANGS_": false, diff --git a/src/client/components/note-detailed.vue b/src/client/components/note-detailed.vue index 1ef3f43389993e39ae64385564211959b0e9a393..ea26d31100e6cec8fbd288c4ccd2ba47f571fb6c 100644 --- a/src/client/components/note-detailed.vue +++ b/src/client/components/note-detailed.vue @@ -350,7 +350,8 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); + // TODO: ã“ã®ãƒŽãƒ¼ãƒˆãŒã‚¹ãƒˆãƒªãƒ¼ãƒŸãƒ³ã‚°çµŒç”±ã§æµã‚Œã¦ããŸå ´åˆã®ã¿ sr ã™ã‚‹ + this.connection.send(document.body.contains(this.$el) ? 'sr' : 's', { id: this.appearNote.id }); if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 65e09b780282e1dddb67f98648d9470a2656fb42..70f49fef7e4a310b29ffdbdb4c6aab6406f54099 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -325,7 +325,8 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); + // TODO: ã“ã®ãƒŽãƒ¼ãƒˆãŒã‚¹ãƒˆãƒªãƒ¼ãƒŸãƒ³ã‚°çµŒç”±ã§æµã‚Œã¦ããŸå ´åˆã®ã¿ sr ã™ã‚‹ + this.connection.send(document.body.contains(this.$el) ? 'sr' : 's', { id: this.appearNote.id }); if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/client/components/ui/modal.vue b/src/client/components/ui/modal.vue index ff5b98d39f42e12c88140d7d9fe88b1929830056..db6564bacc0f0bf14d32a01be1d603372f48c325 100644 --- a/src/client/components/ui/modal.vue +++ b/src/client/components/ui/modal.vue @@ -1,7 +1,7 @@ <template> <transition :name="$store.state.animation ? popup ? 'modal-popup' : 'modal' : ''" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered"> <div v-show="manualShowing != null ? manualShowing : showing" class="mk-modal" v-hotkey.global="keymap" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }"> - <div class="bg _modalBg" @click="onBgClick"></div> + <div class="bg _modalBg" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div> <div class="content" :class="{ popup, fixed, top: position === 'top' }" @click.self="onBgClick" ref="content"> <slot></slot> </div> diff --git a/src/client/init.ts b/src/client/init.ts index 1c44e7f23e1ed13203f78d5fcdfc45a08362e2b3..2a2b6a2f862ba96eee3703ea336e9b47479ae9a0 100644 --- a/src/client/init.ts +++ b/src/client/init.ts @@ -4,40 +4,6 @@ import '@/style.scss'; -// TODO: ãã®ã†ã¡æ¶ˆã™ -if (localStorage.getItem('vuex') != null) { - const vuex = JSON.parse(localStorage.getItem('vuex')); - - localStorage.setItem('account', JSON.stringify({ - ...vuex.i, - token: localStorage.getItem('i') - })); - localStorage.setItem('accounts', JSON.stringify(vuex.device.accounts)); - localStorage.setItem('miux:themes', JSON.stringify(vuex.device.themes)); - - if (vuex.device.userData) { - for (const [k, v] of Object.entries(vuex.device.userData)) { - localStorage.setItem('pizzax::base::' + k, JSON.stringify({ - widgets: v.widgets - })); - - if (v.deck) { - localStorage.setItem('pizzax::deck::' + k, JSON.stringify({ - columns: v.deck.columns, - layout: v.deck.layout, - })); - } - } - } - - localStorage.setItem('vuex-old', JSON.stringify(vuex)); - localStorage.removeItem('vuex'); - localStorage.removeItem('i'); - localStorage.removeItem('locale'); - - location.reload(); -} - import * as Sentry from '@sentry/browser'; import { Integrations } from '@sentry/tracing'; import { createApp, watch } from 'vue'; diff --git a/src/client/pages/doc.vue b/src/client/pages/doc.vue index 3379a5fe680e9cfbc7defba0204d4eb0c4e35c9b..ed4eae4d02e3e0ab3f7f38a63faeec6207e95e47 100644 --- a/src/client/pages/doc.vue +++ b/src/client/pages/doc.vue @@ -60,7 +60,7 @@ export default defineComponent({ methods: { fetchDoc() { - fetch(`${url}/assets/docs/${lang}/${this.doc}.md`).then(res => res.text()).then(md => { + fetch(`${url}/doc-assets/${lang}/${this.doc}.md`).then(res => res.text()).then(md => { this.parse(md); }); }, diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue index 45997dfd657e896d1aff44034a85ec04e01edb1a..08856ebfe47ce3b87cf45e3f62ccdeb1622032ac 100644 --- a/src/client/pages/page-editor/page-editor.vue +++ b/src/client/pages/page-editor/page-editor.vue @@ -4,9 +4,9 @@ <MkA class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><Fa :icon="faExternalLinkSquareAlt"/> {{ $ts._pages.viewPage }}</MkA> <div class="buttons" style="margin: 16px 0;"> - <MkButton inline @click="save" primary class="save"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> + <MkButton inline @click="save" primary class="save" v-if="!readonly"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> <MkButton inline @click="duplicate" class="duplicate" v-if="pageId"><Fa :icon="faCopy"/> {{ $ts.duplicate }}</MkButton> - <MkButton inline @click="del" class="delete" v-if="pageId"><Fa :icon="faTrashAlt"/> {{ $ts.delete }}</MkButton> + <MkButton inline @click="del" class="delete" v-if="pageId && !readonly"><Fa :icon="faTrashAlt"/> {{ $ts.delete }}</MkButton> </div> <MkContainer :body-togglable="true" :expanded="true" class="_vMargin"> @@ -134,12 +134,18 @@ export default defineComponent({ data() { return { - INFO: computed(() => this.initPageId ? { - title: this.$ts._pages.editPage, - icon: faPencilAlt, - } : { - title: this.$ts._pages.newPage, - icon: faPencilAlt, + INFO: computed(() => { + let title = this.$ts._pages.newPage; + if (this.initPageId) { + title = this.$ts._pages.editPage; + } + else if (this.initPageName && this.initUser) { + title = this.$ts._pages.readPage; + } + return { + title: title, + icon: faPencilAlt, + }; }), author: this.$i, readonly: false, diff --git a/src/client/store.ts b/src/client/store.ts index 14924dadd08d9e162a814697987063acdf6ffde4..e6fdd12f1dd7df79a9af11b2ca07016dcb36063b 100644 --- a/src/client/store.ts +++ b/src/client/store.ts @@ -212,7 +212,6 @@ type Plugin = { */ export class ColdDeviceStorage { public static default = { - themes: [] as Theme[], // TODO: ãã®ã†ã¡æ¶ˆã™ // TODO: テーマをアカウントã«ä¿å˜ã™ã‚‹ã‚ˆã†ã«ãªã£ãŸã®ã«ã‚‚ã‹ã‹ã‚らãšã€ä»¥ä¸‹ã®ã©ã®ãƒ†ãƒ¼ãƒžã‚’使ã†ã‹ã¨ã„ã†æƒ…å ±ã ã‘ãŒãƒ–ラウザä¿å˜ã«ãªã£ã¦ã„ã¦ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆåˆ‡ã‚Šæ›¿ãˆãŸã‚Šãƒã‚°ã‚¢ã‚¦ãƒˆã—ãŸã¨ãã«ä¸å…·åˆãŒç™ºç”Ÿã™ã‚‹ã®ã§ãªã‚“ã¨ã‹ã™ã‚‹ // テーマIDã‚’ä¿å˜ã™ã‚‹ã®ã§ã¯ãªãã€ãƒ†ãƒ¼ãƒžè‡ªä½“ã‚’ä¿å˜ã™ã‚‹ã‚ˆã†ã«ã™ã‚Œã°è§£æ±ºã™ã‚‹ã‹ã‚‚ darkTheme: '8050783a-7f63-445a-b270-36d0f6ba1677', diff --git a/src/client/theme-store.ts b/src/client/theme-store.ts index 5e440efbf9841759470fce16148fef902b50400d..8e21af70fcdd80680b87992e8582f55f4cdd0df9 100644 --- a/src/client/theme-store.ts +++ b/src/client/theme-store.ts @@ -33,30 +33,3 @@ export async function removeTheme(theme: Theme): Promise<void> { await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); localStorage.setItem(lsCacheKey, JSON.stringify(themes)); } - -// TODO: ãã®ã†ã¡æ¶ˆã™ -if (ColdDeviceStorage.get('themes').length > 0) { - const lsThemes = ColdDeviceStorage.get('themes'); - let registryThemes; - try { - registryThemes = await api('i/registry/get', { scope: ['client'], key: 'themes' }); - } catch (e) { - if (e.code === 'NO_SUCH_KEY') { - registryThemes = []; - } else { - throw e; - } - } - const themes = [] as Theme[]; - for (const theme of lsThemes) { - if (themes.some(x => x.id === theme.id)) continue; - themes.push(theme); - } - for (const theme of registryThemes) { - if (themes.some(x => x.id === theme.id)) continue; - themes.push(theme); - } - await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); - localStorage.setItem(lsCacheKey, JSON.stringify(themes)); - ColdDeviceStorage.set('themes', []); -} diff --git a/src/client/ui/chat/note.vue b/src/client/ui/chat/note.vue index 5a4a13d8896c408786fe5ad2c7bb8275221ad6da..97275875ca8ba374f18bd4935e1939db4f696072 100644 --- a/src/client/ui/chat/note.vue +++ b/src/client/ui/chat/note.vue @@ -325,7 +325,8 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); + // TODO: ã“ã®ãƒŽãƒ¼ãƒˆãŒã‚¹ãƒˆãƒªãƒ¼ãƒŸãƒ³ã‚°çµŒç”±ã§æµã‚Œã¦ããŸå ´åˆã®ã¿ sr ã™ã‚‹ + this.connection.send(document.body.contains(this.$el) ? 'sr' : 's', { id: this.appearNote.id }); if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/daemons/janitor.ts b/src/daemons/janitor.ts index 462ebf915c434855d94b1582eae5cae5824e22b8..c079086427cae8d94194eb0c80581d7033acb1ea 100644 --- a/src/daemons/janitor.ts +++ b/src/daemons/janitor.ts @@ -1,3 +1,5 @@ +// TODO: 消ã—ãŸã„ + const interval = 30 * 60 * 1000; import { AttestationChallenges } from '../models'; import { LessThan } from 'typeorm'; diff --git a/src/daemons/queue-stats.ts b/src/daemons/queue-stats.ts index 288e855ae973aa8f2771aedb71025bbb07403417..77f09b18d63dede2cd1cf20641745499cffc6aba 100644 --- a/src/daemons/queue-stats.ts +++ b/src/daemons/queue-stats.ts @@ -1,5 +1,5 @@ import Xev from 'xev'; -import { deliverQueue, inboxQueue } from '../queue'; +import { deliverQueue, inboxQueue } from '../queue/queues'; const ev = new Xev(); diff --git a/src/daemons/server-stats.ts b/src/daemons/server-stats.ts index bc1da5eef665a9a691cf0e503c2f406247adcfd3..8dfa946250408ca3e9e5573d58929a7e06da1ad1 100644 --- a/src/daemons/server-stats.ts +++ b/src/daemons/server-stats.ts @@ -75,5 +75,5 @@ async function net() { // FS STAT async function fs() { const data = await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 })); - return data; + return data || { rIO_sec: 0, wIO_sec: 0 }; } diff --git a/src/db/postgre.ts b/src/db/postgre.ts index 2f3c910163ae07030f1485304c9b8c899a2e26b7..831e5e05923d22bacee4e46ff9be7d42ab6f98b7 100644 --- a/src/db/postgre.ts +++ b/src/db/postgre.ts @@ -1,3 +1,7 @@ +// https://github.com/typeorm/typeorm/issues/2400 +const types = require('pg').types; +types.setTypeParser(20, Number); + import { createConnection, Logger, getConnection } from 'typeorm'; import config from '../config'; import { entities as charts } from '../services/chart/entities'; diff --git a/src/docs/fr-FR/api.md b/src/docs/fr-FR/api.md index 76019b61458259bed1eef3ae3b5890e4c09c3837..0147f95bac24c2150b0c0bd89e9647a319fef482 100644 --- a/src/docs/fr-FR/api.md +++ b/src/docs/fr-FR/api.md @@ -1,58 +1,58 @@ -# Misskey API +# API de Misskey -MisskeyAPIを使ã£ã¦Misskeyクライアントã€Misskey連æºWebサービスã€Botç‰(以下「アプリケーションã€ã¨å‘¼ã³ã¾ã™)を開発ã§ãã¾ã™ã€‚ ストリーミングAPIã‚‚ã‚ã‚‹ã®ã§ã€ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ 性ã®ã‚るアプリケーションを作るã“ã¨ã‚‚å¯èƒ½ã§ã™ã€‚ +Vous pouvez utiliser l'API de Misskey pour développer des clients Misskey, des services web s'intégrant à Misskey, des Bots (que nous appellerons plus loin "Applications"), etc. Comme l'API streaming est aussi implémenté, vous avez la possibilité de créer des applications de temps réel. -APIを使ã„始ã‚ã‚‹ã«ã¯ã€ã¾ãšã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’å–å¾—ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ ã“ã®ãƒ‰ã‚ュメントã§ã¯ã€ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’å–å¾—ã™ã‚‹æ‰‹é †ã‚’説明ã—ãŸå¾Œã€åŸºæœ¬çš„ãªAPIã®ä½¿ã„方を説明ã—ã¾ã™ã€‚ +Pour pouvoir vous servir de l'API, il vous faudra d'abord obtenir un jeton d'accès. Ce guide a été conçu pour vous accompagner dans le processus d'obtention du jeton d'accès, puis donner des instructions de base sur l'utilisation de l'API. -## アクセストークンã®å–å¾— -基本的ã«ã€APIã¯ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«ã¯ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ãŒå¿…è¦ã¨ãªã‚Šã¾ã™ã€‚ APIã«ãƒªã‚¯ã‚¨ã‚¹ãƒˆã™ã‚‹ã®ãŒè‡ªåˆ†è‡ªèº«ãªã®ã‹ã€ä¸ç‰¹å®šã®åˆ©ç”¨è€…ã«ä½¿ã£ã¦ã‚‚らã†ã‚¢ãƒ—リケーションãªã®ã‹ã«ã‚ˆã£ã¦å–å¾—æ‰‹é †ã¯ç•°ãªã‚Šã¾ã™ã€‚ +## Obtenir le jeton d'accès +Une requête d'API, par essence, nécessite un jeton d'accès. La procédure d'acquisition du jeton diffère selon que vous effectuez la requête vous-même, ou qu'elle est envoyée via une application par un utilisateur final non défini. -* å‰è€…ã®å ´åˆ: [「自分自身ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’手動発行ã™ã‚‹ã€](#自分自身ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’手動発行ã™ã‚‹)ã«é€²ã‚€ -* 後者ã®å ´åˆ: [「アプリケーション利用者ã«ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã®ç™ºè¡Œã‚’リクエストã™ã‚‹ã€](#アプリケーション利用者ã«ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã®ç™ºè¡Œã‚’リクエストã™ã‚‹)ã«é€²ã‚€ +* Dans le premier cas : allez à [« Générer manuellement un jeton d'accès pour son propre compte »](#自分自身ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’手動発行ã™ã‚‹). +* Dans le second cas : allez à [« Demander la génération du jeton d'accès via un utilisateur d'application »](#アプリケーション利用者ã«ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã®ç™ºè¡Œã‚’リクエストã™ã‚‹). -### 自分自身ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’手動発行ã™ã‚‹ -「è¨å®š > APIã€ã§ã€è‡ªåˆ†ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’発行ã§ãã¾ã™ã€‚ +### Générer manuellement un jeton d'accès pour son propre compte +Vous pouvez générer votre propre jeton d'accès en allant dans { Paramètres > API }. -[「APIã®ä½¿ã„æ–¹ã€ã¸é€²ã‚€](#APIã®ä½¿ã„æ–¹) +[Continuer avec « Utiliser l'API ».](#APIã®ä½¿ã„æ–¹) -### アプリケーション利用者ã«ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã®ç™ºè¡Œã‚’リクエストã™ã‚‹ -アプリケーション利用者ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’å–å¾—ã™ã‚‹ã«ã¯ã€ä»¥ä¸‹ã®æ‰‹é †ã§ç™ºè¡Œã‚’リクエストã—ã¾ã™ã€‚ +### Demander la génération du jeton d'accès via un utilisateur d'application +Pour obtenir un jeton d'accès pour le compte utilisateur final de votre application, suivez la procédure de génération ci-dessous. -#### Step 1 +#### Étape 1 -UUIDを生æˆã™ã‚‹ã€‚以後ã“れをセッションIDã¨å‘¼ã³ã¾ã™ã€‚ +Générez un UUID. Nous l'appellerons « ID de session » dans la suite de ce guide. -> ã“ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³IDã¯æ¯Žå›žç”Ÿæˆã—ã€ä½¿ã„ã¾ã‚ã•ãªã„よã†ã«ã—ã¦ãã ã•ã„。 +> Un même ID de session ne devrait pas être utilisé plusieurs fois ; veillez à en générer un nouveau pour chaque jeton d'accès. -#### Step 2 +#### Étape 2 -`{_URL_}/miauth/{session}`をユーザーã®ãƒ–ラウザã§è¡¨ç¤ºã•ã›ã‚‹ã€‚`{session}`ã®éƒ¨åˆ†ã¯ã€ã‚»ãƒƒã‚·ãƒ§ãƒ³IDã«ç½®ãæ›ãˆã¦ãã ã•ã„。 -> 例: `{_URL_}/miauth/c1f6d42b-468b-4fd2-8274-e58abdedef6f` +Ouvrez l'adresse `{_URL_}/miauth/{session}` dans le navigateur de l'utilisateur. Remplacez alors la partie `{session}` de l'URL par l'ID de session que vous venez de générer. +> Par ex. : `{_URL_}/miauth/c1f6d42b-468b-4fd2-8274-e58abdedef6f` -表示ã™ã‚‹éš›ã€URLã«ã‚¯ã‚¨ãƒªãƒ‘ラメータã¨ã—ã¦ã„ãã¤ã‹ã®ã‚ªãƒ—ションをè¨å®šã§ãã¾ã™: -* `name` ... アプリケーションå - * > 例: `MissDeck` -* `icon` ... アプリケーションã®ã‚¢ã‚¤ã‚³ãƒ³ç”»åƒURL - * > 例: `https://missdeck.example.com/icon.png` -* `callback` ... èªè¨¼ãŒçµ‚ã‚ã£ãŸå¾Œã«ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã™ã‚‹URL - * > 例: `https://missdeck.example.com/callback` - * リダイレクト時ã«ã¯ã€`session`ã¨ã„ã†ã‚¯ã‚¨ãƒªãƒ‘ラメータã§ã‚»ãƒƒã‚·ãƒ§ãƒ³IDãŒä»˜ãã¾ã™ -* `permission` ... アプリケーションãŒè¦æ±‚ã™ã‚‹æ¨©é™ - * > 例: `write:notes,write:following,read:drive` - * è¦æ±‚ã™ã‚‹æ¨©é™ã‚’`,`ã§åŒºåˆ‡ã£ã¦åˆ—挙ã—ã¾ã™ - * ã©ã®ã‚ˆã†ãªæ¨©é™ãŒã‚ã‚‹ã‹ã¯[APIリファレンス](/api-doc)ã§ç¢ºèªã§ãã¾ã™ +En ouvrant cette URL, vous pourrez configurer un certain nombre d'options pour les paramètres de requête : +* `name` : nom de l'application + * > Ex. : `MissDeck` +* `icon` : URL de l'icône de l'application + * > Ex. : `https://missdeck.example.com/icon.png` +* `callback` : URL de redirection après l'authentification + * > Ex. : `https://missdeck.example.com/callback` + * Lors de la redirection, un paramètre de requête `session` contenant l'ID de session sera joint. +* `permission` : permissions requises par l'application + * > Ex. : `write:notes,write:following,read:drive` + * Listez les permissions requises en utilisant une `,` pour les séparer. + * Vous pouvez vérifier quelles sont les permissions disponibles sur [les références API de Misskey](/api-doc). -#### Step 3 -ユーザーãŒç™ºè¡Œã‚’許å¯ã—ãŸå¾Œã€`{_URL_}/api/miauth/{session}/check`ã«POSTリクエストã™ã‚‹ã¨ã€ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã¨ã—ã¦ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’å«ã‚€JSONãŒè¿”ã‚Šã¾ã™ã€‚ +#### Étape 3 +Si vous envoyez une requête POST à `{_URL_}/api/miauth/{session}/check` une fois que l'utilisateur a validé le jeton d'accès, la réponse arrivera sous forme de fichier JSON contenant ce jeton. -レスãƒãƒ³ã‚¹ã«å«ã¾ã‚Œã‚‹ãƒ—ãƒãƒ‘ティ: -* `token` ... ユーザーã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ -* `user` ... ユーザーã®æƒ…å ± +Propriétés incluses dans la réponse : +* `token` : jeton d'accès de l'utilisateur +* `user` : données de l'utilisateur -[「APIã®ä½¿ã„æ–¹ã€ã¸é€²ã‚€](#APIã®ä½¿ã„æ–¹) +[Continuer avec « Utiliser l'API ».](#APIã®ä½¿ã„æ–¹) -## APIã®ä½¿ã„æ–¹ -**APIã¯ã™ã¹ã¦POSTã§ã€ãƒªã‚¯ã‚¨ã‚¹ãƒˆ/レスãƒãƒ³ã‚¹ã¨ã‚‚ã«JSONå½¢å¼ã§ã™ã€‚RESTã§ã¯ã‚ã‚Šã¾ã›ã‚“。** アクセストークンã¯ã€`i`ã¨ã„ã†ãƒ‘ラメータåã§ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«å«ã‚ã¾ã™ã€‚ +## Utiliser l'API +**L'API utilise seulement la méthode POST, et toutes les requêtes / réponses sont au format JSON. REST n'est pas pris en charge. ** Le jeton d'accès s'insère dans le paramètre de requête nommé `i`. -* [APIリファレンス](/api-doc) -* [ストリーミングAPI](./stream) +* [Références API de Misskey](/api-doc) +* [API streaming](./stream) diff --git a/src/docs/fr-FR/create-plugin.md b/src/docs/fr-FR/create-plugin.md index 540909de425ce205ed637929b502a6b190db8967..c0d5fa9c57c7b148714e9dafe07f2d33a00e6ca9 100644 --- a/src/docs/fr-FR/create-plugin.md +++ b/src/docs/fr-FR/create-plugin.md @@ -34,7 +34,7 @@ Misskey Webクライアントã®ãƒ—ラグイン機能を使ã†ã¨ã€ã‚¯ãƒ©ã‚¤ã‚¢ #### default è¨å®šã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆå€¤ -## APIリファレンス +## Références API de Misskey AiScript標準ã§çµ„ã¿è¾¼ã¾ã‚Œã¦ã„ã‚‹APIã¯æŽ²è¼‰ã—ã¾ã›ã‚“。 ### Mk:dialog(title text type) diff --git a/src/docs/fr-FR/follow.md b/src/docs/fr-FR/follow.md index 5ef74c09031a5d530cb7bb61866aaa31232dea69..fde5c5e47198c32969283153cbfbe34b3ab2bd10 100644 --- a/src/docs/fr-FR/follow.md +++ b/src/docs/fr-FR/follow.md @@ -1,2 +1,2 @@ # Abonnements -Lorsque vous suivez un·e utilisateur·rice, ses publications apparaissent dans votre fil.Cela n'inclut toutefois pas ses réponses aux autres utilisateur·ice·s. Vous pouvez vous désabonner du compte en cliquant une seconde fois. +Lorsque vous suivez un·e utilisateur·rice, ses publications apparaissent dans votre fil.Cela n'inclut toutefois pas ses réponses aux autres utilisateur·ice·s. Pour suivre un compte, rendez-vous sur sa page et cliquez sur le bouton « s'abonner ». Vous pouvez vous désabonner du compte en cliquant une seconde fois. diff --git a/src/docs/fr-FR/pages.md b/src/docs/fr-FR/pages.md index 9021d41b1c62a5dee68aa5c09ebe255cfade8fa6..0dbafb806ec29b812eea1186674116012a22e732 100644 --- a/src/docs/fr-FR/pages.md +++ b/src/docs/fr-FR/pages.md @@ -3,8 +3,8 @@ ## Variables Vous pouvez créer des pages dynamiques en utilisant des variables.Vous pouvez incorporer la valeur d'une variable en insérant le <b>{ variablename }</b> dans votre texte.Par exemple, si la valeur de la variable "thing" dans le texte <b>Hello { thing } world!</b> est <b>ai</b>, votre trexte devient alors : <b>Hello ai world!</b>. -Les variables sont évaluées du haut vers le bas, il n'est donc pas possible de référencer une variable située plus bas que celle en cours.Par exemple, si vous définissez, dans l'ordre, 3 variables telles que <b>Aã€Bã€C</b>, vous pourrez référencer en <b>C</b> aussi bien <b>A</b> que <b>B</b> ; par contre, vous ne pourrez référencer en <b>A</b> ni <b>B</b> ni <b>C</b>. +Les variables sont prises en compte dans l'ordre chronologique, de haut en bas. Il n'est donc pas possible d'appeler une variable située plus bas dans le code. Par exemple, si vous définissez, dans l'ordre, 3 variables telles que <b>A, B, C</b>, vous pourrez appeler en <b>C</b> aussi bien <b>A</b> que <b>B</b> ; par contre, vous ne pourrez appeler en <b>A</b> ni <b>B</b> ni <b>C</b>. Pour recevoir une entrée utilisateur, ajoutez un bloc "Entrée" sur la page et définissez le nom des variables que vous souhaitez stocker dans le champ "Nom de la variable" (les variables seront créées automatiquement).Vous pourrez alors exécuter les actions en fonction de l'entrée utilisateur de ces variables. -Utiliser des fonctions vous permettra de mettre en place une façon de calculer des valeurs que vous pourrez réutiliser.Pour créer des fonctions, il faut d'abord définir une variable du type "fonction".Ensuite, vous pouvez configurer des arguments dont la valeur sera utilisable comme une variable à l'intérieur de la fonction. Par ailleurs, il existe ce que l'on appelle des "fonctions d'ordre supérieur" dont les arguments sont aussi des fonctions. En plus de paramétrer des fonctions à l'avance, vous avez également la possibilité de définir des fonctions à l'improviste directement dans les arguments de ces "fonctions d'ordre supérieur". +Appeler des fonctions vous permet de définir des valeurs que vous pourrez réutiliser. Pour créer des fonctions, il faut d'abord définir une variable du type "fonction". Vous pouvez y configurer des « slots » (arguments), dont la valeur devient alors disponible en tant que variable à l'intérieur de la fonction. Par ailleurs, il existe ce que l'on appelle des "fonctions d'ordre supérieur" dont les arguments sont aussi des fonctions. En plus de paramétrer des fonctions à l'avance, vous avez également la possibilité de définir des fonctions à l'improviste directement dans les « slots » de ces fonctions d'ordre supérieur. diff --git a/src/docs/fr-FR/stream.md b/src/docs/fr-FR/stream.md index 04dad66799c06f35fb4eae6e34ca34882b9aa206..55e6bc680696054c452780addf3b4b735bb127cf 100644 --- a/src/docs/fr-FR/stream.md +++ b/src/docs/fr-FR/stream.md @@ -1,4 +1,4 @@ -# ストリーミングAPI +# API streaming ストリーミングAPIを使ã†ã¨ã€ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ ã§æ§˜ã€…ãªæƒ…å ±(例ãˆã°ã‚¿ã‚¤ãƒ ラインã«æ–°ã—ã„投稿ãŒæµã‚Œã¦ããŸã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ãŒå±Šã„ãŸã€ãƒ•ã‚©ãƒãƒ¼ã•ã‚ŒãŸã€ãªã©)ã‚’å—ã‘å–ã£ãŸã‚Šã€æ§˜ã€…ãªæ“作を行ã£ãŸã‚Šã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ diff --git a/src/docs/fr-FR/theme.md b/src/docs/fr-FR/theme.md index 72c60f1c6b34b654465952b82a7dd768fca121dd..cf15c921e4bdf3da1661a645881236575f261ca1 100644 --- a/src/docs/fr-FR/theme.md +++ b/src/docs/fr-FR/theme.md @@ -6,7 +6,7 @@ Vous pouvez modifier l'apparence de votre client Misskey à l'aide de thèmes. Paramètres > Thèmes ## Créer un thème -Les codes des thèmes sont écrits sous forme d'objets JSON5. Les thèmes comprennent les objets suivants : +Le code des thèmes est écrit sous forme d'objets JSON5. Les thèmes comprennent les objets suivants : ``` js { id: '17587283-dd92-4a2c-a22c-be0637c9e22a', @@ -43,7 +43,7 @@ Les codes des thèmes sont écrits sous forme d'objets JSON5. Les thèmes compre * `props` ... Définir un style de thème.Voir les explications ci-après. ### Définir un style de thème -C'est dans `props` que vous définirez le style de thème. Les propriétés deviendront des variables CSS et les valeurs spécifieront le contenu. Par ailleurs, les objets présents par défaut dans `props` sont hérités du thème de base. Ainsi, si le thème de `base` est clair `light` ce sera l'objet [_light.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_light.json5) ; et s'il est sombre `dark` ce sera l'objet [_dark.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_dark.json5). Cela signifie, par exemple, que s'il n'y pas de propriété `panel` définie dans les `props` du thème, alors ce sera la valeur `panel` du thème de base qui sera prise en compte. +C'est dans `props` que vous définirez le style de thème. Les propriétés deviendront des variables CSS et les valeurs associées spécifieront le contenu de ces variables. Par ailleurs, les objets présents par défaut dans `props` sont hérités du thème de base. Ainsi, si le thème de `base` est clair `light` ce sera l'objet [_light.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_light.json5) ; et s'il est sombre `dark` ce sera l'objet [_dark.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_dark.json5). Cela signifie, par exemple, que s'il n'y pas de propriété `panel` définie dans les `props` du thème, alors ce sera la valeur `panel` du thème de base qui sera prise en compte. #### Syntaxe des valeurs * Codes de couleur Hex diff --git a/src/docs/it-IT/custom-emoji.md b/src/docs/it-IT/custom-emoji.md index ed2e92be16bb35ca0cf59fcd5fd9864f53297eb2..900f115d3c579ebcf776fb9abf8e39dafb2a9360 100644 --- a/src/docs/it-IT/custom-emoji.md +++ b/src/docs/it-IT/custom-emoji.md @@ -1,2 +1,2 @@ -# ã‚«ã‚¹ã‚¿ãƒ çµµæ–‡å— -カスタム絵文å—ã¯ã€ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã§ç”¨æ„ã•ã‚ŒãŸç”»åƒã‚’絵文å—ã®ã‚ˆã†ã«ä½¿ãˆã‚‹æ©Ÿèƒ½ã§ã™ã€‚ ノートã€ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€ãƒãƒ£ãƒƒãƒˆã€è‡ªå·±ç´¹ä»‹ã€åå‰ãªã©ã®å ´æ‰€ã§ä½¿ã†ã“ã¨ãŒã§ãã¾ã™ã€‚ カスタム絵文å—ã‚’ãれらã®å ´æ‰€ã§ä½¿ã†ã«ã¯ã€çµµæ–‡å—ピッカーボタン(ã‚ã‚‹å ´åˆ)を押ã™ã‹ã€`:`を入力ã—ã¦çµµæ–‡å—サジェストを表示ã—ã¾ã™ã€‚ テã‚スト内ã«`:foo:`ã®ã‚ˆã†ãªå½¢å¼ã®æ–‡å—列ãŒè¦‹ã¤ã‹ã‚‹ã¨ã€`foo`ã®éƒ¨åˆ†ãŒã‚«ã‚¹ã‚¿ãƒ 絵文å—åã¨è§£é‡ˆã•ã‚Œã€è¡¨ç¤ºæ™‚ã«ã¯å¯¾å¿œã—ãŸã‚«ã‚¹ã‚¿ãƒ 絵文å—ã«ç½®ãæ›ã‚ã‚Šã¾ã™ã€‚ +# Emoji personalizzati +Gli emoji personalizzati sono una funzionalità che ti permette di usare delle immagini preparate dalla tua istanza come emoji. Si possono usare in note, reazioni, chat, nella biografia di profilo, nel tuo nome, e altrove su Misskey. Per usare gli emoji personalizzati, puoi aprire la tastiera emoji (quando c'è), oppure visualizzare suggerimenti emoji scrivendo `:`. Quando una sequenza di caratteri del tipo `:foo:` è trovata in un testo, la parte centrale `foo` viene interpretata come un nome di emoji personalizzato e quindi viene sostituita dall'emoji corrispondente. diff --git a/src/docs/it-IT/follow.md b/src/docs/it-IT/follow.md index f636a710f3e3dee38b52b0d557cba014cdf82130..c54099a361e562f124a1b3d5ed90da816489ff7a 100644 --- a/src/docs/it-IT/follow.md +++ b/src/docs/it-IT/follow.md @@ -1,2 +1,2 @@ -# Seiguiti -ユーザーをフォãƒãƒ¼ã™ã‚‹ã¨ã€ã‚¿ã‚¤ãƒ ラインã«ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•ç¨¿ãŒè¡¨ç¤ºã•ã‚Œã‚‹ã‚ˆã†ã«ãªã‚Šã¾ã™ã€‚ãŸã ã—ã€ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å¯¾ã™ã‚‹è¿”ä¿¡ã¯å«ã¾ã‚Œã¾ã›ã‚“。 ユーザーをフォãƒãƒ¼ã™ã‚‹ã«ã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒšãƒ¼ã‚¸ã®ã€Œãƒ•ã‚©ãƒãƒ¼ã€ãƒœã‚¿ãƒ³ã‚’クリックã—ã¾ã™ã€‚フォãƒãƒ¼ã‚’解除ã™ã‚‹ã«ã¯ã€ã‚‚ã†ä¸€åº¦ã‚¯ãƒªãƒƒã‚¯ã—ã¾ã™ã€‚ +# Follow +Se segui un utente, le sue pubblicazioni verranno mostrate sulla tua timeline. A esclusione delle sue risposte ad altrÉ™ utenti. Per seguire un utente, bisogna premere il pulsante "seguire" della sua pagina. Premi una seconda volta sul pulsante per smettere di seguire l'account. diff --git a/src/docs/it-IT/keyboard-shortcut.md b/src/docs/it-IT/keyboard-shortcut.md index a547e6115e6c0c8e9956837d9890a7dbfead02b6..c9bb815bae99c0d9f5e1f1a593c095b4018f0e1f 100644 --- a/src/docs/it-IT/keyboard-shortcut.md +++ b/src/docs/it-IT/keyboard-shortcut.md @@ -1,17 +1,17 @@ -# ã‚ーボードショートカット +# Scorciatoie da tastiera -## ã‚°ãƒãƒ¼ãƒãƒ« -ã“れらã®ã‚·ãƒ§ãƒ¼ãƒˆã‚«ãƒƒãƒˆã¯åŸºæœ¬çš„ã«ã©ã“ã§ã‚‚使ãˆã¾ã™ã€‚ +## Generali +Le scorciatoie da tastiera sotto citate si possono usare praticamente ovunque. <table> <thead> - <tr><th>ショートカット</th><th>効果</th><th>ç”±æ¥</th></tr> + <tr><th>Scorciatoia</th><th>Effetto</th><th>Accesso universale</th></tr> </thead> <tbody> - <tr><td><kbd class="key">P</kbd>, <kbd class="key">N</kbd></td><td>æ–°è¦æŠ•ç¨¿</td><td><b>P</b>ost, <b>N</b>ew, <b>N</b>ote</td></tr> - <tr><td><kbd class="key">T</kbd></td><td>タイムラインã®æœ€ã‚‚æ–°ã—ã„投稿ã«ãƒ•ã‚©ãƒ¼ã‚«ã‚¹</td><td><b>T</b>imeline, <b>T</b>op</td></tr> - <tr><td><kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">N</kbd></kbd></td><td>通知を表示/éš ã™</td><td><b>N</b>otifications</td></tr> + <tr><td><kbd class="key">P</kbd>, <kbd class="key">N</kbd></td><td>Nuova pubblicazione</td><td><b>P</b>ost, <b>N</b>ew, <b>N</b>ote</td></tr> + <tr><td><kbd class="key">T</kbd></td><td>Evidenziare l'ultima pubblicazione sulla timeline</td><td><b>T</b>imeline, <b>T</b>op</td></tr> + <tr><td><kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">N</kbd></kbd></td><td>Mostrare/nascondere notifiche</td><td><b>N</b>otifications</td></tr> <tr><td><kbd class="key">S</kbd></td><td>Cerca</td><td><b>S</b>earch</td></tr> - <tr><td><kbd class="key">H</kbd>, <kbd class="key">?</kbd></td><td>ヘルプを表示</td><td><b>H</b>elp</td></tr> + <tr><td><kbd class="key">H</kbd>, <kbd class="key">?</kbd></td><td>Visualizzare l'aiuto</td><td><b>H</b>elp</td></tr> </tbody> </table> @@ -19,7 +19,7 @@ <table> <thead> - <tr><th>ショートカット</th><th>効果</th><th>ç”±æ¥</th></tr> + <tr><th>Scorciatoia</th><th>Effetto</th><th>Accesso universale</th></tr> </thead> <tbody> <tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</kbd>, <kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">Tab</kbd></kbd></td><td>上ã®æŠ•ç¨¿ã«ãƒ•ã‚©ãƒ¼ã‚«ã‚¹ã‚’移動</td><td>-</td></tr> @@ -37,24 +37,24 @@ </tbody> </table> -## Renoteフォーム+## Finestra Rinota <table> <thead> - <tr><th>ショートカット</th><th>効果</th><th>ç”±æ¥</th></tr> + <tr><th>Scorciatoia</th><th>Effetto</th><th>Accesso universale</th></tr> </thead> <tbody> - <tr><td><kbd class="key">Enter</kbd></td><td>Renoteã™ã‚‹</td><td>-</td></tr> - <tr><td><kbd class="key">Q</kbd></td><td>フォームを展開ã™ã‚‹</td><td><b>Q</b>uote</td></tr> - <tr><td><kbd class="key">Esc</kbd></td><td>フォームを閉ã˜ã‚‹</td><td>-</td></tr> + <tr><td><kbd class="key">Enter</kbd></td><td>Rinotare</td><td>-</td></tr> + <tr><td><kbd class="key">Q</kbd></td><td>Aprire finestra</td><td><b>Q</b>uote</td></tr> + <tr><td><kbd class="key">Esc</kbd></td><td>Chiudere finestra</td><td>-</td></tr> </tbody> </table> -## リアクションフォーム-デフォルトã§ã€ŒðŸ‘ã€ã«ãƒ•ã‚©ãƒ¼ã‚«ã‚¹ãŒå½“ãŸã£ã¦ã„る状態ã§ã™ã€‚ +## Pannello reazioni +La reazione "ðŸ‘" è impostata come reazione predefinita. <table> <thead> - <tr><th>ショートカット</th><th>効果</th><th>ç”±æ¥</th></tr> + <tr><th>Scorciatoia</th><th>Effetto</th><th>Accesso universale</th></tr> </thead> <tbody> <tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</kbd></td><td>上ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã«ãƒ•ã‚©ãƒ¼ã‚«ã‚¹ã‚’移動</td><td>-</td></tr> diff --git a/src/docs/it-IT/mute.md b/src/docs/it-IT/mute.md index a6cb0737558e7f62b2ee19f2103a4fd3f37ad2c5..d0fa672f4ab1a9b0a52d5ee161a0fa4c655f04c0 100644 --- a/src/docs/it-IT/mute.md +++ b/src/docs/it-IT/mute.md @@ -1,13 +1,13 @@ -# Silenzia +# Silenziare -ユーザーをミュートã™ã‚‹ã¨ã€ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«é–¢ã™ã‚‹æ¬¡ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒMisskeyã«è¡¨ç¤ºã•ã‚Œãªããªã‚Šã¾ã™: +Quando si silenzia un utente, i successivi contenuti che lo riguardano non saranno più visualizzati su Misskey: -* タイムラインや投稿ã®æ¤œç´¢çµæžœå†…ã®ã€ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•ç¨¿(ãŠã‚ˆã³ãれらã®æŠ•ç¨¿ã«å¯¾ã™ã‚‹è¿”ä¿¡ã‚„Renote) -* ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰ã®é€šçŸ¥ -* メッセージ履æ´ä¸€è¦§å†…ã®ã€ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸å±¥æ´ +* le pubblicazioni dell'utente sia nelle timeline che nei risultati di ricerca, così come le sue risposte e Rinote; +* le notifiche riguardo all'utente; +* la cronologia dei messaggi scambiati con l'utente nella chat. -ユーザーをミュートã™ã‚‹ã«ã¯ã€å¯¾è±¡ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒšãƒ¼ã‚¸ã«è¡¨ç¤ºã•ã‚Œã¦ã„る「ミュートã€ãƒœã‚¿ãƒ³ã‚’押ã—ã¾ã™ã€‚ +Per silenziare un utente, premi il pulsante "Silenzia" che si trova sulla sua pagina profilo. -ミュートを行ã£ãŸã“ã¨ã¯ç›¸æ‰‹ã«é€šçŸ¥ã•ã‚Œãšã€ãƒŸãƒ¥ãƒ¼ãƒˆã•ã‚Œã¦ã„ã‚‹ã“ã¨ã‚’知るã“ã¨ã‚‚ã§ãã¾ã›ã‚“。 +Gli utenti silenziati da te non vengono informati; nello stesso modo, tu non sarai infomat@ se vieni silenziat@ da un altr@ utente. -è¨å®š>ミュート ã‹ã‚‰ã€è‡ªåˆ†ãŒãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザー一覧を確èªã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ +Puoi controllare la lista di utenti che hai silenziato nelle Impostazioni account > "Silenziati / Bloccati". diff --git a/src/docs/it-IT/pages.md b/src/docs/it-IT/pages.md index 81c19dd20ab21b655611034bff4c0401afb32350..5a34971f1a942a0874fb35a1392ae90c8d24a0a4 100644 --- a/src/docs/it-IT/pages.md +++ b/src/docs/it-IT/pages.md @@ -1,4 +1,4 @@ -# Pages +# Pagine ## Variabili 変数を使ã†ã“ã¨ã§å‹•çš„ãªãƒšãƒ¼ã‚¸ã‚’作æˆã§ãã¾ã™ã€‚テã‚スト内㧠<b>{ 変数å }</b> ã¨æ›¸ãã¨ãã“ã«å¤‰æ•°ã®å€¤ã‚’埋ã‚è¾¼ã‚ã¾ã™ã€‚例ãˆã° <b>Hello { thing } world!</b> ã¨ã„ã†ãƒ†ã‚ストã§ã€å¤‰æ•°(thing)ã®å€¤ãŒ <b>ai</b> ã ã£ãŸå ´åˆã€ãƒ†ã‚スト㯠<b>Hello ai world!</b> ã«ãªã‚Šã¾ã™ã€‚ diff --git a/src/docs/it-IT/reaction.md b/src/docs/it-IT/reaction.md index eac6cc88e9aea9f7f2df31c58b30a0758e718e7f..f88dd99c3e20168552b56aaebf58fb859f7601b3 100644 --- a/src/docs/it-IT/reaction.md +++ b/src/docs/it-IT/reaction.md @@ -1,11 +1,11 @@ -# Reazione -ä»–ã®äººã®ãƒŽãƒ¼ãƒˆã«ã€çµµæ–‡å—を付ã‘ã¦ç°¡å˜ã«ã‚ãªãŸã®å応をä¼ãˆã‚‰ã‚Œã‚‹æ©Ÿèƒ½ã§ã™ã€‚ リアクションã™ã‚‹ã«ã¯ã€ãƒŽãƒ¼ãƒˆã® + アイコンをクリックã—ã¦ãƒ”ッカーを表示ã—ã€çµµæ–‡å—ã‚’é¸æŠžã—ã¾ã™ã€‚ リアクションã«ã¯[カスタム絵文å—](./custom-emoji)も使用ã§ãã¾ã™ã€‚ +# Reazioni +Puoi mandare una reazione rapida alle note degli/delle altrÉ™ utenti apponendoci emoji. Per reagire, premi il pulsante "+" della nota per aprire il pannello reazioni e scegliere un emoji. Si possono anche usare gli [emoji personalizzati](./custom-emoji). -## リアクションピッカーã®ã‚«ã‚¹ã‚¿ãƒžã‚¤ã‚º -ピッカーã«è¡¨ç¤ºã•ã‚Œã‚‹çµµæ–‡å—を自分好ã¿ã«ã‚«ã‚¹ã‚¿ãƒžã‚¤ã‚ºã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ è¨å®šã®ã€Œãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€ã§è¨å®šã—ã¾ã™ã€‚ +## Personalizzare il pannello reazioni +È possibile personalizzare il pannello reazioni selezionando gli emoji che vuoi usare. Puoi cambiare i predefiniti nella scheda "Reazioni" delle impostazioni. -## リモート投稿ã¸ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã«ã¤ã„㦠-リアクションã¯Misskeyオリジナルã®æ©Ÿèƒ½ã§ã‚ã‚‹ãŸã‚ã€ãƒªãƒ¢ãƒ¼ãƒˆã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ãŒMisskeyã§ãªã„é™ã‚Šã¯ã€ã»ã¨ã‚“ã©ã®å ´åˆã€ŒLikeã€ã¨ã—ã¦ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティãŒé€ä¿¡ã•ã‚Œã¾ã™ã€‚一般的ã«ã¯Likeã¯ã€ŒãŠæ°—ã«å…¥ã‚Šã€ã¨ã—ã¦å®Ÿè£…ã•ã‚Œã¦ã„るよã†ã§ã™ã€‚ ã¾ãŸã€ç›¸æ‰‹ãŒMisskeyã§ã‚ã£ãŸã¨ã—ã¦ã‚‚ã€ã‚«ã‚¹ã‚¿ãƒ 絵文å—リアクションã¯ä¼ã‚らãšã€è‡ªå‹•çš„ã«ã€ŒðŸ‘ã€ç‰ã«ãƒ•ã‚©ãƒ¼ãƒ«ãƒãƒƒã‚¯ã•ã‚Œã¾ã™ã€‚ +## Inviare reazioni a server remoti +Siccome le reazioni sono una funzionalità originale di Misskey, vengono ricevute come semplici "Mi piace" dalla maggior parte delle istanze remote del Fediverso, a meno che non sia un'altra istanza Misskey.In genere sul Fediverso, la funzionalità "Mi piace" viene implementata come funzione "Preferiti". Inoltre, se reagisci con un emoji personalizzato verrà automaticamente inoltrato come un "ðŸ‘" o simile, anche se quella destinataria è un'istanza Misskey. -## リモートã‹ã‚‰ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã«ã¤ã„㦠-リモートã‹ã‚‰ã€ŒLikeã€ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティをå—ä¿¡ã—ãŸã¨ãã€Misskeyã§ã¯ã€ŒðŸ‘ã€ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¨ã—ã¦è§£é‡ˆã•ã‚Œã¾ã™ã€‚ +## Ricevere reazioni da server remoti +I "Mi piace" ricevuti da utenti di istanze remote vengono interpretati su Misskey come reazioni a forma di "ðŸ‘". diff --git a/src/docs/it-IT/theme.md b/src/docs/it-IT/theme.md index b877bcd4675a785692470ac8899c0bbc8c3cfa1f..ad21299e89a3d4d579b7ea1b13ea2f31952a7e75 100644 --- a/src/docs/it-IT/theme.md +++ b/src/docs/it-IT/theme.md @@ -1,12 +1,12 @@ # Tema -テーマをè¨å®šã—ã¦ã€Misskeyクライアントã®è¦‹ãŸç›®ã‚’変更ã§ãã¾ã™ã€‚ +Puoi utilizzare i temi per cambiare l'aspetto del client Misskey. -## テーマã®è¨å®š -è¨å®š > テーマ +## Impostazioni tema +Impostazioni > Tema -## テーマを作æˆã™ã‚‹ -テーマコードã¯JSON5ã§è¨˜è¿°ã•ã‚ŒãŸãƒ†ãƒ¼ãƒžã‚ªãƒ–ジェクトã§ã™ã€‚ テーマã¯ä»¥ä¸‹ã®ã‚ˆã†ãªã‚ªãƒ–ジェクトã§ã™ã€‚ +## Creare un tema +Il codice dei temi è scritto a forma di oggetti JSON5. I temi contengono gli oggetti sotto citati: ``` js { id: '17587283-dd92-4a2c-a22c-be0637c9e22a', @@ -42,10 +42,10 @@ * テーマã¯ã“ã“ã§è¨å®šã•ã‚ŒãŸãƒ™ãƒ¼ã‚¹ãƒ†ãƒ¼ãƒžã‚’継承ã—ã¾ã™ã€‚ * `props` ... テーマã®ã‚¹ã‚¿ã‚¤ãƒ«å®šç¾©ã€‚ã“ã‚Œã‹ã‚‰èª¬æ˜Žã—ã¾ã™ã€‚ -### テーマã®ã‚¹ã‚¿ã‚¤ãƒ«å®šç¾© +### Impostare uno stile di tema `props`下ã«ã¯ãƒ†ãƒ¼ãƒžã®ã‚¹ã‚¿ã‚¤ãƒ«ã‚’定義ã—ã¾ã™ã€‚ ã‚ーãŒCSSã®å¤‰æ•°åã«ãªã‚Šã€ãƒãƒªãƒ¥ãƒ¼ã§ä¸èº«ã‚’指定ã—ã¾ã™ã€‚ ãªãŠã€ã“ã®`props`オブジェクトã¯ãƒ™ãƒ¼ã‚¹ãƒ†ãƒ¼ãƒžã‹ã‚‰ç¶™æ‰¿ã•ã‚Œã¾ã™ã€‚ ベーステーマã¯ã€ã“ã®ãƒ†ãƒ¼ãƒžã®`base`ãŒ`light`ãªã‚‰[_light.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_light.json5)ã§ã€`dark`ãªã‚‰[_dark.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_dark.json5)ã§ã™ã€‚ ã¤ã¾ã‚Šã€ã“ã®ãƒ†ãƒ¼ãƒžå†…ã®`props`ã«`panel`ã¨ã„ã†ã‚ーãŒç„¡ãã¦ã‚‚ã€ãã“ã«ã¯ãƒ™ãƒ¼ã‚¹ãƒ†ãƒ¼ãƒžã®`panel`ãŒã‚ã‚‹ã¨è¦‹ãªã•ã‚Œã¾ã™ã€‚ -#### ãƒãƒªãƒ¥ãƒ¼ã§ä½¿ãˆã‚‹æ§‹æ–‡ +#### Sintassi dei valori * 16進数ã§è¡¨ã•ã‚ŒãŸè‰² * 例: `#00ff00` * `rgb(r, g, b)`å½¢å¼ã§è¡¨ã•ã‚ŒãŸè‰² @@ -61,8 +61,8 @@ * 関数(後述) * `:{関数å}<{引数}<{色}` -#### Costante +#### Costanti 「CSS変数ã¨ã—ã¦å‡ºåŠ›ã¯ã—ãŸããªã„ãŒã€ä»–ã®CSS変数ã®å€¤ã¨ã—ã¦ä½¿ã„ã¾ã‚ã—ãŸã„ã€å€¤ãŒã‚ã‚‹ã¨ãã¯ã€å®šæ•°ã‚’使ã†ã¨ä¾¿åˆ©ã§ã™ã€‚ ã‚ーåã‚’`$`ã§å§‹ã‚ã‚‹ã¨ã€ãã®ã‚ーã¯CSS変数ã¨ã—ã¦å‡ºåŠ›ã•ã‚Œã¾ã›ã‚“。 -#### Funzione +#### Funzioni wip diff --git a/src/docs/it-IT/timelines.md b/src/docs/it-IT/timelines.md index eb2bb65cad2e17ec42ef393f3ea6cfc957648655..bd462541d26528eef858f09c4c940f3ffb6d6399 100644 --- a/src/docs/it-IT/timelines.md +++ b/src/docs/it-IT/timelines.md @@ -1,15 +1,15 @@ -# タイムラインã®æ¯”較 +# Confronto delle timeline https://docs.google.com/spreadsheets/d/1lxQ2ugKrhz58Bg96HTDK_2F98BUritkMyIiBkOByjHA/edit?usp=sharing ## Home -自分ã®ãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„るユーザーã®æŠ•ç¨¿ +Pubblicazioni degli utenti che segui. ## Locale -å…¨ã¦ã®ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã€Œãƒ›ãƒ¼ãƒ ã€æŒ‡å®šã•ã‚Œã¦ã„ãªã„投稿 +Pubblicazioni degli utenti della tua istanza. Non vengono mostrate le note pubblicate con lo stato "principale". -## ソーシャル -自分ã®ãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„るユーザーã®æŠ•ç¨¿ã¨ã€å…¨ã¦ã®ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã€Œãƒ›ãƒ¼ãƒ ã€æŒ‡å®šã•ã‚Œã¦ã„ãªã„投稿 +## Sociale +Raggruppa le timeline "home" e "locale". ## ã‚°ãƒãƒ¼ãƒãƒ« -å…¨ã¦ã®ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã€Œãƒ›ãƒ¼ãƒ ã€æŒ‡å®šã•ã‚Œã¦ã„ãªã„投稿ã¨ã€ã‚µãƒ¼ãƒãƒ¼ã«å±Šã„ãŸå…¨ã¦ã®ãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã€Œãƒ›ãƒ¼ãƒ ã€æŒ‡å®šã•ã‚Œã¦ã„ãªã„投稿 +Tutte le pubblicazioni ricevute dall'istanza, sia locali che altre. Non vengono mostrate le note pubblicate con lo stato "home". diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..7343aa1994bdaee0b25f499f0ec6dac27b5cf98b --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1 @@ +type FIXME = any; diff --git a/src/misc/before-shutdown.ts b/src/misc/before-shutdown.ts new file mode 100644 index 0000000000000000000000000000000000000000..8639d42b0415162314f52d1d0f44acd66c50e211 --- /dev/null +++ b/src/misc/before-shutdown.ts @@ -0,0 +1,90 @@ +// https://gist.github.com/nfantone/1eaa803772025df69d07f4dbf5df7e58 + +'use strict'; + +/** + * @callback BeforeShutdownListener + * @param {string} [signalOrEvent] The exit signal or event name received on the process. + */ + +/** + * System signals the app will listen to initiate shutdown. + * @const {string[]} + */ +const SHUTDOWN_SIGNALS = ['SIGINT', 'SIGTERM']; + +/** + * Time in milliseconds to wait before forcing shutdown. + * @const {number} + */ +const SHUTDOWN_TIMEOUT = 15000; + +/** + * A queue of listener callbacks to execute before shutting + * down the process. + * @type {BeforeShutdownListener[]} + */ +const shutdownListeners = []; + +/** + * Listen for signals and execute given `fn` function once. + * @param {string[]} signals System signals to listen to. + * @param {function(string)} fn Function to execute on shutdown. + */ +const processOnce = (signals, fn) => { + for (const sig of signals) { + process.once(sig, fn); + } +}; + +/** + * Sets a forced shutdown mechanism that will exit the process after `timeout` milliseconds. + * @param {number} timeout Time to wait before forcing shutdown (milliseconds) + */ +const forceExitAfter = timeout => () => { + setTimeout(() => { + // Force shutdown after timeout + console.warn(`Could not close resources gracefully after ${timeout}ms: forcing shutdown`); + return process.exit(1); + }, timeout).unref(); +}; + +/** + * Main process shutdown handler. Will invoke every previously registered async shutdown listener + * in the queue and exit with a code of `0`. Any `Promise` rejections from any listener will + * be logged out as a warning, but won't prevent other callbacks from executing. + * @param {string} signalOrEvent The exit signal or event name received on the process. + */ +async function shutdownHandler(signalOrEvent) { + console.warn(`Shutting down: received [${signalOrEvent}] signal`); + + for (const listener of shutdownListeners) { + try { + await listener(signalOrEvent); + } catch (err) { + console.warn(`A shutdown handler failed before completing with: ${err.message || err}`); + } + } + + return process.exit(0); +} + +/** + * Registers a new shutdown listener to be invoked before exiting + * the main process. Listener handlers are guaranteed to be called in the order + * they were registered. + * @param {BeforeShutdownListener} listener The shutdown listener to register. + * @returns {BeforeShutdownListener} Echoes back the supplied `listener`. + */ +export function beforeShutdown(listener) { + shutdownListeners.push(listener); + return listener; +} + +// Register shutdown callback that kills the process after `SHUTDOWN_TIMEOUT` milliseconds +// This prevents custom shutdown handlers from hanging the process indefinitely +processOnce(SHUTDOWN_SIGNALS, forceExitAfter(SHUTDOWN_TIMEOUT)); + +// Register process shutdown callback +// Will listen to incoming signal events and execute all registered handlers in the stack +processOnce(SHUTDOWN_SIGNALS, shutdownHandler); diff --git a/src/misc/cache.ts b/src/misc/cache.ts new file mode 100644 index 0000000000000000000000000000000000000000..71fbbd8a4caf326632bd7b62f51ab897ee2a0e45 --- /dev/null +++ b/src/misc/cache.ts @@ -0,0 +1,43 @@ +export class Cache<T> { + private cache: Map<string | null, { date: number; value: T; }>; + private lifetime: number; + + constructor(lifetime: Cache<never>['lifetime']) { + this.cache = new Map(); + this.lifetime = lifetime; + } + + public set(key: string | null, value: T): void { + this.cache.set(key, { + date: Date.now(), + value + }); + } + + public get(key: string | null): T | undefined { + const cached = this.cache.get(key); + if (cached == null) return undefined; + if ((Date.now() - cached.date) > this.lifetime) { + this.cache.delete(key); + return undefined; + } + return cached.value; + } + + public delete(key: string | null) { + this.cache.delete(key); + } + + public async fetch(key: string | null, fetcher: () => Promise<T>): Promise<T> { + const cachedValue = this.get(key); + if (cachedValue !== undefined) { + // Cache HIT + return cachedValue; + } + + // Cache MISS + const value = await fetcher(); + this.set(key, value); + return value; + } +} diff --git a/src/misc/fetch-meta.ts b/src/misc/fetch-meta.ts index 680cf37a727287ead1c9ded5ea27a6bd62ceb310..e7a945dc9edd4d56004556ef3f138eb0a255b022 100644 --- a/src/misc/fetch-meta.ts +++ b/src/misc/fetch-meta.ts @@ -32,4 +32,4 @@ setInterval(() => { fetchMeta(true).then(meta => { cache = meta; }); -}, 5000); +}, 1000 * 10); diff --git a/src/misc/keypair-store.ts b/src/misc/keypair-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..c78fdd75556a14a77b87dc31e6dbd39927fb4277 --- /dev/null +++ b/src/misc/keypair-store.ts @@ -0,0 +1,10 @@ +import { UserKeypairs } from '../models'; +import { User } from '../models/entities/user'; +import { UserKeypair } from '../models/entities/user-keypair'; +import { Cache } from './cache'; + +const cache = new Cache<UserKeypair>(Infinity); + +export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> { + return await cache.fetch(userId, () => UserKeypairs.findOneOrFail(userId)); +} diff --git a/src/misc/populate-emojis.ts b/src/misc/populate-emojis.ts new file mode 100644 index 0000000000000000000000000000000000000000..8052c71489e591759b32a7d441dd588b8e9b9551 --- /dev/null +++ b/src/misc/populate-emojis.ts @@ -0,0 +1,119 @@ +import { In } from 'typeorm'; +import { Emojis } from '../models'; +import { Emoji } from '../models/entities/emoji'; +import { Note } from '../models/entities/note'; +import { Cache } from './cache'; +import { isSelfHost, toPunyNullable } from './convert-host'; +import { decodeReaction } from './reaction-lib'; + +const cache = new Cache<Emoji | null>(1000 * 60 * 60 * 12); + +/** + * 添付用絵文å—æƒ…å ± + */ +type PopulatedEmoji = { + name: string; + url: string; +}; + +function normalizeHost(src: string | undefined, noteUserHost: string | null): string | null { + // クエリã«ä½¿ã†ãƒ›ã‚¹ãƒˆ + let host = src === '.' ? null // .ã¯ãƒãƒ¼ã‚«ãƒ«ãƒ›ã‚¹ãƒˆ (ã“ã“ãŒãƒžãƒƒãƒã™ã‚‹ã®ã¯ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã®ã¿) + : src === undefined ? noteUserHost // ノートãªã©ã§ãƒ›ã‚¹ãƒˆçœç•¥è¡¨è¨˜ã®å ´åˆã¯ãƒãƒ¼ã‚«ãƒ«ãƒ›ã‚¹ãƒˆ (ã“ã“ãŒãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã«ãƒžãƒƒãƒã™ã‚‹ã“ã¨ã¯ãªã„) + : isSelfHost(src) ? null // 自ホスト指定 + : (src || noteUserHost); // 指定ã•ã‚ŒãŸãƒ›ã‚¹ãƒˆ || ノートãªã©ã®æ‰€æœ‰è€…ã®ãƒ›ã‚¹ãƒˆ (ã“ã£ã¡ãŒãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã«ãƒžãƒƒãƒã™ã‚‹ã“ã¨ã¯ãªã„) + + host = toPunyNullable(host); + + return host; +} + +function parseEmojiStr(emojiName: string, noteUserHost: string | null) { + const match = emojiName.match(/^(\w+)(?:@([\w.-]+))?$/); + if (!match) return { name: null, host: null }; + + const name = match[1]; + + // ホストæ£è¦åŒ– + const host = toPunyNullable(normalizeHost(match[2], noteUserHost)); + + return { name, host }; +} + +/** + * 添付用絵文å—æƒ…å ±ã‚’è§£æ±ºã™ã‚‹ + * @param emojiName ノートやユーザープãƒãƒ•ã‚£ãƒ¼ãƒ«ã«æ·»ä»˜ã•ã‚ŒãŸã€ã¾ãŸã¯ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã®ã‚«ã‚¹ã‚¿ãƒ 絵文å—å (:ã¯å«ã‚ãªã„, リアクションã§ãƒãƒ¼ã‚«ãƒ«ãƒ›ã‚¹ãƒˆã®å ´åˆã¯@.を付ã‘ã‚‹ (ã“ã‚Œã¯decodeReactionã§å¯èƒ½)) + * @param noteUserHost ノートやユーザープãƒãƒ•ã‚£ãƒ¼ãƒ«ã®æ‰€æœ‰è€…ã®ãƒ›ã‚¹ãƒˆ + * @returns 絵文å—æƒ…å ±, nullã¯æœªãƒžãƒƒãƒã‚’æ„味ã™ã‚‹ + */ +export async function populateEmoji(emojiName: string, noteUserHost: string | null): Promise<PopulatedEmoji | null> { + const { name, host } = parseEmojiStr(emojiName, noteUserHost); + if (name == null) return null; + + const queryOrNull = async () => (await Emojis.findOne({ + name, + host + })) || null; + + const emoji = await cache.fetch(`${name} ${host}`, queryOrNull); + + if (emoji == null) return null; + + return { + name: emojiName, + url: emoji.url, + }; +} + +/** + * 複数ã®æ·»ä»˜ç”¨çµµæ–‡å—æƒ…å ±ã‚’è§£æ±ºã™ã‚‹ (ã‚ャシュ付ã, å˜åœ¨ã—ãªã„ã‚‚ã®ã¯çµæžœã‹ã‚‰é™¤å¤–ã•ã‚Œã‚‹) + */ +export async function populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise<PopulatedEmoji[]> { + const emojis = await Promise.all(emojiNames.map(x => populateEmoji(x, noteUserHost))); + return emojis.filter((x): x is PopulatedEmoji => x != null); +} + +export function aggregateNoteEmojis(notes: Note[]) { + let emojis: { name: string | null; host: string | null; }[] = []; + for (const note of notes) { + emojis = emojis.concat(note.emojis + .map(e => parseEmojiStr(e, note.userHost))); + if (note.renote) { + emojis = emojis.concat(note.renote.emojis + .map(e => parseEmojiStr(e, note.renote!.userHost))); + if (note.renote.user) { + emojis = emojis.concat(note.renote.user.emojis + .map(e => parseEmojiStr(e, note.renote!.userHost))); + } + } + const customReactions = Object.keys(note.reactions).map(x => decodeReaction(x)).filter(x => x.name != null) as typeof emojis; + emojis = emojis.concat(customReactions); + if (note.user) { + emojis = emojis.concat(note.user.emojis + .map(e => parseEmojiStr(e, note.userHost))); + } + } + return emojis.filter(x => x.name != null) as { name: string; host: string | null; }[]; +} + +/** + * 与ãˆã‚‰ã‚ŒãŸçµµæ–‡å—ã®ãƒªã‚¹ãƒˆã‚’データベースã‹ã‚‰å–å¾—ã—ã€ã‚ャッシュã«è¿½åŠ ã—ã¾ã™ + */ +export async function prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise<void> { + const notCachedEmojis = emojis.filter(emoji => cache.get(`${emoji.name} ${emoji.host}`) == null); + const emojisQuery: any[] = []; + const hosts = new Set(notCachedEmojis.map(e => e.host)); + for (const host of hosts) { + emojisQuery.push({ + name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)), + host: host + }); + } + const _emojis = emojisQuery.length > 0 ? await Emojis.find({ + where: emojisQuery, + select: ['name', 'host', 'url'] + }) : []; + for (const emoji of _emojis) { + cache.set(`${emoji.name} ${emoji.host}`, emoji); + } +} diff --git a/src/models/entities/drive-file.ts b/src/models/entities/drive-file.ts index 680a40bc06ed65938bbc77f284b4b9c284c59fbd..698dfac2225eaf635d4fb1588dfa37fb90ec101e 100644 --- a/src/models/entities/drive-file.ts +++ b/src/models/entities/drive-file.ts @@ -77,7 +77,7 @@ export class DriveFile { default: {}, comment: 'The any properties of the DriveFile. For example, it includes image width/height.' }) - public properties: Record<string, any>; + public properties: { width?: number; height?: number; avgColor?: string }; @Index() @Column('boolean') diff --git a/src/models/entities/note-reaction.ts b/src/models/entities/note-reaction.ts index 69bb663fd3b4c45512c3b4bdda60618712f4d2a4..674dc3639eff0c163aee5540439e8023087c83d6 100644 --- a/src/models/entities/note-reaction.ts +++ b/src/models/entities/note-reaction.ts @@ -23,7 +23,7 @@ export class NoteReaction { onDelete: 'CASCADE' }) @JoinColumn() - public user: User | null; + public user?: User | null; @Index() @Column(id()) @@ -33,7 +33,7 @@ export class NoteReaction { onDelete: 'CASCADE' }) @JoinColumn() - public note: Note | null; + public note?: Note | null; // TODO: 対象noteã®userIdã‚’éžæ£è¦åŒ–ã—ãŸã„(「å—ã‘å–ã£ãŸãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ä¸€è¦§ã€ã®ã‚ˆã†ãªã‚‚ã®ã‚’(JOINç„¡ã—ã§)実装ã—ãŸã„ãŸã‚) diff --git a/src/models/repositories/antenna.ts b/src/models/repositories/antenna.ts index 0f0a5c0171f1672848fff7010242df49e0be106d..84e082bd25c623433833eee0310a36dc41a2cf47 100644 --- a/src/models/repositories/antenna.ts +++ b/src/models/repositories/antenna.ts @@ -56,16 +56,24 @@ export const packedAntennaSchema = { type: 'array' as const, optional: false as const, nullable: false as const, items: { - type: 'string' as const, - optional: false as const, nullable: false as const + type: 'array' as const, + optional: false as const, nullable: false as const, + items: { + type: 'string' as const, + optional: false as const, nullable: false as const + } } }, - execludeKeywords: { + excludeKeywords: { type: 'array' as const, optional: false as const, nullable: false as const, items: { - type: 'string' as const, - optional: false as const, nullable: false as const + type: 'array' as const, + optional: false as const, nullable: false as const, + items: { + type: 'string' as const, + optional: false as const, nullable: false as const + } } }, src: { diff --git a/src/models/repositories/drive-file.ts b/src/models/repositories/drive-file.ts index 5085c76e9226edb6e684063c335ac16a75180160..61d24bd24ee96470ba075455f171f7a135f205d6 100644 --- a/src/models/repositories/drive-file.ts +++ b/src/models/repositories/drive-file.ts @@ -12,6 +12,12 @@ import { fetchMeta } from '../../misc/fetch-meta'; export type PackedDriveFile = SchemaType<typeof packedDriveFileSchema>; +type PackOptions = { + detail?: boolean, + self?: boolean, + withUser?: boolean, +}; + @EntityRepository(DriveFile) export class DriveFileRepository extends Repository<DriveFile> { public validateFileName(name: string): boolean { @@ -89,20 +95,19 @@ export class DriveFileRepository extends Repository<DriveFile> { return parseInt(sum, 10) || 0; } + public async pack(src: DriveFile['id'], options?: PackOptions): Promise<PackedDriveFile | null>; + public async pack(src: DriveFile, options?: PackOptions): Promise<PackedDriveFile>; public async pack( src: DriveFile['id'] | DriveFile, - options?: { - detail?: boolean, - self?: boolean, - withUser?: boolean, - } - ): Promise<PackedDriveFile> { + options?: PackOptions + ): Promise<PackedDriveFile | null> { const opts = Object.assign({ detail: false, self: false }, options); - const file = typeof src === 'object' ? src : await this.findOneOrFail(src); + const file = typeof src === 'object' ? src : await this.findOne(src); + if (file == null) return null; const meta = await fetchMeta(); @@ -128,15 +133,12 @@ export class DriveFileRepository extends Repository<DriveFile> { }); } - public packMany( - files: any[], - options?: { - detail?: boolean - self?: boolean, - withUser?: boolean, - } + public async packMany( + files: (DriveFile['id'] | DriveFile)[], + options?: PackOptions ) { - return Promise.all(files.map(f => this.pack(f, options))); + const items = await Promise.all(files.map(f => this.pack(f, options))); + return items.filter(x => x != null); } } @@ -197,12 +199,12 @@ export const packedDriveFileSchema = { properties: { width: { type: 'number' as const, - optional: false as const, nullable: false as const, + optional: true as const, nullable: false as const, example: 1280 }, height: { type: 'number' as const, - optional: false as const, nullable: false as const, + optional: true as const, nullable: false as const, example: 720 }, avgColor: { diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts index 32552db2feb40ca601dcf9a1879e5fc5f9c9fcb2..73e18f6c5b62089e03004dd39689ffb20f92547e 100644 --- a/src/models/repositories/note.ts +++ b/src/models/repositories/note.ts @@ -1,14 +1,14 @@ import { EntityRepository, Repository, In } from 'typeorm'; import { Note } from '../entities/note'; import { User } from '../entities/user'; -import { Emojis, Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls, Channels } from '..'; +import { Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls, Channels } from '..'; import { SchemaType } from '../../misc/schema'; import { awaitAll } from '../../prelude/await-all'; import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '../../misc/reaction-lib'; import { toString } from '../../mfm/to-string'; import { parse } from '../../mfm/parse'; -import { Emoji } from '../entities/emoji'; -import { concat } from '../../prelude/array'; +import { NoteReaction } from '../entities/note-reaction'; +import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '../../misc/populate-emojis'; export type PackedNote = SchemaType<typeof packedNoteSchema>; @@ -83,6 +83,9 @@ export class NoteRepository extends Repository<Note> { options?: { detail?: boolean; skipHide?: boolean; + _hint_?: { + myReactions: Map<Note['id'], NoteReaction | null>; + }; } ): Promise<PackedNote> { const opts = Object.assign({ @@ -130,64 +133,17 @@ export class NoteRepository extends Repository<Note> { }; } - /** - * 添付用emojisを解決ã™ã‚‹ - * @param emojiNames Noteç‰ã«æ·»ä»˜ã•ã‚ŒãŸã‚«ã‚¹ã‚¿ãƒ 絵文å—å (:ã¯å«ã‚ãªã„) - * @param noteUserHost Noteã®ãƒ›ã‚¹ãƒˆ - * @param reactionNames Noteç‰ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã•ã‚ŒãŸã‚«ã‚¹ã‚¿ãƒ 絵文å—å (:ã¯å«ã‚ãªã„) - */ - async function populateEmojis(emojiNames: string[], noteUserHost: string | null, reactionNames: string[]) { - let all = [] as { - name: string, - url: string - }[]; - - // ã‚«ã‚¹ã‚¿ãƒ çµµæ–‡å— - if (emojiNames?.length > 0) { - const tmp = await Emojis.find({ - where: { - name: In(emojiNames), - host: noteUserHost - }, - select: ['name', 'host', 'url'] - }).then(emojis => emojis.map((emoji: Emoji) => { - return { - name: emoji.name, - url: emoji.url, - }; - })); - - all = concat([all, tmp]); - } - - const customReactions = reactionNames?.map(x => decodeReaction(x)).filter(x => x.name); - - if (customReactions?.length > 0) { - const where = [] as {}[]; - - for (const customReaction of customReactions) { - where.push({ - name: customReaction.name, - host: customReaction.host - }); + async function populateMyReaction() { + if (options?._hint_?.myReactions) { + const reaction = options._hint_.myReactions.get(note.id); + if (reaction) { + return convertLegacyReaction(reaction.reaction); + } else if (reaction === null) { + return undefined; } - - const tmp = await Emojis.find({ - where, - select: ['name', 'host', 'url'] - }).then(emojis => emojis.map((emoji: Emoji) => { - return { - name: `${emoji.name}@${emoji.host || '.'}`, // @host付ãã§ãƒãƒ¼ã‚«ãƒ«ã¯. - url: emoji.url, - }; - })); - all = concat([all, tmp]); + // 実装上抜ã‘ãŒã‚ã‚‹ã ã‘ã‹ã‚‚ã—ã‚Œãªã„ã®ã§ã€ã€Œãƒ’ントã«å«ã¾ã‚Œã¦ãªã‹ã£ãŸã‚‰(=undefinedãªã‚‰)returnã€ã®ã‚ˆã†ã«ã¯ã—ãªã„ } - return all; - } - - async function populateMyReaction() { const reaction = await NoteReactions.findOne({ userId: meId!, noteId: note.id, @@ -212,11 +168,15 @@ export class NoteRepository extends Repository<Note> { : await Channels.findOne(note.channelId) : null; + const reactionEmojiNames = Object.keys(note.reactions).filter(x => x?.startsWith(':')).map(x => decodeReaction(x).reaction).map(x => x.replace(/:/g, '')); + const packed = await awaitAll({ id: note.id, createdAt: note.createdAt.toISOString(), userId: note.userId, - user: Users.pack(note.user || note.userId, meId), + user: Users.pack(note.user || note.userId, meId, { + detail: false, + }), text: text, cw: note.cw, visibility: note.visibility, @@ -227,7 +187,7 @@ export class NoteRepository extends Repository<Note> { repliesCount: note.repliesCount, reactions: convertLegacyReactions(note.reactions), tags: note.tags.length > 0 ? note.tags : undefined, - emojis: populateEmojis(note.emojis, host, Object.keys(note.reactions)), + emojis: populateEmojis(note.emojis.concat(reactionEmojiNames), host), fileIds: note.fileIds, files: DriveFiles.packMany(note.fileIds), replyId: note.replyId, @@ -244,12 +204,14 @@ export class NoteRepository extends Repository<Note> { _prId_: (note as any)._prId_ || undefined, ...(opts.detail ? { - reply: note.replyId ? this.pack(note.replyId, meId, { - detail: false + reply: note.replyId ? this.pack(note.reply || note.replyId, meId, { + detail: false, + _hint_: options?._hint_ }) : undefined, - renote: note.renoteId ? this.pack(note.renoteId, meId, { - detail: true + renote: note.renoteId ? this.pack(note.renote || note.renoteId, meId, { + detail: true, + _hint_: options?._hint_ }) : undefined, poll: note.hasPoll ? populatePoll() : undefined, @@ -272,15 +234,39 @@ export class NoteRepository extends Repository<Note> { return packed; } - public packMany( - notes: (Note['id'] | Note)[], + public async packMany( + notes: Note[], me?: User['id'] | User | null | undefined, options?: { detail?: boolean; skipHide?: boolean; } ) { - return Promise.all(notes.map(n => this.pack(n, me, options))); + if (notes.length === 0) return []; + + const meId = me ? typeof me === 'string' ? me : me.id : null; + const myReactionsMap = new Map<Note['id'], NoteReaction | null>(); + if (meId) { + const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); + const targets = [...notes.map(n => n.id), ...renoteIds]; + const myReactions = await NoteReactions.find({ + userId: meId, + noteId: In(targets), + }); + + for (const target of targets) { + myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); + } + } + + await prefetchEmojis(aggregateNoteEmojis(notes)); + + return await Promise.all(notes.map(n => this.pack(n, me, { + ...options, + _hint_: { + myReactions: myReactionsMap + } + }))); } } diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index 16de6c8c25df8bccd1b0e360ac980ff8bfc672c7..83fe11d5f7da350467b9ef14077064846a6f2ac0 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -1,8 +1,12 @@ -import { EntityRepository, Repository } from 'typeorm'; -import { Users, Notes, UserGroupInvitations, AccessTokens } from '..'; +import { EntityRepository, In, Repository } from 'typeorm'; +import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '..'; import { Notification } from '../entities/notification'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; +import { Note } from '../entities/note'; +import { NoteReaction } from '../entities/note-reaction'; +import { User } from '../entities/user'; +import { aggregateNoteEmojis, prefetchEmojis } from '../../misc/populate-emojis'; export type PackedNotification = SchemaType<typeof packedNotificationSchema>; @@ -10,6 +14,11 @@ export type PackedNotification = SchemaType<typeof packedNotificationSchema>; export class NotificationRepository extends Repository<Notification> { public async pack( src: Notification['id'] | Notification, + options: { + _hintForEachNotes_?: { + myReactions: Map<Note['id'], NoteReaction | null>; + }; + } ): Promise<PackedNotification> { const notification = typeof src === 'object' ? src : await this.findOneOrFail(src); const token = notification.appAccessTokenId ? await AccessTokens.findOneOrFail(notification.appAccessTokenId) : null; @@ -22,23 +31,41 @@ export class NotificationRepository extends Repository<Notification> { userId: notification.notifierId, user: notification.notifierId ? Users.pack(notification.notifier || notification.notifierId) : null, ...(notification.type === 'mention' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'reply' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'renote' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'quote' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'reaction' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), reaction: notification.reaction } : {}), ...(notification.type === 'pollVote' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), choice: notification.choice } : {}), ...(notification.type === 'groupInvited' ? { @@ -52,10 +79,33 @@ export class NotificationRepository extends Repository<Notification> { }); } - public packMany( - notifications: any[], + public async packMany( + notifications: Notification[], + meId: User['id'] ) { - return Promise.all(notifications.map(x => this.pack(x))); + if (notifications.length === 0) return []; + + const notes = notifications.filter(x => x.note != null).map(x => x.note!); + const noteIds = notes.map(n => n.id); + const myReactionsMap = new Map<Note['id'], NoteReaction | null>(); + const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); + const targets = [...noteIds, ...renoteIds]; + const myReactions = await NoteReactions.find({ + userId: meId, + noteId: In(targets), + }); + + for (const target of targets) { + myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); + } + + await prefetchEmojis(aggregateNoteEmojis(notes)); + + return await Promise.all(notifications.map(x => this.pack(x, { + _hintForEachNotes_: { + myReactions: myReactionsMap + } + }))); } } diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index 3a6ab48c5f12c61aa6fff231e51f9cd5a8400136..53c06f3f165ff652356d116f9f3a168c9a53f340 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -1,10 +1,11 @@ import $ from 'cafy'; import { EntityRepository, Repository, In, Not } from 'typeorm'; import { User, ILocalUser, IRemoteUser } from '../entities/user'; -import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances } from '..'; +import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances } from '..'; import config from '../../config'; import { SchemaType } from '../../misc/schema'; import { awaitAll } from '../../prelude/await-all'; +import { populateEmojis } from '../../misc/populate-emojis'; export type PackedUser = SchemaType<typeof packedUserSchema>; @@ -160,10 +161,11 @@ export class UserRepository extends Repository<User> { const meId = me ? typeof me === 'string' ? me : me.id : null; const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null; - const pins = opts.detail ? await UserNotePinings.find({ - where: { userId: user.id }, - order: { id: 'DESC' } - }) : []; + const pins = opts.detail ? await UserNotePinings.createQueryBuilder('pin') + .where('pin.userId = :userId', { userId: user.id }) + .innerJoinAndSelect('pin.note', 'note') + .orderBy('pin.id', 'DESC') + .getMany() : []; const profile = opts.detail ? await UserProfiles.findOneOrFail(user.id) : null; const falsy = opts.detail ? false : undefined; @@ -188,15 +190,7 @@ export class UserRepository extends Repository<User> { faviconUrl: instance.faviconUrl, themeColor: instance.themeColor, } : undefined) : undefined, - - // カスタム絵文å—添付 - emojis: user.emojis.length > 0 ? Emojis.find({ - where: { - name: In(user.emojis), - host: user.host - }, - select: ['name', 'host', 'url', 'aliases'] - }) : [], + emojis: populateEmojis(user.emojis, user.host), ...(opts.detail ? { url: profile!.url, @@ -218,7 +212,7 @@ export class UserRepository extends Repository<User> { followingCount: user.followingCount, notesCount: user.notesCount, pinnedNoteIds: pins.map(pin => pin.noteId), - pinnedNotes: Notes.packMany(pins.map(pin => pin.noteId), meId, { + pinnedNotes: Notes.packMany(pins.map(pin => pin.note!), meId, { detail: true }), pinnedPageId: profile!.pinnedPageId, diff --git a/src/queue/index.ts b/src/queue/index.ts index 163c57d6916523892f0a9205525c948b21c68ea6..9fb4595a350299f1cc47239a83267a5c26f01009 100644 --- a/src/queue/index.ts +++ b/src/queue/index.ts @@ -1,4 +1,3 @@ -import * as Queue from 'bull'; import * as httpSignature from 'http-signature'; import config from '../config'; @@ -13,22 +12,7 @@ import { queueLogger } from './logger'; import { DriveFile } from '../models/entities/drive-file'; import { getJobInfo } from './get-job-info'; import { IActivity } from '../remote/activitypub/type'; - -function initializeQueue(name: string, limitPerSec = -1) { - return new Queue(name, { - redis: { - port: config.redis.port, - host: config.redis.host, - password: config.redis.pass, - db: config.redis.db || 0, - }, - prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue', - limiter: limitPerSec > 0 ? { - max: limitPerSec * 5, - duration: 5000 - } : undefined - }); -} +import { dbQueue, deliverQueue, inboxQueue, objectStorageQueue } from './queues'; export type InboxJobData = { activity: IActivity, @@ -44,11 +28,6 @@ function renderError(e: Error): any { }; } -export const deliverQueue = initializeQueue('deliver', config.deliverJobPerSec || 128); -export const inboxQueue = initializeQueue('inbox', config.inboxJobPerSec || 16); -export const dbQueue = initializeQueue('db'); -export const objectStorageQueue = initializeQueue('objectStorage'); - const deliverLogger = queueLogger.createSubLogger('deliver'); const inboxLogger = queueLogger.createSubLogger('inbox'); const dbLogger = queueLogger.createSubLogger('db'); diff --git a/src/queue/initialize.ts b/src/queue/initialize.ts new file mode 100644 index 0000000000000000000000000000000000000000..92579531e434e48ed1fa986a49e4ab1b698704ab --- /dev/null +++ b/src/queue/initialize.ts @@ -0,0 +1,18 @@ +import * as Queue from 'bull'; +import config from '../config'; + +export function initialize(name: string, limitPerSec = -1) { + return new Queue(name, { + redis: { + port: config.redis.port, + host: config.redis.host, + password: config.redis.pass, + db: config.redis.db || 0, + }, + prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue', + limiter: limitPerSec > 0 ? { + max: limitPerSec * 5, + duration: 5000 + } : undefined + }); +} diff --git a/src/queue/processors/deliver.ts b/src/queue/processors/deliver.ts index cb7587ef81d688ad4c8b89378f793753690de866..a8b4ed4fe3c709714b188c0e2f75429745e2d151 100644 --- a/src/queue/processors/deliver.ts +++ b/src/queue/processors/deliver.ts @@ -7,11 +7,15 @@ import { instanceChart } from '../../services/chart'; import { fetchInstanceMetadata } from '../../services/fetch-instance-metadata'; import { fetchMeta } from '../../misc/fetch-meta'; import { toPuny } from '../../misc/convert-host'; +import { Cache } from '../../misc/cache'; +import { Instance } from '../../models/entities/instance'; const logger = new Logger('deliver'); let latest: string | null = null; +const suspendedHostsCache = new Cache<Instance[]>(1000 * 60 * 60); + export default async (job: Bull.Job) => { const { host } = new URL(job.data.to); @@ -22,12 +26,15 @@ export default async (job: Bull.Job) => { } // isSuspendedãªã‚‰ä¸æ– - const suspendedHosts = await Instances.find({ - where: { - isSuspended: true - }, - cache: 60 * 1000 - }); + let suspendedHosts = suspendedHostsCache.get(null); + if (suspendedHosts == null) { + suspendedHosts = await Instances.find({ + where: { + isSuspended: true + }, + }); + suspendedHostsCache.set(null, suspendedHosts); + } if (suspendedHosts.map(x => x.host).includes(toPuny(host))) { return 'skip (suspended)'; } diff --git a/src/queue/processors/inbox.ts b/src/queue/processors/inbox.ts index b4e8b85a4670aecff22ba19df7373ebcec9db5a5..a5822ff25f6ddb7774462be51f0271c9ccff0f8a 100644 --- a/src/queue/processors/inbox.ts +++ b/src/queue/processors/inbox.ts @@ -40,6 +40,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => { return `Old keyId is no longer supported. ${keyIdLower}`; } + // TDOO: ã‚ャッシュ const dbResolver = new DbResolver(); // HTTP-Signature keyIdã‚’å…ƒã«DBã‹ã‚‰å–å¾— diff --git a/src/queue/queues.ts b/src/queue/queues.ts new file mode 100644 index 0000000000000000000000000000000000000000..d589d9f7dac3f509e1239c42bf12135d8e0dbe7c --- /dev/null +++ b/src/queue/queues.ts @@ -0,0 +1,7 @@ +import config from '../config'; +import { initialize as initializeQueue } from './initialize'; + +export const deliverQueue = initializeQueue('deliver', config.deliverJobPerSec || 128); +export const inboxQueue = initializeQueue('inbox', config.inboxJobPerSec || 16); +export const dbQueue = initializeQueue('db'); +export const objectStorageQueue = initializeQueue('objectStorage'); diff --git a/src/remote/activitypub/deliver-manager.ts b/src/remote/activitypub/deliver-manager.ts index d147b3c9b0559db76bdeae5a1d230c1937607476..92721f552581b5947e79c5e227d317006603356a 100644 --- a/src/remote/activitypub/deliver-manager.ts +++ b/src/remote/activitypub/deliver-manager.ts @@ -76,7 +76,7 @@ export default class DeliverManager { public async execute() { if (!Users.isLocalUser(this.actor)) return; - const inboxes: string[] = []; + const inboxes = new Set<string>(); // build inbox list for (const recipe of this.recipes) { @@ -89,13 +89,13 @@ export default class DeliverManager { for (const following of followers) { if (Followings.isRemoteFollower(following)) { const inbox = following.followerSharedInbox || following.followerInbox; - if (!inboxes.includes(inbox)) inboxes.push(inbox); + inboxes.add(inbox); } } } else if (isDirect(recipe)) { // direct deliver const inbox = recipe.to.inbox; - if (inbox && !inboxes.includes(inbox)) inboxes.push(inbox); + if (inbox) inboxes.add(inbox); } } diff --git a/src/remote/activitypub/renderer/index.ts b/src/remote/activitypub/renderer/index.ts index e74affdadfbed367ee82dea3eae08781cae898f5..4c33fdafb1dfa61676b535af2d3fd9c7847fae4a 100644 --- a/src/remote/activitypub/renderer/index.ts +++ b/src/remote/activitypub/renderer/index.ts @@ -3,7 +3,7 @@ import { v4 as uuid } from 'uuid'; import { IActivity } from '../type'; import { LdSignature } from '../misc/ld-signature'; import { ILocalUser } from '../../../models/entities/user'; -import { UserKeypairs } from '../../../models'; +import { getUserKeypair } from '../../../misc/keypair-store'; export const renderActivity = (x: any): IActivity | null => { if (x == null) return null; @@ -23,9 +23,7 @@ export const renderActivity = (x: any): IActivity | null => { export const attachLdSignature = async (activity: any, user: ILocalUser): Promise<IActivity | null> => { if (activity == null) return null; - const keypair = await UserKeypairs.findOneOrFail({ - userId: user.id - }); + const keypair = await getUserKeypair(user.id); const obj = { // as non-standards diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts index 4907e3bc6f7f234caea596429851d0e69e6d3db4..479e6d76bff57ca95310095f91c3678ac48791ab 100644 --- a/src/remote/activitypub/renderer/person.ts +++ b/src/remote/activitypub/renderer/person.ts @@ -8,7 +8,8 @@ import { getEmojis } from './note'; import renderEmoji from './emoji'; import { IIdentifier } from '../models/identifier'; import renderHashtag from './hashtag'; -import { DriveFiles, UserProfiles, UserKeypairs } from '../../../models'; +import { DriveFiles, UserProfiles } from '../../../models'; +import { getUserKeypair } from '../../../misc/keypair-store'; export async function renderPerson(user: ILocalUser) { const id = `${config.url}/users/${user.id}`; @@ -49,7 +50,7 @@ export async function renderPerson(user: ILocalUser) { ...hashtagTags, ]; - const keypair = await UserKeypairs.findOneOrFail(user.id); + const keypair = await getUserKeypair(user.id); const person = { type: isSystem ? 'Application' : user.isBot ? 'Service' : 'Person', diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts index 2f0735163528e84e2aed3eefdebcb06a5dd511e2..5f15d5480cd35b72be2a8a5aabeb73ca82ecfb51 100644 --- a/src/remote/activitypub/request.ts +++ b/src/remote/activitypub/request.ts @@ -5,11 +5,11 @@ import * as crypto from 'crypto'; import config from '../../config'; import { ILocalUser } from '../../models/entities/user'; -import { UserKeypairs } from '../../models'; import { getAgentByUrl } from '../../misc/fetch'; import { URL } from 'url'; import got from 'got'; import * as Got from 'got'; +import { getUserKeypair } from '../../misc/keypair-store'; export default async (user: ILocalUser, url: string, object: any) => { const timeout = 10 * 1000; @@ -22,9 +22,7 @@ export default async (user: ILocalUser, url: string, object: any) => { sha256.update(data); const hash = sha256.digest('base64'); - const keypair = await UserKeypairs.findOneOrFail({ - userId: user.id - }); + const keypair = await getUserKeypair(user.id); await new Promise((resolve, reject) => { const req = https.request({ @@ -74,9 +72,7 @@ export default async (user: ILocalUser, url: string, object: any) => { export async function signedGet(url: string, user: ILocalUser) { const timeout = 10 * 1000; - const keypair = await UserKeypairs.findOneOrFail({ - userId: user.id - }); + const keypair = await getUserKeypair(user.id); const req = got.get<any>(url, { headers: { diff --git a/src/server/activitypub.ts b/src/server/activitypub.ts index bf712586255dd39312d2ac2db87643dc6a9d20ff..694807239b44781062112ec1b23197601c361c66 100644 --- a/src/server/activitypub.ts +++ b/src/server/activitypub.ts @@ -13,10 +13,11 @@ import Following from './activitypub/following'; import Featured from './activitypub/featured'; import { inbox as processInbox } from '../queue'; import { isSelfHost } from '../misc/convert-host'; -import { Notes, Users, Emojis, UserKeypairs, NoteReactions } from '../models'; +import { Notes, Users, Emojis, NoteReactions } from '../models'; import { ILocalUser, User } from '../models/entities/user'; import { In } from 'typeorm'; import { renderLike } from '../remote/activitypub/renderer/like'; +import { getUserKeypair } from '../misc/keypair-store'; // Init router const router = new Router(); @@ -135,7 +136,7 @@ router.get('/users/:user/publickey', async ctx => { return; } - const keypair = await UserKeypairs.findOneOrFail(user.id); + const keypair = await getUserKeypair(user.id); if (Users.isLocalUser(user)) { ctx.body = renderActivity(renderKey(user, keypair)); diff --git a/src/server/api/authenticate.ts b/src/server/api/authenticate.ts index 0374ca35ea8fc87bd515fbd886c5e6adb21d6255..9c9ef74352186bb77648e1727ed3a6cf8d81b0a7 100644 --- a/src/server/api/authenticate.ts +++ b/src/server/api/authenticate.ts @@ -2,6 +2,11 @@ import isNativeToken from './common/is-native-token'; import { User } from '../../models/entities/user'; import { Users, AccessTokens, Apps } from '../../models'; import { AccessToken } from '../../models/entities/access-token'; +import { Cache } from '../../misc/cache'; + +// TODO: TypeORMã®ã‚«ã‚¹ã‚¿ãƒ ã‚ャッシュプãƒãƒã‚¤ãƒ€ã‚’使ã£ã¦ã‚‚良ã„ã‹ã‚‚ +// ref. https://github.com/typeorm/typeorm/blob/master/docs/caching.md +const cache = new Cache<User>(1000 * 60 * 60); export default async (token: string): Promise<[User | null | undefined, AccessToken | null | undefined]> => { if (token == null) { @@ -9,6 +14,11 @@ export default async (token: string): Promise<[User | null | undefined, AccessTo } if (isNativeToken(token)) { + const cached = cache.get(token); + if (cached) { + return [cached, null]; + } + // Fetch user const user = await Users .findOne({ token }); @@ -17,8 +27,11 @@ export default async (token: string): Promise<[User | null | undefined, AccessTo throw new Error('user not found'); } + cache.set(token, user); + return [user, null]; } else { + // TODO: cache const accessToken = await AccessTokens.findOne({ where: [{ hash: token.toLowerCase() // app diff --git a/src/server/api/common/inject-featured.ts b/src/server/api/common/inject-featured.ts index 3f47c133859eb42a9f21bcf0f27f1d5c1258ae23..bbed7f69cb6b66bd3504db709a7d6617d693b5a0 100644 --- a/src/server/api/common/inject-featured.ts +++ b/src/server/api/common/inject-featured.ts @@ -23,7 +23,7 @@ export async function injectFeatured(timeline: Note[], user?: User | null) { .andWhere(`note.score > 0`) .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) .andWhere(`note.visibility = 'public'`) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user'); if (user) { query.andWhere('note.userId != :userId', { userId: user.id }); diff --git a/src/server/api/define.ts b/src/server/api/define.ts index 1c7ee26479b52a180781042c6e9012a64d09828b..4e59357c13e7b6ff8f13c4d153c7f1b11799c403 100644 --- a/src/server/api/define.ts +++ b/src/server/api/define.ts @@ -18,6 +18,7 @@ type executor<T extends IEndpointMeta> = (params: Params<T>, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any, cleanup?: Function) => Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>; +// TODO: API関数㫠user ã¾ã‚‹ã”ã¨æ¸¡ã™ã®ã§ã¯ãªãユーザーIDãªã©ã®æœ€å°é™ã®ãƒ—ãƒãƒ‘ティã ã‘渡ã™ã‚ˆã†ã«ã—ãŸã„(ã‚ャッシュã¨ã‹è€ƒãˆãªã„ã§ã‚ˆããªã‚‹ãŸã‚) export default function <T extends IEndpointMeta>(meta: T, cb: executor<T>) : (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any) => Promise<any> { return (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any) => { diff --git a/src/server/api/endpoints/admin/invite.ts b/src/server/api/endpoints/admin/invite.ts index 4529d16adfee58ee8363b611979b91fee3a0dfef..987105791f5670a0fd45691f9dd4ab77b7c990c4 100644 --- a/src/server/api/endpoints/admin/invite.ts +++ b/src/server/api/endpoints/admin/invite.ts @@ -38,7 +38,7 @@ export default define(meta, async () => { chars: '2-9A-HJ-NP-Z', // [0-9A-Z] w/o [01IO] (32 patterns) }); - await RegistrationTickets.save({ + await RegistrationTickets.insert({ id: genId(), createdAt: new Date(), code, diff --git a/src/server/api/endpoints/admin/promo/create.ts b/src/server/api/endpoints/admin/promo/create.ts index 8b96d563c2d89c6dca5d09b13f67e370e907174c..aa22e68ebdf9cd8203e77aff24b4dd5dcd061998 100644 --- a/src/server/api/endpoints/admin/promo/create.ts +++ b/src/server/api/endpoints/admin/promo/create.ts @@ -53,7 +53,7 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.alreadyPromoted); } - await PromoNotes.save({ + await PromoNotes.insert({ noteId: note.id, createdAt: new Date(), expiresAt: new Date(ps.expiresAt), diff --git a/src/server/api/endpoints/antennas/notes.ts b/src/server/api/endpoints/antennas/notes.ts index 750fc080cf24c340c3bbeb5d7b5a2d93097f35ca..6fd3cf2df5ba512fa1e80f17c1f4076794c814ca 100644 --- a/src/server/api/endpoints/antennas/notes.ts +++ b/src/server/api/endpoints/antennas/notes.ts @@ -73,7 +73,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ antennaQuery.getQuery() })`) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(antennaQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/src/server/api/endpoints/auth/accept.ts b/src/server/api/endpoints/auth/accept.ts index 6d4d31fa1e7709444a8417a2d31afde784eb7446..444053a94608296581565e84455788f8a2d79539 100644 --- a/src/server/api/endpoints/auth/accept.ts +++ b/src/server/api/endpoints/auth/accept.ts @@ -58,7 +58,7 @@ export default define(meta, async (ps, user) => { const now = new Date(); // Insert access token doc - await AccessTokens.save({ + await AccessTokens.insert({ id: genId(), createdAt: now, lastUsedAt: now, diff --git a/src/server/api/endpoints/channels/follow.ts b/src/server/api/endpoints/channels/follow.ts index bf2f2bbb579f8c70fc52286f9ca71c686ac3b2f8..c5976a8a34860885f6f86c61419161e569b49fb7 100644 --- a/src/server/api/endpoints/channels/follow.ts +++ b/src/server/api/endpoints/channels/follow.ts @@ -4,6 +4,7 @@ import define from '../../define'; import { ApiError } from '../../error'; import { Channels, ChannelFollowings } from '../../../../models'; import { genId } from '../../../../misc/gen-id'; +import { publishUserEvent } from '../../../../services/stream'; export const meta = { tags: ['channels'], @@ -36,10 +37,12 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.noSuchChannel); } - await ChannelFollowings.save({ + await ChannelFollowings.insert({ id: genId(), createdAt: new Date(), followerId: user.id, followeeId: channel.id, }); + + publishUserEvent(user.id, 'followChannel', channel); }); diff --git a/src/server/api/endpoints/channels/timeline.ts b/src/server/api/endpoints/channels/timeline.ts index acb34f124d99b395df83d13628305e0fd4e75fdd..00a7cd86d597888c56fde8a17111ae273cb9b73b 100644 --- a/src/server/api/endpoints/channels/timeline.ts +++ b/src/server/api/endpoints/channels/timeline.ts @@ -87,7 +87,11 @@ export default define(meta, async (ps, user) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.channelId = :channelId', { channelId: channel.id }) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); //#endregion diff --git a/src/server/api/endpoints/channels/unfollow.ts b/src/server/api/endpoints/channels/unfollow.ts index 8cab5c36a67133eafd1ecf31e41ce846e7b1d61c..3eb0f1519bf3e10a8f57e8c83555dbd8469bb5b2 100644 --- a/src/server/api/endpoints/channels/unfollow.ts +++ b/src/server/api/endpoints/channels/unfollow.ts @@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Channels, ChannelFollowings } from '../../../../models'; +import { publishUserEvent } from '../../../../services/stream'; export const meta = { tags: ['channels'], @@ -39,4 +40,6 @@ export default define(meta, async (ps, user) => { followerId: user.id, followeeId: channel.id, }); + + publishUserEvent(user.id, 'unfollowChannel', channel); }); diff --git a/src/server/api/endpoints/clips/add-note.ts b/src/server/api/endpoints/clips/add-note.ts index 4f5cc649e31d58dabc4c5915ebae6413a65a1327..ee6a117b2dec5018765d31b9743445e4846768ec 100644 --- a/src/server/api/endpoints/clips/add-note.ts +++ b/src/server/api/endpoints/clips/add-note.ts @@ -68,7 +68,7 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.alreadyClipped); } - await ClipNotes.save({ + await ClipNotes.insert({ id: genId(), noteId: note.id, clipId: clip.id diff --git a/src/server/api/endpoints/clips/notes.ts b/src/server/api/endpoints/clips/notes.ts index 6a507e2036f00a90c32427c5ded4da6e289a02ca..676629c328f1773183ba96cf2199068d3208b872 100644 --- a/src/server/api/endpoints/clips/notes.ts +++ b/src/server/api/endpoints/clips/notes.ts @@ -71,7 +71,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ clipQuery.getQuery() })`) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(clipQuery.getParameters()); if (user) { diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts index e5b65e09301e5ae3b2eddb331d3f33e939cbfac4..87f6ae778daeb8e460ab83deb8b80b668e2b7286 100644 --- a/src/server/api/endpoints/i.ts +++ b/src/server/api/endpoints/i.ts @@ -1,6 +1,5 @@ import define from '../define'; -import { RegistryItems, UserProfiles, Users } from '../../../models'; -import { genId } from '../../../misc/gen-id'; +import { Users } from '../../../models'; export const meta = { desc: { @@ -23,28 +22,8 @@ export const meta = { export default define(meta, async (ps, user, token) => { const isSecure = token == null; - // TODO: ãã®ã†ã¡æ¶ˆã™ - const profile = await UserProfiles.findOneOrFail(user.id); - for (const [k, v] of Object.entries(profile.clientData)) { - await RegistryItems.insert({ - id: genId(), - createdAt: new Date(), - updatedAt: new Date(), - userId: user.id, - domain: null, - scope: ['client', 'base'], - key: k, - value: v - }); - } - await UserProfiles.createQueryBuilder().update() - .set({ - clientData: {}, - }) - .where('userId = :id', { id: user.id }) - .execute(); - - return await Users.pack(user, user, { + // ã“ã“ã§æ¸¡ã£ã¦ãã¦ã„ã‚‹ user ã¯ã‚ャッシュã•ã‚Œã¦ã„ã¦å¤ã„å¯èƒ½æ€§ã‚‚ã‚ã‚‹ã®ã§ id ã ã‘渡㙠+ return await Users.pack(user.id, user, { detail: true, includeSecrets: isSecure }); diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 0e09bc73fdd7b2c8fb06c8da03d4574eb2f0c20b..812a4bd1dd5a4f3596850df0004a21352b43d1b6 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -85,7 +85,13 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId) .andWhere(`notification.notifieeId = :meId`, { meId: user.id }) - .leftJoinAndSelect('notification.notifier', 'notifier'); + .leftJoinAndSelect('notification.notifier', 'notifier') + .leftJoinAndSelect('notification.note', 'note') + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); query.andWhere(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`); query.setParameters(mutingQuery.getParameters()); @@ -110,5 +116,5 @@ export default define(meta, async (ps, user) => { readNotification(user.id, notifications.map(x => x.id)); } - return await Notifications.packMany(notifications); + return await Notifications.packMany(notifications, user.id); }); diff --git a/src/server/api/endpoints/i/read-announcement.ts b/src/server/api/endpoints/i/read-announcement.ts index 4a4a021af9b2c54885395268f076a62eac99adc1..d6acb3d2e6047293e1dcb6ae119f415d1c31f157 100644 --- a/src/server/api/endpoints/i/read-announcement.ts +++ b/src/server/api/endpoints/i/read-announcement.ts @@ -52,7 +52,7 @@ export default define(meta, async (ps, user) => { } // Create read - await AnnouncementReads.save({ + await AnnouncementReads.insert({ id: genId(), createdAt: new Date(), announcementId: ps.announcementId, diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index a1faf8f1c2b5a795be95da4cb387eb0b42440a3e..92be2e9e6d2cbd90f41a5d62ff5ef01466c643f1 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -1,6 +1,6 @@ import $ from 'cafy'; import { ID } from '../../../../misc/cafy-id'; -import { publishMainStream } from '../../../../services/stream'; +import { publishMainStream, publishUserEvent } from '../../../../services/stream'; import acceptAllFollowRequests from '../../../../services/following/requests/accept-all'; import { publishToFollowers } from '../../../../services/i/update'; import define from '../../define'; @@ -317,6 +317,7 @@ export default define(meta, async (ps, user, token) => { // Publish meUpdated event publishMainStream(user.id, 'meUpdated', iObj); + publishUserEvent(user.id, 'updateUserProfile', await UserProfiles.findOne(user.id)); // éµåž¢ã‚’解除ã—ãŸã¨ãã€æºœã¾ã£ã¦ã„ãŸãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒã‚ã‚‹ãªã‚‰ã™ã¹ã¦æ‰¿èª if (user.isLocked && ps.isLocked === false) { diff --git a/src/server/api/endpoints/miauth/gen-token.ts b/src/server/api/endpoints/miauth/gen-token.ts index 0634debb1ea6ad9b830a56c6bf153a1077f5e9c2..401ed16389eb3b902fcda2ee397f0c299a0c3e24 100644 --- a/src/server/api/endpoints/miauth/gen-token.ts +++ b/src/server/api/endpoints/miauth/gen-token.ts @@ -52,7 +52,7 @@ export default define(meta, async (ps, user) => { const now = new Date(); // Insert access token doc - await AccessTokens.save({ + await AccessTokens.insert({ id: genId(), createdAt: now, lastUsedAt: now, diff --git a/src/server/api/endpoints/mute/create.ts b/src/server/api/endpoints/mute/create.ts index 437ad961076981d24f4a05dd63ad9819710fb5c7..ebfc6028ed457acaec4f16ace74b5e496506df75 100644 --- a/src/server/api/endpoints/mute/create.ts +++ b/src/server/api/endpoints/mute/create.ts @@ -6,6 +6,7 @@ import { getUser } from '../../common/getters'; import { genId } from '../../../../misc/gen-id'; import { Mutings, NoteWatchings } from '../../../../models'; import { Muting } from '../../../../models/entities/muting'; +import { publishUserEvent } from '../../../../services/stream'; export const meta = { desc: { @@ -82,6 +83,8 @@ export default define(meta, async (ps, user) => { muteeId: mutee.id, } as Muting); + publishUserEvent(user.id, 'mute', mutee); + NoteWatchings.delete({ userId: muter.id, noteUserId: mutee.id diff --git a/src/server/api/endpoints/mute/delete.ts b/src/server/api/endpoints/mute/delete.ts index 217352acb405e7a0212be3ec6c649f447ef96e9d..67a59e3ae48ea8911c3517040953fcafd237d994 100644 --- a/src/server/api/endpoints/mute/delete.ts +++ b/src/server/api/endpoints/mute/delete.ts @@ -4,6 +4,7 @@ import define from '../../define'; import { ApiError } from '../../error'; import { getUser } from '../../common/getters'; import { Mutings } from '../../../../models'; +import { publishUserEvent } from '../../../../services/stream'; export const meta = { desc: { @@ -76,4 +77,6 @@ export default define(meta, async (ps, user) => { await Mutings.delete({ id: exist.id }); + + publishUserEvent(user.id, 'unmute', mutee); }); diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index fab8455d78b4af2cb2a2704caadd2aecae634403..30e6e92fec07149ea8deca0b54e35f6681b9a778 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -76,7 +76,11 @@ export default define(meta, async (ps) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.visibility = 'public'`) .andWhere(`note.localOnly = FALSE`) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); if (ps.local) { query.andWhere('note.userHost IS NULL'); diff --git a/src/server/api/endpoints/notes/children.ts b/src/server/api/endpoints/notes/children.ts index 0875e0f240a1ab1ef22fe963a5aef697a3b0dcf2..072a25e024eae9275a6ba427b146589222b85760 100644 --- a/src/server/api/endpoints/notes/children.ts +++ b/src/server/api/endpoints/notes/children.ts @@ -64,7 +64,11 @@ export default define(meta, async (ps, user) => { })); })); })) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/favorites/create.ts b/src/server/api/endpoints/notes/favorites/create.ts index 952bbfd0ebf607706f713a103e3f3bbe8425c4e7..d66ce37a46d2f5a722e5624a8cfae227e295efa2 100644 --- a/src/server/api/endpoints/notes/favorites/create.ts +++ b/src/server/api/endpoints/notes/favorites/create.ts @@ -61,7 +61,7 @@ export default define(meta, async (ps, user) => { } // Create favorite - await NoteFavorites.save({ + await NoteFavorites.insert({ id: genId(), createdAt: new Date(), noteId: note.id, diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts index 4dda7d0edb36a27487283189043b9427560ef3c6..b3dffa427252ef82f874d37595dbe6f6c7a19695 100644 --- a/src/server/api/endpoints/notes/featured.ts +++ b/src/server/api/endpoints/notes/featured.ts @@ -49,7 +49,11 @@ export default define(meta, async (ps, user) => { .andWhere(`note.score > 0`) .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) .andWhere(`note.visibility = 'public'`) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 6d99f1fdbce7c0a28eea4645026a9b4752271355..64fc3cbf6cce37abe0f46f4e9c3b18b53b81eb0a 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -8,8 +8,6 @@ import { Notes } from '../../../../models'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { activeUsersChart } from '../../../../services/chart'; import { generateRepliesQuery } from '../../common/generate-replies-query'; -import { injectPromo } from '../../common/inject-promo'; -import { injectFeatured } from '../../common/inject-featured'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; export const meta = { @@ -81,7 +79,11 @@ export default define(meta, async (ps, user) => { ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateRepliesQuery(query, user); if (user) generateMutedUserQuery(query, user); @@ -94,9 +96,6 @@ export default define(meta, async (ps, user) => { const timeline = await query.take(ps.limit!).getMany(); - await injectPromo(timeline, user); - await injectFeatured(timeline, user); - process.nextTick(() => { if (user) { activeUsersChart.update(user); diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index 2b91b8c67b7f47b8c620578506a2896a24296bbd..19c4593f5be07b512299f7a67e030bd290065b94 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -10,8 +10,6 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query' import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { activeUsersChart } from '../../../../services/chart'; import { generateRepliesQuery } from '../../common/generate-replies-query'; -import { injectPromo } from '../../common/inject-promo'; -import { injectFeatured } from '../../common/inject-featured'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; import { generateChannelQuery } from '../../common/generate-channel-query'; @@ -129,7 +127,11 @@ export default define(meta, async (ps, user) => { qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id }) .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); })) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); @@ -175,9 +177,6 @@ export default define(meta, async (ps, user) => { const timeline = await query.take(ps.limit!).getMany(); - await injectPromo(timeline, user); - await injectFeatured(timeline, user); - process.nextTick(() => { if (user) { activeUsersChart.update(user); diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index 51e35e62411d2107c4980b46496f2c44dd571dbe..546d3619f7f6a65d1132a61bcdce897649464329 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -10,8 +10,6 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query' import { activeUsersChart } from '../../../../services/chart'; import { Brackets } from 'typeorm'; import { generateRepliesQuery } from '../../common/generate-replies-query'; -import { injectPromo } from '../../common/inject-promo'; -import { injectFeatured } from '../../common/inject-featured'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; import { generateChannelQuery } from '../../common/generate-channel-query'; @@ -98,7 +96,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateChannelQuery(query, user); generateRepliesQuery(query, user); @@ -128,9 +130,6 @@ export default define(meta, async (ps, user) => { const timeline = await query.take(ps.limit!).getMany(); - await injectPromo(timeline, user); - await injectFeatured(timeline, user); - process.nextTick(() => { if (user) { activeUsersChart.update(user); diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 8a9d295d381e554641607ba7e14831eb96c9cdb2..30368ea578e23f1e5addcc6b5c3caa6ae62bb4a9 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -63,7 +63,11 @@ export default define(meta, async (ps, user) => { .where(`:meId = ANY(note.mentions)`, { meId: user.id }) .orWhere(`:meId = ANY(note.visibleUserIds)`, { meId: user.id }); })) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); @@ -79,9 +83,7 @@ export default define(meta, async (ps, user) => { const mentions = await query.take(ps.limit!).getMany(); - for (const note of mentions) { - read(user.id, note.id); - } + read(user.id, mentions.map(note => note.id)); return await Notes.packMany(mentions, user); }); diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts index 31c24f294ac17b6ae599d21691f4a858ecd4dacd..dcda213918b846ada1178194f0753ef64e78443b 100644 --- a/src/server/api/endpoints/notes/renotes.ts +++ b/src/server/api/endpoints/notes/renotes.ts @@ -68,7 +68,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.renoteId = :renoteId`, { renoteId: note.id }) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 9fad74c78e84c04312892929d8dccda852014d34..6f33e2f23327ede35e05817df05fac11013899fa 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -59,7 +59,11 @@ export const meta = { export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts index e0f7f4d62c3124943a9028574ac5397c9ec62f27..47b41d9294a45ac46f9804ff7098a03e161031fc 100644 --- a/src/server/api/endpoints/notes/search-by-tag.ts +++ b/src/server/api/endpoints/notes/search-by-tag.ts @@ -95,7 +95,11 @@ export const meta = { export default define(meta, async (ps, me) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index 1aca056299f0527961db4bcf92a9f5a6d000a4ec..230d2b02945f29714b4afef79a62ebd51f24c4ae 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -79,7 +79,11 @@ export default define(meta, async (ps, me) => { query .andWhere('note.text ILIKE :q', { q: `%${ps.query}%` }) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index f09f3d1733fdcaa85d072d0f7f2512219463015d..d025944cc2c1210d1de1f4aaf016b36e72b2e225 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -8,8 +8,6 @@ import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { activeUsersChart } from '../../../../services/chart'; import { Brackets } from 'typeorm'; import { generateRepliesQuery } from '../../common/generate-replies-query'; -import { injectPromo } from '../../common/inject-promo'; -import { injectFeatured } from '../../common/inject-featured'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; import { generateChannelQuery } from '../../common/generate-channel-query'; @@ -122,7 +120,11 @@ export default define(meta, async (ps, user) => { .where('note.userId = :meId', { meId: user.id }); if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); })) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); @@ -168,9 +170,6 @@ export default define(meta, async (ps, user) => { const timeline = await query.take(ps.limit!).getMany(); - await injectPromo(timeline, user); - await injectFeatured(timeline, user); - process.nextTick(() => { if (user) { activeUsersChart.update(user); diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index b0ff499d955c49ad98842f25c1f7a6347c813961..9ffb38bddc00e60ba3ae1e735559b316b934fd30 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -130,7 +130,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.userId IN (${ listQuery.getQuery() })`) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(listQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/src/server/api/endpoints/pages/like.ts b/src/server/api/endpoints/pages/like.ts index 5c7e13f1c819645ed56fbb77218b9fff1e6334d5..3fc2b6ca2325699011cf06443ece27d857bb39d1 100644 --- a/src/server/api/endpoints/pages/like.ts +++ b/src/server/api/endpoints/pages/like.ts @@ -68,7 +68,7 @@ export default define(meta, async (ps, user) => { } // Create like - await PageLikes.save({ + await PageLikes.insert({ id: genId(), createdAt: new Date(), pageId: page.id, diff --git a/src/server/api/endpoints/promo/read.ts b/src/server/api/endpoints/promo/read.ts index 57eb0681e5897d6c3482a205fed597d35acdd512..63c90e5d7f7ff4fe88f82d22d07889997c8363e8 100644 --- a/src/server/api/endpoints/promo/read.ts +++ b/src/server/api/endpoints/promo/read.ts @@ -46,7 +46,7 @@ export default define(meta, async (ps, user) => { return; } - await PromoReads.save({ + await PromoReads.insert({ id: genId(), createdAt: new Date(), noteId: note.id, diff --git a/src/server/api/endpoints/sw/register.ts b/src/server/api/endpoints/sw/register.ts index ceb70a92743e20be67c1263e99da0f231f5b11b7..9fc70b56094625dc0ba172f180dea7790b8e57af 100644 --- a/src/server/api/endpoints/sw/register.ts +++ b/src/server/api/endpoints/sw/register.ts @@ -58,7 +58,7 @@ export default define(meta, async (ps, user) => { }; } - await SwSubscriptions.save({ + await SwSubscriptions.insert({ id: genId(), createdAt: new Date(), userId: user.id, diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts index bd4a2739c6077c0f97dd25b5cdac25540161a68d..fb83d7beb88c35ecfd9a36e4d7737013f731bb64 100644 --- a/src/server/api/endpoints/users/followers.ts +++ b/src/server/api/endpoints/users/followers.ts @@ -76,7 +76,8 @@ export default define(meta, async (ps, me) => { } const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere(`following.followeeId = :userId`, { userId: user.id }); + .andWhere(`following.followeeId = :userId`, { userId: user.id }) + .innerJoinAndSelect('following.follower', 'follower'); const followings = await query .take(ps.limit!) diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts index 9efb8bfc937c80cac9454476e4d6efc1f2abe237..d5e8dc1f920111c3faa58b3ece9e5482deca2060 100644 --- a/src/server/api/endpoints/users/following.ts +++ b/src/server/api/endpoints/users/following.ts @@ -76,7 +76,8 @@ export default define(meta, async (ps, me) => { } const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere(`following.followerId = :userId`, { userId: user.id }); + .andWhere(`following.followerId = :userId`, { userId: user.id }) + .innerJoinAndSelect('following.followee', 'followee'); const followings = await query .take(ps.limit!) diff --git a/src/server/api/endpoints/users/groups/create.ts b/src/server/api/endpoints/users/groups/create.ts index ca011d5cd653d8276f01d71ea746ad188ccc0487..78d27148743775bf3203d2915c2697bff711af3a 100644 --- a/src/server/api/endpoints/users/groups/create.ts +++ b/src/server/api/endpoints/users/groups/create.ts @@ -39,7 +39,7 @@ export default define(meta, async (ps, user) => { } as UserGroup); // Push the owner - await UserGroupJoinings.save({ + await UserGroupJoinings.insert({ id: genId(), createdAt: new Date(), userId: user.id, diff --git a/src/server/api/endpoints/users/groups/invitations/accept.ts b/src/server/api/endpoints/users/groups/invitations/accept.ts index e86709f83b946a7cd717e8f49afad90f97872214..2fa22bcf7e3758c232ac69c0aa6e4209d1541c8b 100644 --- a/src/server/api/endpoints/users/groups/invitations/accept.ts +++ b/src/server/api/endpoints/users/groups/invitations/accept.ts @@ -52,7 +52,7 @@ export default define(meta, async (ps, user) => { } // Push the user - await UserGroupJoinings.save({ + await UserGroupJoinings.insert({ id: genId(), createdAt: new Date(), userId: user.id, diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index 33e3ecb03f60dffa9d9803141804a2138908fcde..fc5998c378b3376f92b93dd45690a048b1b357e4 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -131,7 +131,11 @@ export default define(meta, async (ps, me) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.userId = :userId', { userId: user.id }) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me, user); diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts index 7a5efc6cc9ba42bcc8b91b47c775bb6127590e71..d8f2e6d5168414c0eb367625f5316f8c70591c3e 100644 --- a/src/server/api/private/signin.ts +++ b/src/server/api/private/signin.ts @@ -53,7 +53,7 @@ export default async (ctx: Koa.Context) => { async function fail(status?: number, failure?: { error: string }) { // Append signin history - await Signins.save({ + await Signins.insert({ id: genId(), createdAt: new Date(), userId: user.id, @@ -198,7 +198,7 @@ export default async (ctx: Koa.Context) => { const challengeId = genId(); - await AttestationChallenges.save({ + await AttestationChallenges.insert({ userId: user.id, id: challengeId, challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), diff --git a/src/server/api/stream/channels/antenna.ts b/src/server/api/stream/channels/antenna.ts index b5a792f81405d51de7fd3c7e59f0d6c6fda714bd..36a474f2aca7b438a6fdc2ee701d69d6baf93339 100644 --- a/src/server/api/stream/channels/antenna.ts +++ b/src/server/api/stream/channels/antenna.ts @@ -27,6 +27,8 @@ export default class extends Channel { // æµã‚Œã¦ããŸNoteãŒãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isMutedUserRelated(note, this.muting)) return; + this.connection.cacheNote(note); + this.send('note', note); } else { this.send(type, body); diff --git a/src/server/api/stream/channels/channel.ts b/src/server/api/stream/channels/channel.ts index aa570d1ef402485b0f50aaf86c9892cba81e16e3..47a52465b29f935707e58557fa9976d7d963a70d 100644 --- a/src/server/api/stream/channels/channel.ts +++ b/src/server/api/stream/channels/channel.ts @@ -43,6 +43,8 @@ export default class extends Channel { // æµã‚Œã¦ããŸNoteãŒãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isMutedUserRelated(note, this.muting)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/global-timeline.ts b/src/server/api/stream/channels/global-timeline.ts index 8c97e67226f14be4b492a5e2ff8179630f238a52..8353f45323a7f08f025cd7a53e7ccb9e22540492 100644 --- a/src/server/api/stream/channels/global-timeline.ts +++ b/src/server/api/stream/channels/global-timeline.ts @@ -56,6 +56,8 @@ export default class extends Channel { // ãã®ãŸã‚レコードãŒå˜åœ¨ã™ã‚‹ã‹ã®ãƒã‚§ãƒƒã‚¯ã§ã¯ä¸å分ãªã®ã§ã€æ”¹ã‚ã¦checkWordMuteを呼んã§ã„ã‚‹ if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/hashtag.ts b/src/server/api/stream/channels/hashtag.ts index 41447039d534295c7951ed75a320dbdd75d2d86b..1b7f8efcc1ca56169277914c922ae2efd67bac41 100644 --- a/src/server/api/stream/channels/hashtag.ts +++ b/src/server/api/stream/channels/hashtag.ts @@ -37,6 +37,8 @@ export default class extends Channel { // æµã‚Œã¦ããŸNoteãŒãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isMutedUserRelated(note, this.muting)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts index 6cfa6eae7bfd1fd808f6b17b2dcce607d9366193..59ba31c3167651ef08516f4dd726b69ef807d0d1 100644 --- a/src/server/api/stream/channels/home-timeline.ts +++ b/src/server/api/stream/channels/home-timeline.ts @@ -64,6 +64,8 @@ export default class extends Channel { // ãã®ãŸã‚レコードãŒå˜åœ¨ã™ã‚‹ã‹ã®ãƒã‚§ãƒƒã‚¯ã§ã¯ä¸å分ãªã®ã§ã€æ”¹ã‚ã¦checkWordMuteを呼んã§ã„ã‚‹ if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/hybrid-timeline.ts b/src/server/api/stream/channels/hybrid-timeline.ts index a9e577cacb83af132dc442073b437990d297f4a7..9715e9973f98e8c3773875e6e3ab8669f0a2964f 100644 --- a/src/server/api/stream/channels/hybrid-timeline.ts +++ b/src/server/api/stream/channels/hybrid-timeline.ts @@ -73,6 +73,8 @@ export default class extends Channel { // ãã®ãŸã‚レコードãŒå˜åœ¨ã™ã‚‹ã‹ã®ãƒã‚§ãƒƒã‚¯ã§ã¯ä¸å分ãªã®ã§ã€æ”¹ã‚ã¦checkWordMuteを呼んã§ã„ã‚‹ if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/local-timeline.ts b/src/server/api/stream/channels/local-timeline.ts index a3a5e491fc0473dadbeb4054b6e7d94079633563..e159c72d60aa887c8d89edbd29e2ef5bec42ede3 100644 --- a/src/server/api/stream/channels/local-timeline.ts +++ b/src/server/api/stream/channels/local-timeline.ts @@ -58,6 +58,8 @@ export default class extends Channel { // ãã®ãŸã‚レコードãŒå˜åœ¨ã™ã‚‹ã‹ã®ãƒã‚§ãƒƒã‚¯ã§ã¯ä¸å分ãªã®ã§ã€æ”¹ã‚ã¦checkWordMuteを呼んã§ã„ã‚‹ if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/main.ts b/src/server/api/stream/channels/main.ts index b69c2ec35558f7953b5fea8280d7152f6df4ead7..780bc0b89feb30d60b4f88d783da5ff775432a4c 100644 --- a/src/server/api/stream/channels/main.ts +++ b/src/server/api/stream/channels/main.ts @@ -18,18 +18,22 @@ export default class extends Channel { case 'notification': { if (this.muting.has(body.userId)) return; if (body.note && body.note.isHidden) { - body.note = await Notes.pack(body.note.id, this.user, { + const note = await Notes.pack(body.note.id, this.user, { detail: true }); + this.connection.cacheNote(note); + body.note = note; } break; } case 'mention': { if (this.muting.has(body.userId)) return; if (body.isHidden) { - body = await Notes.pack(body.id, this.user, { + const note = await Notes.pack(body.id, this.user, { detail: true }); + this.connection.cacheNote(note); + body = note; } break; } diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts index c56a0a157b0fcd1bd6149aa1bd76c0472bdcb059..99ae558696c78ad67102f39f64d26cae7974fea3 100644 --- a/src/server/api/stream/index.ts +++ b/src/server/api/stream/index.ts @@ -14,6 +14,7 @@ import { AccessToken } from '../../../models/entities/access-token'; import { UserProfile } from '../../../models/entities/user-profile'; import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '../../../services/stream'; import { UserGroup } from '../../../models/entities/user-group'; +import { PackedNote } from '../../../models/repositories/note'; /** * Main stream connection @@ -29,10 +30,7 @@ export default class Connection { public subscriber: EventEmitter; private channels: Channel[] = []; private subscribingNotes: any = {}; - private followingClock: ReturnType<typeof setInterval>; - private mutingClock: ReturnType<typeof setInterval>; - private followingChannelsClock: ReturnType<typeof setInterval>; - private userProfileClock: ReturnType<typeof setInterval>; + private cachedNotes: PackedNote[] = []; constructor( wsConnection: websocket.connection, @@ -53,16 +51,49 @@ export default class Connection { if (this.user) { this.updateFollowing(); - this.followingClock = setInterval(this.updateFollowing, 5000); - this.updateMuting(); - this.mutingClock = setInterval(this.updateMuting, 5000); - this.updateFollowingChannels(); - this.followingChannelsClock = setInterval(this.updateFollowingChannels, 5000); - this.updateUserProfile(); - this.userProfileClock = setInterval(this.updateUserProfile, 5000); + + this.subscriber.on(`user:${this.user.id}`, ({ type, body }) => { + this.onUserEvent(type, body); + }); + } + } + + @autobind + private onUserEvent(type: string, body: any) { + switch (type) { + case 'follow': + this.following.add(body.id); + break; + + case 'unfollow': + this.following.delete(body.id); + break; + + case 'mute': + this.muting.add(body.id); + break; + + case 'unmute': + this.muting.delete(body.id); + break; + + case 'followChannel': + this.followingChannels.add(body.id); + break; + + case 'unfollowChannel': + this.followingChannels.delete(body.id); + break; + + case 'updateUserProfile': + this.userProfile = body; + break; + + default: + break; } } @@ -86,9 +117,9 @@ export default class Connection { switch (type) { case 'api': this.onApiRequest(body); break; case 'readNotification': this.onReadNotification(body); break; - case 'subNote': this.onSubscribeNote(body, true); break; - case 'sn': this.onSubscribeNote(body, true); break; // alias - case 's': this.onSubscribeNote(body, false); break; + case 'subNote': this.onSubscribeNote(body); break; + case 's': this.onSubscribeNote(body); break; // alias + case 'sr': this.onSubscribeNote(body); this.readNote(body); break; case 'unsubNote': this.onUnsubscribeNote(body); break; case 'un': this.onUnsubscribeNote(body); break; // alias case 'connect': this.onChannelConnectRequested(body); break; @@ -109,6 +140,48 @@ export default class Connection { this.sendMessageToWs(type, body); } + @autobind + public cacheNote(note: PackedNote) { + const add = (note: PackedNote) => { + const existIndex = this.cachedNotes.findIndex(n => n.id === note.id); + if (existIndex > -1) { + this.cachedNotes[existIndex] = note; + return; + } + + this.cachedNotes.unshift(note); + if (this.cachedNotes.length > 32) { + this.cachedNotes.splice(32); + } + }; + + add(note); + if (note.reply) add(note.reply); + if (note.renote) add(note.renote); + } + + @autobind + private readNote(body: any) { + const id = body.id; + + const note = this.cachedNotes.find(n => n.id === id); + if (note == null) return; + + if (this.user && (note.userId !== this.user.id)) { + if (note.mentions && note.mentions.includes(this.user.id)) { + readNote(this.user.id, [note]); + } else if (note.visibleUserIds && note.visibleUserIds.includes(this.user.id)) { + readNote(this.user.id, [note]); + } + + if (this.followingChannels.has(note.channelId)) { + // TODO + } + + // TODO: アンテナã®æ—¢èªå‡¦ç† + } + } + /** * APIリクエストè¦æ±‚時 */ @@ -145,7 +218,7 @@ export default class Connection { * 投稿購èªè¦æ±‚時 */ @autobind - private onSubscribeNote(payload: any, read: boolean) { + private onSubscribeNote(payload: any) { if (!payload.id) return; if (this.subscribingNotes[payload.id] == null) { @@ -157,10 +230,6 @@ export default class Connection { if (this.subscribingNotes[payload.id] === 1) { this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage); } - - if (this.user && read) { - readNote(this.user.id, payload.id); - } } /** @@ -335,10 +404,5 @@ export default class Connection { for (const c of this.channels.filter(c => c.dispose)) { if (c.dispose) c.dispose(); } - - if (this.followingClock) clearInterval(this.followingClock); - if (this.mutingClock) clearInterval(this.mutingClock); - if (this.followingChannelsClock) clearInterval(this.followingChannelsClock); - if (this.userProfileClock) clearInterval(this.userProfileClock); } } diff --git a/src/server/web/index.ts b/src/server/web/index.ts index 7b0b82eedff4f3fe12bc4d6761abfb1e3fefaece..ea356206ffd8f32b94b7f275bece648483ea6d7b 100644 --- a/src/server/web/index.ts +++ b/src/server/web/index.ts @@ -29,6 +29,7 @@ const markdown = MarkdownIt({ }); const staticAssets = `${__dirname}/../../../assets/`; +const docAssets = `${__dirname}/../../../src/docs/`; const assets = `${__dirname}/../../assets/`; // Init app @@ -44,7 +45,7 @@ app.use(views(__dirname + '/views', { })); // Serve favicon -app.use(favicon(`${__dirname}/../../../assets/favicon.png`)); +app.use(favicon(`${__dirname}/../../../assets/favicon.ico`)); // Common request handler app.use(async (ctx, next) => { @@ -65,6 +66,13 @@ router.get('/static-assets/(.*)', async ctx => { }); }); +router.get('/doc-assets/(.*)', async ctx => { + await send(ctx as any, ctx.path.replace('/doc-assets/', ''), { + root: docAssets, + maxage: ms('7 days'), + }); +}); + router.get('/assets/(.*)', async ctx => { await send(ctx as any, ctx.path.replace('/assets/', ''), { root: assets, @@ -75,7 +83,7 @@ router.get('/assets/(.*)', async ctx => { // Apple touch icon router.get('/apple-touch-icon.png', async ctx => { await send(ctx as any, '/apple-touch-icon.png', { - root: assets + root: staticAssets }); }); diff --git a/src/server/web/views/flush.pug b/src/server/web/views/flush.pug index 59fed1f15de77944725eb5cacff55cfd2b681beb..ec585a34db547c0ff2938200d2c4f787e91a336d 100644 --- a/src/server/web/views/flush.pug +++ b/src/server/web/views/flush.pug @@ -4,35 +4,44 @@ html #msg script. const msg = document.getElementById('msg'); + const successText = `\nSuccess Flush! <a href="/">Back to Misskey</a>\næˆåŠŸã—ã¾ã—ãŸã€‚<a href="/">Misskeyã‚’é–‹ãç›´ã—ã¦ãã ã•ã„。</a>`; - try { - localStorage.clear(); - message('localStorage cleared'); - - const delidb = indexedDB.deleteDatabase('MisskeyClient'); - delidb.onsuccess = () => message('indexedDB cleared'); - - if (navigator.serviceWorker.controller) { - navigator.serviceWorker.controller.postMessage('clear'); - navigator.serviceWorker.getRegistrations() - .then(registrations => { - return Promise.all(registrations.map(registration => registration.unregister())); - }) - .then(() => { - message('Success Flush! Please reopen Misskey.\næˆåŠŸã—ã¾ã—ãŸã€‚Misskeyã‚’é–‹ãç›´ã—ã¦ãã ã•ã„。'); - }) - .catch(e => { throw Error(e) }); - } else { - message('Success Flush! Please reopen Misskey.\næˆåŠŸã—ã¾ã—ãŸã€‚Misskeyã‚’é–‹ãç›´ã—ã¦ãã ã•ã„。'); + message('Start flushing.'); + + (async function() { + try { + localStorage.clear(); + message('localStorage cleared.'); + + const idbPromises = ['MisskeyClient', 'keyval-store'].map((name, i, arr) => new Promise((res, rej) => { + const delidb = indexedDB.deleteDatabase(name); + delidb.onsuccess = () => res(message(`indexedDB "${name}" cleared. (${i + 1}/${arr.length})`)); + delidb.onerror = e => rej(e) + })); + + await Promise.all(idbPromises); + + if (navigator.serviceWorker.controller) { + navigator.serviceWorker.controller.postMessage('clear'); + await navigator.serviceWorker.getRegistrations() + .then(registrations => { + return Promise.all(registrations.map(registration => registration.unregister())); + }) + .catch(e => { throw Error(e) }); + } + + message(successText); + } catch (e) { + message(`\n${e}\n\nFlush Failed. <a href="/flush">Please retry.</a>\n失敗ã—ã¾ã—ãŸã€‚<a href="/flush">ã‚‚ã†ä¸€åº¦è©¦ã—ã¦ã¿ã¦ãã ã•ã„。</a>`); + message(`\nIf you retry more than 3 times, clear the browser cache or contact to instance admin.\n3回以上試ã—ã¦ã‚‚失敗ã™ã‚‹å ´åˆã€ãƒ–ラウザã®ã‚ャッシュを消去ã—ã€ãã‚Œã§ã‚‚ã ã‚ãªã‚‰ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ç®¡ç†è€…ã«é€£çµ¡ã—ã¦ã¿ã¦ãã ã•ã„。\n`) + + console.error(e); + setTimeout(() => { + location = '/'; + }, 10000) } - } catch (e) { - console.error(e); - message(`${e}Â¥nÂ¥nFlush Failed. Please reopen Misskey.\n失敗ã—ã¾ã—ãŸã€‚Misskeyã‚’é–‹ãç›´ã—ã¦ãã ã•ã„。`); - setTimeout(() => { - location = '/'; - }, 10000) - } + })(); function message(text) { - msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/Â¥n/g,'<br>')}</p>`) + msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/\n/g,'<br>')}</p>`) } diff --git a/src/services/add-note-to-antenna.ts b/src/services/add-note-to-antenna.ts index 2c893488c33fd6bfb5e2293b8a2476ecd1fa4ad0..3ba3d1eef5e7dd957b4d68377da950efd1093212 100644 --- a/src/services/add-note-to-antenna.ts +++ b/src/services/add-note-to-antenna.ts @@ -10,7 +10,7 @@ export async function addNoteToAntenna(antenna: Antenna, note: Note, noteUser: U // 通知ã—ãªã„è¨å®šã«ãªã£ã¦ã„ã‚‹ã‹ã€è‡ªåˆ†è‡ªèº«ã®æŠ•ç¨¿ãªã‚‰æ—¢èªã«ã™ã‚‹ const read = !antenna.notify || (antenna.userId === noteUser.id); - AntennaNotes.save({ + AntennaNotes.insert({ id: genId(), antennaId: antenna.id, noteId: note.id, diff --git a/src/services/blocking/create.ts b/src/services/blocking/create.ts index def4f3358572fa8bdf910c84dbf58f258b8fa1ba..dec48d26de6611b8086a036b7b720c7397938701 100644 --- a/src/services/blocking/create.ts +++ b/src/services/blocking/create.ts @@ -1,4 +1,4 @@ -import { publishMainStream } from '../stream'; +import { publishMainStream, publishUserEvent } from '../stream'; import { renderActivity } from '../../remote/activitypub/renderer'; import renderFollow from '../../remote/activitypub/renderer/follow'; import renderUndo from '../../remote/activitypub/renderer/undo'; @@ -18,7 +18,7 @@ export default async function(blocker: User, blockee: User) { unFollow(blockee, blocker) ]); - await Blockings.save({ + await Blockings.insert({ id: genId(), createdAt: new Date(), blockerId: blocker.id, @@ -55,7 +55,10 @@ async function cancelRequest(follower: User, followee: User) { if (Users.isLocalUser(follower)) { Users.pack(followee, follower, { detail: true - }).then(packed => publishMainStream(follower.id, 'unfollow', packed)); + }).then(packed => { + publishUserEvent(follower.id, 'unfollow', packed); + publishMainStream(follower.id, 'unfollow', packed); + }); } // リモートã«ãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ã—ã¦ã„ãŸã‚‰UndoFollowé€ä¿¡ @@ -97,7 +100,10 @@ async function unFollow(follower: User, followee: User) { if (Users.isLocalUser(follower)) { Users.pack(followee, follower, { detail: true - }).then(packed => publishMainStream(follower.id, 'unfollow', packed)); + }).then(packed => { + publishUserEvent(follower.id, 'unfollow', packed); + publishMainStream(follower.id, 'unfollow', packed); + }); } // リモートã«ãƒ•ã‚©ãƒãƒ¼ã‚’ã—ã¦ã„ãŸã‚‰UndoFollowé€ä¿¡ diff --git a/src/services/chart/charts/classes/active-users.ts b/src/services/chart/charts/classes/active-users.ts index 5128150de6cf0b1ed20c6730200d5198e439fa06..4820f8281b5c802c42910e83c5a7bbb00c46f5a9 100644 --- a/src/services/chart/charts/classes/active-users.ts +++ b/src/services/chart/charts/classes/active-users.ts @@ -17,6 +17,18 @@ export default class ActiveUsersChart extends Chart<ActiveUsersLog> { return {}; } + @autobind + protected aggregate(logs: ActiveUsersLog[]): ActiveUsersLog { + return { + local: { + users: logs.reduce((a, b) => a.concat(b.local.users), [] as ActiveUsersLog['local']['users']), + }, + remote: { + users: logs.reduce((a, b) => a.concat(b.remote.users), [] as ActiveUsersLog['remote']['users']), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<ActiveUsersLog>> { return {}; @@ -25,11 +37,11 @@ export default class ActiveUsersChart extends Chart<ActiveUsersLog> { @autobind public async update(user: User) { const update: Obj = { - count: 1 + users: [user.id] }; - await this.incIfUnique({ + await this.inc({ [Users.isLocalUser(user) ? 'local' : 'remote']: update - }, 'users', user.id); + }); } } diff --git a/src/services/chart/charts/classes/drive.ts b/src/services/chart/charts/classes/drive.ts index 57bb120beb28ef07931b849bdf47d049946824f1..46399a34d8860cc231430f74e8fb69d4712e9026 100644 --- a/src/services/chart/charts/classes/drive.ts +++ b/src/services/chart/charts/classes/drive.ts @@ -27,6 +27,28 @@ export default class DriveChart extends Chart<DriveLog> { }; } + @autobind + protected aggregate(logs: DriveLog[]): DriveLog { + return { + local: { + totalCount: logs[0].local.totalCount, + totalSize: logs[0].local.totalSize, + incCount: logs.reduce((a, b) => a + b.local.incCount, 0), + incSize: logs.reduce((a, b) => a + b.local.incSize, 0), + decCount: logs.reduce((a, b) => a + b.local.decCount, 0), + decSize: logs.reduce((a, b) => a + b.local.decSize, 0), + }, + remote: { + totalCount: logs[0].remote.totalCount, + totalSize: logs[0].remote.totalSize, + incCount: logs.reduce((a, b) => a + b.remote.incCount, 0), + incSize: logs.reduce((a, b) => a + b.remote.incSize, 0), + decCount: logs.reduce((a, b) => a + b.remote.decCount, 0), + decSize: logs.reduce((a, b) => a + b.remote.decSize, 0), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<DriveLog>> { const [localCount, remoteCount, localSize, remoteSize] = await Promise.all([ diff --git a/src/services/chart/charts/classes/federation.ts b/src/services/chart/charts/classes/federation.ts index bd2c497e7b8dfbdeaa05c4550908e536acaac255..ab6ec2d4ddd6c5db1215bff259472deb8493136e 100644 --- a/src/services/chart/charts/classes/federation.ts +++ b/src/services/chart/charts/classes/federation.ts @@ -20,6 +20,17 @@ export default class FederationChart extends Chart<FederationLog> { }; } + @autobind + protected aggregate(logs: FederationLog[]): FederationLog { + return { + instance: { + total: logs[0].instance.total, + inc: logs.reduce((a, b) => a + b.instance.inc, 0), + dec: logs.reduce((a, b) => a + b.instance.dec, 0), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<FederationLog>> { const [total] = await Promise.all([ diff --git a/src/services/chart/charts/classes/hashtag.ts b/src/services/chart/charts/classes/hashtag.ts index 38c3a94f0cb21ce98301193a497389534dbbb8b8..43db5b0a8323942144edad5d0ff28b988d7a7d4a 100644 --- a/src/services/chart/charts/classes/hashtag.ts +++ b/src/services/chart/charts/classes/hashtag.ts @@ -17,6 +17,18 @@ export default class HashtagChart extends Chart<HashtagLog> { return {}; } + @autobind + protected aggregate(logs: HashtagLog[]): HashtagLog { + return { + local: { + users: logs.reduce((a, b) => a.concat(b.local.users), [] as HashtagLog['local']['users']), + }, + remote: { + users: logs.reduce((a, b) => a.concat(b.remote.users), [] as HashtagLog['remote']['users']), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<HashtagLog>> { return {}; @@ -25,11 +37,11 @@ export default class HashtagChart extends Chart<HashtagLog> { @autobind public async update(hashtag: string, user: User) { const update: Obj = { - count: 1 + users: [user.id] }; - await this.incIfUnique({ + await this.inc({ [Users.isLocalUser(user) ? 'local' : 'remote']: update - }, 'users', user.id, hashtag); + }, hashtag); } } diff --git a/src/services/chart/charts/classes/instance.ts b/src/services/chart/charts/classes/instance.ts index 7575abfb6f9ac0998548afc296fc8b53829da2f8..c32b864d87fe373557d458eaf803f4a511ad54f7 100644 --- a/src/services/chart/charts/classes/instance.ts +++ b/src/services/chart/charts/classes/instance.ts @@ -36,6 +36,50 @@ export default class InstanceChart extends Chart<InstanceLog> { }; } + @autobind + protected aggregate(logs: InstanceLog[]): InstanceLog { + return { + requests: { + failed: logs.reduce((a, b) => a + b.requests.failed, 0), + succeeded: logs.reduce((a, b) => a + b.requests.succeeded, 0), + received: logs.reduce((a, b) => a + b.requests.received, 0), + }, + notes: { + total: logs[0].notes.total, + inc: logs.reduce((a, b) => a + b.notes.inc, 0), + dec: logs.reduce((a, b) => a + b.notes.dec, 0), + diffs: { + reply: logs.reduce((a, b) => a + b.notes.diffs.reply, 0), + renote: logs.reduce((a, b) => a + b.notes.diffs.renote, 0), + normal: logs.reduce((a, b) => a + b.notes.diffs.normal, 0), + }, + }, + users: { + total: logs[0].users.total, + inc: logs.reduce((a, b) => a + b.users.inc, 0), + dec: logs.reduce((a, b) => a + b.users.dec, 0), + }, + following: { + total: logs[0].following.total, + inc: logs.reduce((a, b) => a + b.following.inc, 0), + dec: logs.reduce((a, b) => a + b.following.dec, 0), + }, + followers: { + total: logs[0].followers.total, + inc: logs.reduce((a, b) => a + b.followers.inc, 0), + dec: logs.reduce((a, b) => a + b.followers.dec, 0), + }, + drive: { + totalFiles: logs[0].drive.totalFiles, + totalUsage: logs[0].drive.totalUsage, + incFiles: logs.reduce((a, b) => a + b.drive.incFiles, 0), + incUsage: logs.reduce((a, b) => a + b.drive.incUsage, 0), + decFiles: logs.reduce((a, b) => a + b.drive.decFiles, 0), + decUsage: logs.reduce((a, b) => a + b.drive.decUsage, 0), + }, + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<InstanceLog>> { const [ diff --git a/src/services/chart/charts/classes/network.ts b/src/services/chart/charts/classes/network.ts index 8b26e5c4c22f10c614c27f0e8dbe082088266322..693af48f735993de5c9769b0b8d28c7bae1ae0fe 100644 --- a/src/services/chart/charts/classes/network.ts +++ b/src/services/chart/charts/classes/network.ts @@ -15,6 +15,17 @@ export default class NetworkChart extends Chart<NetworkLog> { return {}; } + @autobind + protected aggregate(logs: NetworkLog[]): NetworkLog { + return { + incomingRequests: logs.reduce((a, b) => a + b.incomingRequests, 0), + outgoingRequests: logs.reduce((a, b) => a + b.outgoingRequests, 0), + totalTime: logs.reduce((a, b) => a + b.totalTime, 0), + incomingBytes: logs.reduce((a, b) => a + b.incomingBytes, 0), + outgoingBytes: logs.reduce((a, b) => a + b.outgoingBytes, 0), + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<NetworkLog>> { return {}; diff --git a/src/services/chart/charts/classes/notes.ts b/src/services/chart/charts/classes/notes.ts index 815061c4453dfd7e614991f524734e934cf6c2e4..965087bc08b752e411ed083a96e226fba3b86daa 100644 --- a/src/services/chart/charts/classes/notes.ts +++ b/src/services/chart/charts/classes/notes.ts @@ -25,6 +25,32 @@ export default class NotesChart extends Chart<NotesLog> { }; } + @autobind + protected aggregate(logs: NotesLog[]): NotesLog { + return { + local: { + total: logs[0].local.total, + inc: logs.reduce((a, b) => a + b.local.inc, 0), + dec: logs.reduce((a, b) => a + b.local.dec, 0), + diffs: { + reply: logs.reduce((a, b) => a + b.local.diffs.reply, 0), + renote: logs.reduce((a, b) => a + b.local.diffs.renote, 0), + normal: logs.reduce((a, b) => a + b.local.diffs.normal, 0), + }, + }, + remote: { + total: logs[0].remote.total, + inc: logs.reduce((a, b) => a + b.remote.inc, 0), + dec: logs.reduce((a, b) => a + b.remote.dec, 0), + diffs: { + reply: logs.reduce((a, b) => a + b.remote.diffs.reply, 0), + renote: logs.reduce((a, b) => a + b.remote.diffs.renote, 0), + normal: logs.reduce((a, b) => a + b.remote.diffs.normal, 0), + }, + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<NotesLog>> { const [localCount, remoteCount] = await Promise.all([ diff --git a/src/services/chart/charts/classes/per-user-drive.ts b/src/services/chart/charts/classes/per-user-drive.ts index aed9f6fce769af00c29cf18c49ad757a23b693f1..e778f7bf6104364124d493d3f91d67a72605714c 100644 --- a/src/services/chart/charts/classes/per-user-drive.ts +++ b/src/services/chart/charts/classes/per-user-drive.ts @@ -20,6 +20,18 @@ export default class PerUserDriveChart extends Chart<PerUserDriveLog> { }; } + @autobind + protected aggregate(logs: PerUserDriveLog[]): PerUserDriveLog { + return { + totalCount: logs[0].totalCount, + totalSize: logs[0].totalSize, + incCount: logs.reduce((a, b) => a + b.incCount, 0), + incSize: logs.reduce((a, b) => a + b.incSize, 0), + decCount: logs.reduce((a, b) => a + b.decCount, 0), + decSize: logs.reduce((a, b) => a + b.decSize, 0), + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<PerUserDriveLog>> { const [count, size] = await Promise.all([ diff --git a/src/services/chart/charts/classes/per-user-following.ts b/src/services/chart/charts/classes/per-user-following.ts index 8295c0cb0dba233d775f5f9ddad7aaa85f143081..8b536009c8b63e5dfd0bf4d64d9e76d10f8c99dd 100644 --- a/src/services/chart/charts/classes/per-user-following.ts +++ b/src/services/chart/charts/classes/per-user-following.ts @@ -35,6 +35,36 @@ export default class PerUserFollowingChart extends Chart<PerUserFollowingLog> { }; } + @autobind + protected aggregate(logs: PerUserFollowingLog[]): PerUserFollowingLog { + return { + local: { + followings: { + total: logs[0].local.followings.total, + inc: logs.reduce((a, b) => a + b.local.followings.inc, 0), + dec: logs.reduce((a, b) => a + b.local.followings.dec, 0), + }, + followers: { + total: logs[0].local.followers.total, + inc: logs.reduce((a, b) => a + b.local.followers.inc, 0), + dec: logs.reduce((a, b) => a + b.local.followers.dec, 0), + }, + }, + remote: { + followings: { + total: logs[0].remote.followings.total, + inc: logs.reduce((a, b) => a + b.remote.followings.inc, 0), + dec: logs.reduce((a, b) => a + b.remote.followings.dec, 0), + }, + followers: { + total: logs[0].remote.followers.total, + inc: logs.reduce((a, b) => a + b.remote.followers.inc, 0), + dec: logs.reduce((a, b) => a + b.remote.followers.dec, 0), + }, + }, + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<PerUserFollowingLog>> { const [ diff --git a/src/services/chart/charts/classes/per-user-notes.ts b/src/services/chart/charts/classes/per-user-notes.ts index cccd49560453c1e3814ad1527e571296e0b86634..8d1fb8d2b085a813e00c844c21cb2c75486fb0a7 100644 --- a/src/services/chart/charts/classes/per-user-notes.ts +++ b/src/services/chart/charts/classes/per-user-notes.ts @@ -20,6 +20,20 @@ export default class PerUserNotesChart extends Chart<PerUserNotesLog> { }; } + @autobind + protected aggregate(logs: PerUserNotesLog[]): PerUserNotesLog { + return { + total: logs[0].total, + inc: logs.reduce((a, b) => a + b.inc, 0), + dec: logs.reduce((a, b) => a + b.dec, 0), + diffs: { + reply: logs.reduce((a, b) => a + b.diffs.reply, 0), + renote: logs.reduce((a, b) => a + b.diffs.renote, 0), + normal: logs.reduce((a, b) => a + b.diffs.normal, 0), + }, + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<PerUserNotesLog>> { const [count] = await Promise.all([ diff --git a/src/services/chart/charts/classes/per-user-reactions.ts b/src/services/chart/charts/classes/per-user-reactions.ts index 124fb4153cbd8302b8ea2c2d39c22cffa9a26772..b4cdced40cb0462753c0bd0edeef2ef1300d06d7 100644 --- a/src/services/chart/charts/classes/per-user-reactions.ts +++ b/src/services/chart/charts/classes/per-user-reactions.ts @@ -18,6 +18,18 @@ export default class PerUserReactionsChart extends Chart<PerUserReactionsLog> { return {}; } + @autobind + protected aggregate(logs: PerUserReactionsLog[]): PerUserReactionsLog { + return { + local: { + count: logs.reduce((a, b) => a + b.local.count, 0), + }, + remote: { + count: logs.reduce((a, b) => a + b.remote.count, 0), + }, + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<PerUserReactionsLog>> { return {}; diff --git a/src/services/chart/charts/classes/test-grouped.ts b/src/services/chart/charts/classes/test-grouped.ts index e32cbcf41630e0a1b3c9d3862c39482aba67951c..92c8df636ea79dfd56603911186cd2070f3b61de 100644 --- a/src/services/chart/charts/classes/test-grouped.ts +++ b/src/services/chart/charts/classes/test-grouped.ts @@ -21,6 +21,17 @@ export default class TestGroupedChart extends Chart<TestGroupedLog> { }; } + @autobind + protected aggregate(logs: TestGroupedLog[]): TestGroupedLog { + return { + foo: { + total: logs[0].foo.total, + inc: logs.reduce((a, b) => a + b.foo.inc, 0), + dec: logs.reduce((a, b) => a + b.foo.dec, 0), + }, + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<TestGroupedLog>> { return { diff --git a/src/services/chart/charts/classes/test-unique.ts b/src/services/chart/charts/classes/test-unique.ts index 1eb396c2936e22d19d3f1643418c503738a5ebe2..5680d713eccc07193d3d8f136ba8d69cca5c7425 100644 --- a/src/services/chart/charts/classes/test-unique.ts +++ b/src/services/chart/charts/classes/test-unique.ts @@ -15,6 +15,13 @@ export default class TestUniqueChart extends Chart<TestUniqueLog> { return {}; } + @autobind + protected aggregate(logs: TestUniqueLog[]): TestUniqueLog { + return { + foo: logs.reduce((a, b) => a.concat(b.foo), [] as TestUniqueLog['foo']), + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<TestUniqueLog>> { return {}; @@ -22,8 +29,8 @@ export default class TestUniqueChart extends Chart<TestUniqueLog> { @autobind public async uniqueIncrement(key: string) { - await this.incIfUnique({ - foo: 1 - }, 'foos', key); + await this.inc({ + foo: [key] + }); } } diff --git a/src/services/chart/charts/classes/test.ts b/src/services/chart/charts/classes/test.ts index ea64040f3e5b2dea99405e8f8a03110d4260b565..d37d298de743034397dc13f99378a0dde71c616a 100644 --- a/src/services/chart/charts/classes/test.ts +++ b/src/services/chart/charts/classes/test.ts @@ -21,6 +21,17 @@ export default class TestChart extends Chart<TestLog> { }; } + @autobind + protected aggregate(logs: TestLog[]): TestLog { + return { + foo: { + total: logs[0].foo.total, + inc: logs.reduce((a, b) => a + b.foo.inc, 0), + dec: logs.reduce((a, b) => a + b.foo.dec, 0), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<TestLog>> { return { diff --git a/src/services/chart/charts/classes/users.ts b/src/services/chart/charts/classes/users.ts index 47e4caa1b715f9876a6e6a7d1b9dffe53892f09e..87b19d88f92cd275bf1a3532c707bf52ad940320 100644 --- a/src/services/chart/charts/classes/users.ts +++ b/src/services/chart/charts/classes/users.ts @@ -25,6 +25,22 @@ export default class UsersChart extends Chart<UsersLog> { }; } + @autobind + protected aggregate(logs: UsersLog[]): UsersLog { + return { + local: { + total: logs[0].local.total, + inc: logs.reduce((a, b) => a + b.local.inc, 0), + dec: logs.reduce((a, b) => a + b.local.dec, 0), + }, + remote: { + total: logs[0].remote.total, + inc: logs.reduce((a, b) => a + b.remote.inc, 0), + dec: logs.reduce((a, b) => a + b.remote.dec, 0), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<UsersLog>> { const [localCount, remoteCount] = await Promise.all([ diff --git a/src/services/chart/charts/schemas/active-users.ts b/src/services/chart/charts/schemas/active-users.ts index 6e26bb469815ad98aa43e27e9292e61ab988f922..cdf0579efb6adc36147d42664d00e8563d4639d7 100644 --- a/src/services/chart/charts/schemas/active-users.ts +++ b/src/services/chart/charts/schemas/active-users.ts @@ -1,11 +1,15 @@ export const logSchema = { /** - * アクティブユーザー数 + * アクティブユーザー */ - count: { - type: 'number' as const, + users: { + type: 'array' as const, optional: false as const, nullable: false as const, - description: 'アクティブユーザー数', + description: 'アクティブユーザー', + items: { + type: 'string' as const, + optional: false as const, nullable: false as const, + } }, }; diff --git a/src/services/chart/charts/schemas/hashtag.ts b/src/services/chart/charts/schemas/hashtag.ts index 4dfd61c97fe6a49e660e9912e0a44cbf2315264c..791d0d17217a1294e122d346915cdd7a4ecbacee 100644 --- a/src/services/chart/charts/schemas/hashtag.ts +++ b/src/services/chart/charts/schemas/hashtag.ts @@ -1,11 +1,15 @@ export const logSchema = { /** - * 投稿ã•ã‚ŒãŸæ•° + * 投稿ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ */ - count: { - type: 'number' as const, + users: { + type: 'array' as const, optional: false as const, nullable: false as const, - description: '投稿ã•ã‚ŒãŸæ•°', + description: '投稿ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼', + items: { + type: 'string' as const, + optional: false as const, nullable: false as const, + } }, }; diff --git a/src/services/chart/charts/schemas/test-unique.ts b/src/services/chart/charts/schemas/test-unique.ts index 075a8092d94368975d385a30d158463e12ece506..51280400ac85103dd6437ec8e72e48bd7a9c3537 100644 --- a/src/services/chart/charts/schemas/test-unique.ts +++ b/src/services/chart/charts/schemas/test-unique.ts @@ -3,9 +3,12 @@ export const schema = { optional: false as const, nullable: false as const, properties: { foo: { - type: 'number' as const, + type: 'array' as const, optional: false as const, nullable: false as const, - description: '' + items: { + type: 'string' as const, + optional: false as const, nullable: false as const, + } }, } }; diff --git a/src/services/chart/core.ts b/src/services/chart/core.ts index dc09923ae4024645eedc4ebb6679c8a1442bdc69..10621be07306f3ac8a74881aa2082192c6860af9 100644 --- a/src/services/chart/core.ts +++ b/src/services/chart/core.ts @@ -24,8 +24,6 @@ type ArrayValue<T> = { [P in keyof T]: T[P] extends number ? T[P][] : ArrayValue<T[P]>; }; -type Span = 'day' | 'hour'; - type Log = { id: number; @@ -38,22 +36,14 @@ type Log = { * 集計日時ã®Unixタイムスタンプ(秒) */ date: number; - - /** - * 集計期間 - */ - span: Span; - - /** - * ユニークインクリメント用 - */ - unique?: Record<string, any>; }; const camelToSnake = (str: string) => { return str.replace(/([A-Z])/g, s => '_' + s.charAt(0).toLowerCase()); }; +const removeDuplicates = (array: any[]) => Array.from(new Set(array)); + /** * 様々ãªãƒãƒ£ãƒ¼ãƒˆã®ç®¡ç†ã‚’å¸ã‚‹ã‚¯ãƒ©ã‚¹ */ @@ -62,10 +52,21 @@ export default abstract class Chart<T extends Record<string, any>> { private static readonly columnDot = '_'; private name: string; + private queue: { + diff: DeepPartial<T>; + group: string | null; + }[] = []; public schema: Schema; protected repository: Repository<Log>; + protected abstract genNewLog(latest: T): DeepPartial<T>; - protected abstract async fetchActual(group: string | null): Promise<DeepPartial<T>>; + + /** + * @param logs 日時ãŒæ–°ã—ã„æ–¹ãŒå…ˆé + */ + protected abstract aggregate(logs: T[]): T; + + protected abstract fetchActual(group: string | null): Promise<DeepPartial<T>>; @autobind private static convertSchemaToFlatColumnDefinitions(schema: Schema) { @@ -75,10 +76,15 @@ export default abstract class Chart<T extends Record<string, any>> { const p = path ? `${path}${this.columnDot}${k}` : k; if (v.type === 'object') { flatColumns(v.properties, p); - } else { + } else if (v.type === 'number') { columns[this.columnPrefix + p] = { type: 'bigint', }; + } else if (v.type === 'array' && v.items.type === 'string') { + columns[this.columnPrefix + p] = { + type: 'varchar', + array: true, + }; } } }; @@ -99,11 +105,11 @@ export default abstract class Chart<T extends Record<string, any>> { @autobind private static convertObjectToFlattenColumns(x: Record<string, any>) { - const columns = {} as Record<string, number>; + const columns = {} as Record<string, number | unknown[]>; const flatten = (x: Obj, path?: string) => { for (const [k, v] of Object.entries(x)) { const p = path ? `${path}${this.columnDot}${k}` : k; - if (typeof v === 'object') { + if (typeof v === 'object' && !Array.isArray(v)) { flatten(v, p); } else { columns[this.columnPrefix + p] = v; @@ -115,14 +121,37 @@ export default abstract class Chart<T extends Record<string, any>> { } @autobind - private static convertQuery(x: Record<string, any>) { - const query: Record<string, Function> = {}; + private static countUniqueFields(x: Record<string, any>) { + const exec = (x: Obj) => { + const res = {} as Record<string, any>; + for (const [k, v] of Object.entries(x)) { + if (typeof v === 'object' && !Array.isArray(v)) { + res[k] = exec(v); + } else if (Array.isArray(v)) { + res[k] = Array.from(new Set(v)).length; + } else { + res[k] = v; + } + } + return res; + }; + return exec(x); + } - const columns = Chart.convertObjectToFlattenColumns(x); + @autobind + private static convertQuery(diff: Record<string, number | unknown[]>) { + const query: Record<string, Function> = {}; - for (const [k, v] of Object.entries(columns)) { - if (v > 0) query[k] = () => `"${k}" + ${v}`; - if (v < 0) query[k] = () => `"${k}" - ${Math.abs(v)}`; + for (const [k, v] of Object.entries(diff)) { + if (typeof v === 'number') { + if (v > 0) query[k] = () => `"${k}" + ${v}`; + if (v < 0) query[k] = () => `"${k}" - ${Math.abs(v)}`; + } else if (Array.isArray(v)) { + // TODO: item ãŒæ–‡å—列以外ã®å ´åˆã‚‚対応 + // TODO: item ã‚’SQLエスケープ + const items = v.map(item => `"${item}"`).join(','); + query[k] = () => `array_cat("${k}", '{${items}}'::varchar[])`; + } } return query; @@ -169,28 +198,14 @@ export default abstract class Chart<T extends Record<string, any>> { length: 128, nullable: true }, - span: { - type: 'enum', - enum: ['hour', 'day'] - }, - unique: { - type: 'jsonb', - default: {} - }, ...Chart.convertSchemaToFlatColumnDefinitions(schema) }, indices: [{ columns: ['date'] - }, { - columns: ['span'] }, { columns: ['group'] - }, { - columns: ['span', 'date'] }, { columns: ['date', 'group'] - }, { - columns: ['span', 'date', 'group'] }] }); } @@ -200,7 +215,7 @@ export default abstract class Chart<T extends Record<string, any>> { this.schema = schema; const entity = Chart.schemaToEntity(name, schema); - const keys = ['span', 'date']; + const keys = ['date']; if (grouped) keys.push('group'); entity.options.uniques = [{ @@ -220,7 +235,8 @@ export default abstract class Chart<T extends Record<string, any>> { flatColumns(v.properties, p); } else { if (nestedProperty.get(log, p) == null) { - nestedProperty.set(log, p, 0); + const emptyValue = v.type === 'number' ? 0 : []; + nestedProperty.set(log, p, emptyValue); } } } @@ -230,10 +246,9 @@ export default abstract class Chart<T extends Record<string, any>> { } @autobind - private getLatestLog(span: Span, group: string | null = null): Promise<Log | null> { + private getLatestLog(group: string | null = null): Promise<Log | null> { return this.repository.findOne({ group: group, - span: span }, { order: { date: -1 @@ -242,17 +257,13 @@ export default abstract class Chart<T extends Record<string, any>> { } @autobind - private async getCurrentLog(span: Span, group: string | null = null): Promise<Log> { + private async getCurrentLog(group: string | null = null): Promise<Log> { const [y, m, d, h] = Chart.getCurrentDate(); - const current = - span == 'day' ? dateUTC([y, m, d, 0]) : - span == 'hour' ? dateUTC([y, m, d, h]) : - null as never; + const current = dateUTC([y, m, d, h]); - // ç¾åœ¨(今日ã¾ãŸã¯ä»Šã®Hour)ã®ãƒã‚° + // ç¾åœ¨(=今ã®Hour)ã®ãƒã‚° const currentLog = await this.repository.findOne({ - span: span, date: Chart.dateToTimestamp(current), ...(group ? { group: group } : {}) }); @@ -271,7 +282,7 @@ export default abstract class Chart<T extends Record<string, any>> { // * 昨日何もãƒãƒ£ãƒ¼ãƒˆã‚’æ›´æ–°ã™ã‚‹ã‚ˆã†ãªå‡ºæ¥äº‹ãŒãªã‹ã£ãŸå ´åˆã¯ã€ // * ãƒã‚°ãŒãã‚‚ãも作られãšãƒ‰ã‚ュメントãŒå˜åœ¨ã—ãªã„ã¨ã„ã†ã“ã¨ãŒã‚ã‚Šå¾—ã‚‹ãŸã‚〠// * 「昨日ã®ã€ã¨æ±ºã‚打ã¡ã›ãšã«ã€Œã‚‚ã£ã¨ã‚‚最近ã®ã€ã¨ã—ã¾ã™ - const latest = await this.getLatestLog(span, group); + const latest = await this.getLatestLog(group); if (latest != null) { const obj = Chart.convertFlattenColumnsToObject( @@ -286,17 +297,16 @@ export default abstract class Chart<T extends Record<string, any>> { // åˆæœŸãƒã‚°ãƒ‡ãƒ¼ã‚¿ã‚’ä½œæˆ data = this.getNewLog(null); - logger.info(`${this.name + (group ? `:${group}` : '')} (${span}): Initial commit created`); + logger.info(`${this.name + (group ? `:${group}` : '')}: Initial commit created`); } const date = Chart.dateToTimestamp(current); - const lockKey = `${this.name}:${date}:${group}:${span}`; + const lockKey = `${this.name}:${date}:${group}`; const unlock = await getChartInsertLock(lockKey); try { // ãƒãƒƒã‚¯å†…ã§ã‚‚ã†1回ãƒã‚§ãƒƒã‚¯ã™ã‚‹ const currentLog = await this.repository.findOne({ - span: span, date: date, ...(group ? { group: group } : {}) }); @@ -307,12 +317,11 @@ export default abstract class Chart<T extends Record<string, any>> { // æ–°è¦ãƒã‚°æŒ¿å…¥ log = await this.repository.save({ group: group, - span: span, date: date, ...Chart.convertObjectToFlattenColumns(data) }); - logger.info(`${this.name + (group ? `:${group}` : '')} (${span}): New commit created`); + logger.info(`${this.name + (group ? `:${group}` : '')}: New commit created`); return log; } finally { @@ -321,38 +330,62 @@ export default abstract class Chart<T extends Record<string, any>> { } @autobind - protected commit(query: Record<string, Function>, group: string | null = null, uniqueKey?: string, uniqueValue?: string): Promise<any> { + protected commit(diff: DeepPartial<T>, group: string | null = null): void { + this.queue.push({ + diff, group, + }); + } + + @autobind + public async save() { + if (this.queue.length === 0) { + logger.info(`${this.name}: Write skipped`); + return; + } + + // TODO: å‰ã®æ™‚é–“ã®ãƒã‚°ãŒqueueã«ã‚ã£ãŸå ´åˆã®ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚° + // 例ãˆã°ã€save ãŒ20分ã”ã¨ã«è¡Œã‚れるã¨ã—ã¦ã€å‰å›žè¡Œã‚ã‚ŒãŸã®ã¯ 01:50 ã ã£ãŸã¨ã™ã‚‹ã€‚ + // 次㫠save ãŒè¡Œã‚れるã®ã¯ 02:10 ã¨ã„ã†ã“ã¨ã«ãªã‚‹ãŒã€ã‚‚ã— 01:55 ã«æ–°è¦ãƒã‚°ãŒ queue ã«è¿½åŠ ã•ã‚ŒãŸã¨ã™ã‚‹ã¨ã€ + // ãã®ãƒã‚°ã¯æœ¬æ¥ã¯ 01:00~ ã®ãƒã‚°ã¨ã—ã¦DBã«ä¿å˜ã•ã‚Œã¦æ¬²ã—ã„ã®ã«ã€02:00~ ã®ãƒã‚°æ‰±ã„ã«ãªã£ã¦ã—ã¾ã†ã€‚ + // ã“れを回é¿ã™ã‚‹ãŸã‚ã®å®Ÿè£…ã¯è¤‡é›‘ã«ãªã‚Šãã†ãªãŸã‚ã€ä¸€æ—¦ä¿ç•™ã€‚ + const update = async (log: Log) => { - // ユニークインクリメントã®å ´åˆã€æŒ‡å®šã®ã‚ーã«æŒ‡å®šã®å€¤ãŒæ—¢ã«å˜åœ¨ã—ã¦ã„ãŸã‚‰å¼¾ã - if ( - uniqueKey && log.unique && - log.unique[uniqueKey] && - log.unique[uniqueKey].includes(uniqueValue) - ) return; - - // ユニークインクリメントã®æŒ‡å®šã®ã‚ーã«å€¤ã‚’è¿½åŠ - if (uniqueKey && log.unique) { - if (log.unique[uniqueKey]) { - const sql = `jsonb_set("unique", '{${uniqueKey}}', ("unique"->>'${uniqueKey}')::jsonb || '["${uniqueValue}"]'::jsonb)`; - query['unique'] = () => sql; - } else { - const sql = `jsonb_set("unique", '{${uniqueKey}}', '["${uniqueValue}"]')`; - query['unique'] = () => sql; + const finalDiffs = {} as Record<string, number | unknown[]>; + + for (const diff of this.queue.filter(q => q.group === log.group).map(q => q.diff)) { + const columns = Chart.convertObjectToFlattenColumns(diff); + + for (const [k, v] of Object.entries(columns)) { + if (finalDiffs[k] == null) { + finalDiffs[k] = v; + } else { + if (typeof finalDiffs[k] === 'number') { + (finalDiffs[k] as number) += v as number; + } else { + (finalDiffs[k] as unknown[]) = (finalDiffs[k] as unknown[]).concat(v); + } + } } } + const query = Chart.convertQuery(finalDiffs); + // ãƒã‚°æ›´æ–° await this.repository.createQueryBuilder() .update() .set(query) .where('id = :id', { id: log.id }) .execute(); + + logger.info(`${this.name + (log.group ? `:${log.group}` : '')}: Updated`); + + // TODO: ã“ã®ä¸€é€£ã®å‡¦ç†ãŒå§‹ã¾ã£ãŸå¾Œã«æ–°ãŸã«queueã«å…¥ã£ãŸã‚‚ã®ã¯æ¶ˆã•ãªã„よã†ã«ã™ã‚‹ + this.queue = this.queue.filter(q => q.group !== log.group); }; - return Promise.all([ - this.getCurrentLog('day', group).then(log => update(log)), - this.getCurrentLog('hour', group).then(log => update(log)), - ]); + const groups = removeDuplicates(this.queue.map(log => log.group)); + + await Promise.all(groups.map(group => this.getCurrentLog(group).then(log => update(log)))); } @autobind @@ -367,39 +400,30 @@ export default abstract class Chart<T extends Record<string, any>> { .execute(); }; - return Promise.all([ - this.getCurrentLog('day', group).then(log => update(log)), - this.getCurrentLog('hour', group).then(log => update(log)), - ]); + return this.getCurrentLog(group).then(log => update(log)); } @autobind protected async inc(inc: DeepPartial<T>, group: string | null = null): Promise<void> { - await this.commit(Chart.convertQuery(inc as any), group); + await this.commit(inc, group); } @autobind - protected async incIfUnique(inc: DeepPartial<T>, key: string, value: string, group: string | null = null): Promise<void> { - await this.commit(Chart.convertQuery(inc as any), group, key, value); - } - - @autobind - public async getChart(span: Span, amount: number, begin: Date | null, group: string | null = null): Promise<ArrayValue<T>> { - const [y, m, d, h, _m, _s, _ms] = begin ? Chart.parseDate(subtractTime(addTime(begin, 1, span), 1)) : Chart.getCurrentDate(); - const [y2, m2, d2, h2] = begin ? Chart.parseDate(addTime(begin, 1, span)) : [] as never; + public async getChart(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise<ArrayValue<T>> { + const [y, m, d, h, _m, _s, _ms] = cursor ? Chart.parseDate(subtractTime(addTime(cursor, 1, span), 1)) : Chart.getCurrentDate(); + const [y2, m2, d2, h2] = cursor ? Chart.parseDate(addTime(cursor, 1, span)) : [] as never; const lt = dateUTC([y, m, d, h, _m, _s, _ms]); const gt = - span === 'day' ? subtractTime(begin ? dateUTC([y2, m2, d2, 0]) : dateUTC([y, m, d, 0]), amount - 1, 'day') : - span === 'hour' ? subtractTime(begin ? dateUTC([y2, m2, d2, h2]) : dateUTC([y, m, d, h]), amount - 1, 'hour') : + span === 'day' ? subtractTime(cursor ? dateUTC([y2, m2, d2, 0]) : dateUTC([y, m, d, 0]), amount - 1, 'day') : + span === 'hour' ? subtractTime(cursor ? dateUTC([y2, m2, d2, h2]) : dateUTC([y, m, d, h]), amount - 1, 'hour') : null as never; // ãƒã‚°å–å¾— let logs = await this.repository.find({ where: { group: group, - span: span, date: Between(Chart.dateToTimestamp(gt), Chart.dateToTimestamp(lt)) }, order: { @@ -413,7 +437,6 @@ export default abstract class Chart<T extends Record<string, any>> { // (ã™ããªãã¨ã‚‚ã²ã¨ã¤ãƒã‚°ãŒç„¡ã„ã¨éš™é–“埋ã‚ã§ããªã„ãŸã‚) const recentLog = await this.repository.findOne({ group: group, - span: span }, { order: { date: -1 @@ -430,7 +453,6 @@ export default abstract class Chart<T extends Record<string, any>> { // (隙間埋ã‚ã§ããªã„ãŸã‚) const outdatedLog = await this.repository.findOne({ group: group, - span: span, date: LessThan(Chart.dateToTimestamp(gt)) }, { order: { @@ -445,23 +467,56 @@ export default abstract class Chart<T extends Record<string, any>> { const chart: T[] = []; - // æ•´å½¢ - for (let i = (amount - 1); i >= 0; i--) { - const current = - span === 'day' ? subtractTime(dateUTC([y, m, d, 0]), i, 'day') : - span === 'hour' ? subtractTime(dateUTC([y, m, d, h]), i, 'hour') : - null as never; - - const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current)); - - if (log) { - const data = Chart.convertFlattenColumnsToObject(log as Record<string, any>); - chart.unshift(data); - } else { - // 隙間埋゠- const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); - const data = latest ? Chart.convertFlattenColumnsToObject(latest as Record<string, any>) : null; - chart.unshift(this.getNewLog(data)); + if (span === 'hour') { + for (let i = (amount - 1); i >= 0; i--) { + const current = subtractTime(dateUTC([y, m, d, h]), i, 'hour'); + + const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current)); + + if (log) { + const data = Chart.convertFlattenColumnsToObject(log as Record<string, any>); + chart.unshift(Chart.countUniqueFields(data)); + } else { + // 隙間埋゠+ const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); + const data = latest ? Chart.convertFlattenColumnsToObject(latest as Record<string, any>) : null; + chart.unshift(Chart.countUniqueFields(this.getNewLog(data))); + } + } + } else if (span === 'day') { + const logsForEachDays: T[][] = []; + let currentDay = -1; + let currentDayIndex = -1; + for (let i = ((amount - 1) * 24) + h; i >= 0; i--) { + const current = subtractTime(dateUTC([y, m, d, h]), i, 'hour'); + const _currentDay = Chart.parseDate(current)[2]; + if (currentDay != _currentDay) currentDayIndex++; + currentDay = _currentDay; + + const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current)); + + if (log) { + if (logsForEachDays[currentDayIndex]) { + logsForEachDays[currentDayIndex].unshift(Chart.convertFlattenColumnsToObject(log)); + } else { + logsForEachDays[currentDayIndex] = [Chart.convertFlattenColumnsToObject(log)]; + } + } else { + // 隙間埋゠+ const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); + const data = latest ? Chart.convertFlattenColumnsToObject(latest as Record<string, any>) : null; + const newLog = this.getNewLog(data); + if (logsForEachDays[currentDayIndex]) { + logsForEachDays[currentDayIndex].unshift(newLog); + } else { + logsForEachDays[currentDayIndex] = [newLog]; + } + } + } + + for (const logs of logsForEachDays) { + const log = this.aggregate(logs); + chart.unshift(Chart.countUniqueFields(log)); } } @@ -473,20 +528,19 @@ export default abstract class Chart<T extends Record<string, any>> { * { foo: [1, 2, 3], bar: [5, 6, 7] } * ã«ã™ã‚‹ */ - const dive = (x: Obj, path?: string) => { + const compact = (x: Obj, path?: string) => { for (const [k, v] of Object.entries(x)) { const p = path ? `${path}.${k}` : k; - if (typeof v == 'object') { - dive(v, p); + if (typeof v === 'object' && !Array.isArray(v)) { + compact(v, p); } else { - const values = chart.map(s => nestedProperty.get(s, p)) - .map(v => parseInt(v, 10)); // TypeORMã®ãƒã‚°(?)ã§ä½•æ•…ã‹æ•°å€¤ã‚«ãƒ©ãƒ ã®å€¤ãŒæ–‡å—列型ã«ãªã£ã¦ã„ã‚‹ã®ã§æ•°å€¤ã«æˆ»ã™ + const values = chart.map(s => nestedProperty.get(s, p)); nestedProperty.set(res, p, values); } } }; - dive(chart[0]); + compact(chart[0]); return res; } diff --git a/src/services/chart/index.ts b/src/services/chart/index.ts index 9626e3d6b3a308e904eac86a386b94c0ada0ba54..dde02bd64d3ca01dba0698a73ddfb5ce1a5757e6 100644 --- a/src/services/chart/index.ts +++ b/src/services/chart/index.ts @@ -10,6 +10,7 @@ import PerUserReactionsChart from './charts/classes/per-user-reactions'; import HashtagChart from './charts/classes/hashtag'; import PerUserFollowingChart from './charts/classes/per-user-following'; import PerUserDriveChart from './charts/classes/per-user-drive'; +import { beforeShutdown } from '../../misc/before-shutdown'; export const federationChart = new FederationChart(); export const notesChart = new NotesChart(); @@ -23,3 +24,27 @@ export const perUserReactionsChart = new PerUserReactionsChart(); export const hashtagChart = new HashtagChart(); export const perUserFollowingChart = new PerUserFollowingChart(); export const perUserDriveChart = new PerUserDriveChart(); + +const charts = [ + federationChart, + notesChart, + usersChart, + networkChart, + activeUsersChart, + instanceChart, + perUserNotesChart, + driveChart, + perUserReactionsChart, + hashtagChart, + perUserFollowingChart, + perUserDriveChart, +]; + +// 20分ãŠãã«ãƒ¡ãƒ¢ãƒªæƒ…å ±ã‚’DBã«æ›¸ã込㿠+setInterval(() => { + for (const chart of charts) { + chart.save(); + } +}, 1000 * 60 * 20); + +beforeShutdown(() => Promise.all(charts.map(chart => chart.save()))); diff --git a/src/services/create-notification.ts b/src/services/create-notification.ts index 6cd116040a2447e01531e5e736c718eb3d8c7cdd..dedb8eac8d005f440ede61b37d7536126149481d 100644 --- a/src/services/create-notification.ts +++ b/src/services/create-notification.ts @@ -30,7 +30,7 @@ export async function createNotification( ...data } as Partial<Notification>); - const packed = await Notifications.pack(notification); + const packed = await Notifications.pack(notification, {}); // Publish notification event publishMainStream(notifieeId, 'notification', packed); diff --git a/src/services/following/create.ts b/src/services/following/create.ts index c0583cdb867fb75e7cc2d06bbc1a466b98988f8c..1ce75caca070edbf236be35f1366977eb012f807 100644 --- a/src/services/following/create.ts +++ b/src/services/following/create.ts @@ -1,4 +1,4 @@ -import { publishMainStream } from '../stream'; +import { publishMainStream, publishUserEvent } from '../stream'; import { renderActivity } from '../../remote/activitypub/renderer'; import renderFollow from '../../remote/activitypub/renderer/follow'; import renderAccept from '../../remote/activitypub/renderer/accept'; @@ -22,7 +22,7 @@ export async function insertFollowingDoc(followee: User, follower: User) { let alreadyFollowed = false; - await Followings.save({ + await Followings.insert({ id: genId(), createdAt: new Date(), followerId: follower.id, @@ -88,12 +88,15 @@ export async function insertFollowingDoc(followee: User, follower: User) { if (Users.isLocalUser(follower)) { Users.pack(followee, follower, { detail: true - }).then(packed => publishMainStream(follower.id, 'follow', packed)); + }).then(packed => { + publishUserEvent(follower.id, 'follow', packed); + publishMainStream(follower.id, 'follow', packed); + }); } // Publish followed event if (Users.isLocalUser(followee)) { - Users.pack(follower, followee).then(packed => publishMainStream(followee.id, 'followed', packed)), + Users.pack(follower, followee).then(packed => publishMainStream(followee.id, 'followed', packed)); // é€šçŸ¥ã‚’ä½œæˆ createNotification(followee.id, 'follow', { diff --git a/src/services/following/delete.ts b/src/services/following/delete.ts index 882161151594b79a08a9912921f397c30e473630..32c47ea7f40ca72a7cc9d95947df53e96971c43f 100644 --- a/src/services/following/delete.ts +++ b/src/services/following/delete.ts @@ -1,4 +1,4 @@ -import { publishMainStream } from '../stream'; +import { publishMainStream, publishUserEvent } from '../stream'; import { renderActivity } from '../../remote/activitypub/renderer'; import renderFollow from '../../remote/activitypub/renderer/follow'; import renderUndo from '../../remote/activitypub/renderer/undo'; @@ -30,7 +30,10 @@ export default async function(follower: User, followee: User, silent = false) { if (!silent && Users.isLocalUser(follower)) { Users.pack(followee, follower, { detail: true - }).then(packed => publishMainStream(follower.id, 'unfollow', packed)); + }).then(packed => { + publishUserEvent(follower.id, 'unfollow', packed); + publishMainStream(follower.id, 'unfollow', packed); + }); } if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) { diff --git a/src/services/following/requests/reject.ts b/src/services/following/requests/reject.ts index 9a8b14bbfd1f156ad7780bb4971b07cc6ed05add..d8d3788088c1f4005a6725cc4bd35d9f5a0a6805 100644 --- a/src/services/following/requests/reject.ts +++ b/src/services/following/requests/reject.ts @@ -2,7 +2,7 @@ import { renderActivity } from '../../../remote/activitypub/renderer'; import renderFollow from '../../../remote/activitypub/renderer/follow'; import renderReject from '../../../remote/activitypub/renderer/reject'; import { deliver } from '../../../queue'; -import { publishMainStream } from '../../stream'; +import { publishMainStream, publishUserEvent } from '../../stream'; import { User, ILocalUser } from '../../../models/entities/user'; import { Users, FollowRequests, Followings } from '../../../models'; import { decrementFollowing } from '../delete'; @@ -39,5 +39,8 @@ export default async function(followee: User, follower: User) { Users.pack(followee, follower, { detail: true - }).then(packed => publishMainStream(follower.id, 'unfollow', packed)); + }).then(packed => { + publishUserEvent(follower.id, 'unfollow', packed); + publishMainStream(follower.id, 'unfollow', packed); + }); } diff --git a/src/services/i/pin.ts b/src/services/i/pin.ts index 1ff5476b40ad7cc71a6c1eab3e4d39c7182bcd71..f727a10fb65605c832b3ddf5366adda3daa2cf38 100644 --- a/src/services/i/pin.ts +++ b/src/services/i/pin.ts @@ -37,7 +37,7 @@ export async function addPinned(user: User, noteId: Note['id']) { throw new IdentifiableError('23f0cf4e-59a3-4276-a91d-61a5891c1514', 'That note has already been pinned.'); } - await UserNotePinings.save({ + await UserNotePinings.insert({ id: genId(), createdAt: new Date(), userId: user.id, diff --git a/src/services/insert-moderation-log.ts b/src/services/insert-moderation-log.ts index 33dab97259f42e658596b9867b55479f3955eca0..87587d3bed6fefea900150df4b00e70b1d260ecf 100644 --- a/src/services/insert-moderation-log.ts +++ b/src/services/insert-moderation-log.ts @@ -3,7 +3,7 @@ import { ModerationLogs } from '../models'; import { genId } from '../misc/gen-id'; export async function insertModerationLog(moderator: ILocalUser, type: string, info?: Record<string, any>) { - await ModerationLogs.save({ + await ModerationLogs.insert({ id: genId(), createdAt: new Date(), userId: moderator.id, diff --git a/src/services/messages/create.ts b/src/services/messages/create.ts index 8646ce37fc17b51c7461548bca111196ecac02d9..413266d0290dc75f8484645e28a7bfb42e4e0e44 100644 --- a/src/services/messages/create.ts +++ b/src/services/messages/create.ts @@ -14,7 +14,7 @@ import { renderActivity } from '../../remote/activitypub/renderer'; import { deliver } from '../../queue'; export async function createMessage(user: User, recipientUser: User | undefined, recipientGroup: UserGroup | undefined, text: string | undefined, file: DriveFile | null, uri?: string) { - const message = await MessagingMessages.save({ + const message = { id: genId(), createdAt: new Date(), fileId: file ? file.id : null, @@ -25,7 +25,9 @@ export async function createMessage(user: User, recipientUser: User | undefined, isRead: false, reads: [] as any[], uri - } as MessagingMessage); + } as MessagingMessage; + + await MessagingMessages.insert(message); const messageObj = await MessagingMessages.pack(message); diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 563eaac7584c87f1588be50f84ad4a8e00fd73eb..4a737e8516421f964afc049042b5f69c9a16417b 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -247,7 +247,7 @@ export default async (user: User, data: Option, silent = false) => new Promise<N for (const u of us) { checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => { if (shouldMute) { - MutedNotes.save({ + MutedNotes.insert({ id: genId(), userId: u.userId, noteId: note.id, @@ -259,21 +259,21 @@ export default async (user: User, data: Option, silent = false) => new Promise<N }); // Antenna - Antennas.find().then(async antennas => { - const followings = await Followings.createQueryBuilder('following') - .andWhere(`following.followeeId = :userId`, { userId: note.userId }) - .getMany(); - - const followers = followings.map(f => f.followerId); - - for (const antenna of antennas) { - checkHitAntenna(antenna, note, user, followers).then(hit => { - if (hit) { - addNoteToAntenna(antenna, note, user); + Followings.createQueryBuilder('following') + .andWhere(`following.followeeId = :userId`, { userId: note.userId }) + .getMany() + .then(followings => { + const followers = followings.map(f => f.followerId); + Antennas.find().then(async antennas => { + for (const antenna of antennas) { + checkHitAntenna(antenna, note, user, followers).then(hit => { + if (hit) { + addNoteToAntenna(antenna, note, user); + } + }); } }); - } - }); + }); // Channel if (note.channelId) { @@ -444,8 +444,13 @@ async function renderNoteOrRenoteActivity(data: Option, note: Note) { } function incRenoteCount(renote: Note) { - Notes.increment({ id: renote.id }, 'renoteCount', 1); - Notes.increment({ id: renote.id }, 'score', 1); + Notes.createQueryBuilder().update() + .set({ + renoteCount: () => '"renoteCount" + 1', + score: () => '"score" + 1' + }) + .where('id = :id', { id: renote.id }) + .execute(); } async function insertNote(user: User, data: Option, tags: string[], emojis: string[], mentionedUsers: User[]) { @@ -525,7 +530,7 @@ async function insertNote(user: User, data: Option, tags: string[], emojis: stri await Notes.insert(insert); } - return await Notes.findOneOrFail(insert.id); + return insert; } catch (e) { // duplicate key error if (isDuplicateKeyValueError(e)) { @@ -594,10 +599,13 @@ function saveReply(reply: Note, note: Note) { } function incNotesCountOfUser(user: User) { - Users.increment({ id: user.id }, 'notesCount', 1); - Users.update({ id: user.id }, { - updatedAt: new Date() - }); + Users.createQueryBuilder().update() + .set({ + updatedAt: new Date(), + notesCount: () => '"notesCount" + 1' + }) + .where('id = :id', { id: user.id }) + .execute(); } async function extractMentionedUsers(user: User, tokens: ReturnType<typeof parse>): Promise<User[]> { diff --git a/src/services/note/polls/vote.ts b/src/services/note/polls/vote.ts index bfcaaa09beb7793e9277dd76e1d8acaaf6f24c38..b4ce03ab6067a54005894bb1ee90a6610a72a103 100644 --- a/src/services/note/polls/vote.ts +++ b/src/services/note/polls/vote.ts @@ -29,7 +29,7 @@ export default async function(user: User, note: Note, choice: number) { } // Create vote - await PollVotes.save({ + await PollVotes.insert({ id: genId(), createdAt: new Date(), noteId: note.id, diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts index adc96ddc1fb7946f9c04cb81be3c861e30357218..181099cc2dbf7ff2282087cebc77b7d0c0a7ade4 100644 --- a/src/services/note/reaction/create.ts +++ b/src/services/note/reaction/create.ts @@ -11,45 +11,53 @@ import { perUserReactionsChart } from '../../chart'; import { genId } from '../../../misc/gen-id'; import { createNotification } from '../../create-notification'; import deleteReaction from './delete'; +import { isDuplicateKeyValueError } from '../../../misc/is-duplicate-key-value-error'; +import { NoteReaction } from '../../../models/entities/note-reaction'; export default async (user: User, note: Note, reaction?: string) => { + // TODO: cache reaction = await toDbReaction(reaction, user.host); - const exist = await NoteReactions.findOne({ + let record: NoteReaction = { + id: genId(), + createdAt: new Date(), noteId: note.id, userId: user.id, - }); + reaction + }; - if (exist) { - if (exist.reaction !== reaction) { - // 別ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãŒã™ã§ã«ã•ã‚Œã¦ã„ãŸã‚‰ç½®ãæ›ãˆã‚‹ - await deleteReaction(user, note); + // Create reaction + try { + await NoteReactions.insert(record); + } catch (e) { + if (isDuplicateKeyValueError(e)) { + record = await NoteReactions.findOneOrFail({ + noteId: note.id, + userId: user.id, + }); + + if (record.reaction !== reaction) { + // 別ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãŒã™ã§ã«ã•ã‚Œã¦ã„ãŸã‚‰ç½®ãæ›ãˆã‚‹ + await deleteReaction(user, note); + } else { + // åŒã˜ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãŒã™ã§ã«ã•ã‚Œã¦ã„ãŸã‚‰ä½•ã‚‚ã—ãªã„ + return; + } } else { - // åŒã˜ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãŒã™ã§ã«ã•ã‚Œã¦ã„ãŸã‚‰ä½•ã‚‚ã—ãªã„ - return; + throw e; } } - // Create reaction - const inserted = await NoteReactions.save({ - id: genId(), - createdAt: new Date(), - noteId: note.id, - userId: user.id, - reaction - }); - // Increment reactions count const sql = `jsonb_set("reactions", '{${reaction}}', (COALESCE("reactions"->>'${reaction}', '0')::int + 1)::text::jsonb)`; await Notes.createQueryBuilder().update() .set({ reactions: () => sql, + score: () => '"score" + 1' }) .where('id = :id', { id: note.id }) .execute(); - Notes.increment({ id: note.id }, 'score', 1); - perUserReactionsChart.update(user, note); // カスタム絵文å—リアクションã ã£ãŸã‚‰çµµæ–‡å—æƒ…å ±ã‚‚é€ã‚‹ @@ -101,7 +109,7 @@ export default async (user: User, note: Note, reaction?: string) => { //#region é…ä¿¡ if (Users.isLocalUser(user) && !note.localOnly) { - const content = renderActivity(await renderLike(inserted, note)); + const content = renderActivity(await renderLike(record, note)); const dm = new DeliverManager(user, content); if (note.userHost !== null) { const reactee = await Users.findOne(note.userId); diff --git a/src/services/note/read.ts b/src/services/note/read.ts index 5a39ab30b7414e2cf2bf074810ee92c88678a053..35279db41122e4136ef4d4ad3ab80909738d1157 100644 --- a/src/services/note/read.ts +++ b/src/services/note/read.ts @@ -2,70 +2,54 @@ import { publishMainStream } from '../stream'; import { Note } from '../../models/entities/note'; import { User } from '../../models/entities/user'; import { NoteUnreads, Antennas, AntennaNotes, Users } from '../../models'; -import { Not, IsNull } from 'typeorm'; +import { Not, IsNull, In } from 'typeorm'; /** - * Mark a note as read + * Mark notes as read */ export default async function( userId: User['id'], - noteId: Note['id'] + noteIds: Note['id'][] ) { async function careNoteUnreads() { - const exist = await NoteUnreads.findOne({ - userId: userId, - noteId: noteId, - }); - - if (!exist) return; - // Remove the record await NoteUnreads.delete({ userId: userId, - noteId: noteId, + noteId: In(noteIds), }); - if (exist.isMentioned) { - NoteUnreads.count({ - userId: userId, - isMentioned: true - }).then(mentionsCount => { - if (mentionsCount === 0) { - // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 - publishMainStream(userId, 'readAllUnreadMentions'); - } - }); - } + NoteUnreads.count({ + userId: userId, + isMentioned: true + }).then(mentionsCount => { + if (mentionsCount === 0) { + // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 + publishMainStream(userId, 'readAllUnreadMentions'); + } + }); - if (exist.isSpecified) { - NoteUnreads.count({ - userId: userId, - isSpecified: true - }).then(specifiedCount => { - if (specifiedCount === 0) { - // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 - publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); - } - }); - } + NoteUnreads.count({ + userId: userId, + isSpecified: true + }).then(specifiedCount => { + if (specifiedCount === 0) { + // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 + publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); + } + }); - if (exist.noteChannelId) { - NoteUnreads.count({ - userId: userId, - noteChannelId: Not(IsNull()) - }).then(channelNoteCount => { - if (channelNoteCount === 0) { - // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 - publishMainStream(userId, 'readAllChannels'); - } - }); - } + NoteUnreads.count({ + userId: userId, + noteChannelId: Not(IsNull()) + }).then(channelNoteCount => { + if (channelNoteCount === 0) { + // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 + publishMainStream(userId, 'readAllChannels'); + } + }); } async function careAntenna() { - const beforeUnread = await Users.getHasUnreadAntenna(userId); - if (!beforeUnread) return; - const antennas = await Antennas.find({ userId }); await Promise.all(antennas.map(async antenna => { @@ -78,7 +62,7 @@ export default async function( await AntennaNotes.update({ antennaId: antenna.id, - noteId: noteId + noteId: In(noteIds) }, { read: true }); diff --git a/src/services/note/unread.ts b/src/services/note/unread.ts index 6fd9ee2cfe82cfcef1a02a1caa81a43aa5f0d17c..8e6fb4abe8e0c91101f3976b26a059e94a278070 100644 --- a/src/services/note/unread.ts +++ b/src/services/note/unread.ts @@ -17,7 +17,7 @@ export default async function(userId: User['id'], note: Note, params: { if (mute.map(m => m.muteeId).includes(note.userId)) return; //#endregion - const unread = await NoteUnreads.save({ + const unread = { id: genId(), noteId: note.id, userId: userId, @@ -25,7 +25,9 @@ export default async function(userId: User['id'], note: Note, params: { isMentioned: params.isMentioned, noteChannelId: note.channelId, noteUserId: note.userId, - }); + }; + + await NoteUnreads.insert(unread); // 2秒経ã£ã¦ã‚‚æ—¢èªã«ãªã‚‰ãªã‹ã£ãŸã‚‰ã€Œæœªèªã®æŠ•ç¨¿ãŒã‚ã‚Šã¾ã™ã‚ˆã€ã‚¤ãƒ™ãƒ³ãƒˆã‚’発行ã™ã‚‹ setTimeout(async () => { diff --git a/src/services/note/watch.ts b/src/services/note/watch.ts index d3c955369646a4d0ead7049987536761676545a2..966b7f0054bbcbc1406d9db73f4311b89ea2c1db 100644 --- a/src/services/note/watch.ts +++ b/src/services/note/watch.ts @@ -10,7 +10,7 @@ export default async (me: User['id'], note: Note) => { return; } - await NoteWatchings.save({ + await NoteWatchings.insert({ id: genId(), createdAt: new Date(), noteId: note.id, diff --git a/src/services/register-or-fetch-instance-doc.ts b/src/services/register-or-fetch-instance-doc.ts index 3501e20de12ad7396931e4de6d781c35c88b7fcc..2c39502288fd6077f0491a7b57d0251e21ff930e 100644 --- a/src/services/register-or-fetch-instance-doc.ts +++ b/src/services/register-or-fetch-instance-doc.ts @@ -3,10 +3,16 @@ import { Instances } from '../models'; import { federationChart } from './chart'; import { genId } from '../misc/gen-id'; import { toPuny } from '../misc/convert-host'; +import { Cache } from '../misc/cache'; + +const cache = new Cache<Instance>(1000 * 60 * 60); export async function registerOrFetchInstanceDoc(host: string): Promise<Instance> { host = toPuny(host); + const cached = cache.get(host); + if (cached) return cached; + const index = await Instances.findOne({ host }); if (index == null) { @@ -19,8 +25,10 @@ export async function registerOrFetchInstanceDoc(host: string): Promise<Instance federationChart.update(true); + cache.set(host, i); return i; } else { + cache.set(host, index); return index; } } diff --git a/src/services/stream.ts b/src/services/stream.ts index d833d700fec9f4c8500568e4b6703f71a9bfb002..75385847cea89c60de64682db0142e19049bb613 100644 --- a/src/services/stream.ts +++ b/src/services/stream.ts @@ -20,6 +20,10 @@ class Publisher { })); } + public publishUserEvent = (userId: User['id'], type: string, value?: any): void => { + this.publish(`user:${userId}`, type, typeof value === 'undefined' ? null : value); + } + public publishBroadcastStream = (type: string, value?: any): void => { this.publish('broadcast', type, typeof value === 'undefined' ? null : value); } @@ -84,6 +88,7 @@ const publisher = new Publisher(); export default publisher; +export const publishUserEvent = publisher.publishUserEvent; export const publishBroadcastStream = publisher.publishBroadcastStream; export const publishMainStream = publisher.publishMainStream; export const publishDriveStream = publisher.publishDriveStream; diff --git a/src/services/update-hashtag.ts b/src/services/update-hashtag.ts index 1dcb5827918b839e8bd1fa4748fb81a0f51ace91..3e228467315d098c9db04210717c920bb1702c8e 100644 --- a/src/services/update-hashtag.ts +++ b/src/services/update-hashtag.ts @@ -86,7 +86,7 @@ export async function updateHashtag(user: User, tag: string, isUserAttached = fa } } else { if (isUserAttached) { - Hashtags.save({ + Hashtags.insert({ id: genId(), name: tag, mentionedUserIds: [], @@ -103,7 +103,7 @@ export async function updateHashtag(user: User, tag: string, isUserAttached = fa attachedRemoteUsersCount: Users.isRemoteUser(user) ? 1 : 0, } as Hashtag); } else { - Hashtags.save({ + Hashtags.insert({ id: genId(), name: tag, mentionedUserIds: [user.id], diff --git a/src/services/user-list/push.ts b/src/services/user-list/push.ts index e67be4b02734ccd16b3230a7a67b24a68982d8a8..ba54c044752381e10ec7fb6e8b261f07ae1f57fb 100644 --- a/src/services/user-list/push.ts +++ b/src/services/user-list/push.ts @@ -8,7 +8,7 @@ import { fetchProxyAccount } from '../../misc/fetch-proxy-account'; import createFollowing from '../following/create'; export async function pushUserToUserList(target: User, list: UserList) { - await UserListJoinings.save({ + await UserListJoinings.insert({ id: genId(), createdAt: new Date(), userId: target.id, diff --git a/tsconfig.json b/src/tsconfig.json similarity index 89% rename from tsconfig.json rename to src/tsconfig.json index 075450bf64e1cde1e309dc4c99787ff2b24afee3..95cb35fc5fbf7c8ed6a77ed17d52d0a71fe9e0e8 100644 --- a/tsconfig.json +++ b/src/tsconfig.json @@ -22,8 +22,8 @@ "resolveJsonModule": true, "isolatedModules": true, "typeRoots": [ - "node_modules/@types", - "src/@types" + "../node_modules/@types", + "./@types" ], "lib": [ "esnext" @@ -31,9 +31,9 @@ }, "compileOnSave": false, "include": [ - "./src/**/*.ts" + "./**/*.ts" ], "exclude": [ - "./src/client/**/*.ts" + "./client/**/*.ts" ] } diff --git a/test/.eslintrc b/test/.eslintrc new file mode 100644 index 0000000000000000000000000000000000000000..cea1b11388cfd499ccd692e84cf5aee0397848d9 --- /dev/null +++ b/test/.eslintrc @@ -0,0 +1,7 @@ +{ + "env": { + "node": true, + "mocha": true, + "commonjs": true + } +} diff --git a/test/chart.ts b/test/chart.ts index 25b083db174740a622dbf79401dd2fd75f5bd208..55f6bd696c92aec99dbafc3973cc7ced533bb5b3 100644 --- a/test/chart.ts +++ b/test/chart.ts @@ -72,7 +72,7 @@ describe('Chart', () => { testUniqueChart = new TestUniqueChart(); clock = lolex.install({ - now: new Date('2000-01-01 00:00:00') + now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)) }); done(); }); @@ -85,6 +85,7 @@ describe('Chart', () => { it('Can updates', async(async () => { await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -105,9 +106,10 @@ describe('Chart', () => { }, }); })); - + it('Can updates (dec)', async(async () => { await testChart.decrement(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -154,6 +156,7 @@ describe('Chart', () => { await testChart.increment(); await testChart.increment(); await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -177,10 +180,12 @@ describe('Chart', () => { it('Can updates at different times', async(async () => { await testChart.increment(); + await testChart.save(); clock.tick('01:00:00'); await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -202,12 +207,45 @@ describe('Chart', () => { }); })); + // 仕様上ã¯ã“ã†ãªã£ã¦ã»ã—ã„ã‘ã©ã€å®Ÿè£…ã¯é›£ã—ãã†ãªã®ã§skip + /* + it('Can updates at different times without save', async(async () => { + await testChart.increment(); + + clock.tick('01:00:00'); + + await testChart.increment(); + await testChart.save(); + + const chartHours = await testChart.getChart('hour', 3, null); + const chartDays = await testChart.getChart('day', 3, null); + + assert.deepStrictEqual(chartHours, { + foo: { + dec: [0, 0, 0], + inc: [1, 1, 0], + total: [2, 1, 0] + }, + }); + + assert.deepStrictEqual(chartDays, { + foo: { + dec: [0, 0, 0], + inc: [2, 0, 0], + total: [2, 0, 0] + }, + }); + })); + */ + it('Can padding', async(async () => { await testChart.increment(); + await testChart.save(); clock.tick('02:00:00'); await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -232,6 +270,7 @@ describe('Chart', () => { // è¦æ±‚ã•ã‚ŒãŸç¯„囲ã«ãƒã‚°ãŒã²ã¨ã¤ã‚‚ãªã„å ´åˆã§ã‚‚パディングã§ãã‚‹ it('Can padding from past range', async(async () => { await testChart.increment(); + await testChart.save(); clock.tick('05:00:00'); @@ -259,8 +298,12 @@ describe('Chart', () => { // Issue #3190 it('Can padding from past range 2', async(async () => { await testChart.increment(); + await testChart.save(); + clock.tick('05:00:00'); + await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -284,10 +327,12 @@ describe('Chart', () => { it('Can specify offset', async(async () => { await testChart.increment(); + await testChart.save(); clock.tick('01:00:00'); await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, new Date(Date.UTC(2000, 0, 1, 0, 0, 0))); const chartDays = await testChart.getChart('day', 3, new Date(Date.UTC(2000, 0, 1, 0, 0, 0))); @@ -313,10 +358,12 @@ describe('Chart', () => { clock.tick('00:30:00'); await testChart.increment(); + await testChart.save(); clock.tick('01:30:00'); await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, new Date(Date.UTC(2000, 0, 1, 0, 0, 0))); const chartDays = await testChart.getChart('day', 3, new Date(Date.UTC(2000, 0, 1, 0, 0, 0))); @@ -341,6 +388,7 @@ describe('Chart', () => { describe('Grouped', () => { it('Can updates', async(async () => { await testGroupedChart.increment('alice'); + await testGroupedChart.save(); const aliceChartHours = await testGroupedChart.getChart('hour', 3, null, 'alice'); const aliceChartDays = await testGroupedChart.getChart('day', 3, null, 'alice'); @@ -386,6 +434,7 @@ describe('Chart', () => { await testUniqueChart.uniqueIncrement('alice'); await testUniqueChart.uniqueIncrement('alice'); await testUniqueChart.uniqueIncrement('bob'); + await testUniqueChart.save(); const chartHours = await testUniqueChart.getChart('hour', 3, null); const chartDays = await testUniqueChart.getChart('day', 3, null); @@ -428,6 +477,7 @@ describe('Chart', () => { it('Can resync (2)', async(async () => { await testChart.increment(); + await testChart.save(); clock.tick('01:00:00'); diff --git a/test/fetch-resource.ts b/test/fetch-resource.ts new file mode 100644 index 0000000000000000000000000000000000000000..12a37b6cb0025ea95a2907225e96900bef2730a8 --- /dev/null +++ b/test/fetch-resource.ts @@ -0,0 +1,187 @@ +/* + * Tests for Fetch resource + * + * How to run the tests: + * > npx cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true npx mocha test/fetch-resource.ts --require ts-node/register + * + * To specify test: + * > npx cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true npx mocha test/fetch-resource.ts --require ts-node/register -g 'test name' + */ + +process.env.NODE_ENV = 'test'; + +import * as assert from 'assert'; +import * as childProcess from 'child_process'; +import { async, launchServer, signup, post, request, simpleGet } from './utils'; + +// Request Accept +const ONLY_AP = 'application/activity+json'; +const PREFER_AP = 'application/activity+json, */*'; +const PREFER_HTML = 'text/html, */*'; +const UNSPECIFIED = '*/*'; + +// Response Contet-Type +const AP = 'application/activity+json; charset=utf-8'; +const JSON = 'application/json; charset=utf-8'; +const HTML = 'text/html; charset=utf-8'; + +describe('Fetch resource', () => { + let p: childProcess.ChildProcess; + + let alice: any; + let alicesPost: any; + + before(launchServer(g => p = g, async () => { + alice = await signup({ username: 'alice' }); + alicesPost = await post(alice, { + text: 'test' + }); + })); + + after(() => { + p.kill(); + }); + + describe('Common', () => { + it('meta', async(async () => { + const res = await request('/meta', { + }); + + assert.strictEqual(res.status, 200); + })); + + it('GET root', async(async () => { + const res = await simpleGet('/'); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); + })); + + it('GET docs', async(async () => { + const res = await simpleGet('/docs/ja-JP/about'); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); + })); + + it('GET api-doc', async(async () => { + const res = await simpleGet('/api-doc'); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); + })); + + it('GET api.json', async(async () => { + const res = await simpleGet('/api.json'); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, JSON); + })); + + it('GET favicon.ico', async(async () => { + const res = await simpleGet('/favicon.ico'); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, 'image/x-icon'); + })); + + it('GET apple-touch-icon.png', async(async () => { + const res = await simpleGet('/apple-touch-icon.png'); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, 'image/png'); + })); + }); + + describe('/@:username', () => { + it('Only AP => AP', async(async () => { + const res = await simpleGet(`/@${alice.username}`, ONLY_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer AP => AP', async(async () => { + const res = await simpleGet(`/@${alice.username}`, PREFER_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer HTML => HTML', async(async () => { + const res = await simpleGet(`/@${alice.username}`, PREFER_HTML); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); + })); + + it('Unspecified => HTML', async(async () => { + const res = await simpleGet(`/@${alice.username}`, UNSPECIFIED); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); + })); + }); + + describe('/users/:id', () => { + it('Only AP => AP', async(async () => { + const res = await simpleGet(`/users/${alice.id}`, ONLY_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer AP => AP', async(async () => { + const res = await simpleGet(`/users/${alice.id}`, PREFER_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer HTML => Redirect to /@:username', async(async () => { + const res = await simpleGet(`/users/${alice.id}`, PREFER_HTML); + assert.strictEqual(res.status, 302); + assert.strictEqual(res.location, `/@${alice.username}`); + })); + + it('Undecided => HTML', async(async () => { + const res = await simpleGet(`/users/${alice.id}`, UNSPECIFIED); + assert.strictEqual(res.status, 302); + assert.strictEqual(res.location, `/@${alice.username}`); + })); + }); + + describe('/notes/:id', () => { + it('Only AP => AP', async(async () => { + const res = await simpleGet(`/notes/${alicesPost.id}`, ONLY_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer AP => AP', async(async () => { + const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer HTML => HTML', async(async () => { + const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_HTML); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); + })); + + it('Unspecified => HTML', async(async () => { + const res = await simpleGet(`/notes/${alicesPost.id}`, UNSPECIFIED); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); + })); + }); + + describe('Feeds', () => { + it('RSS', async(async () => { + const res = await simpleGet(`/@${alice.username}.rss`, UNSPECIFIED); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, 'application/rss+xml; charset=utf-8'); + })); + + it('ATOM', async(async () => { + const res = await simpleGet(`/@${alice.username}.atom`, UNSPECIFIED); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, 'application/atom+xml; charset=utf-8'); + })); + + it('JSON', async(async () => { + const res = await simpleGet(`/@${alice.username}.json`, UNSPECIFIED); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, 'application/json; charset=utf-8'); + })); + }); +}); diff --git a/test/get-file-info.ts b/test/get-file-info.ts index 312b225aa26d39008fdfb135c84d08173338dddb..8757d5a901613d0744743874e8d8ae592c22005b 100644 --- a/test/get-file-info.ts +++ b/test/get-file-info.ts @@ -2,10 +2,10 @@ * Tests for detection of file information * * How to run the tests: - * > TS_NODE_FILES=true npx mocha test/get-file-info.ts --require ts-node/register + * > npx cross-env TS_NODE_FILES=true npx mocha test/get-file-info.ts --require ts-node/register * * To specify test: - * > TS_NODE_FILES=true npx mocha test/get-file-info.ts --require ts-node/register -g 'test name' + * > npx cross-env TS_NODE_FILES=true npx mocha test/get-file-info.ts --require ts-node/register -g 'test name' */ import * as assert from 'assert'; diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..987067ba81919379b5b75d13a2ff2749578ed91c --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "allowJs": true, + "noEmitOnError": false, + "noImplicitAny": true, + "noImplicitReturns": true, + "noUnusedParameters": false, + "noUnusedLocals": true, + "noFallthroughCasesInSwitch": true, + "declaration": false, + "sourceMap": true, + "target": "es2017", + "module": "commonjs", + "moduleResolution": "node", + "removeComments": false, + "noLib": false, + "strict": true, + "strictNullChecks": true, + "strictPropertyInitialization": false, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "resolveJsonModule": true, + "isolatedModules": true, + "typeRoots": [ + "../node_modules/@types", + "../src/@types" + ], + "lib": [ + "esnext" + ] + }, + "compileOnSave": false, + "include": [ + "./**/*.ts" + ] +} diff --git a/test/utils.ts b/test/utils.ts index 066bd33a56469389601eea0b7bd6002222d4b3ec..b0393ee192dc42ed43c391ae1972edc4e561a434 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -3,6 +3,10 @@ import * as WebSocket from 'ws'; import fetch from 'node-fetch'; const FormData = require('form-data'); import * as childProcess from 'child_process'; +import * as http from 'http'; +import loadConfig from '../src/config/load'; + +const port = loadConfig().port; export const async = (fn: Function) => (done: Function) => { fn().then(() => { @@ -17,26 +21,20 @@ export const request = async (endpoint: string, params: any, me?: any): Promise< i: me.token } : {}; - try { - const res = await fetch('http://localhost:8080/api' + endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(Object.assign(auth, params)) - }); + const res = await fetch(`http://localhost:${port}/api${endpoint}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(Object.assign(auth, params)) + }); - const status = res.status; - const body = res.status !== 204 ? await res.json().catch() : null; - - return { - body, status - }; - } catch (e) { - return { - body: null, status: 500 - }; - } + const status = res.status; + const body = res.status !== 204 ? await res.json().catch() : null; + + return { + body, status + }; }; export const signup = async (params?: any): Promise<any> => { @@ -72,7 +70,7 @@ export const uploadFile = (user: any, path?: string): Promise<any> => { formData.append('i', user.token); formData.append('file', fs.createReadStream(path || __dirname + '/resources/Lenna.png')); - return fetch('http://localhost:8080/api/drive/files/create', { + return fetch(`http://localhost:${port}/api/drive/files/create`, { method: 'post', body: formData, timeout: 30 * 1000, @@ -87,7 +85,7 @@ export const uploadFile = (user: any, path?: string): Promise<any> => { export function connectStream(user: any, channel: string, listener: (message: Record<string, any>) => any, params?: any): Promise<WebSocket> { return new Promise((res, rej) => { - const ws = new WebSocket(`ws://localhost:8080/streaming?i=${user.token}`); + const ws = new WebSocket(`ws://localhost:${port}/streaming?i=${user.token}`); ws.on('open', () => { ws.on('message', data => { @@ -112,6 +110,29 @@ export function connectStream(user: any, channel: string, listener: (message: Re }); } +export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status?: number, type?: string, location?: string }> => { + // node-fetchã ã¨3xxã‚’å–ã‚Œãªã„ + return await new Promise((resolve, reject) => { + const req = http.request(`http://localhost:${port}${path}`, { + headers: { + Accept: accept + } + }, res => { + if (res.statusCode! >= 400) { + reject(res); + } else { + resolve({ + status: res.statusCode, + type: res.headers['content-type'], + location: res.headers.location, + }); + } + }); + + req.end(); + }); +}; + export function launchServer(callbackSpawnedProcess: (p: childProcess.ChildProcess) => void, moreProcess: () => Promise<void> = async () => {}) { return (done: (err?: Error) => any) => { const p = childProcess.spawn('node', [__dirname + '/../index.js'], { diff --git a/yarn.lock b/yarn.lock index c03fa4182e57424b98109470134a3f8ea23c1f0a..eecd562aac6d1c03e3b671de27f4b43b6ce05aa5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -89,11 +89,6 @@ dependencies: "@babel/types" "^7.12.13" -"@babel/helper-validator-identifier@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" - integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== - "@babel/helper-validator-identifier@^7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" @@ -113,20 +108,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.12.0", "@babel/parser@^7.6.0": - version "7.12.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.0.tgz#2ad388f3960045b22f9b7d4bf85e80b15a1c9e3a" - integrity sha512-dYmySMYnlus2jwl7JnnajAj11obRStZoW9cG04wh4ZuhozDn11tDUrhHcUZ9iuNHqALAhh60XqNaYXpvuuE/Gg== - -"@babel/parser@^7.12.13", "@babel/parser@^7.13.0": - version "7.13.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.4.tgz#340211b0da94a351a6f10e63671fa727333d13ab" - integrity sha512-uvoOulWHhI+0+1f9L4BoozY7U5cIkZ9PgJqvb041d6vypgUmtVPG4vmGm4pSggjl8BELzvHyUeJSUyEMY6b+qA== - -"@babel/plugin-transform-runtime@7.13.9": +"@babel/parser@^7.12.0", "@babel/parser@^7.12.13", "@babel/parser@^7.13.0", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6": version "7.13.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.9.tgz#744d3103338a0d6c90dee0497558150b490cee07" - integrity sha512-XCxkY/wBI6M6Jj2mlWxkmqbKPweRanszWbF3Tyut+hKh+PHcuIH/rSr/7lmmE7C3WW+HSIm2GT+d5jwmheuB0g== + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.9.tgz#ca34cb95e1c2dd126863a84465ae8ef66114be99" + integrity sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw== + +"@babel/plugin-transform-runtime@7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.10.tgz#a1e40d22e2bf570c591c9c7e5ab42d6bf1e419e1" + integrity sha512-Y5k8ipgfvz5d/76tx7JYbKQTcgFSU6VgJ3kKQv4zGTKr+a9T/KBvfRvGtSFgKDQGt/DBykQixV0vNWKIdzWErA== dependencies: "@babel/helper-module-imports" "^7.12.13" "@babel/helper-plugin-utils" "^7.13.0" @@ -166,25 +156,7 @@ globals "^11.1.0" lodash "^4.17.19" -"@babel/types@^7.12.0", "@babel/types@^7.6.1": - version "7.12.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.7.tgz#6039ff1e242640a29452c9ae572162ec9a8f5d13" - integrity sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ== - dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - -"@babel/types@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.13.tgz#8be1aa8f2c876da11a9cf650c0ecf656913ad611" - integrity sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ== - dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - -"@babel/types@^7.13.0": +"@babel/types@^7.12.0", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.6.1", "@babel/types@^7.9.6": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== @@ -229,38 +201,38 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@fortawesome/fontawesome-common-types@^0.2.34": - version "0.2.34" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.34.tgz#0a8c348bb23b7b760030f5b1d912e582be4ec915" - integrity sha512-XcIn3iYbTEzGIxD0/dY5+4f019jIcEIWBiHc3KrmK/ROahwxmZ/s+tdj97p/5K0klz4zZUiMfUlYP0ajhSJjmA== +"@fortawesome/fontawesome-common-types@^0.2.35": + version "0.2.35" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz#01dd3d054da07a00b764d78748df20daf2b317e9" + integrity sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw== -"@fortawesome/fontawesome-svg-core@1.2.34": - version "1.2.34" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.34.tgz#1d1a7c92537cbc2b8a83eef6b6d824b4b5b46b26" - integrity sha512-0KNN0nc5eIzaJxlv43QcDmTkDY1CqeN6J7OCGSs+fwGPdtv0yOQqRjieopBCmw+yd7uD3N2HeNL3Zm5isDleLg== +"@fortawesome/fontawesome-svg-core@1.2.35": + version "1.2.35" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz#85aea8c25645fcec88d35f2eb1045c38d3e65cff" + integrity sha512-uLEXifXIL7hnh2sNZQrIJWNol7cTVIzwI+4qcBIq9QWaZqUblm0IDrtSqbNg+3SQf8SMGHkiSigD++rHmCHjBg== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.34" + "@fortawesome/fontawesome-common-types" "^0.2.35" -"@fortawesome/free-brands-svg-icons@5.15.2": - version "5.15.2" - resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.2.tgz#d74e2540b5552b915d6bef719f17e361b70a8d65" - integrity sha512-YPlVjE1cEO+OJ9I9ay3TQ3I88+XkxMTYwnnddqAboxLhPNGncsHV0DjWOVLCyuAY66yPfyndWwVn4v7vuqsO1g== +"@fortawesome/free-brands-svg-icons@5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.3.tgz#bec2821d23b9c667be1d192a6c5bfb2667e588eb" + integrity sha512-1hirPcbjj72ZJtFvdnXGPbAbpn3Ox6mH3g5STbANFp3vGSiE5u5ingAKV06mK6ZVqNYxUPlh4DlTnaIvLtF2kw== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.34" + "@fortawesome/fontawesome-common-types" "^0.2.35" -"@fortawesome/free-regular-svg-icons@5.15.2": - version "5.15.2" - resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.2.tgz#61eeb8c206e792c530eaa58279cc32c55332fe8f" - integrity sha512-Uv5NQCYjyisNVTu/1Xjs+z8vwQjbfT6hiqYvQNfF0n8qdgfWLM581bAfVMQ3BCs1SPy+eEUKNcGkK4n0FihFHg== +"@fortawesome/free-regular-svg-icons@5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.3.tgz#1ec4f2410ff638db549c5c5484fc60b66407dbe6" + integrity sha512-q4/p8Xehy9qiVTdDWHL4Z+o5PCLRChePGZRTXkl+/Z7erDVL8VcZUuqzJjs6gUz6czss4VIPBRdCz6wP37/zMQ== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.34" + "@fortawesome/fontawesome-common-types" "^0.2.35" -"@fortawesome/free-solid-svg-icons@5.15.2": - version "5.15.2" - resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.2.tgz#25bb035de57cf85aee8072965732368ccc8e8943" - integrity sha512-ZfCU+QjaFsdNZmOGmfqEWhzI3JOe37x5dF4kz9GeXvKn/sTxhqMtZ7mh3lBf76SvcYY5/GKFuyG7p1r4iWMQqw== +"@fortawesome/free-solid-svg-icons@5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz#52eebe354f60dc77e0bde934ffc5c75ffd04f9d8" + integrity sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.34" + "@fortawesome/fontawesome-common-types" "^0.2.35" "@fortawesome/vue-fontawesome@3.0.0-3": version "3.0.0-3" @@ -386,10 +358,10 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== +"@sinonjs/fake-timers@7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.0.2.tgz#a53e71d4154ee704ea9b36a6d0b0780e246fadd1" + integrity sha512-dF84L5YC90gIOegPDCYymPIsDmwMWWSh7BwfDXQYePi8lVIEp7IZ1UVGkME8FjXOsDPxan12x4aaK+Lo6wVh9A== dependencies: "@sinonjs/commons" "^1.7.0" @@ -524,11 +496,6 @@ resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== -"@types/double-ended-queue@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/double-ended-queue/-/double-ended-queue-2.1.1.tgz#f077386134f0f736d927812c85c43a04f21ddc27" - integrity sha512-O2+umEIlHBVyi+ePmucPjpINqTvSnsz+hAok0D4IpvrOsIsDr6c34B0AbNXW2UDVYuxbv51z5dxnrRt23ohgWg== - "@types/escape-regexp@0.0.0": version "0.0.0" resolved "https://registry.yarnpkg.com/@types/escape-regexp/-/escape-regexp-0.0.0.tgz#bff0225f9ef30d0dbdbe0e2a24283ee5342990c3" @@ -681,10 +648,10 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.0.tgz#d1a11688112091f2c711674df3a65ea2f47b5dfb" integrity sha512-4vlpCM5KPCL5CfGmTbpjwVKbISRYhduEJvvUWsH5EB7QInhEj94XPZ3ts/9FPiLZFqYO0xoW4ZL8z2AabTGgJA== -"@types/jsdom@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.6.tgz#9ddf0521e49be5365797e690c3ba63148e562c29" - integrity sha512-yQA+HxknGtW9AkRTNyiSH3OKW5V+WzO8OPTdne99XwJkYC+KYxfNIcoJjeiSqP3V00PUUpFP6Myoo9wdIu78DQ== +"@types/jsdom@16.2.7": + version "16.2.7" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.7.tgz#27d2f77d655a3db15f7c3f104f1a6d15e3112938" + integrity sha512-jJ0QDvwZxAO+SninBaQdW6najEs1dCZ1uMsXFBTitwfAtz+0wfDZWd3GFEqkL4flD3IefB+VGBcrN9HbRdAdog== dependencies: "@types/node" "*" "@types/parse5" "*" @@ -700,10 +667,10 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== -"@types/jsonld@1.5.4": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@types/jsonld/-/jsonld-1.5.4.tgz#37e7869fae65bd9b35aa3467cbfc60c52f881a36" - integrity sha512-e51UoELQzKJWGsVNqoKI5nnXazupqoOYTBMAGe3Iram0sWxeVTzgk38BKYgmOZmuIXaMFfuAz1ZimK2oyQNnRA== +"@types/jsonld@1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@types/jsonld/-/jsonld-1.5.5.tgz#c2238462a83e90f003929cef6a36eded2ceed069" + integrity sha512-/4PvZJJh3Lqz9bkwvSwfyLWnR1xFQ7h4KLq8i0yaGjBBq09qLMJlCqvfXfE5qFEnClmWM+GK2Edjbb6QDS2SXQ== "@types/katex@0.11.0": version "0.11.0" @@ -778,24 +745,10 @@ dependencies: "@types/koa" "*" -"@types/koa@*": - version "2.11.6" - resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.11.6.tgz#b7030caa6b44af801c2aea13ba77d74aff7484d5" - integrity sha512-BhyrMj06eQkk04C97fovEDQMpLpd2IxCB4ecitaXwOKGq78Wi2tooaDOWOFGajPk8IkQOAtMppApgSVkYe1F/A== - dependencies: - "@types/accepts" "*" - "@types/content-disposition" "*" - "@types/cookies" "*" - "@types/http-assert" "*" - "@types/http-errors" "*" - "@types/keygrip" "*" - "@types/koa-compose" "*" - "@types/node" "*" - -"@types/koa@2.13.0": - version "2.13.0" - resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.0.tgz#6356c48521c0941103b6fcfb97bb01426a99d56d" - integrity sha512-hNs1Z2lX+R5sZroIy/WIGbPlH/719s/Nd5uIjSIAdHn9q+g7z6mxOnhwMjK1urE4/NUP0SOoYUOD4MnvD9FRNw== +"@types/koa@*", "@types/koa@2.13.1", "@types/koa@^2.13.1": + version "2.13.1" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.1.tgz#e29877a6b5ad3744ab1024f6ec75b8cbf6ec45db" + integrity sha512-Qbno7FWom9nNqu0yHZ6A0+RWt4mrYBhw3wpBAQ3+IuzGcLlfeYkzZrnMq5wsxulN2np8M4KKeUpTodsOsSad5Q== dependencies: "@types/accepts" "*" "@types/content-disposition" "*" @@ -875,22 +828,27 @@ form-data "^3.0.0" "@types/node@*": - version "14.14.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.13.tgz#9e425079799322113ae8477297ae6ef51b8e0cdf" - integrity sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ== - -"@types/node@14.14.31": version "14.14.31" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055" integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g== -"@types/nodemailer@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.0.tgz#d8c039be3ed685c4719a026455555be82c124b74" - integrity sha512-KY7bFWB0MahRZvVW4CuW83qcCDny59pJJ0MQ5ifvfcjNwPlIT0vW4uARO4u1gtkYnWdhSvURegecY/tzcukJcA== +"@types/node@14.14.35": + version "14.14.35" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313" + integrity sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag== + +"@types/nodemailer@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.1.tgz#31f96f4410632f781d3613bd1f4293649e423f75" + integrity sha512-8081UY/0XTTDpuGqCnDc8IY+Q3DSg604wB3dBH0CaZlj4nZWHWuxtZ3NRZ9c9WUrz1Vfm6wioAUnqL3bsh49uQ== dependencies: "@types/node" "*" +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + "@types/nprogress@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@types/nprogress/-/nprogress-0.2.0.tgz#86c593682d4199212a0509cc3c4d562bbbd6e45f" @@ -1158,10 +1116,10 @@ "@types/webpack-sources" "*" source-map "^0.6.0" -"@types/websocket@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.1.tgz#039272c196c2c0e4868a0d8a1a27bbb86e9e9138" - integrity sha512-f5WLMpezwVxCLm1xQe/kdPpQIOmL0TXYx2O15VYfYzc7hTIdxiOoOvez+McSIw3b7z/1zGovew9YSL7+h4h7/Q== +"@types/websocket@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.2.tgz#d2855c6a312b7da73ed16ba6781815bf30c6187a" + integrity sha512-B5m9aq7cbbD/5/jThEr33nUY8WEfVi6A2YKCTOvw5Ldy7mtsOkqRvGjnzy6g7iMMDsgu7xREuCzqATLDLQVKcQ== dependencies: "@types/node" "*" @@ -1172,48 +1130,48 @@ dependencies: "@types/node" "*" -"@typescript-eslint/parser@4.16.1": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.16.1.tgz#3bbd3234dd3c5b882b2bcd9899bc30e1e1586d2a" - integrity sha512-/c0LEZcDL5y8RyI1zLcmZMvJrsR6SM1uetskFkoh3dvqDKVXPsXI+wFB/CbVw7WkEyyTKobC1mUNp/5y6gRvXg== +"@typescript-eslint/parser@4.18.0": + version "4.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.18.0.tgz#a211edb14a69fc5177054bec04c95b185b4dde21" + integrity sha512-W3z5S0ZbecwX3PhJEAnq4mnjK5JJXvXUDBYIYGoweCyWyuvAKfGHvzmpUzgB5L4cRBb+cTu9U/ro66dx7dIimA== dependencies: - "@typescript-eslint/scope-manager" "4.16.1" - "@typescript-eslint/types" "4.16.1" - "@typescript-eslint/typescript-estree" "4.16.1" + "@typescript-eslint/scope-manager" "4.18.0" + "@typescript-eslint/types" "4.18.0" + "@typescript-eslint/typescript-estree" "4.18.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.16.1": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.16.1.tgz#244e2006bc60cfe46987e9987f4ff49c9e3f00d5" - integrity sha512-6IlZv9JaurqV0jkEg923cV49aAn8V6+1H1DRfhRcvZUrptQ+UtSKHb5kwTayzOYTJJ/RsYZdcvhOEKiBLyc0Cw== +"@typescript-eslint/scope-manager@4.18.0": + version "4.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.18.0.tgz#d75b55234c35d2ff6ac945758d6d9e53be84a427" + integrity sha512-olX4yN6rvHR2eyFOcb6E4vmhDPsfdMyfQ3qR+oQNkAv8emKKlfxTWUXU5Mqxs2Fwe3Pf1BoPvrwZtwngxDzYzQ== dependencies: - "@typescript-eslint/types" "4.16.1" - "@typescript-eslint/visitor-keys" "4.16.1" + "@typescript-eslint/types" "4.18.0" + "@typescript-eslint/visitor-keys" "4.18.0" -"@typescript-eslint/types@4.16.1": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.16.1.tgz#5ba2d3e38b1a67420d2487519e193163054d9c15" - integrity sha512-nnKqBwMgRlhzmJQF8tnFDZWfunXmJyuXj55xc8Kbfup4PbkzdoDXZvzN8//EiKR27J6vUSU8j4t37yUuYPiLqA== +"@typescript-eslint/types@4.18.0": + version "4.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.18.0.tgz#bebe323f81f2a7e2e320fac9415e60856267584a" + integrity sha512-/BRociARpj5E+9yQ7cwCF/SNOWwXJ3qhjurMuK2hIFUbr9vTuDeu476Zpu+ptxY2kSxUHDGLLKy+qGq2sOg37A== -"@typescript-eslint/typescript-estree@4.16.1": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.16.1.tgz#c2fc46b05a48fbf8bbe8b66a63f0a9ba04b356f1" - integrity sha512-m8I/DKHa8YbeHt31T+UGd/l8Kwr0XCTCZL3H4HMvvLCT7HU9V7yYdinTOv1gf/zfqNeDcCgaFH2BMsS8x6NvJg== +"@typescript-eslint/typescript-estree@4.18.0": + version "4.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.18.0.tgz#756d3e61da8c16ab99185532c44872f4cd5538cb" + integrity sha512-wt4xvF6vvJI7epz+rEqxmoNQ4ZADArGQO9gDU+cM0U5fdVv7N+IAuVoVAoZSOZxzGHBfvE3XQMLdy+scsqFfeg== dependencies: - "@typescript-eslint/types" "4.16.1" - "@typescript-eslint/visitor-keys" "4.16.1" + "@typescript-eslint/types" "4.18.0" + "@typescript-eslint/visitor-keys" "4.18.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.16.1": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.16.1.tgz#d7571fb580749fae621520deeb134370bbfc7293" - integrity sha512-s/aIP1XcMkEqCNcPQtl60ogUYjSM8FU2mq1O7y5cFf3Xcob1z1iXWNB6cC43Op+NGRTFgGolri6s8z/efA9i1w== +"@typescript-eslint/visitor-keys@4.18.0": + version "4.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz#4e6fe2a175ee33418318a029610845a81e2ff7b6" + integrity sha512-Q9t90JCvfYaN0OfFUgaLqByOfz8yPeTAdotn/XYNm5q9eHax90gzdb+RJ6E9T5s97Kv/UHWKERTmqA0jTKAEHw== dependencies: - "@typescript-eslint/types" "4.16.1" + "@typescript-eslint/types" "4.18.0" eslint-visitor-keys "^2.0.0" "@ungap/promise-all-settled@1.1.2": @@ -1221,83 +1179,83 @@ resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== -"@vue/compiler-core@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.5.tgz#a6e54cabe9536e74c6513acd2649f311af1d43ac" - integrity sha512-iFXwk2gmU/GGwN4hpBwDWWMLvpkIejf/AybcFtlQ5V1ur+5jwfBaV0Y1RXoR6ePfBPJixtKZ3PmN+M+HgMAtfQ== +"@vue/compiler-core@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.7.tgz#421782a4c67cc3f2b7c30457ef446d74f8524f74" + integrity sha512-JFohgBXoyUc3mdeI2WxlhjQZ5fakfemJkZHX8Gu/nFbEg3+lKVUZmNKWmmnp9aOzJQZKoj77LjmFxiP+P+7lMQ== dependencies: "@babel/parser" "^7.12.0" "@babel/types" "^7.12.0" - "@vue/shared" "3.0.5" + "@vue/shared" "3.0.7" estree-walker "^2.0.1" source-map "^0.6.1" -"@vue/compiler-dom@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.5.tgz#7885a13e6d18f64dde8ebceec052ed2c102696c2" - integrity sha512-HSOSe2XSPuCkp20h4+HXSiPH9qkhz6YbW9z9ZtL5vef2T2PMugH7/osIFVSrRZP/Ul5twFZ7MIRlp8tPX6e4/g== +"@vue/compiler-dom@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.7.tgz#54d2e12fb9a7aff53abd19dac2c2679533f0c919" + integrity sha512-VnIH9EbWQm/Tkcp+8dCaNVsVvhm/vxCrIKWRkXY9215hTqOqQOvejT8IMjd2kc++nIsYMsdQk6H9qqBvoLe/Cw== dependencies: - "@vue/compiler-core" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/compiler-core" "3.0.7" + "@vue/shared" "3.0.7" -"@vue/compiler-sfc@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.5.tgz#3ae08e60244a72faf9598361874fb7bdb5b1d37c" - integrity sha512-uOAC4X0Gx3SQ9YvDC7YMpbDvoCmPvP0afVhJoxRotDdJ+r8VO3q4hFf/2f7U62k4Vkdftp6DVni8QixrfYzs+w== +"@vue/compiler-sfc@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.7.tgz#900414750cc726553b870490f48073451fd14f07" + integrity sha512-37/QILpGE+J3V+bP9Slg9e6xGqfk+MmS2Yj8ChR4fS0/qWUU/YoYHE0GPIzjmBdH0JVOOmJqunxowIXmqNiHng== dependencies: "@babel/parser" "^7.12.0" "@babel/types" "^7.12.0" - "@vue/compiler-core" "3.0.5" - "@vue/compiler-dom" "3.0.5" - "@vue/compiler-ssr" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/compiler-core" "3.0.7" + "@vue/compiler-dom" "3.0.7" + "@vue/compiler-ssr" "3.0.7" + "@vue/shared" "3.0.7" consolidate "^0.16.0" estree-walker "^2.0.1" hash-sum "^2.0.0" lru-cache "^5.1.1" magic-string "^0.25.7" merge-source-map "^1.1.0" - postcss "^7.0.32" - postcss-modules "^3.2.2" + postcss "^8.1.10" + postcss-modules "^4.0.0" postcss-selector-parser "^6.0.4" source-map "^0.6.1" -"@vue/compiler-ssr@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.5.tgz#7661ad891a0be948726c7f7ad1e425253c587b83" - integrity sha512-Wm//Kuxa1DpgjE4P9W0coZr8wklOfJ35Jtq61CbU+t601CpPTK4+FL2QDBItaG7aoUUDCWL5nnxMkuaOgzTBKg== +"@vue/compiler-ssr@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.7.tgz#28b85d497381d75fe44234057b140b0065ca9dbf" + integrity sha512-nHRbHeSpfXwjypettjrA16TjgfDcPEwq3m/zHnGyLC1QqdLtklXmpSM43/CPwwTCRa/qdt0pldJf22MiCEuTSQ== dependencies: - "@vue/compiler-dom" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/compiler-dom" "3.0.7" + "@vue/shared" "3.0.7" -"@vue/reactivity@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.0.5.tgz#e3789e4d523d845f9ae0b4d770e2b45594742fd2" - integrity sha512-3xodUE3sEIJgS7ntwUbopIpzzvi7vDAOjVamfb2l+v1FUg0jpd3gf62N2wggJw3fxBMr+QvyxpD+dBoxLsmAjw== +"@vue/reactivity@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.0.7.tgz#e6ccc7bef7fc10b0972e4d974bad71679d3b26ad" + integrity sha512-FotWcNNaKhqpFZrdgsUOZ1enlJ5lhTt01CNTtLSyK7jYFgZBTuw8vKsEutZKDYZ1XKotOfoeO8N3pZQqmM6Etw== dependencies: - "@vue/shared" "3.0.5" + "@vue/shared" "3.0.7" -"@vue/runtime-core@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.0.5.tgz#da6331d5f300d5794e9e0ebdc8a8bd72a9e19962" - integrity sha512-Cnyi2NqREwOLcTEsIi1DQX1hHtkVj4eGm4hBG7HhokS05DqpK4/80jG6PCCnCH9rIJDB2FqtaODX397210plXg== +"@vue/runtime-core@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.0.7.tgz#d44c0b0a57d7e392912a87362a4430ccf446ecea" + integrity sha512-DBAZAwVvdmMXuyd6/9qqj/kYr/GaLTmn1L2/QLxLwP+UfhIboiTSBc/tUUb8MRk7Bb98GzNeAWkkT6AfooS3dQ== dependencies: - "@vue/reactivity" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/reactivity" "3.0.7" + "@vue/shared" "3.0.7" -"@vue/runtime-dom@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.0.5.tgz#1ce2c9c449e26ab06963da0064096e882a7a8935" - integrity sha512-iilX1KySeIzHHtErT6Y44db1rhWK5tAI0CiJIPr+SJoZ2jbjoOSE6ff/jfIQakchbm1d6jq6VtRVnp5xYdOXKA== +"@vue/runtime-dom@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.0.7.tgz#b70668d729020bc4ad608c20367223f259576ba6" + integrity sha512-Oij4ruOtnpQpCj+/Q3JPzgpTJ1Q7+N67pA53A8KVITEtxfvKL46NN6dhAZ5NGqwX6RWZpYqWQNewITeF0pHr8g== dependencies: - "@vue/runtime-core" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/runtime-core" "3.0.7" + "@vue/shared" "3.0.7" csstype "^2.6.8" -"@vue/shared@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.5.tgz#c131d88bd6713cc4d93b3bb1372edb1983225ff0" - integrity sha512-gYsNoGkWejBxNO6SNRjOh/xKeZ0H0V+TFzaPzODfBjkAIb0aQgBuixC1brandC/CDJy1wYPwSoYrXpvul7m6yw== +"@vue/shared@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.7.tgz#96d52988efc07444c108c7c6803ba7cc93e40045" + integrity sha512-dn5FyfSc4ky424jH4FntiHno7Ss5yLkqKNmM/NXwANRnlkmqu74pnGetexDFVG5phMk9/FhwovUZCWGxsotVKg== "@webassemblyjs/ast@1.11.0": version "1.11.0" @@ -1447,10 +1405,10 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -abab@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" - integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== +abab@^2.0.3, abab@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== abbrev@1: version "1.1.1" @@ -1472,13 +1430,6 @@ accepts@^1.3.5: mime-types "~2.1.24" negotiator "0.6.2" -acorn-globals@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" - integrity sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8= - dependencies: - acorn "^4.0.4" - acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -1502,25 +1453,15 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== -acorn@^3.1.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= - -acorn@^4.0.4, acorn@~4.0.2: - version "4.0.13" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" - integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c= - acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.4: - version "8.0.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.0.4.tgz#7a3ae4191466a6984eee0fe3407a4f3aa9db8354" - integrity sha512-XNP0PqF1XD19ZlLKvB7cMmnZswW4C/03pRHgirB30uSJTaS3A3V1/P4sS3HPvFmjoriPCJQs+JDSbm4bL1TxGQ== +acorn@^8.0.4, acorn@^8.0.5: + version "8.1.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe" + integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA== agent-base@6: version "6.0.0" @@ -1544,15 +1485,6 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc= - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - alphanum-sort@^1.0.0, alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -1643,10 +1575,10 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -apexcharts@3.25.0: - version "3.25.0" - resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.25.0.tgz#f3f0f9f344f997230f5c7f2918408aa072627496" - integrity sha512-uM7OF+jLL4ba79noYcrMwMgJW8DI+Ff28CCQoGq23g25z8nGSQEoU+u12YWlECA9gBA5tbmdaQhMxjlK+M6B9Q== +apexcharts@3.26.0: + version "3.26.0" + resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.26.0.tgz#a78abc108b2e1b3086a738f0ec7c98e292f4a14b" + integrity sha512-zdYHs3k3tgmCn1BpYLj7rhGEndBYF33Pq1+g0ora37xAr+3act5CJrpdXM2jx2boVUyXgavoSp6sa8WpK7RkSA== dependencies: svg.draggable.js "^2.2.2" svg.easing.js "^2.0.0" @@ -1807,6 +1739,11 @@ asn1@~0.2.3: dependencies: safer-buffer "~2.1.0" +assert-never@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/assert-never/-/assert-never-1.2.1.tgz#11f0e363bf146205fb08193b5c7b90f4d1cf44fe" + integrity sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw== + assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" @@ -1902,10 +1839,10 @@ autwh@0.1.0: dependencies: oauth "0.9.15" -aws-sdk@2.848.0: - version "2.848.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.848.0.tgz#5e7706ddd30a55a2d5a5b64c29682a757607ee64" - integrity sha512-c/e5kaEFl+9aYkrYDkmu5mSZlL+EfP6DnBOMD06fH12gIsaFSMBGtbsDTHABhvSu++LxeI1dJAD148O17MuZvg== +aws-sdk@2.867.0: + version "2.867.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.867.0.tgz#3fae9a583bfd89f6098dc9bca6553b5b85e2a2fc" + integrity sha512-6iXRTW6dlCU4UwMivDGHoRyMCvX9Ch1G2gIZZl5yKwzVNaBNpJiAyByWFuxrcBqQsSIFKBEbxeRjePE6QiBbkw== dependencies: buffer "4.9.2" events "1.1.1" @@ -1951,6 +1888,13 @@ babel-plugin-polyfill-regenerator@^0.1.2: dependencies: "@babel/helper-define-polyfill-provider" "^0.1.2" +babel-walk@3.0.0-canary-5: + version "3.0.0-canary-5" + resolved "https://registry.yarnpkg.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz#f66ecd7298357aee44955f235a6ef54219104b11" + integrity sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw== + dependencies: + "@babel/types" "^7.9.6" + bach@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" @@ -2040,7 +1984,7 @@ bl@^4.0.1, bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" -bluebird@^3.1.1, bluebird@^3.7.2: +bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -2091,10 +2035,10 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -broadcast-channel@3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.4.1.tgz#65b63068d0a5216026a19905c9b2d5e9adf0928a" - integrity sha512-VXYivSkuBeQY+pL5hNQQNvBdKKQINBAROm4G8lAbWQfOZ7Yn4TMcgLNlJyEqlkxy5G8JJBsI3VJ1u8FUTOROcg== +broadcast-channel@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.5.3.tgz#c75c39d923ae8af6284a893bfdc8bd3996d2dd2d" + integrity sha512-OLOXfwReZa2AAAh9yOUyiALB3YxBe0QpThwwuyRHLgpl8bSznSDmV6Mz7LeBJg1VZsMcDcNMy7B53w12qHrIhQ== dependencies: "@babel/runtime" "^7.7.2" detect-node "^2.0.4" @@ -2200,16 +2144,16 @@ builtin-modules@^1.1.1: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= -bull@3.20.1: - version "3.20.1" - resolved "https://registry.yarnpkg.com/bull/-/bull-3.20.1.tgz#97c4156f48001565baf3c04b81267a5604f40754" - integrity sha512-wDwpVu47WKaGhiguEPa/US5UMrtGLPKNPiGFPo4OgVs3EEGzJEWwv3LRPfjJVIf1COdMAN/yowGhZwYmoOonjQ== +bull@3.21.1: + version "3.21.1" + resolved "https://registry.yarnpkg.com/bull/-/bull-3.21.1.tgz#f0ff54b73c6a5ec54c5e12bb9d3ceb54229d055d" + integrity sha512-7IjQBz+mm2GYwlNOf5Pbq8F+EwqkBv0RjwWpG+5Kfk/QDWylQ6eL3FzJbvFQQ7uHjTVPsuzsQocArjhm/SUsqA== dependencies: cron-parser "^2.13.0" debuglog "^1.0.0" get-port "^5.1.1" ioredis "^4.22.0" - lodash "^4.17.19" + lodash "^4.17.21" p-timeout "^3.2.0" promise.prototype.finally "^3.1.2" semver "^7.3.2" @@ -2307,11 +2251,6 @@ camel-case@^3.0.0: no-case "^2.2.0" upper-case "^1.1.1" -camelcase@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= - camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" @@ -2372,32 +2311,24 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -cbor@7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.3.tgz#216d292f2aedd1bb61414a8f949b63e4de11b33b" - integrity sha512-Io+lJytjYBJKgJqZQUz2bFaMPj+HtlsnT9kHSUiIJFqzWa05lm5/ycQ+NiZWpks3DR2Fz7j7axiTGeT57w/syg== +cbor@7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.4.tgz#8a3ef39f07ac3fdb69dda461b87db7113233481b" + integrity sha512-9hBTn31l7+9qteBso7+HPp2R5ytqFRBd98fHK4ZTpvrba8V7CuoOsEL0S6vf7+11gubMTd3RW97lOgMTl5SNfg== dependencies: "@cto.af/textdecoder" "^0.0.0" nofilter "^2.0.3" -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60= - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - -chai@4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.0.tgz#5523a5faf7f819c8a92480d70a8cccbadacfc25f" - integrity sha512-/BFd2J30EcOwmdOgXvVsmM48l0Br0nmZPlO0uOW4XKh6kpsUumRXBgPV+IlaqFaqr9cYbeoZAM1Npx0i4A+aiA== +chai@4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== dependencies: assertion-error "^1.1.0" check-error "^1.0.2" deep-eql "^3.0.1" get-func-name "^2.0.0" - pathval "^1.1.0" + pathval "^1.1.1" type-detect "^4.0.5" chalk@4.0.0: @@ -2441,7 +2372,7 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -character-parser@^2.1.1: +character-parser@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-2.2.0.tgz#c7ce28f36d4bcd9744e5ffc2c5fcde1c73261fc0" integrity sha1-x84o821LzZdE5f/CxfzeHHMmH8A= @@ -2576,7 +2507,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -clean-css@^4.1.11, clean-css@^4.2.1: +clean-css@^4.2.1: version "4.2.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== @@ -2604,15 +2535,6 @@ clipboard@^2.0.0: select "^1.1.2" tiny-emitter "^2.0.0" -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE= - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - cliui@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" @@ -2871,6 +2793,11 @@ commander@^2.12.1, commander@^2.19.0, commander@^2.20.0, commander@~2.20.3: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + commander@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.0.0.tgz#3e2bbfd8bb6724760980988fb5b22b7ee6b71ab2" @@ -2901,6 +2828,21 @@ concat-stream@^1.5.2, concat-stream@^1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" +concurrently@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.0.0.tgz#c1a876dd99390979c71f8c6fe6796882f3a13199" + integrity sha512-Ik9Igqnef2ONLjN2o/OVx1Ow5tymVvvEwQeYCQdD/oV+CN9oWhxLk7ibcBdOtv0UzBqHCEKRwbKceYoTK8t3fQ== + dependencies: + chalk "^4.1.0" + date-fns "^2.16.1" + lodash "^4.17.20" + read-pkg "^5.2.0" + rxjs "^6.6.3" + spawn-command "^0.0.2-1" + supports-color "^8.1.0" + tree-kill "^1.2.2" + yargs "^16.2.0" + condense-newlines@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/condense-newlines/-/condense-newlines-0.2.1.tgz#3de985553139475d32502c83b02f60684d24c55f" @@ -2923,13 +2865,6 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -consolidate@0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" - integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw== - dependencies: - bluebird "^3.1.1" - consolidate@^0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.16.0.tgz#a11864768930f2f19431660a65906668f5fbdc16" @@ -2937,7 +2872,7 @@ consolidate@^0.16.0: dependencies: bluebird "^3.7.2" -constantinople@^3.0.1, constantinople@^3.1.2, constantinople@^4.0.1: +constantinople@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-4.0.1.tgz#0def113fa0e4dc8de83331a5cf79c8b325213151" integrity sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw== @@ -3006,10 +2941,10 @@ core-js-compat@^3.8.1: browserslist "^4.16.3" semver "7.0.0" -core-js@3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.0.tgz#790b1bb11553a2272b36e2625c7179db345492f8" - integrity sha512-PyFBJaLq93FlyYdsndE5VaueA9K5cNB7CGzeCj191YYLhkQM0gdZR2SKihM70oF0wdqKSKClv/tEBOpoRmdOVQ== +core-js@3.9.1: + version "3.9.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.1.tgz#cec8de593db8eb2a85ffb0dbdeb312cb6e5460ae" + integrity sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -3098,16 +3033,16 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" -css-loader@5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.0.2.tgz#24f758dae349bad0a440c50d7e2067742e0899cb" - integrity sha512-gbkBigdcHbmNvZ1Cg6aV6qh6k9N6XOr8YWzISLQGrwk2mgOH8LLrizhkxbDhQtaLtktyKHD4970S0xwz5btfTA== +css-loader@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.1.3.tgz#87f6fc96816b20debe3cf682f85c7e56a963d0d1" + integrity sha512-CoPZvyh8sLiGARK3gqczpfdedbM74klGWurF2CsNZ2lhNaXdLIUks+3Mfax3WBeRuHoglU+m7KG/+7gY6G4aag== dependencies: camelcase "^6.2.0" cssesc "^3.0.0" icss-utils "^5.1.0" loader-utils "^2.0.0" - postcss "^8.2.4" + postcss "^8.2.8" postcss-modules-extract-imports "^3.0.0" postcss-modules-local-by-default "^4.0.0" postcss-modules-scope "^3.0.0" @@ -3303,10 +3238,10 @@ cssom@~0.3.6: resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssstyle@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.2.0.tgz#e4c44debccd6b7911ed617a4395e5754bba59992" - integrity sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA== +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: cssom "~0.3.6" @@ -3339,6 +3274,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +date-fns@^2.16.1: + version "2.19.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.19.0.tgz#65193348635a28d5d916c43ec7ce6fbd145059e1" + integrity sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg== + dateformat@4.5.1: version "4.5.1" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.5.1.tgz#c20e7a9ca77d147906b6dc2261a8be0a5bd2173c" @@ -3365,7 +3305,7 @@ debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "2.1.2" -debug@^3.1.0, debug@^3.2.6: +debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -3377,7 +3317,7 @@ debuglog@^1.0.0: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: +decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -3387,10 +3327,10 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -decimal.js@^10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231" - integrity sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw== +decimal.js@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" + integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== decode-uri-component@^0.2.0: version "0.2.0" @@ -3662,11 +3602,6 @@ dotenv@^8.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== -double-ended-queue@2.1.0-0: - version "2.1.0-0" - resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" - integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw= - duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" @@ -3922,27 +3857,27 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.3, escape-string-regexp@^ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@^1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" - integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== dependencies: esprima "^4.0.1" - estraverse "^4.2.0" + estraverse "^5.2.0" esutils "^2.0.2" optionator "^0.8.1" optionalDependencies: source-map "~0.6.1" -eslint-plugin-vue@7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.6.0.tgz#ea616e6dfd45d545adb16cba628c5a992cc31f0b" - integrity sha512-qYpKwAvpcQXyUXVcG8Zd+fxHDx9iSgTQuO7dql7Ug/2BCvNNDr6s3I9p8MoUo23JJdO7ZAjW3vSwY/EBf4uBcw== +eslint-plugin-vue@7.7.0: + version "7.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.7.0.tgz#a90df4595e670821bf243bd2750ededdb74948b8" + integrity sha512-mYz4bpLGv5jx6YG/GvKkqbGSfV7uma2u1P3mLA41Q5vQl8W1MeuTneB8tfsLq6xxxesFubcrOC0BZBJ5R+eaCQ== dependencies: eslint-utils "^2.1.0" natural-compare "^1.4.0" semver "^7.3.2" - vue-eslint-parser "^7.5.0" + vue-eslint-parser "^7.6.0" eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" @@ -3969,10 +3904,10 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@7.21.0: - version "7.21.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.21.0.tgz#4ecd5b8c5b44f5dedc9b8a110b01bbfeb15d1c83" - integrity sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg== +eslint@7.22.0: + version "7.22.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.22.0.tgz#07ecc61052fec63661a2cab6bd507127c07adc6f" + integrity sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.0" @@ -3991,7 +3926,7 @@ eslint@7.21.0: file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" - globals "^12.1.0" + globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" @@ -3999,7 +3934,7 @@ eslint@7.21.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.20" + lodash "^4.17.21" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -4054,7 +3989,7 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -4245,6 +4180,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-xml-parser@^3.19.0: + version "3.19.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" + integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg== + fastest-levenshtein@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" @@ -4283,10 +4223,10 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -file-type@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.2.0.tgz#d4f1da71ddda758db7f15f93adfaed09ce9e2715" - integrity sha512-1Wwww3mmZCMmLjBfslCluwt2mxH80GsAXYrvPnfQ42G1EGWag336kB1iyCgyn7UXiKY3cJrNykXPrCwA7xb5Ag== +file-type@16.3.0: + version "16.3.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.3.0.tgz#f03af91db30f92cc9a0b335c0644c46101522f6d" + integrity sha512-ZA0hV64611vJT42ltw0T9IDwHApQuxRdrmQZWTeDmeAUtZBBVSQW3nSQqhhW1cAgpXgqcJvm410BYHXJQ9AymA== dependencies: readable-web-to-node-stream "^3.0.0" strtok3 "^6.0.3" @@ -4693,6 +4633,13 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globals@^13.6.0: + version "13.7.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.7.0.tgz#aed3bcefd80ad3ec0f0be2cf0c895110c0591795" + integrity sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA== + dependencies: + type-fest "^0.20.2" + globby@^11.0.1: version "11.0.1" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" @@ -4719,10 +4666,10 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" -got@11.8.1: - version "11.8.1" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.1.tgz#df04adfaf2e782babb3daabc79139feec2f7e85d" - integrity sha512-9aYdZL+6nHmvJwHALLwKSUZ0hMwGaJGYv3hoPLPgnT8BoBXm1SjnZeky+91tfwJaDzun2s4RsBRy48IEYv2q2Q== +got@11.8.2: + version "11.8.2" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" + integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== dependencies: "@sindresorhus/is" "^4.0.0" "@szmarczak/http-timer" "^4.0.5" @@ -5000,7 +4947,7 @@ hsla-regex@^1.0.0: resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= -html-comment-regex@^1.1.0, html-comment-regex@^1.1.2: +html-comment-regex@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== @@ -5155,22 +5102,15 @@ icss-replace-symbols@^1.1.0: resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= -icss-utils@^4.0.0, icss-utils@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" - integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== - dependencies: - postcss "^7.0.14" - icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -idb-keyval@5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-5.0.2.tgz#243cf2b7db1bee2a8a41b78c14a18a85db0e1646" - integrity sha512-1DYjY/nX2U9pkTkwFoAmKcK1ZWmkNgO32Oon9tp/9+HURizxUQ4fZRxMJZs093SldP7q6dotVj03kIkiqOILyA== +idb-keyval@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-5.0.4.tgz#182881b1eafbb47d11a269422ae6d5243f0db0c7" + integrity sha512-qS0kplHuadZujoE90ze0NUkhW0/Fbfib7d+mYNMXNEn45NSh2NWY3fBewoX4GZUsKkGHBgc8JiAwMx0zrfL3LQ== ieee754@1.1.13, ieee754@^1.1.13, ieee754@^1.1.4: version "1.1.13" @@ -5427,13 +5367,13 @@ is-directory@^0.3.1: resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= -is-expression@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-3.0.0.tgz#39acaa6be7fd1f3471dc42c7416e61c24317ac9f" - integrity sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8= +is-expression@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-4.0.0.tgz#c33155962abf21d0afd2552514d67d2ec16fd2ab" + integrity sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A== dependencies: - acorn "~4.0.2" - object-assign "^4.0.1" + acorn "^7.1.1" + object-assign "^4.1.1" is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" @@ -5590,12 +5530,12 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-svg@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.2.1.tgz#095b496e345fec9211c2a7d5d021003e040d6f81" - integrity sha512-PHx3ANecKsKNl5y5+Jvt53Y4J7MfMpbNZkv384QNiswMKAWIbvcqbPz+sYbFKJI8Xv3be01GSFniPmoaP+Ai5A== +is-svg@4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.3.1.tgz#8c63ec8c67c8c7f0a8de0a71c8c7d58eccf4406b" + integrity sha512-h2CGs+yPUyvkgTJQS9cJzo9lYK06WgRiXUqBBHtglSzVKAuH4/oWsqk7LGfbSa1hGk9QcZ0SyQtVggvBA8LZXA== dependencies: - html-comment-regex "^1.1.2" + fast-xml-parser "^3.19.0" is-svg@^2.0.0: version "2.1.0" @@ -5736,7 +5676,7 @@ js-sha3@0.8.0: resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== -js-stringify@^1.0.1: +js-stringify@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" integrity sha1-Fzb939lyTyijaCrcYjCufk6Weds= @@ -5779,36 +5719,36 @@ jschardet@^2.1.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== -jsdom@16.4.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz#36005bde2d136f73eee1a830c6d45e55408edddb" - integrity sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w== +jsdom@16.5.1: + version "16.5.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.1.tgz#4ced6bbd7b77d67fb980e64d9e3e6fb900f97dd6" + integrity sha512-pF73EOsJgwZekbDHEY5VO/yKXUkab/DuvrQB/ANVizbr6UAHJsDdHXuotZYwkJSGQl1JM+ivXaqY+XBDDL4TiA== dependencies: - abab "^2.0.3" - acorn "^7.1.1" + abab "^2.0.5" + acorn "^8.0.5" acorn-globals "^6.0.0" cssom "^0.4.4" - cssstyle "^2.2.0" + cssstyle "^2.3.0" data-urls "^2.0.0" - decimal.js "^10.2.0" + decimal.js "^10.2.1" domexception "^2.0.1" - escodegen "^1.14.1" + escodegen "^2.0.0" html-encoding-sniffer "^2.0.1" is-potential-custom-element-name "^1.0.0" nwsapi "^2.2.0" - parse5 "5.1.1" + parse5 "6.0.1" request "^2.88.2" - request-promise-native "^1.0.8" - saxes "^5.0.0" + request-promise-native "^1.0.9" + saxes "^5.0.1" symbol-tree "^3.2.4" - tough-cookie "^3.0.1" + tough-cookie "^4.0.0" w3c-hr-time "^1.0.2" w3c-xmlserializer "^2.0.0" webidl-conversions "^6.1.0" whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" - ws "^7.2.3" + ws "^7.4.4" xml-name-validator "^3.0.0" jsesc@^2.5.1: @@ -5938,12 +5878,12 @@ jws@^4.0.0: jwa "^2.0.0" safe-buffer "^5.0.1" -katex@0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/katex/-/katex-0.12.0.tgz#2fb1c665dbd2b043edcf8a1f5c555f46beaa0cb9" - integrity sha512-y+8btoc/CK70XqcHqjxiGWBOeIL8upbS0peTPXTvgrh21n1RiWWcIpSWM+4uXq+IAgNh9YYQWdc7LVDPDAEEAg== +katex@0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.13.0.tgz#62900e56c1ad8fdf7da23399e50d7a7b690b39ab" + integrity sha512-6cHbzbegYgS9vvVGuH8UA+o97X+ZshtboSqJJCdq7trBYzuD75JNwr7Ef606xkUjecPPhFnyB+afx1dVafielg== dependencies: - commander "^2.19.0" + commander "^6.0.0" keygrip@~1.0.3: version "1.0.3" @@ -6058,7 +5998,7 @@ koa-mount@4.0.0: debug "^4.0.1" koa-compose "^4.1.0" -koa-send@5.0.1: +koa-send@5.0.1, koa-send@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.1.tgz#39dceebfafb395d0d60beaffba3a70b4f543fe79" integrity sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ== @@ -6067,16 +6007,6 @@ koa-send@5.0.1: http-errors "^1.7.3" resolve-path "^1.4.0" -koa-send@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.0.tgz#5e8441e07ef55737734d7ced25b842e50646e7eb" - integrity sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ== - dependencies: - debug "^3.1.0" - http-errors "^1.6.3" - mz "^2.7.0" - resolve-path "^1.4.0" - koa-slow@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/koa-slow/-/koa-slow-2.1.0.tgz#39007ca628c620f2b307b90dbf423d7a0c9be971" @@ -6085,17 +6015,19 @@ koa-slow@2.1.0: lodash.isregexp "3.0.5" q "1.4.1" -koa-views@6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/koa-views/-/koa-views-6.3.1.tgz#8d23fa2118c71e9119fb47a75a58053345e37356" - integrity sha512-weIaPs2cCHWT2qK8qHRmwlZ29xRCvUVy1v/z12AGavVV5j4QIU0W/Y7OVBBu1sTkcO9dDJ25ajGYHGZ/aY43IQ== +koa-views@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/koa-views/-/koa-views-7.0.1.tgz#0c8f8e65d5cd2e08249430cb83dc361e49a17a5a" + integrity sha512-yS8751DXHXXDbdl/oUZd0PsgnxR0MLiguu77Eqrgu6yawE9Hi99wNKiVENb0Kfgsmvq/8px7YCI+USgxaTB1LA== dependencies: - consolidate "0.15.1" + "@types/koa" "^2.13.1" + consolidate "^0.16.0" debug "^4.1.0" get-paths "0.0.7" koa-send "^5.0.0" mz "^2.4.0" pretty "^2.0.0" + resolve-path "^1.4.0" koa@2.13.1: version "2.13.1" @@ -6169,11 +6101,6 @@ last-run@^1.1.0: default-resolution "^2.0.0" es6-weak-map "^2.0.1" -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= - lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" @@ -6400,7 +6327,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20: +lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -6412,11 +6339,6 @@ log-symbols@4.0.0: dependencies: chalk "^4.0.0" -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= - lookup-dns-cache@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/lookup-dns-cache/-/lookup-dns-cache-2.1.0.tgz#6362340e269071e20b6f0bcf51da98873411e051" @@ -6516,10 +6438,10 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-it-anchor@7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-7.0.2.tgz#1ac28261e0ea377b8298a1a6760e422921346a03" - integrity sha512-UtYFAkce16mJlixXUMXUf14ZmOWK2YHLGKUpkZUn98w3qP8nVhb7k5sCBZmVHGMq3SpYtrxQ5XOdHQHRuLR40Q== +markdown-it-anchor@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-7.1.0.tgz#30fb21497bf59e83ff4d1ddc052d821962e2489e" + integrity sha512-loQggrwsIkkP7TOrESvmYkV2ikbQNNKhHcWyqC7/C2CmfHl1tkUizJJU8C5aGgg7J6oXVQJx17gk7i47tNn/lQ== markdown-it@12.0.4: version "12.0.4" @@ -6742,10 +6664,10 @@ mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mocha@8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.3.0.tgz#a83a7432d382ae1ca29686062d7fdc2c36f63fe5" - integrity sha512-TQqyC89V1J/Vxx0DhJIXlq9gbbL9XFNdeLQ1+JsnZsVaSOV1z3tWfw0qZmQJGQRIfkvZcs7snQnZnOCKoldq1Q== +mocha@8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.3.2.tgz#53406f195fa86fbdebe71f8b1c6fb23221d69fcc" + integrity sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg== dependencies: "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" @@ -7010,7 +6932,7 @@ nopt@^5.0.0: dependencies: abbrev "1" -normalize-package-data@^2.3.2: +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -7333,7 +7255,7 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-cancelable@2.0.0, p-cancelable@^2.0.0: +p-cancelable@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== @@ -7487,16 +7409,16 @@ parse5-htmlparser2-tree-adapter@^6.0.0: dependencies: parse5 "^6.0.1" -parse5@5.1.1, parse5@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== - parse5@6.0.1, parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parse5@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + parseurl@^1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -7590,10 +7512,10 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pathval@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" - integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== peek-readable@^3.1.0: version "3.1.0" @@ -7855,10 +7777,10 @@ postcss-filter-plugins@^2.0.0: dependencies: postcss "^5.0.4" -postcss-loader@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-5.0.0.tgz#bea95363dcb550d72ceb612ce44663356b7782d7" - integrity sha512-bOvyWP5VHCJbThbv7wrBwCBc3DsVpyCfd+k/wHOL3wTAMMHmSSfNts90EADf8bHa6I810ird1JBEKmBRgJu3cg== +postcss-loader@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-5.2.0.tgz#ccd6668a778902d653602289c765a8bc481986dc" + integrity sha512-uSuCkENFeUaOYsKrXm0eNNgVIxc71z8RcckLMbVw473rGojFnrUeqEz6zBgXsH2q1EIzXnO/4pEz9RhALjlITA== dependencies: cosmiconfig "^7.0.0" klona "^2.0.4" @@ -7995,28 +7917,11 @@ postcss-minify-selectors@^4.0.2: postcss "^7.0.0" postcss-selector-parser "^3.0.0" -postcss-modules-extract-imports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" - integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== - dependencies: - postcss "^7.0.5" - postcss-modules-extract-imports@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== -postcss-modules-local-by-default@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" - integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== - dependencies: - icss-utils "^4.1.1" - postcss "^7.0.32" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.1.0" - postcss-modules-local-by-default@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" @@ -8026,14 +7931,6 @@ postcss-modules-local-by-default@^4.0.0: postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" -postcss-modules-scope@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" - integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - postcss-modules-scope@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" @@ -8041,14 +7938,6 @@ postcss-modules-scope@^3.0.0: dependencies: postcss-selector-parser "^6.0.4" -postcss-modules-values@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" - integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== - dependencies: - icss-utils "^4.0.0" - postcss "^7.0.6" - postcss-modules-values@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" @@ -8056,19 +7945,18 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-modules@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-3.2.2.tgz#ee390de0f9f18e761e1778dfb9be26685c02c51f" - integrity sha512-JQ8IAqHELxC0N6tyCg2UF40pACY5oiL6UpiqqcIFRWqgDYO8B0jnxzoQ0EOpPrWXvcpu6BSbQU/3vSiq7w8Nhw== +postcss-modules@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-4.0.0.tgz#2bc7f276ab88f3f1b0fadf6cbd7772d43b5f3b9b" + integrity sha512-ghS/ovDzDqARm4Zj6L2ntadjyQMoyJmi0JkLlYtH2QFLrvNlxH5OAVRPWPeKilB0pY7SbuhO173KOWkPAxRJcw== dependencies: generic-names "^2.0.1" icss-replace-symbols "^1.1.0" lodash.camelcase "^4.3.0" - postcss "^7.0.32" - postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.2" - postcss-modules-scope "^2.2.0" - postcss-modules-values "^3.0.0" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" string-hash "^1.1.1" postcss-normalize-charset@^1.1.0: @@ -8248,7 +8136,7 @@ postcss-selector-parser@^3.0.0: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== @@ -8315,10 +8203,10 @@ postcss-zindex@^2.0.1: postcss "^5.0.4" uniqs "^2.0.0" -postcss@8.2.7, postcss@^8.2.4: - version "8.2.7" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.7.tgz#48ed8d88b4de10afa0dfd1c3f840aa57b55c4d47" - integrity sha512-DsVLH3xJzut+VT+rYr0mtvOtpTjSyqDwPf5EZWXcb0uAKfitGpTY9Ec+afi2+TgdN8rWS9Cs88UDYehKo/RvOw== +postcss@8.2.8, postcss@^8.1.10, postcss@^8.2.8: + version "8.2.8" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.8.tgz#0b90f9382efda424c4f0f69a2ead6f6830d08ece" + integrity sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw== dependencies: colorette "^1.2.2" nanoid "^3.1.20" @@ -8334,7 +8222,7 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 source-map "^0.5.6" supports-color "^3.2.3" -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27: version "7.0.32" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== @@ -8365,10 +8253,10 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" -prebuild-install@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.0.tgz#669022bcde57c710a869e39c5ca6bf9cd207f316" - integrity sha512-h2ZJ1PXHKWZpp1caLw0oX9sagVpL2YTk+ZwInQbQ3QqNd4J03O6MpFNmMTJlkfgPENWqe5kP0WjQLqz5OjLfsw== +prebuild-install@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.1.tgz#5902172f7a40eb67305b96c2a695db32636ee26d" + integrity sha512-7GOJrLuow8yeiyv75rmvZyeMGzl8mdEX5gY69d6a6bHWmiPevwqFw+tQavhK0EYMaSg3/KD24cWqeQv1EWsqDQ== dependencies: detect-libc "^1.0.3" expand-template "^2.0.3" @@ -8435,10 +8323,10 @@ prismjs@1.23.0: optionalDependencies: clipboard "^2.0.0" -probe-image-size@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-6.0.0.tgz#4a85b19d5af4e29a8de7d53a9aa036f6fd02f5f4" - integrity sha512-99PZ5+RU4gqiTfK5ZDMDkZtn6eL4WlKfFyVJV7lFQvH3iGmQ85DqMTOdxorERO26LHkevR2qsxnHp0x/2UDJPA== +probe-image-size@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.0.1.tgz#ac815d78bdf8c5643af6e541a572c0077c75f2c1" + integrity sha512-4ua2TGdO761JWaTgbLD2Uq9yFvB34QBAxnqz9GOs3w4+SC4dfbQcnnNY8loHjZV5NRW8PSQRz+WZ6NfUvbuRDA== dependencies: deepmerge "^4.0.0" needle "^2.5.2" @@ -8495,115 +8383,113 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.24, psl@^1.1.28: +psl@^1.1.24, psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== -pug-attrs@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-2.0.4.tgz#b2f44c439e4eb4ad5d4ef25cac20d18ad28cc336" - integrity sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ== +pug-attrs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-3.0.0.tgz#b10451e0348165e31fad1cc23ebddd9dc7347c41" + integrity sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA== dependencies: - constantinople "^3.0.1" - js-stringify "^1.0.1" - pug-runtime "^2.0.5" + constantinople "^4.0.1" + js-stringify "^1.0.2" + pug-runtime "^3.0.0" -pug-code-gen@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-2.0.3.tgz#122eb9ada9b5bf601705fe15aaa0a7d26bc134ab" - integrity sha512-r9sezXdDuZJfW9J91TN/2LFbiqDhmltTFmGpHTsGdrNGp3p4SxAjjXEfnuK2e4ywYsRIVP0NeLbSAMHUcaX1EA== +pug-code-gen@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-3.0.2.tgz#ad190f4943133bf186b60b80de483100e132e2ce" + integrity sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg== dependencies: - constantinople "^3.1.2" + constantinople "^4.0.1" doctypes "^1.1.0" - js-stringify "^1.0.1" - pug-attrs "^2.0.4" - pug-error "^1.3.3" - pug-runtime "^2.0.5" - void-elements "^2.0.1" - with "^5.0.0" - -pug-error@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-1.3.3.tgz#f342fb008752d58034c185de03602dd9ffe15fa6" - integrity sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ== + js-stringify "^1.0.2" + pug-attrs "^3.0.0" + pug-error "^2.0.0" + pug-runtime "^3.0.0" + void-elements "^3.1.0" + with "^7.0.0" + +pug-error@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-2.0.0.tgz#5c62173cb09c34de2a2ce04f17b8adfec74d8ca5" + integrity sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ== -pug-filters@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-3.1.1.tgz#ab2cc82db9eeccf578bda89130e252a0db026aa7" - integrity sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg== +pug-filters@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-4.0.0.tgz#d3e49af5ba8472e9b7a66d980e707ce9d2cc9b5e" + integrity sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A== dependencies: - clean-css "^4.1.11" - constantinople "^3.0.1" + constantinople "^4.0.1" jstransformer "1.0.0" - pug-error "^1.3.3" - pug-walk "^1.1.8" - resolve "^1.1.6" - uglify-js "^2.6.1" + pug-error "^2.0.0" + pug-walk "^2.0.0" + resolve "^1.15.1" -pug-lexer@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-4.1.0.tgz#531cde48c7c0b1fcbbc2b85485c8665e31489cfd" - integrity sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA== +pug-lexer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-5.0.1.tgz#ae44628c5bef9b190b665683b288ca9024b8b0d5" + integrity sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w== dependencies: - character-parser "^2.1.1" - is-expression "^3.0.0" - pug-error "^1.3.3" + character-parser "^2.2.0" + is-expression "^4.0.0" + pug-error "^2.0.0" -pug-linker@^3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-3.0.6.tgz#f5bf218b0efd65ce6670f7afc51658d0f82989fb" - integrity sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg== +pug-linker@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-4.0.0.tgz#12cbc0594fc5a3e06b9fc59e6f93c146962a7708" + integrity sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw== dependencies: - pug-error "^1.3.3" - pug-walk "^1.1.8" + pug-error "^2.0.0" + pug-walk "^2.0.0" -pug-load@^2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-2.0.12.tgz#d38c85eb85f6e2f704dea14dcca94144d35d3e7b" - integrity sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg== +pug-load@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-3.0.0.tgz#9fd9cda52202b08adb11d25681fb9f34bd41b662" + integrity sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ== dependencies: - object-assign "^4.1.0" - pug-walk "^1.1.8" + object-assign "^4.1.1" + pug-walk "^2.0.0" -pug-parser@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-5.0.1.tgz#03e7ada48b6840bd3822f867d7d90f842d0ffdc9" - integrity sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA== +pug-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-6.0.0.tgz#a8fdc035863a95b2c1dc5ebf4ecf80b4e76a1260" + integrity sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw== dependencies: - pug-error "^1.3.3" - token-stream "0.0.1" + pug-error "^2.0.0" + token-stream "1.0.0" -pug-runtime@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-2.0.5.tgz#6da7976c36bf22f68e733c359240d8ae7a32953a" - integrity sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw== +pug-runtime@^3.0.0, pug-runtime@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-3.0.1.tgz#f636976204723f35a8c5f6fad6acda2a191b83d7" + integrity sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg== -pug-strip-comments@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz#cc1b6de1f6e8f5931cf02ec66cdffd3f50eaf8a8" - integrity sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw== +pug-strip-comments@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz#f94b07fd6b495523330f490a7f554b4ff876303e" + integrity sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ== dependencies: - pug-error "^1.3.3" + pug-error "^2.0.0" -pug-walk@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-1.1.8.tgz#b408f67f27912f8c21da2f45b7230c4bd2a5ea7a" - integrity sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA== +pug-walk@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-2.0.0.tgz#417aabc29232bb4499b5b5069a2b2d2a24d5f5fe" + integrity sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ== -pug@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pug/-/pug-2.0.4.tgz#ee7682ec0a60494b38d48a88f05f3b0ac931377d" - integrity sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw== - dependencies: - pug-code-gen "^2.0.2" - pug-filters "^3.1.1" - pug-lexer "^4.1.0" - pug-linker "^3.0.6" - pug-load "^2.0.12" - pug-parser "^5.0.1" - pug-runtime "^2.0.5" - pug-strip-comments "^1.0.4" +pug@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pug/-/pug-3.0.2.tgz#f35c7107343454e43bc27ae0ff76c731b78ea535" + integrity sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw== + dependencies: + pug-code-gen "^3.0.2" + pug-filters "^4.0.0" + pug-lexer "^5.0.1" + pug-linker "^4.0.0" + pug-load "^3.0.0" + pug-parser "^6.0.0" + pug-runtime "^3.0.1" + pug-strip-comments "^2.0.0" pump@^2.0.0: version "2.0.1" @@ -8645,10 +8531,10 @@ punycode@^1.4.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -pureimage@0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/pureimage/-/pureimage-0.2.5.tgz#10c9d314bcdfba712229ec70c3849164f537c248" - integrity sha512-D/oP8uaS8HLIOPqaxeVU0ZcJHUwvvFjeclKwn0RBeZJn3TBtZgKn7FVBN5auuCHQTC4K/wDAHfkY/JOuGCiohQ== +pureimage@0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/pureimage/-/pureimage-0.2.7.tgz#e6da5868e42d03a94aab7ad1f9da67c865914ab8" + integrity sha512-F3z3QAoIpgqI0Uu122Y18oTRo0Y/aloQSqaAfRSiO4LRIQIHOjP+qUFl8sJ04mhOJ1sHsRfgZ8CUjQqVSXfV1g== dependencies: jpeg-js "^0.4.1" opentype.js "^0.4.3" @@ -8783,6 +8669,16 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + readable-stream@1.1.x: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -8964,7 +8860,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== -repeat-string@^1.5.2, repeat-string@^1.6.1: +repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -8999,12 +8895,12 @@ request-promise-core@1.1.2: dependencies: lodash "^4.17.11" -request-promise-core@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" - integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== dependencies: - lodash "^4.17.15" + lodash "^4.17.19" request-promise-native@1.0.7: version "1.0.7" @@ -9015,12 +8911,12 @@ request-promise-native@1.0.7: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request-promise-native@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" - integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== +request-promise-native@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== dependencies: - request-promise-core "1.1.3" + request-promise-core "1.1.4" stealthy-require "^1.1.1" tough-cookie "^2.3.3" @@ -9172,7 +9068,7 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.4.0, is-core-module "^2.0.0" path-parse "^1.0.6" -resolve@^1.14.2: +resolve@^1.14.2, resolve@^1.15.1: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -9207,13 +9103,6 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8= - dependencies: - align-text "^0.1.1" - rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -9251,6 +9140,13 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== +rxjs@^6.6.3: + version "6.6.6" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" + integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== + dependencies: + tslib "^1.9.0" + s-age@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/s-age/-/s-age-1.1.2.tgz#c0cf15233ccc93f41de92ea42c36d957977d1ea2" @@ -9308,7 +9204,7 @@ sax@>=0.6.0, sax@^1.2.4, sax@~1.2.1, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^5.0.0: +saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== @@ -9425,17 +9321,17 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -sharp@0.27.1: - version "0.27.1" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.27.1.tgz#cd04926406a697b58dfc5fb62e3c7a3a2d68389a" - integrity sha512-IQNXWdspb4nZcJemXa6cfgz+JvKONsuqP8Mwi1Oti23Uo7+J+UF2jihJDf6I1BQbrmhcZ0lagH/1WYG+ReAzyQ== +sharp@0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.27.2.tgz#a939775e630e88600c0b5e68f20593aea722252f" + integrity sha512-w3FVoONPG/x5MXCc3wsjOS+b9h3CI60qkus6EPQU4dkT0BDm0PyGhDCK6KhtfT3/vbeOMOXAKFNSw+I3QGWkMA== dependencies: array-flatten "^3.0.0" color "^3.1.3" detect-libc "^1.0.3" node-addon-api "^3.1.0" npmlog "^4.1.2" - prebuild-install "^6.0.0" + prebuild-install "^6.0.1" semver "^7.3.4" simple-get "^4.0.0" tar-fs "^2.1.1" @@ -9597,7 +9493,7 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: +source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -9622,6 +9518,11 @@ sparkles@^1.0.0: resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== +spawn-command@^0.0.2-1: + version "0.0.2-1" + resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" + integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= + spawn-sync@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-2.0.0.tgz#3af5ba4b73cc5dc8a41d3747eede71e98d949555" @@ -9951,7 +9852,7 @@ summaly@2.4.0: require-all "2.2.0" trace-redirect "1.0.6" -supports-color@8.1.1: +supports-color@8.1.1, supports-color@^8.1.0: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -10098,10 +9999,10 @@ syslog-pro@1.0.0: dependencies: moment "^2.22.2" -systeminformation@5.6.1: - version "5.6.1" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.6.1.tgz#3726ee88b0ba50ec2268f64242fbd9a93df6546c" - integrity sha512-5wJlHB4fzcrNENaqDVzy51+NlL0QRrLQ6pSRQOKl4k8v4jvYTlJUxChZYwpphk3McUw9iWPRcjdpKbTEj2Ucuw== +systeminformation@5.6.7: + version "5.6.7" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.6.7.tgz#f56bbc89cac2de4bcb085b96a93b9d627bd6aba8" + integrity sha512-NTgaL6AsRoXKbfZs6t+BkCUwLZjqIiT4IwqGUV2f7PgvDz8359HzOF0xYgDlTHCBR2GeWhFQAfo2wmoYTpz/og== syuilo-password-strength@0.0.1: version "0.0.1" @@ -10370,10 +10271,10 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -token-stream@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-0.0.1.tgz#ceeefc717a76c4316f126d0b9dbaa55d7e7df01a" - integrity sha1-zu78cXp2xDFvEm0LnbqlXX598Bo= +token-stream@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-1.0.0.tgz#cc200eab2613f4166d27ff9afc7ca56d49df6eb4" + integrity sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ= token-types@^2.0.0: version "2.0.0" @@ -10400,6 +10301,15 @@ tough-cookie@^3.0.1: psl "^1.1.28" punycode "^2.1.1" +tough-cookie@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.1.2" + tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" @@ -10420,10 +10330,15 @@ trace-redirect@1.0.6: resolved "https://registry.yarnpkg.com/trace-redirect/-/trace-redirect-1.0.6.tgz#ac629b5bf8247d30dde5a35fe9811b811075b504" integrity sha512-UUfa1DjjU5flcjMdaFIiIEGDTyu2y/IiMjOX4uGXa7meKBS4vD4f2Uy/tken9Qkd4Jsm4sRsfZcIIPqrRVF3Mg== -ts-loader@8.0.17: - version "8.0.17" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.17.tgz#98f2ccff9130074f4079fd89b946b4c637b1f2fc" - integrity sha512-OeVfSshx6ot/TCxRwpBHQ/4lRzfgyTkvi7ghDVrLXOHzTbSK413ROgu/xNqM72i3AFeAIJgQy78FwSMKmOW68w== +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + +ts-loader@8.0.18: + version "8.0.18" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.18.tgz#b2385cbe81c34ad9f997915129cdde3ad92a61ea" + integrity sha512-hRZzkydPX30XkLaQwJTDcWDoxZHK6IrEMDQpNd7tgcakFruFkeUp/aY+9hBb7BUGb+ZWKI0jiOGMo0MckwzdDQ== dependencies: chalk "^4.1.0" enhanced-resolve "^4.0.0" @@ -10534,6 +10449,16 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +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== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -10596,26 +10521,16 @@ typeorm@0.2.31: yargonaut "^1.1.2" yargs "^16.0.3" -typescript@4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72" - integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== +typescript@4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3" + integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== -uglify-js@^2.6.1: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" - integrity sha1-KcVzMUgFe7Th913zW3qcty5qWd0= - dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" - uglify-js@^3.5.1: version "3.9.1" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.9.1.tgz#a56a71c8caa2d36b5556cc1fd57df01ae3491539" @@ -10623,11 +10538,6 @@ uglify-js@^3.5.1: dependencies: commander "~2.20.3" -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc= - ulid@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/ulid/-/ulid-2.3.0.tgz#93063522771a9774121a84d126ecd3eb9804071f" @@ -10686,6 +10596,11 @@ unique-stream@^2.0.2: json-stable-stringify-without-jsonify "^1.0.1" through2-filter "^3.0.0" +universalify@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + unload@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7" @@ -10912,10 +10827,10 @@ vinyl@^2.0.0, vinyl@^2.2.0: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -void-elements@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" - integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= +void-elements@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk= vue-color@2.8.1: version "2.8.1" @@ -10927,10 +10842,10 @@ vue-color@2.8.1: material-colors "^1.0.0" tinycolor2 "^1.1.2" -vue-eslint-parser@^7.5.0: - version "7.5.0" - resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.5.0.tgz#b68221c55fee061899afcfb4441ec74c1495285e" - integrity sha512-6EHzl00hIpy4yWZo3qSbtvtVw1A1cTKOv1w95QSuAqGgk4113XtRjvNIiEGo49r0YWOPYsrmI4Dl64axL5Agrw== +vue-eslint-parser@^7.6.0: + version "7.6.0" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.6.0.tgz#01ea1a2932f581ff244336565d712801f8f72561" + integrity sha512-QXxqH8ZevBrtiZMZK0LpwaMfevQi9UL7lY6Kcp+ogWHC88AuwUPwwCIzkOUc1LR4XsYAt/F9yHXAB/QoD17QXA== dependencies: debug "^4.1.1" eslint-scope "^5.0.0" @@ -10958,10 +10873,10 @@ vue-prism-editor@2.0.0-alpha.2: resolved "https://registry.yarnpkg.com/vue-prism-editor/-/vue-prism-editor-2.0.0-alpha.2.tgz#aa53a88efaaed628027cbb282c2b1d37fc7c5c69" integrity sha512-Gu42ba9nosrE+gJpnAEuEkDMqG9zSUysIR8SdXUw8MQKDjBnnNR9lHC18uOr/ICz7yrA/5c7jHJr9lpElODC7w== -vue-router@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.0.4.tgz#ad9b4b7bbdad622407b4ff189b1646f48c1e9053" - integrity sha512-uN6PDEaYdU9aRO7mU+Dkr1uaY49hV3fucEDG/Vre/Qj8ct3RoJS16vcPrvKVzn69zDDjBV5b9Xw7fZA9r6b/Iw== +vue-router@4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.0.5.tgz#dd0a4134bc950c37aef64b973e9ee1008428d8fa" + integrity sha512-AQq+pllb6FCc7rS6vh4PPcce3XA1jgK3hKNkQ4hXHwoVN7jOeAOMKCnX7XAX3etV9rmN7iNW8iIwgPk95ckBjw== vue-style-loader@4.1.3: version "4.1.3" @@ -10971,14 +10886,14 @@ vue-style-loader@4.1.3: hash-sum "^1.0.2" loader-utils "^1.0.2" -vue@3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.5.tgz#de1b82eba24abfe71e0970fc9b8d4b2babdc3fe1" - integrity sha512-TfaprOmtsAfhQau7WsomXZ8d9op/dkQLNIq8qPV3A0Vxs6GR5E+c1rfJS1SDkXRQj+dFyfnec7+U0Be1huiScg== +vue@3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.7.tgz#8bcff51f8be570f9e4ce8cc5f52e2ab0fe3c74a1" + integrity sha512-8h4TikD+JabbMK9aRlBO4laG0AtNHRPHynxYgWZ9sq1YUPfzynd9Jeeb27XNyZytC7aCQRX9xe1+TQJuc181Tw== dependencies: - "@vue/compiler-dom" "3.0.5" - "@vue/runtime-dom" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/compiler-dom" "3.0.7" + "@vue/runtime-dom" "3.0.7" + "@vue/shared" "3.0.7" vuedraggable@4.0.1: version "4.0.1" @@ -11075,10 +10990,10 @@ webpack-sources@^2.1.1: source-list-map "^2.0.1" source-map "^0.6.1" -webpack@5.24.2: - version "5.24.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.24.2.tgz#33790dad631e8b639f4246d762e257720875fe54" - integrity sha512-uxxKYEY4kMNjP+D2Y+8aw5Vd7ar4pMuKCNemxV26ysr1nk0YDiQTylg9U3VZIdkmI0YHa0uC8ABxL+uGxGWWJg== +webpack@5.26.3: + version "5.26.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.26.3.tgz#bafd439abac08fbb82657ec855d038743b725ab8" + integrity sha512-z/F2lt2N1fZqaud1B4SzjL3OW03eULThbBXQ2OX4LSrZX4N9k1A5d0Rje3zS2g887DTWyAV0KGqEf64ois2dhg== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.46" @@ -11183,29 +11098,21 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0= - -with@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/with/-/with-5.1.1.tgz#fa4daa92daf32c4ea94ed453c81f04686b575dfe" - integrity sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4= +with@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/with/-/with-7.0.2.tgz#ccee3ad542d25538a7a7a80aad212b9828495bac" + integrity sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w== dependencies: - acorn "^3.1.0" - acorn-globals "^3.0.0" + "@babel/parser" "^7.9.6" + "@babel/types" "^7.9.6" + assert-never "^1.2.1" + babel-walk "3.0.0-canary-5" word-wrap@^1.2.3, 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== -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= - workerpool@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" @@ -11263,15 +11170,10 @@ write-json-file@^2.3.0: sort-keys "^2.0.0" write-file-atomic "^2.0.0" -ws@7.4.3: - version "7.4.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" - integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== - -ws@^7.2.3: - version "7.4.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb" - integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ== +ws@7.4.4, ws@^7.4.4: + version "7.4.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" + integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== xev@2.0.1: version "2.0.1" @@ -11406,7 +11308,7 @@ yargs-unparser@2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" -yargs@16.2.0, yargs@^16.0.0, yargs@^16.0.3: +yargs@16.2.0, yargs@^16.0.0, yargs@^16.0.3, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== @@ -11454,16 +11356,6 @@ yargs@^7.1.0: y18n "^3.2.1" yargs-parser "^5.0.0" -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - integrity sha1-9+572FfdfB0tOMDnTvvWgdFDH9E= - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" - ylru@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f"