Merge remote-tracking branch 'parent/main' into kb_migration

This commit is contained in:
KMY 2023-09-10 16:17:34 +09:00
commit 979292d950
56 changed files with 774 additions and 277 deletions

View file

@ -250,3 +250,116 @@ jobs:
with: with:
name: e2e-screenshots name: e2e-screenshots
path: tmp/screenshots/ path: tmp/screenshots/
test-search:
name: Testing search
runs-on: ubuntu-latest
needs:
- build
services:
postgres:
image: postgres:14-alpine
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
redis:
image: redis:7-alpine
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.9
env:
discovery.type: single-node
xpack.security.enabled: false
options: >-
--health-cmd "curl http://localhost:9200/_cluster/health"
--health-interval 10s
--health-timeout 5s
--health-retries 10
ports:
- 9200:9200
env:
DB_HOST: localhost
DB_USER: postgres
DB_PASS: postgres
DISABLE_SIMPLECOV: true
RAILS_ENV: test
BUNDLE_WITH: test
ES_ENABLED: true
ES_HOST: localhost
ES_PORT: 9200
strategy:
fail-fast: false
matrix:
ruby-version:
- '3.0'
- '3.1'
- '.ruby-version'
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
path: './public'
name: ${{ github.sha }}
- name: Update package index
run: sudo apt-get update
- name: Set up Node.js
uses: actions/setup-node@v3
with:
cache: yarn
node-version-file: '.nvmrc'
- name: Install native Ruby dependencies
run: sudo apt-get install -y libicu-dev libidn11-dev
- name: Install additional system dependencies
run: sudo apt-get install -y ffmpeg imagemagick
- name: Set up bundler cache
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version}}
bundler-cache: true
- run: yarn --frozen-lockfile
- name: Load database schema
run: './bin/rails db:create db:schema:load db:seed'
- run: bundle exec rake spec:search
- name: Archive logs
uses: actions/upload-artifact@v3
if: failure()
with:
name: test-search-logs-${{ matrix.ruby-version }}
path: log/
- name: Archive test screenshots
uses: actions/upload-artifact@v3
if: failure()
with:
name: test-search-screenshots
path: tmp/screenshots/

2
.nvmrc
View file

@ -1 +1 @@
16.20 20.6

View file

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1.4 # syntax=docker/dockerfile:1.4
# This needs to be bookworm-slim because the Ruby image is built on bookworm-slim # This needs to be bookworm-slim because the Ruby image is built on bookworm-slim
ARG NODE_VERSION="16.20-bookworm-slim" ARG NODE_VERSION="20.6-bookworm-slim"
FROM ghcr.io/moritzheiber/ruby-jemalloc:3.2.2-slim as ruby FROM ghcr.io/moritzheiber/ruby-jemalloc:3.2.2-slim as ruby
FROM node:${NODE_VERSION} as build FROM node:${NODE_VERSION} as build

View file

@ -27,4 +27,5 @@ More information on HTTP Signatures, as well as examples, can be found here: htt
- Linked-Data Signatures: https://docs.joinmastodon.org/spec/security/#ld - Linked-Data Signatures: https://docs.joinmastodon.org/spec/security/#ld
- Bearcaps: https://docs.joinmastodon.org/spec/bearcaps/ - Bearcaps: https://docs.joinmastodon.org/spec/bearcaps/
- Followers collection synchronization: https://git.activitypub.dev/ActivityPubDev/Fediverse-Enhancement-Proposals/src/branch/main/feps/fep-8fcf.md - Followers collection synchronization: https://codeberg.org/fediverse/fep/src/branch/main/fep/8fcf/fep-8fcf.md
- Search indexing consent for actors: https://codeberg.org/fediverse/fep/src/branch/main/fep/5feb/fep-5feb.md

View file

@ -520,7 +520,7 @@ GEM
pastel (0.8.0) pastel (0.8.0)
tty-color (~> 0.5) tty-color (~> 0.5)
pg (1.5.4) pg (1.5.4)
pghero (3.3.3) pghero (3.3.4)
activerecord (>= 6) activerecord (>= 6)
posix-spawn (0.3.15) posix-spawn (0.3.15)
premailer (1.21.0) premailer (1.21.0)

View file

@ -13,9 +13,9 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through
## Supported Versions ## Supported Versions
| Version | Supported | | Version | Supported |
| ------- | --------- | | ------- | ---------------- |
| 4.1.x | Yes | | 4.1.x | Yes |
| 4.0.x | Yes | | 4.0.x | Until 2023-10-31 |
| 3.5.x | Yes | | 3.5.x | Until 2023-12-31 |
| < 3.5 | No | | < 3.5 | No |

3
Vagrantfile vendored
View file

@ -76,7 +76,8 @@ path.logs: /var/log/elasticsearch
network.host: 0.0.0.0 network.host: 0.0.0.0
http.port: 9200 http.port: 9200
discovery.seed_hosts: ["localhost"] discovery.seed_hosts: ["localhost"]
cluster.initial_master_nodes: ["node-1"]' > /etc/elasticsearch/elasticsearch.yml cluster.initial_master_nodes: ["node-1"]
xpack.security.enabled: false' > /etc/elasticsearch/elasticsearch.yml
sudo systemctl restart elasticsearch sudo systemctl restart elasticsearch

View file

@ -16,7 +16,9 @@ class Api::V1::DirectoriesController < Api::BaseController
end end
def set_accounts def set_accounts
@accounts = accounts_scope.offset(params[:offset]).limit(limit_param(DEFAULT_ACCOUNTS_LIMIT)) with_read_replica do
@accounts = accounts_scope.offset(params[:offset]).limit(limit_param(DEFAULT_ACCOUNTS_LIMIT))
end
end end
def accounts_scope def accounts_scope

View file

@ -119,7 +119,7 @@ module SignatureVerification
private private
def fail_with!(message, **options) def fail_with!(message, **options)
Rails.logger.warn { "Signature verification failed: #{message}" } Rails.logger.debug { "Signature verification failed: #{message}" }
@signature_verification_failure_reason = { error: message }.merge(options) @signature_verification_failure_reason = { error: message }.merge(options)
@signed_request_actor = nil @signed_request_actor = nil

View file

@ -1,3 +1,7 @@
import { fromJS } from 'immutable';
import { searchHistory } from 'mastodon/settings';
import api from '../api'; import api from '../api';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
@ -15,8 +19,7 @@ export const SEARCH_EXPAND_REQUEST = 'SEARCH_EXPAND_REQUEST';
export const SEARCH_EXPAND_SUCCESS = 'SEARCH_EXPAND_SUCCESS'; export const SEARCH_EXPAND_SUCCESS = 'SEARCH_EXPAND_SUCCESS';
export const SEARCH_EXPAND_FAIL = 'SEARCH_EXPAND_FAIL'; export const SEARCH_EXPAND_FAIL = 'SEARCH_EXPAND_FAIL';
export const SEARCH_RESULT_CLICK = 'SEARCH_RESULT_CLICK'; export const SEARCH_HISTORY_UPDATE = 'SEARCH_HISTORY_UPDATE';
export const SEARCH_RESULT_FORGET = 'SEARCH_RESULT_FORGET';
export function changeSearch(value) { export function changeSearch(value) {
return { return {
@ -170,16 +173,34 @@ export const openURL = (value, history, onFailure) => (dispatch, getState) => {
}); });
}; };
export const clickSearchResult = (q, type) => ({ export const clickSearchResult = (q, type) => (dispatch, getState) => {
type: SEARCH_RESULT_CLICK, const previous = getState().getIn(['search', 'recent']);
const me = getState().getIn(['meta', 'me']);
const current = previous.add(fromJS({ type, q })).takeLast(4);
result: { searchHistory.set(me, current.toJS());
type, dispatch(updateSearchHistory(current));
q, };
},
export const forgetSearchResult = q => (dispatch, getState) => {
const previous = getState().getIn(['search', 'recent']);
const me = getState().getIn(['meta', 'me']);
const current = previous.filterNot(result => result.get('q') === q);
searchHistory.set(me, current.toJS());
dispatch(updateSearchHistory(current));
};
export const updateSearchHistory = recent => ({
type: SEARCH_HISTORY_UPDATE,
recent,
}); });
export const forgetSearchResult = q => ({ export const hydrateSearch = () => (dispatch, getState) => {
type: SEARCH_RESULT_FORGET, const me = getState().getIn(['meta', 'me']);
q, const history = searchHistory.get(me);
});
if (history !== null) {
dispatch(updateSearchHistory(history));
}
};

View file

@ -2,6 +2,7 @@ import { Iterable, fromJS } from 'immutable';
import { hydrateCompose } from './compose'; import { hydrateCompose } from './compose';
import { importFetchedAccounts } from './importer'; import { importFetchedAccounts } from './importer';
import { hydrateSearch } from './search';
export const STORE_HYDRATE = 'STORE_HYDRATE'; export const STORE_HYDRATE = 'STORE_HYDRATE';
export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY'; export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY';
@ -20,6 +21,7 @@ export function hydrateStore(rawState) {
}); });
dispatch(hydrateCompose()); dispatch(hydrateCompose());
dispatch(hydrateSearch());
dispatch(importFetchedAccounts(Object.values(rawState.accounts))); dispatch(importFetchedAccounts(Object.values(rawState.accounts)));
}; };
} }

View file

@ -205,11 +205,11 @@ class Audio extends PureComponent {
}; };
toggleMute = () => { toggleMute = () => {
const muted = !this.state.muted; const muted = !(this.state.muted || this.state.volume === 0);
this.setState({ muted }, () => { this.setState((state) => ({ muted, volume: Math.max(state.volume || 0.5, 0.05) }), () => {
if (this.gainNode) { if (this.gainNode) {
this.gainNode.gain.value = muted ? 0 : this.state.volume; this.gainNode.gain.value = this.state.muted ? 0 : this.state.volume;
} }
}); });
}; };
@ -287,7 +287,7 @@ class Audio extends PureComponent {
const { x } = getPointerPosition(this.volume, e); const { x } = getPointerPosition(this.volume, e);
if(!isNaN(x)) { if(!isNaN(x)) {
this.setState({ volume: x }, () => { this.setState((state) => ({ volume: x, muted: state.muted && x === 0 }), () => {
if (this.gainNode) { if (this.gainNode) {
this.gainNode.gain.value = this.state.muted ? 0 : x; this.gainNode.gain.value = this.state.muted ? 0 : x;
} }
@ -466,8 +466,9 @@ class Audio extends PureComponent {
render () { render () {
const { src, intl, alt, lang, editable, autoPlay, sensitive, blurhash } = this.props; const { src, intl, alt, lang, editable, autoPlay, sensitive, blurhash } = this.props;
const { paused, muted, volume, currentTime, duration, buffer, dragging, revealed } = this.state; const { paused, volume, currentTime, duration, buffer, dragging, revealed } = this.state;
const progress = Math.min((currentTime / duration) * 100, 100); const progress = Math.min((currentTime / duration) * 100, 100);
const muted = this.state.muted || volume === 0;
let warning; let warning;
@ -557,12 +558,12 @@ class Audio extends PureComponent {
<button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button> <button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
<div className={classNames('video-player__volume', { active: this.state.hovered })} ref={this.setVolumeRef} onMouseDown={this.handleVolumeMouseDown}> <div className={classNames('video-player__volume', { active: this.state.hovered })} ref={this.setVolumeRef} onMouseDown={this.handleVolumeMouseDown}>
<div className='video-player__volume__current' style={{ width: `${volume * 100}%`, backgroundColor: this._getAccentColor() }} /> <div className='video-player__volume__current' style={{ width: `${muted ? 0 : volume * 100}%`, backgroundColor: this._getAccentColor() }} />
<span <span
className='video-player__volume__handle' className='video-player__volume__handle'
tabIndex={0} tabIndex={0}
style={{ left: `${volume * 100}%`, backgroundColor: this._getAccentColor() }} style={{ left: `${muted ? 0 : volume * 100}%`, backgroundColor: this._getAccentColor() }}
/> />
</div> </div>

View file

@ -8,7 +8,7 @@ import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { searchEnabled } from 'mastodon/initial_state'; import { domain, searchEnabled } from 'mastodon/initial_state';
import { HASHTAG_REGEX } from 'mastodon/utils/hashtags'; import { HASHTAG_REGEX } from 'mastodon/utils/hashtags';
const messages = defineMessages({ const messages = defineMessages({
@ -16,6 +16,17 @@ const messages = defineMessages({
placeholderSignedIn: { id: 'search.search_or_paste', defaultMessage: 'Search or paste URL' }, placeholderSignedIn: { id: 'search.search_or_paste', defaultMessage: 'Search or paste URL' },
}); });
const labelForRecentSearch = search => {
switch(search.get('type')) {
case 'account':
return `@${search.get('q')}`;
case 'hashtag':
return `#${search.get('q')}`;
default:
return search.get('q');
}
};
class Search extends PureComponent { class Search extends PureComponent {
static contextTypes = { static contextTypes = {
@ -54,6 +65,7 @@ class Search extends PureComponent {
{ label: <><mark>before:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('before:') } }, { label: <><mark>before:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('before:') } },
{ label: <><mark>during:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('during:') } }, { label: <><mark>during:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('during:') } },
{ label: <><mark>after:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('after:') } }, { label: <><mark>after:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('after:') } },
{ label: <><mark>in:</mark> <FormattedList type='disjunction' value={['all', 'library']} /></>, action: e => { e.preventDefault(); this._insertText('in:') } }
]; ];
setRef = c => { setRef = c => {
@ -187,12 +199,16 @@ class Search extends PureComponent {
}; };
handleRecentSearchClick = search => { handleRecentSearchClick = search => {
const { onChange } = this.props;
const { router } = this.context; const { router } = this.context;
if (search.get('type') === 'account') { if (search.get('type') === 'account') {
router.history.push(`/@${search.get('q')}`); router.history.push(`/@${search.get('q')}`);
} else if (search.get('type') === 'hashtag') { } else if (search.get('type') === 'hashtag') {
router.history.push(`/tags/${search.get('q')}`); router.history.push(`/tags/${search.get('q')}`);
} else {
onChange(search.get('q'));
this._submit(search.get('type'));
} }
this._unfocus(); this._unfocus();
@ -221,11 +237,15 @@ class Search extends PureComponent {
} }
_submit (type) { _submit (type) {
const { onSubmit, openInRoute } = this.props; const { onSubmit, openInRoute, value, onClickSearchResult } = this.props;
const { router } = this.context; const { router } = this.context;
onSubmit(type); onSubmit(type);
if (value) {
onClickSearchResult(value, type);
}
if (openInRoute) { if (openInRoute) {
router.history.push('/search'); router.history.push('/search');
} }
@ -243,7 +263,7 @@ class Search extends PureComponent {
const { recent } = this.props; const { recent } = this.props;
return recent.toArray().map(search => ({ return recent.toArray().map(search => ({
label: search.get('type') === 'account' ? `@${search.get('q')}` : `#${search.get('q')}`, label: labelForRecentSearch(search),
action: () => this.handleRecentSearchClick(search), action: () => this.handleRecentSearchClick(search),
@ -354,18 +374,20 @@ class Search extends PureComponent {
</> </>
)} )}
{searchEnabled && ( <h4><FormattedMessage id='search_popout.options' defaultMessage='Search options' /></h4>
<>
<h4><FormattedMessage id='search_popout.options' defaultMessage='Search options' /></h4>
<div className='search__popout__menu'> {searchEnabled ? (
{this.defaultOptions.map(({ key, label, action }, i) => ( <div className='search__popout__menu'>
<button key={key} onMouseDown={action} className={classNames('search__popout__menu__item', { selected: selectedOption === (options.length + i) })}> {this.defaultOptions.map(({ key, label, action }, i) => (
{label} <button key={key} onMouseDown={action} className={classNames('search__popout__menu__item', { selected: selectedOption === ((options.length || recent.size) + i) })}>
</button> {label}
))} </button>
</div> ))}
</> </div>
) : (
<div className='search__popout__menu__message'>
<FormattedMessage id='search_popout.full_text_search_disabled_message' defaultMessage='Not available on {domain}.' values={{ domain }} />
</div>
)} )}
</div> </div>
</div> </div>

View file

@ -15,7 +15,7 @@ import Search from '../components/search';
const mapStateToProps = state => ({ const mapStateToProps = state => ({
value: state.getIn(['search', 'value']), value: state.getIn(['search', 'value']),
submitted: state.getIn(['search', 'submitted']), submitted: state.getIn(['search', 'submitted']),
recent: state.getIn(['search', 'recent']), recent: state.getIn(['search', 'recent']).reverse(),
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({

View file

@ -217,8 +217,9 @@ class Video extends PureComponent {
const { x } = getPointerPosition(this.volume, e); const { x } = getPointerPosition(this.volume, e);
if(!isNaN(x)) { if(!isNaN(x)) {
this.setState({ volume: x }, () => { this.setState((state) => ({ volume: x, muted: state.muted && x === 0 }), () => {
this.video.volume = x; this.video.volume = x;
this.video.muted = this.state.muted;
}); });
} }
}, 15); }, 15);
@ -425,10 +426,11 @@ class Video extends PureComponent {
}; };
toggleMute = () => { toggleMute = () => {
const muted = !this.video.muted; const muted = !(this.video.muted || this.state.volume === 0);
this.setState({ muted }, () => { this.setState((state) => ({ muted, volume: Math.max(state.volume || 0.5, 0.05) }), () => {
this.video.muted = muted; this.video.volume = this.state.volume;
this.video.muted = this.state.muted;
}); });
}; };
@ -501,8 +503,9 @@ class Video extends PureComponent {
render () { render () {
const { preview, src, aspectRatio, onOpenVideo, onCloseVideo, intl, alt, lang, detailed, sensitive, editable, blurhash, autoFocus } = this.props; const { preview, src, aspectRatio, onOpenVideo, onCloseVideo, intl, alt, lang, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
const { currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state; const { currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, revealed } = this.state;
const progress = Math.min((currentTime / duration) * 100, 100); const progress = Math.min((currentTime / duration) * 100, 100);
const muted = this.state.muted || volume === 0;
let preload; let preload;
@ -593,12 +596,12 @@ class Video extends PureComponent {
<button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button> <button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
<div className={classNames('video-player__volume', { active: this.state.hovered })} onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}> <div className={classNames('video-player__volume', { active: this.state.hovered })} onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
<div className='video-player__volume__current' style={{ width: `${volume * 100}%` }} /> <div className='video-player__volume__current' style={{ width: `${muted ? 0 : volume * 100}%` }} />
<span <span
className={classNames('video-player__volume__handle')} className={classNames('video-player__volume__handle')}
tabIndex={0} tabIndex={0}
style={{ left: `${volume * 100}%` }} style={{ left: `${muted ? 0 : volume * 100}%` }}
/> />
</div> </div>

View file

@ -621,6 +621,7 @@
"searchability.public.short": "Public", "searchability.public.short": "Public",
"searchability.unlisted.long": "Your followers and reactionners can find", "searchability.unlisted.long": "Your followers and reactionners can find",
"searchability.unlisted.short": "Followers and reactionners", "searchability.unlisted.short": "Followers and reactionners",
"search_popout.full_text_search_disabled_message": "Not available on {domain}.",
"search_popout.language_code": "ISO language code", "search_popout.language_code": "ISO language code",
"search_popout.options": "Search options", "search_popout.options": "Search options",
"search_popout.quick_actions": "Quick actions", "search_popout.quick_actions": "Quick actions",

View file

@ -14,8 +14,7 @@ import {
SEARCH_SHOW, SEARCH_SHOW,
SEARCH_EXPAND_REQUEST, SEARCH_EXPAND_REQUEST,
SEARCH_EXPAND_SUCCESS, SEARCH_EXPAND_SUCCESS,
SEARCH_RESULT_CLICK, SEARCH_HISTORY_UPDATE,
SEARCH_RESULT_FORGET,
} from '../actions/search'; } from '../actions/search';
const initialState = ImmutableMap({ const initialState = ImmutableMap({
@ -80,10 +79,8 @@ export default function search(state = initialState, action) {
case SEARCH_EXPAND_SUCCESS: case SEARCH_EXPAND_SUCCESS:
const results = action.searchType === 'hashtags' ? ImmutableOrderedSet(fromJS(action.results.hashtags)) : action.results[action.searchType].map(item => item.id); const results = action.searchType === 'hashtags' ? ImmutableOrderedSet(fromJS(action.results.hashtags)) : action.results[action.searchType].map(item => item.id);
return state.updateIn(['results', action.searchType], list => list.union(results)).setIn(['noMoreResults', action.searchType], results.size <= 0); return state.updateIn(['results', action.searchType], list => list.union(results)).setIn(['noMoreResults', action.searchType], results.size <= 0);
case SEARCH_RESULT_CLICK: case SEARCH_HISTORY_UPDATE:
return state.update('recent', set => set.add(fromJS(action.result))); return state.set('recent', ImmutableOrderedSet(fromJS(action.recent)));
case SEARCH_RESULT_FORGET:
return state.update('recent', set => set.filterNot(result => result.get('q') === action.q));
default: default:
return state; return state;
} }

View file

@ -46,3 +46,4 @@ export default class Settings {
export const pushNotificationsSetting = new Settings('mastodon_push_notification_data'); export const pushNotificationsSetting = new Settings('mastodon_push_notification_data');
export const tagHistory = new Settings('mastodon_tag_history'); export const tagHistory = new Settings('mastodon_tag_history');
export const bannerSettings = new Settings('mastodon_banner_settings'); export const bannerSettings = new Settings('mastodon_banner_settings');
export const searchHistory = new Settings('mastodon_search_history');

View file

@ -845,7 +845,7 @@ body > [data-popper-placement] {
} }
p { p {
margin-bottom: 20px; margin-bottom: 22px;
white-space: pre-wrap; white-space: pre-wrap;
unicode-bidi: plaintext; unicode-bidi: plaintext;
@ -9236,7 +9236,6 @@ noscript {
border-radius: 8px; border-radius: 8px;
border: 1px solid $highlight-text-color; border: 1px solid $highlight-text-color;
background: rgba($highlight-text-color, 0.15); background: rgba($highlight-text-color, 0.15);
padding-inline-end: 45px;
overflow: hidden; overflow: hidden;
&__background-image { &__background-image {
@ -9299,7 +9298,7 @@ noscript {
position: absolute; position: absolute;
inset-inline-end: 0; inset-inline-end: 0;
top: 0; top: 0;
padding: 10px; padding: 15px 10px;
.icon-button { .icon-button {
color: $highlight-text-color; color: $highlight-text-color;

View file

@ -65,7 +65,14 @@ class AccountStatusesFilter
end end
def filtered_reblogs_scope def filtered_reblogs_scope
Status.left_outer_joins(:reblog).where(reblog_of_id: nil).or(Status.where.not(reblogs_statuses: { account_id: current_account.excluded_from_timeline_account_ids })) scope = Status.left_outer_joins(reblog: :account)
scope
.where(reblog_of_id: nil)
.or(
scope
.where.not(reblog: { account_id: current_account.excluded_from_timeline_account_ids })
.where.not(reblog: { accounts: { domain: current_account.excluded_from_timeline_domains } })
)
end end
def only_media_scope def only_media_scope

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
class Admin::AccountStatusesFilter < AccountStatusesFilter
private
def blocked?
false
end
end

View file

@ -8,7 +8,7 @@ class SearchQueryParser < Parslet::Parser
rule(:operator) { (str('+') | str('-')).as(:operator) } rule(:operator) { (str('+') | str('-')).as(:operator) }
rule(:prefix) { term >> colon } rule(:prefix) { term >> colon }
rule(:shortcode) { (colon >> term >> colon.maybe).as(:shortcode) } rule(:shortcode) { (colon >> term >> colon.maybe).as(:shortcode) }
rule(:phrase) { (quote >> (term >> space.maybe).repeat >> quote).as(:phrase) } rule(:phrase) { (quote >> (match('[^\s"]').repeat(1).as(:term) >> space.maybe).repeat >> quote).as(:phrase) }
rule(:clause) { (operator.maybe >> prefix.maybe.as(:prefix) >> (phrase | term | shortcode)).as(:clause) | prefix.as(:clause) | quote.as(:junk) } rule(:clause) { (operator.maybe >> prefix.maybe.as(:prefix) >> (phrase | term | shortcode)).as(:clause) | prefix.as(:clause) | quote.as(:junk) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) } rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query) root(:query)

View file

@ -10,6 +10,7 @@ class SearchQueryTransformer < Parslet::Transform
after after
during during
in in
domain
).freeze ).freeze
class Query class Query
@ -72,7 +73,7 @@ class SearchQueryTransformer < Parslet::Transform
searchability_limited, searchability_limited,
] ]
definition_should << searchability_public if %i(public).include?(@searchability) definition_should << searchability_public if %i(public).include?(@searchability)
definition_should << searchability_private if %i(public private).include?(@searchability) definition_should << searchability_private if %i(public unlisted private).include?(@searchability)
{ {
bool: { bool: {
@ -95,9 +96,7 @@ class SearchQueryTransformer < Parslet::Transform
bool: { bool: {
must: [ must: [
{ {
term: { term: { _index: StatusesIndex.index_name },
_index: StatusesIndex.index_name,
},
}, },
{ {
term: { term: {
@ -117,12 +116,7 @@ class SearchQueryTransformer < Parslet::Transform
term: { _index: StatusesIndex.index_name }, term: { _index: StatusesIndex.index_name },
}, },
{ {
exists: { term: { searchable_by: @options[:current_account].id },
field: 'searchability',
},
},
{
term: { searchable_by: @account.id },
}, },
], ],
must_not: [ must_not: [
@ -139,9 +133,7 @@ class SearchQueryTransformer < Parslet::Transform
bool: { bool: {
must: [ must: [
{ {
exists: { term: { _index: StatusesIndex.index_name },
field: 'searchability',
},
}, },
{ {
term: { searchability: 'public' }, term: { searchability: 'public' },
@ -156,9 +148,7 @@ class SearchQueryTransformer < Parslet::Transform
bool: { bool: {
must: [ must: [
{ {
exists: { term: { _index: StatusesIndex.index_name },
field: 'searchability',
},
}, },
{ {
term: { searchability: 'private' }, term: { searchability: 'private' },
@ -176,20 +166,27 @@ class SearchQueryTransformer < Parslet::Transform
bool: { bool: {
must: [ must: [
{ {
exists: { term: { _index: StatusesIndex.index_name },
field: 'searchability',
},
}, },
{ {
term: { searchability: 'limited' }, term: { searchability: 'limited' },
}, },
{ {
term: { account_id: @account.id }, term: { account_id: @options[:current_account].id },
}, },
], ],
}, },
} }
end end
def following_account_ids
return @following_account_ids if defined?(@following_account_ids)
account_exists_sql = Account.where('accounts.id = follows.target_account_id').where(searchability: %w(public private)).reorder(nil).select(1).to_sql
status_exists_sql = Status.where('statuses.account_id = follows.target_account_id').where(reblog_of_id: nil).where(searchability: %w(public private)).reorder(nil).select(1).to_sql
following_accounts = Follow.where(account_id: @options[:current_account].id).merge(Account.where("EXISTS (#{account_exists_sql})").or(Account.where("EXISTS (#{status_exists_sql})")))
@following_account_ids = following_accounts.pluck(:target_account_id)
end
end end
class Operator class Operator
@ -217,7 +214,7 @@ class SearchQueryTransformer < Parslet::Transform
def to_query def to_query
if @term.start_with?('#') if @term.start_with?('#')
{ match: { tags: { query: @term } } } { match: { tags: { query: @term, operator: 'and' } } }
else else
# { multi_match: { type: 'most_fields', query: @term, fields: ['text', 'text.stemmed'], operator: 'and' } } # { multi_match: { type: 'most_fields', query: @term, fields: ['text', 'text.stemmed'], operator: 'and' } }
{ match_phrase: { text: { query: @term } } } { match_phrase: { text: { query: @term } } }
@ -332,17 +329,16 @@ class SearchQueryTransformer < Parslet::Transform
rule(clause: subtree(:clause)) do rule(clause: subtree(:clause)) do
prefix = clause[:prefix][:term].to_s if clause[:prefix] prefix = clause[:prefix][:term].to_s if clause[:prefix]
operator = clause[:operator]&.to_s operator = clause[:operator]&.to_s
term = clause[:phrase] ? clause[:phrase].map { |term| term[:term].to_s }.join(' ') : clause[:term].to_s
if clause[:prefix] && SUPPORTED_PREFIXES.include?(prefix) if clause[:prefix] && SUPPORTED_PREFIXES.include?(prefix)
PrefixClause.new(prefix, operator, clause[:term].to_s, current_account: current_account) PrefixClause.new(prefix, operator, term, current_account: current_account)
elsif clause[:prefix] elsif clause[:prefix]
TermClause.new(operator, "#{prefix} #{clause[:term]}") TermClause.new(operator, "#{prefix} #{term}")
elsif clause[:term] elsif clause[:term]
TermClause.new(operator, clause[:term].to_s) TermClause.new(operator, term)
elsif clause[:shortcode]
TermClause.new(operator, ":#{clause[:term]}:")
elsif clause[:phrase] elsif clause[:phrase]
PhraseClause.new(operator, clause[:phrase].is_a?(Array) ? clause[:phrase].map { |p| p[:term].to_s }.join(' ') : clause[:phrase].to_s) PhraseClause.new(operator, term)
else else
raise "Unexpected clause type: #{clause}" raise "Unexpected clause type: #{clause}"
end end

View file

@ -29,7 +29,7 @@ class TagManager
domain = uri.host + (uri.port ? ":#{uri.port}" : '') domain = uri.host + (uri.port ? ":#{uri.port}" : '')
TagManager.instance.web_domain?(domain) TagManager.instance.web_domain?(domain)
rescue Addressable::URI::InvalidURIError rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
false false
end end
end end

View file

@ -130,10 +130,10 @@ class Account < ApplicationRecord
scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
scope :without_unapproved, -> { left_outer_joins(:user).merge(User.approved.confirmed).or(remote) } scope :without_unapproved, -> { left_outer_joins(:user).merge(User.approved.confirmed).or(remote) }
scope :searchable, -> { without_unapproved.without_suspended.where(moved_to_account_id: nil) } scope :searchable, -> { without_unapproved.without_suspended.where(moved_to_account_id: nil) }
scope :discoverable, -> { searchable.without_silenced.where(discoverable: true).left_outer_joins(:account_stat) } scope :discoverable, -> { searchable.without_silenced.where(discoverable: true).joins(:account_stat) }
scope :followable_by, ->(account) { joins(arel_table.join(Follow.arel_table, Arel::Nodes::OuterJoin).on(arel_table[:id].eq(Follow.arel_table[:target_account_id]).and(Follow.arel_table[:account_id].eq(account.id))).join_sources).where(Follow.arel_table[:id].eq(nil)).joins(arel_table.join(FollowRequest.arel_table, Arel::Nodes::OuterJoin).on(arel_table[:id].eq(FollowRequest.arel_table[:target_account_id]).and(FollowRequest.arel_table[:account_id].eq(account.id))).join_sources).where(FollowRequest.arel_table[:id].eq(nil)) } scope :followable_by, ->(account) { joins(arel_table.join(Follow.arel_table, Arel::Nodes::OuterJoin).on(arel_table[:id].eq(Follow.arel_table[:target_account_id]).and(Follow.arel_table[:account_id].eq(account.id))).join_sources).where(Follow.arel_table[:id].eq(nil)).joins(arel_table.join(FollowRequest.arel_table, Arel::Nodes::OuterJoin).on(arel_table[:id].eq(FollowRequest.arel_table[:target_account_id]).and(FollowRequest.arel_table[:account_id].eq(account.id))).join_sources).where(FollowRequest.arel_table[:id].eq(nil)) }
scope :by_recent_status, -> { order(Arel.sql('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc, accounts.id desc')) } scope :by_recent_status, -> { order(Arel.sql('account_stats.last_status_at DESC NULLS LAST')) }
scope :by_recent_sign_in, -> { order(Arel.sql('(case when users.current_sign_in_at is null then 1 else 0 end) asc, users.current_sign_in_at desc, accounts.id desc')) } scope :by_recent_sign_in, -> { order(Arel.sql('users.current_sign_in_at DESC NULLS LAST')) }
scope :popular, -> { order('account_stats.followers_count desc') } scope :popular, -> { order('account_stats.followers_count desc') }
scope :by_domain_and_subdomains, ->(domain) { where(domain: Instance.by_domain_and_subdomains(domain).select(:domain)) } scope :by_domain_and_subdomains, ->(domain) { where(domain: Instance.by_domain_and_subdomains(domain).select(:domain)) }
scope :not_excluded_by_account, ->(account) { where.not(id: account.excluded_from_timeline_account_ids) } scope :not_excluded_by_account, ->(account) { where.not(id: account.excluded_from_timeline_account_ids) }

View file

@ -183,6 +183,6 @@ class Admin::StatusBatchAction
end end
def allowed_status_ids def allowed_status_ids
AccountStatusesFilter.new(@report.target_account, current_account).results.with_discarded.where(id: status_ids).pluck(:id) Admin::AccountStatusesFilter.new(@report.target_account, current_account).results.with_discarded.where(id: status_ids).pluck(:id)
end end
end end

View file

@ -176,7 +176,7 @@ class MediaAttachment < ApplicationRecord
DEFAULT_STYLES = [:original].freeze DEFAULT_STYLES = [:original].freeze
GLOBAL_CONVERT_OPTIONS = { GLOBAL_CONVERT_OPTIONS = {
all: '-quality 90 +profile "!icc,*" +set modify-date +set create-date', all: '-quality 90 +profile "!icc,*" +set modify-date -define jpeg:dct-method=float +set create-date',
}.freeze }.freeze
belongs_to :account, inverse_of: :media_attachments, optional: true belongs_to :account, inverse_of: :media_attachments, optional: true

View file

@ -12,7 +12,7 @@ class Admin::StatusPolicy < ApplicationPolicy
end end
def show? def show?
role.can?(:manage_reports, :manage_users) && (record.public_visibility? || record.unlisted_visibility? || record.public_unlisted_visibility? || record.reported?) role.can?(:manage_reports, :manage_users) && (record.public_visibility? || record.unlisted_visibility? || record.public_unlisted_visibility? || record.reported? || viewable_through_normal_policy?)
end end
def destroy? def destroy?
@ -26,4 +26,10 @@ class Admin::StatusPolicy < ApplicationPolicy
def review? def review?
role.can?(:manage_taxonomies) role.can?(:manage_taxonomies)
end end
private
def viewable_through_normal_policy?
StatusPolicy.new(current_account, record, @preloaded_relations).show?
end
end end

View file

@ -9,6 +9,7 @@ class StatusesSearchService < BaseService
@offset = options[:offset].to_i @offset = options[:offset].to_i
@searchability = options[:searchability]&.to_sym @searchability = options[:searchability]&.to_sym
convert_deprecated_options!
status_search_results status_search_results
end end
@ -26,16 +27,28 @@ class StatusesSearchService < BaseService
[] []
end end
def following_account_ids
return @following_account_ids if defined?(@following_account_ids)
account_exists_sql = Account.where('accounts.id = follows.target_account_id').where(searchability: %w(public private)).reorder(nil).select(1).to_sql
status_exists_sql = Status.where('statuses.account_id = follows.target_account_id').where(reblog_of_id: nil).where(searchability: %w(public private)).reorder(nil).select(1).to_sql
following_accounts = Follow.where(account_id: @account.id).merge(Account.where("EXISTS (#{account_exists_sql})").or(Account.where("EXISTS (#{status_exists_sql})")))
@following_account_ids = following_accounts.pluck(:target_account_id)
end
def parsed_query def parsed_query
SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query), current_account: @account, searchability: @searchability) SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query), current_account: @account, searchability: @searchability)
end end
def convert_deprecated_options!
syntax_options = []
if @options[:account_id]
username = Account.select(:username, :domain).find(@options[:account_id]).acct
syntax_options << "from:@#{username}"
end
if @options[:min_id]
timestamp = Mastodon::Snowflake.to_time(@options[:min_id])
syntax_options << "after:\"#{timestamp.iso8601}\""
end
if @options[:max_id]
timestamp = Mastodon::Snowflake.to_time(@options[:max_id])
syntax_options << "before:\"#{timestamp.iso8601}\""
end
@query = "#{@query} #{syntax_options.join(' ')}".strip if syntax_options.any?
end
end end

View file

@ -10,7 +10,8 @@
%br/ %br/
= t('admin.trends.tags.used_by_over_week', count: tag.history.reduce(0) { |sum, day| sum + day.accounts }) = link_to tag_path(tag), target: '_blank' do
= t('admin.trends.tags.used_by_over_week', count: tag.history.reduce(0) { |sum, day| sum + day.accounts })
- if tag.trendable? && (rank = Trends.tags.rank(tag.id)) - if tag.trendable? && (rank = Trends.tags.rank(tag.id))
· ·

View file

@ -1,6 +1,6 @@
<%= raw t('application_mailer.salutation', name: display_name(@me)) %> <%= raw t('application_mailer.salutation', name: display_name(@me)) %>
<%= raw t('admin_mailer.new_appeal.body', target: @appeal.account.username, action_taken_by: @appeal.strike.account.username, date: l(@appeal.strike.created_at), type: t(@appeal.strike.action, scope: 'admin_mailer.new_appeal.actions')) %> <%= raw t('admin_mailer.new_appeal.body', target: @appeal.account.username, action_taken_by: @appeal.strike.account.username, date: l(@appeal.strike.created_at, format: :with_time_zone), type: t(@appeal.strike.action, scope: 'admin_mailer.new_appeal.actions')) %>
> <%= raw word_wrap(@appeal.text, break_sequence: "\n> ") %> > <%= raw word_wrap(@appeal.text, break_sequence: "\n> ") %>

View file

@ -42,4 +42,4 @@
= link_to a.remote_url, a.remote_url = link_to a.remote_url, a.remote_url
%p.status-footer %p.status-footer
= link_to l(status.created_at.in_time_zone(time_zone.presence)), web_url("@#{status.account.pretty_acct}/#{status.id}") = link_to l(status.created_at.in_time_zone(time_zone.presence), format: :with_time_zone), web_url("@#{status.account.pretty_acct}/#{status.id}")

View file

@ -36,7 +36,7 @@
%tbody %tbody
%tr %tr
%td.column-cell.text-center %td.column-cell.text-center
%p= t 'user_mailer.appeal_approved.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence)), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence)) %p= t 'user_mailer.appeal_approved.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone)
%table.email-table{ cellspacing: 0, cellpadding: 0 } %table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody %tbody

View file

@ -2,6 +2,6 @@
=== ===
<%= t 'user_mailer.appeal_approved.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence)), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence)) %> <%= t 'user_mailer.appeal_approved.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone) %>
=> <%= root_url %> => <%= root_url %>

View file

@ -36,7 +36,7 @@
%tbody %tbody
%tr %tr
%td.column-cell.text-center %td.column-cell.text-center
%p= t 'user_mailer.appeal_rejected.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence)), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence)) %p= t 'user_mailer.appeal_rejected.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone)
%table.email-table{ cellspacing: 0, cellpadding: 0 } %table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody %tbody

View file

@ -2,6 +2,6 @@
=== ===
<%= t 'user_mailer.appeal_rejected.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence)), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence)) %> <%= t 'user_mailer.appeal_rejected.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone) %>
=> <%= root_url %> => <%= root_url %>

View file

@ -47,7 +47,7 @@
%strong= "#{t('sessions.browser')}:" %strong= "#{t('sessions.browser')}:"
%span{ title: @user_agent }= t 'sessions.description', browser: t("sessions.browsers.#{@detection.id}", default: @detection.id.to_s), platform: t("sessions.platforms.#{@detection.platform.id}", default: @detection.platform.id.to_s) %span{ title: @user_agent }= t 'sessions.description', browser: t("sessions.browsers.#{@detection.id}", default: @detection.id.to_s), platform: t("sessions.platforms.#{@detection.platform.id}", default: @detection.platform.id.to_s)
%br/ %br/
= l(@timestamp.in_time_zone(@resource.time_zone.presence)) = l(@timestamp.in_time_zone(@resource.time_zone.presence), format: :with_time_zone)
%table.email-table{ cellspacing: 0, cellpadding: 0 } %table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody %tbody

View file

@ -8,7 +8,7 @@
<%= t('sessions.ip') %>: <%= @remote_ip %> <%= t('sessions.ip') %>: <%= @remote_ip %>
<%= t('sessions.browser') %>: <%= t('sessions.description', browser: t("sessions.browsers.#{@detection.id}", default: "#{@detection.id}"), platform: t("sessions.platforms.#{@detection.platform.id}", default: "#{@detection.platform.id}")) %> <%= t('sessions.browser') %>: <%= t('sessions.description', browser: t("sessions.browsers.#{@detection.id}", default: "#{@detection.id}"), platform: t("sessions.platforms.#{@detection.platform.id}", default: "#{@detection.platform.id}")) %>
<%= l(@timestamp.in_time_zone(@resource.time_zone.presence)) %> <%= l(@timestamp.in_time_zone(@resource.time_zone.presence), format: :with_time_zone) %>
<%= t 'user_mailer.suspicious_sign_in.further_actions_html', action: t('user_mailer.suspicious_sign_in.change_password') %> <%= t 'user_mailer.suspicious_sign_in.further_actions_html', action: t('user_mailer.suspicious_sign_in.change_password') %>

View file

@ -1888,6 +1888,7 @@ en:
default: "%b %d, %Y, %H:%M" default: "%b %d, %Y, %H:%M"
month: "%b %Y" month: "%b %Y"
time: "%H:%M" time: "%H:%M"
with_time_zone: "%b %d, %Y, %H:%M %Z"
translation: translation:
errors: errors:
quota_exceeded: The server-wide usage quota for the translation service has been exceeded. quota_exceeded: The server-wide usage quota for the translation service has been exceeded.

View file

@ -4,7 +4,7 @@ const { createHash } = require('crypto');
const { readFileSync } = require('fs'); const { readFileSync } = require('fs');
const { resolve } = require('path'); const { resolve } = require('path');
const CompressionPlugin = require('compression-webpack-plugin'); const CompressionPlugin = require('@renchap/compression-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const { merge } = require('webpack-merge'); const { merge } = require('webpack-merge');

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddIndexAccountStatsOnLastStatusAtAndAccountId < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
def change
add_index :account_stats, [:last_status_at, :account_id], order: { last_status_at: 'DESC NULLS LAST' }, algorithm: :concurrently
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_09_04_134623) do ActiveRecord::Schema[7.0].define(version: 2023_09_07_150100) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -100,6 +100,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_09_04_134623) do
t.datetime "last_status_at", precision: nil t.datetime "last_status_at", precision: nil
t.integer "group_activitypub_count" t.integer "group_activitypub_count"
t.index ["account_id"], name: "index_account_stats_on_account_id", unique: true t.index ["account_id"], name: "index_account_stats_on_account_id", unique: true
t.index ["last_status_at", "account_id"], name: "index_account_stats_on_last_status_at_and_account_id", order: { last_status_at: "DESC NULLS LAST" }
end end
create_table "account_statuses_cleanup_policies", force: :cascade do |t| create_table "account_statuses_cleanup_policies", force: :cascade do |t|

View file

@ -16,7 +16,7 @@ class Mastodon::SidekiqMiddleware
private private
def limit_backtrace_and_raise(exception) def limit_backtrace_and_raise(exception)
exception.set_backtrace(exception.backtrace.first(BACKTRACE_LIMIT)) exception.set_backtrace(exception.backtrace.first(BACKTRACE_LIMIT)) unless ENV['BACKTRACE']
raise exception raise exception
end end

View file

@ -104,6 +104,10 @@ module Mastodon::Snowflake
id id
end end
def to_time(id)
Time.at((id >> 16) / 1000).utc
end
private private
def already_defined? def already_defined?

View file

@ -9,3 +9,13 @@ if Rake::Task.task_defined?('spec:system')
Rake::Task['spec:system'].enhance ['spec:enable_system_specs'] Rake::Task['spec:system'].enhance ['spec:enable_system_specs']
end end
if Rake::Task.task_defined?('spec:search')
namespace :spec do
task :enable_search_specs do # rubocop:disable Rails/RakeEnvironment
ENV['RUN_SEARCH_SPECS'] = 'true'
end
end
Rake::Task['spec:search'].enhance ['spec:enable_search_specs']
end

View file

@ -47,6 +47,7 @@
"@material-design-icons/svg": "^0.14.10", "@material-design-icons/svg": "^0.14.10",
"@rails/ujs": "^7.0.6", "@rails/ujs": "^7.0.6",
"@reduxjs/toolkit": "^1.9.5", "@reduxjs/toolkit": "^1.9.5",
"@renchap/compression-webpack-plugin": "^6.1.4",
"@svgr/webpack": "^5.5.0", "@svgr/webpack": "^5.5.0",
"abortcontroller-polyfill": "^1.7.5", "abortcontroller-polyfill": "^1.7.5",
"arrow-key-navigation": "^1.2.0", "arrow-key-navigation": "^1.2.0",
@ -63,7 +64,6 @@
"classnames": "^2.3.2", "classnames": "^2.3.2",
"cocoon-js-vanilla": "^1.3.0", "cocoon-js-vanilla": "^1.3.0",
"color-blend": "^4.0.0", "color-blend": "^4.0.0",
"compression-webpack-plugin": "^6.1.1",
"core-js": "^3.30.2", "core-js": "^3.30.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "^5.2.7", "css-loader": "^5.2.7",
@ -137,7 +137,7 @@
"tiny-queue": "^0.2.1", "tiny-queue": "^0.2.1",
"twitter-text": "3.1.0", "twitter-text": "3.1.0",
"uuid": "^9.0.0", "uuid": "^9.0.0",
"webpack": "^4.46.0", "webpack": "^4.47.0",
"webpack-assets-manifest": "^4.0.6", "webpack-assets-manifest": "^4.0.6",
"webpack-bundle-analyzer": "^4.8.0", "webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^3.3.12", "webpack-cli": "^3.3.12",

View file

@ -52,24 +52,36 @@ describe Admin::StatusesController do
end end
describe 'POST #batch' do describe 'POST #batch' do
before do subject { post :batch, params: { :account_id => account.id, action => '', :admin_status_batch_action => { status_ids: status_ids } } }
post :batch, params: { :account_id => account.id, action => '', :admin_status_batch_action => { status_ids: status_ids } }
end
let(:status_ids) { [media_attached_status.id] } let(:status_ids) { [media_attached_status.id] }
context 'when action is report' do shared_examples 'when action is report' do
let(:action) { 'report' } let(:action) { 'report' }
it 'creates a report' do it 'creates a report' do
subject
report = Report.last report = Report.last
expect(report.target_account_id).to eq account.id expect(report.target_account_id).to eq account.id
expect(report.status_ids).to eq status_ids expect(report.status_ids).to eq status_ids
end end
it 'redirects to report page' do it 'redirects to report page' do
subject
expect(response).to redirect_to(admin_report_path(Report.last.id)) expect(response).to redirect_to(admin_report_path(Report.last.id))
end end
end end
it_behaves_like 'when action is report'
context 'when the moderator is blocked by the author' do
before do
account.block!(user.account)
end
it_behaves_like 'when action is report'
end
end end
end end

View file

@ -15,12 +15,13 @@ describe Api::V1::DirectoriesController do
describe 'GET #show' do describe 'GET #show' do
context 'with no params' do context 'with no params' do
before do before do
_local_unconfirmed_account = Fabricate( local_unconfirmed_account = Fabricate(
:account, :account,
domain: nil, domain: nil,
user: Fabricate(:user, confirmed_at: nil, approved: true), user: Fabricate(:user, confirmed_at: nil, approved: true),
username: 'local_unconfirmed' username: 'local_unconfirmed'
) )
local_unconfirmed_account.create_account_stat!
local_unapproved_account = Fabricate( local_unapproved_account = Fabricate(
:account, :account,
@ -28,15 +29,17 @@ describe Api::V1::DirectoriesController do
user: Fabricate(:user, confirmed_at: 10.days.ago), user: Fabricate(:user, confirmed_at: 10.days.ago),
username: 'local_unapproved' username: 'local_unapproved'
) )
local_unapproved_account.create_account_stat!
local_unapproved_account.user.update(approved: false) local_unapproved_account.user.update(approved: false)
_local_undiscoverable_account = Fabricate( local_undiscoverable_account = Fabricate(
:account, :account,
domain: nil, domain: nil,
user: Fabricate(:user, confirmed_at: 10.days.ago, approved: true), user: Fabricate(:user, confirmed_at: 10.days.ago, approved: true),
discoverable: false, discoverable: false,
username: 'local_undiscoverable' username: 'local_undiscoverable'
) )
local_undiscoverable_account.create_account_stat!
excluded_from_timeline_account = Fabricate( excluded_from_timeline_account = Fabricate(
:account, :account,
@ -44,18 +47,20 @@ describe Api::V1::DirectoriesController do
discoverable: true, discoverable: true,
username: 'remote_excluded_from_timeline' username: 'remote_excluded_from_timeline'
) )
excluded_from_timeline_account.create_account_stat!
Fabricate(:block, account: user.account, target_account: excluded_from_timeline_account) Fabricate(:block, account: user.account, target_account: excluded_from_timeline_account)
_domain_blocked_account = Fabricate( domain_blocked_account = Fabricate(
:account, :account,
domain: 'test.example', domain: 'test.example',
discoverable: true, discoverable: true,
username: 'remote_domain_blocked' username: 'remote_domain_blocked'
) )
domain_blocked_account.create_account_stat!
Fabricate(:account_domain_block, account: user.account, domain: 'test.example') Fabricate(:account_domain_block, account: user.account, domain: 'test.example')
end end
it 'returns only the local discoverable account' do it 'returns the local discoverable account and the remote discoverable account' do
local_discoverable_account = Fabricate( local_discoverable_account = Fabricate(
:account, :account,
domain: nil, domain: nil,
@ -63,6 +68,7 @@ describe Api::V1::DirectoriesController do
discoverable: true, discoverable: true,
username: 'local_discoverable' username: 'local_discoverable'
) )
local_discoverable_account.create_account_stat!
eligible_remote_account = Fabricate( eligible_remote_account = Fabricate(
:account, :account,
@ -70,13 +76,13 @@ describe Api::V1::DirectoriesController do
discoverable: true, discoverable: true,
username: 'eligible_remote' username: 'eligible_remote'
) )
eligible_remote_account.create_account_stat!
get :show get :show
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(body_as_json.size).to eq(2) expect(body_as_json.size).to eq(2)
expect(body_as_json.first[:id]).to include(eligible_remote_account.id.to_s) expect(body_as_json.pluck(:id)).to contain_exactly(eligible_remote_account.id.to_s, local_discoverable_account.id.to_s)
expect(body_as_json.second[:id]).to include(local_discoverable_account.id.to_s)
end end
end end
@ -85,6 +91,8 @@ describe Api::V1::DirectoriesController do
user = Fabricate(:user, confirmed_at: 10.days.ago, approved: true) user = Fabricate(:user, confirmed_at: 10.days.ago, approved: true)
local_account = Fabricate(:account, domain: nil, user: user) local_account = Fabricate(:account, domain: nil, user: user)
remote_account = Fabricate(:account, domain: 'host.example') remote_account = Fabricate(:account, domain: 'host.example')
local_account.create_account_stat!
remote_account.create_account_stat!
get :show, params: { local: '1' } get :show, params: { local: '1' }
@ -97,24 +105,23 @@ describe Api::V1::DirectoriesController do
context 'when ordered by active' do context 'when ordered by active' do
it 'returns accounts in order of most recent status activity' do it 'returns accounts in order of most recent status activity' do
status_old = Fabricate(:status) old_stat = Fabricate(:account_stat, last_status_at: 1.day.ago)
travel_to 10.seconds.from_now new_stat = Fabricate(:account_stat, last_status_at: 1.minute.ago)
status_new = Fabricate(:status)
get :show, params: { order: 'active' } get :show, params: { order: 'active' }
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(body_as_json.size).to eq(2) expect(body_as_json.size).to eq(2)
expect(body_as_json.first[:id]).to include(status_new.account.id.to_s) expect(body_as_json.first[:id]).to include(new_stat.account_id.to_s)
expect(body_as_json.second[:id]).to include(status_old.account.id.to_s) expect(body_as_json.second[:id]).to include(old_stat.account_id.to_s)
end end
end end
context 'when ordered by new' do context 'when ordered by new' do
it 'returns accounts in order of creation' do it 'returns accounts in order of creation' do
account_old = Fabricate(:account) account_old = Fabricate(:account_stat).account
travel_to 10.seconds.from_now travel_to 10.seconds.from_now
account_new = Fabricate(:account) account_new = Fabricate(:account_stat).account
get :show, params: { order: 'new' } get :show, params: { order: 'new' }

View file

@ -57,4 +57,24 @@ describe SearchQueryTransformer do
expect(subject.send(:filter_clauses)).to be_empty expect(subject.send(:filter_clauses)).to be_empty
end end
end end
context 'with \'"hello world"\'' do
let(:query) { '"hello world"' }
it 'transforms clauses' do
expect(subject.send(:must_clauses).map(&:phrase)).to contain_exactly('hello world')
expect(subject.send(:must_not_clauses)).to be_empty
expect(subject.send(:filter_clauses)).to be_empty
end
end
context 'with \'before:"2022-01-01 23:00"\'' do
let(:query) { 'before:"2022-01-01 23:00"' }
it 'transforms clauses' do
expect(subject.send(:must_clauses)).to be_empty
expect(subject.send(:must_not_clauses)).to be_empty
expect(subject.send(:filter_clauses).map(&:term)).to contain_exactly(lt: '2022-01-01 23:00', time_zone: 'UTC')
end
end
end end

View file

@ -228,6 +228,20 @@ RSpec.describe AccountStatusesFilter do
end end
end end
context 'when blocking a reblogged domain' do
let(:other_account) { Fabricate(:account, domain: 'example.com') }
let(:reblogging_status) { Fabricate(:status, account: other_account) }
let(:reblog) { Fabricate(:status, account: account, visibility: 'public', reblog: reblogging_status) }
before do
current_account.block_domain!(other_account.domain)
end
it 'does not return reblog of blocked domain' do
expect(subject.results.pluck(:id)).to_not include(reblog.id)
end
end
context 'when muting a reblogged account' do context 'when muting a reblogged account' do
let(:reblog) { status_with_reblog!('public') } let(:reblog) { status_with_reblog!('public') }

View file

@ -7,7 +7,8 @@ describe Admin::StatusPolicy do
let(:policy) { described_class } let(:policy) { described_class }
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account } let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
let(:john) { Fabricate(:account) } let(:john) { Fabricate(:account) }
let(:status) { Fabricate(:status) } let(:status) { Fabricate(:status, visibility: status_visibility) }
let(:status_visibility) { :public }
permissions :index?, :update?, :review?, :destroy? do permissions :index?, :update?, :review?, :destroy? do
context 'with an admin' do context 'with an admin' do
@ -26,7 +27,7 @@ describe Admin::StatusPolicy do
permissions :show? do permissions :show? do
context 'with an admin' do context 'with an admin' do
context 'with a public visible status' do context 'with a public visible status' do
before { allow(status).to receive(:public_visibility?).and_return(true) } let(:status_visibility) { :public }
it 'permits' do it 'permits' do
expect(policy).to permit(admin, status) expect(policy).to permit(admin, status)
@ -34,11 +35,21 @@ describe Admin::StatusPolicy do
end end
context 'with a not public visible status' do context 'with a not public visible status' do
before { allow(status).to receive(:public_visibility?).and_return(false) } let(:status_visibility) { :direct }
it 'denies' do it 'denies' do
expect(policy).to_not permit(admin, status) expect(policy).to_not permit(admin, status)
end end
context 'when the status mentions the admin' do
before do
status.mentions.create!(account: admin)
end
it 'permits' do
expect(policy).to permit(admin, status)
end
end
end end
end end

View file

@ -4,11 +4,17 @@ ENV['RAILS_ENV'] ||= 'test'
# This needs to be defined before Rails is initialized # This needs to be defined before Rails is initialized
RUN_SYSTEM_SPECS = ENV.fetch('RUN_SYSTEM_SPECS', false) RUN_SYSTEM_SPECS = ENV.fetch('RUN_SYSTEM_SPECS', false)
RUN_SEARCH_SPECS = ENV.fetch('RUN_SEARCH_SPECS', false)
if RUN_SYSTEM_SPECS if RUN_SYSTEM_SPECS
STREAMING_PORT = ENV.fetch('TEST_STREAMING_PORT', '4020') STREAMING_PORT = ENV.fetch('TEST_STREAMING_PORT', '4020')
ENV['STREAMING_API_BASE_URL'] = "http://localhost:#{STREAMING_PORT}" ENV['STREAMING_API_BASE_URL'] = "http://localhost:#{STREAMING_PORT}"
end end
if RUN_SEARCH_SPECS
# Include any configuration or setups specific to search tests here
end
require File.expand_path('../config/environment', __dir__) require File.expand_path('../config/environment', __dir__)
abort('The Rails environment is running in production mode!') if Rails.env.production? abort('The Rails environment is running in production mode!') if Rails.env.production?
@ -30,6 +36,7 @@ Sidekiq.logger = nil
# System tests config # System tests config
DatabaseCleaner.strategy = [:deletion] DatabaseCleaner.strategy = [:deletion]
streaming_server_manager = StreamingServerManager.new streaming_server_manager = StreamingServerManager.new
search_data_manager = SearchDataManager.new
Devise::Test::ControllerHelpers.module_eval do Devise::Test::ControllerHelpers.module_eval do
alias_method :original_sign_in, :sign_in alias_method :original_sign_in, :sign_in
@ -69,7 +76,14 @@ end
RSpec.configure do |config| RSpec.configure do |config|
# This is set before running spec:system, see lib/tasks/tests.rake # This is set before running spec:system, see lib/tasks/tests.rake
config.filter_run_excluding type: :system unless RUN_SYSTEM_SPECS config.filter_run_excluding type: lambda { |type|
case type
when :system
!RUN_SYSTEM_SPECS
when :search
!RUN_SEARCH_SPECS
end
}
config.fixture_path = Rails.root.join('spec', 'fixtures') config.fixture_path = Rails.root.join('spec', 'fixtures')
config.use_transactional_fixtures = true config.use_transactional_fixtures = true
config.order = 'random' config.order = 'random'
@ -113,10 +127,17 @@ RSpec.configure do |config|
Webpacker.compile Webpacker.compile
streaming_server_manager.start(port: STREAMING_PORT) streaming_server_manager.start(port: STREAMING_PORT)
end end
if RUN_SEARCH_SPECS
Chewy.strategy(:urgent)
search_data_manager.prepare_test_data
end
end end
config.after :suite do config.after :suite do
streaming_server_manager.stop streaming_server_manager.stop
search_data_manager.cleanup_test_data if RUN_SEARCH_SPECS
end end
config.around :each, type: :system do |example| config.around :each, type: :system do |example|
@ -137,6 +158,12 @@ RSpec.configure do |config|
self.use_transactional_tests = true self.use_transactional_tests = true
end end
config.around :each, type: :search do |example|
search_data_manager.populate_indexes
example.run
search_data_manager.remove_indexes
end
config.before(:each) do |example| config.before(:each) do |example|
unless example.metadata[:paperclip_processing] unless example.metadata[:paperclip_processing]
allow_any_instance_of(Paperclip::Attachment).to receive(:post_process).and_return(true) # rubocop:disable RSpec/AnyInstance allow_any_instance_of(Paperclip::Attachment).to receive(:post_process).and_return(true) # rubocop:disable RSpec/AnyInstance

View file

@ -0,0 +1,51 @@
# frozen_string_literal: true
require 'rails_helper'
describe AccountSearch do
describe 'a non-discoverable account becoming discoverable' do
let(:account) { Account.find_by(username: 'search_test_account_1') }
context 'when picking a non-discoverable account' do
it 'its bio is not in the AccountsIndex' do
results = AccountsIndex.filter(term: { username: account.username })
expect(results.count).to eq(1)
expect(results.first.text).to be_nil
end
end
context 'when the non-discoverable account becomes discoverable' do
it 'its bio is added to the AccountsIndex' do
account.discoverable = true
account.save!
results = AccountsIndex.filter(term: { username: account.username })
expect(results.count).to eq(1)
expect(results.first.text).to eq(account.note)
end
end
end
describe 'a discoverable account becoming non-discoverable' do
let(:account) { Account.find_by(username: 'search_test_account_0') }
context 'when picking an discoverable account' do
it 'has its bio in the AccountsIndex' do
results = AccountsIndex.filter(term: { username: account.username })
expect(results.count).to eq(1)
expect(results.first.text).to eq(account.note)
end
end
context 'when the discoverable account becomes non-discoverable' do
it 'its bio is removed from the AccountsIndex' do
account.discoverable = false
account.save!
results = AccountsIndex.filter(term: { username: account.username })
expect(results.count).to eq(1)
expect(results.first.text).to be_nil
end
end
end
end

View file

@ -0,0 +1,53 @@
# frozen_string_literal: true
require 'rails_helper'
describe AccountStatusesSearch do
describe 'a non-indexable account becoming indexable' do
let(:account) { Account.find_by(username: 'search_test_account_1') }
context 'when picking a non-indexable account' do
it 'has no statuses in the PublicStatusesIndex' do
expect(PublicStatusesIndex.filter(term: { account_id: account.id }).count).to eq(0)
end
it 'has statuses in the StatusesIndex' do
expect(StatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.count)
end
end
context 'when the non-indexable account becomes indexable' do
it 'adds the public statuses to the PublicStatusesIndex' do
account.indexable = true
account.save!
expect(PublicStatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.where(visibility: :public).count)
expect(StatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.count)
end
end
end
describe 'an indexable account becoming non-indexable' do
let(:account) { Account.find_by(username: 'search_test_account_0') }
context 'when picking an indexable account' do
it 'has statuses in the PublicStatusesIndex' do
expect(PublicStatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.where(visibility: :public).count)
end
it 'has statuses in the StatusesIndex' do
expect(StatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.count)
end
end
context 'when the indexable account becomes non-indexable' do
it 'removes the statuses from the PublicStatusesIndex' do
account.indexable = false
account.save!
expect(PublicStatusesIndex.filter(term: { account_id: account.id }).count).to eq(0)
expect(StatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.count)
end
end
end
end

View file

@ -135,3 +135,45 @@ class StreamingServerManager
@running_thread.join @running_thread.join
end end
end end
class SearchDataManager
def prepare_test_data
4.times do |i|
username = "search_test_account_#{i}"
account = Fabricate.create(:account, username: username, indexable: i.even?, discoverable: i.even?, note: "Lover of #{i}.")
2.times do |j|
Fabricate.create(:status, account: account, text: "#{username}'s #{j} post", visibility: j.even? ? :public : :private)
end
end
3.times do |i|
Fabricate.create(:tag, name: "search_test_tag_#{i}")
end
end
def indexes
[
AccountsIndex,
PublicStatusesIndex,
StatusesIndex,
TagsIndex,
]
end
def populate_indexes
indexes.each do |index_class|
index_class.purge!
index_class.import!
end
end
def remove_indexes
indexes.each(&:delete!)
end
def cleanup_test_data
Status.destroy_all
Account.destroy_all
Tag.destroy_all
end
end

307
yarn.lock
View file

@ -88,7 +88,7 @@
"@jridgewell/trace-mapping" "^0.3.17" "@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1" jsesc "^2.5.1"
"@babel/generator@^7.22.5", "@babel/generator@^7.7.2": "@babel/generator@^7.7.2":
version "7.22.10" version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722"
integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A== integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==
@ -262,7 +262,7 @@
dependencies: dependencies:
"@babel/types" "^7.22.5" "@babel/types" "^7.22.5"
"@babel/helper-split-export-declaration@^7.22.5", "@babel/helper-split-export-declaration@^7.22.6": "@babel/helper-split-export-declaration@^7.22.6":
version "7.22.6" version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
@ -320,16 +320,16 @@
chalk "^2.4.2" chalk "^2.4.2"
js-tokens "^4.0.0" js-tokens "^4.0.0"
"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7": "@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.22.5":
version "7.22.16"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.16.tgz#180aead7f247305cce6551bea2720934e2fa2c95"
integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==
"@babel/parser@^7.14.7":
version "7.22.10" version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55"
integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==
"@babel/parser@^7.22.15", "@babel/parser@^7.22.5":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.15.tgz#d34592bfe288a32e741aa0663dbc4829fcd55160"
integrity sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA==
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.15": "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.15":
version "7.22.15" version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz#02dc8a03f613ed5fdc29fb2f728397c78146c962" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz#02dc8a03f613ed5fdc29fb2f728397c78146c962"
@ -1112,23 +1112,7 @@
"@babel/parser" "^7.22.5" "@babel/parser" "^7.22.5"
"@babel/types" "^7.22.5" "@babel/types" "^7.22.5"
"@babel/traverse@7": "@babel/traverse@7", "@babel/traverse@^7.22.15":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1"
integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==
dependencies:
"@babel/code-frame" "^7.22.5"
"@babel/generator" "^7.22.5"
"@babel/helper-environment-visitor" "^7.22.5"
"@babel/helper-function-name" "^7.22.5"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.5"
"@babel/parser" "^7.22.5"
"@babel/types" "^7.22.5"
debug "^4.1.0"
globals "^11.1.0"
"@babel/traverse@^7.22.15":
version "7.22.15" version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.15.tgz#75be4d2d6e216e880e93017f4e2389aeb77ef2d9" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.15.tgz#75be4d2d6e216e880e93017f4e2389aeb77ef2d9"
integrity sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ== integrity sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ==
@ -1144,16 +1128,16 @@
debug "^4.1.0" debug "^4.1.0"
globals "^11.1.0" globals "^11.1.0"
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.3.3": "@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.4.4":
version "7.22.10" version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.15.tgz#266cb21d2c5fd0b3931e7a91b6dd72d2f617d282"
integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== integrity sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==
dependencies: dependencies:
"@babel/helper-string-parser" "^7.22.5" "@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.5" "@babel/helper-validator-identifier" "^7.22.15"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@babel/types@^7.0.0-beta.49", "@babel/types@^7.12.11", "@babel/types@^7.12.6": "@babel/types@^7.0.0-beta.49", "@babel/types@^7.12.6":
version "7.22.5" version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe"
integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==
@ -1162,13 +1146,13 @@
"@babel/helper-validator-identifier" "^7.22.5" "@babel/helper-validator-identifier" "^7.22.5"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.4.4": "@babel/types@^7.3.3":
version "7.22.15" version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.15.tgz#266cb21d2c5fd0b3931e7a91b6dd72d2f617d282" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03"
integrity sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA== integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==
dependencies: dependencies:
"@babel/helper-string-parser" "^7.22.5" "@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.15" "@babel/helper-validator-identifier" "^7.22.5"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@bcoe/v8-coverage@^0.2.3": "@bcoe/v8-coverage@^0.2.3":
@ -1355,6 +1339,14 @@
"@formatjs/intl-localematcher" "0.4.0" "@formatjs/intl-localematcher" "0.4.0"
tslib "^2.4.0" tslib "^2.4.0"
"@formatjs/ecma402-abstract@1.17.1":
version "1.17.1"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.1.tgz#6ac7d6a1d1c9c8eff76ab6ed949f2a5cbe424030"
integrity sha512-N2sjSUrmsEoynG8Q61pkrKlJ9PxcUGxJke1x3301aGyprGgl58wHWhgGUnzTfS4OHNNNQDxzjcXVp1t5fGW6yQ==
dependencies:
"@formatjs/intl-localematcher" "0.4.1"
tslib "^2.4.0"
"@formatjs/fast-memoize@2.2.0": "@formatjs/fast-memoize@2.2.0":
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz#33bd616d2e486c3e8ef4e68c99648c196887802b" resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz#33bd616d2e486c3e8ef4e68c99648c196887802b"
@ -1371,6 +1363,15 @@
"@formatjs/icu-skeleton-parser" "1.6.0" "@formatjs/icu-skeleton-parser" "1.6.0"
tslib "^2.4.0" tslib "^2.4.0"
"@formatjs/icu-messageformat-parser@2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.6.1.tgz#ca497d5a2bff641dc0978bd9b64d1d02597980cb"
integrity sha512-dTDNupwdovxT1xDXC96zzPUua/XrxTQTOulJZSvaJP0pt3rr/cGR/tq4d7BnxY9oqPZpc4fjWBmrRlhcUyBSiw==
dependencies:
"@formatjs/ecma402-abstract" "1.17.1"
"@formatjs/icu-skeleton-parser" "1.6.1"
tslib "^2.4.0"
"@formatjs/icu-skeleton-parser@1.6.0": "@formatjs/icu-skeleton-parser@1.6.0":
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.6.0.tgz#0728be8b6b3656f1a4b8e6e5b0e02dffffc23c6c" resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.6.0.tgz#0728be8b6b3656f1a4b8e6e5b0e02dffffc23c6c"
@ -1379,22 +1380,30 @@
"@formatjs/ecma402-abstract" "1.17.0" "@formatjs/ecma402-abstract" "1.17.0"
tslib "^2.4.0" tslib "^2.4.0"
"@formatjs/intl-displaynames@6.5.0": "@formatjs/icu-skeleton-parser@1.6.1":
version "6.5.0" version "1.6.1"
resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-6.5.0.tgz#32737088e7d943fb3e22140e64bb634e0ba05fcf" resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.6.1.tgz#3647f41b82e362c08bb80bd9b653c7eb6ff31118"
integrity sha512-sg/nR8ILEdUl+2sWu6jc1nQ5s04yucGlH1RVfatW8TSJ5uG3Yy3vgigi8NNC/BuhcncUNPWqSpTCSI1hA+rhiw== integrity sha512-/LQ6ovxYd8FQjVLmbV+WmuFy86o+JTc0cIQuWixuLuUMfRRif8eUQw3vPK5hx7C/g1UVmKAaOcYRTEsvyEGz9g==
dependencies: dependencies:
"@formatjs/ecma402-abstract" "1.17.0" "@formatjs/ecma402-abstract" "1.17.1"
"@formatjs/intl-localematcher" "0.4.0"
tslib "^2.4.0" tslib "^2.4.0"
"@formatjs/intl-listformat@7.4.0": "@formatjs/intl-displaynames@6.5.1":
version "7.4.0" version "6.5.1"
resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-7.4.0.tgz#fa8ac535d82fc716f052f2fd60eeaa7331362357" resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-6.5.1.tgz#d25d260b81845abf6f35a13dbc87c25b0ec22a33"
integrity sha512-ifupb+balZUAF/Oh3QyGRqPRWGSKwWoMPR0cYZEG7r61SimD+m38oFQqVx/3Fp7LfQFF11m7IS+MlxOo2sKINA== integrity sha512-BD+coSUka8fppErGnXbWthd6YxTXdCGvQzGR/rSEdPgiZO5sh+0gVLjp0FgXi6cOf9C2z2axqhGifFsqyTVhkw==
dependencies: dependencies:
"@formatjs/ecma402-abstract" "1.17.0" "@formatjs/ecma402-abstract" "1.17.1"
"@formatjs/intl-localematcher" "0.4.0" "@formatjs/intl-localematcher" "0.4.1"
tslib "^2.4.0"
"@formatjs/intl-listformat@7.4.1":
version "7.4.1"
resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-7.4.1.tgz#cb6f1399a41b9fd568cc4b4b80fc815d0c5b37ab"
integrity sha512-mWd30ndvYw8JydOIVb5Y1ElK2iwsaDY+ajPR5aWWgEZaH04aL+4hzX/8VXPsilu7CF3DN1IP5ZSwJuj7ZyBIHw==
dependencies:
"@formatjs/ecma402-abstract" "1.17.1"
"@formatjs/intl-localematcher" "0.4.1"
tslib "^2.4.0" tslib "^2.4.0"
"@formatjs/intl-localematcher@0.4.0": "@formatjs/intl-localematcher@0.4.0":
@ -1404,26 +1413,33 @@
dependencies: dependencies:
tslib "^2.4.0" tslib "^2.4.0"
"@formatjs/intl-pluralrules@^5.2.2": "@formatjs/intl-localematcher@0.4.1":
version "5.2.4" version "0.4.1"
resolved "https://registry.yarnpkg.com/@formatjs/intl-pluralrules/-/intl-pluralrules-5.2.4.tgz#b417aa503186c2cbb4715f47114ed65211b4ada9" resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.4.1.tgz#af63e2c065731a33f6fed36dc85058009a7f8062"
integrity sha512-6meo376d8I4zikRFSUxATLnqzGwezmc57SmToP4z1/NQwTHXGe0yIG/ABPbO3QMx7IUkofH/ROP3A4DhtPTpnA== integrity sha512-Fs4MhhHlLC0RrspX2u2KP7zlwL9eHrBZsOBxaPOeqrCZYLaOUK4cYXQ1ErpAB0HnGV/GUXNa5smzV/7jCuRzxg==
dependencies: dependencies:
"@formatjs/ecma402-abstract" "1.17.0"
"@formatjs/intl-localematcher" "0.4.0"
tslib "^2.4.0" tslib "^2.4.0"
"@formatjs/intl@2.9.0": "@formatjs/intl-pluralrules@^5.2.2":
version "2.9.0" version "5.2.5"
resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-2.9.0.tgz#e1335572af3ca8a53e136a78e866f1851a9718c2" resolved "https://registry.yarnpkg.com/@formatjs/intl-pluralrules/-/intl-pluralrules-5.2.5.tgz#72ff79bec4c3383a46e6b3d6b0af73534e544302"
integrity sha512-Ym0trUoC/VO6wQu4YHa0H1VR2tEixFRmwZgADkDLm7nD+vv1Ob+/88mUAoT0pwvirFqYKgUKEwp1tFepqyqvVA== integrity sha512-od9KJzKB5CF9Hfq3g6CJzWA0rW9yaNrF2hMZK3/26aTuZ4Fm3mPm/JI6QReF9qZSC1qkJyqS1GN0JtTm2sVyGQ==
dependencies: dependencies:
"@formatjs/ecma402-abstract" "1.17.0" "@formatjs/ecma402-abstract" "1.17.1"
"@formatjs/intl-localematcher" "0.4.1"
tslib "^2.4.0"
"@formatjs/intl@2.9.1":
version "2.9.1"
resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-2.9.1.tgz#f224109620b71f48c049bd42df770f202e4b6d29"
integrity sha512-NsDMke+lAiu+c6/9KXrp8aldd7sFeyxgQZqFUJSZIeaKrN+gEoYKqWgsiRa7/mxlWqWNJSGuNQnF1P2ChC/yaA==
dependencies:
"@formatjs/ecma402-abstract" "1.17.1"
"@formatjs/fast-memoize" "2.2.0" "@formatjs/fast-memoize" "2.2.0"
"@formatjs/icu-messageformat-parser" "2.6.0" "@formatjs/icu-messageformat-parser" "2.6.1"
"@formatjs/intl-displaynames" "6.5.0" "@formatjs/intl-displaynames" "6.5.1"
"@formatjs/intl-listformat" "7.4.0" "@formatjs/intl-listformat" "7.4.1"
intl-messageformat "10.5.0" intl-messageformat "10.5.1"
tslib "^2.4.0" tslib "^2.4.0"
"@formatjs/ts-transformer@3.13.3": "@formatjs/ts-transformer@3.13.3":
@ -1439,6 +1455,19 @@
tslib "^2.4.0" tslib "^2.4.0"
typescript "^4.7 || 5" typescript "^4.7 || 5"
"@formatjs/ts-transformer@3.13.4":
version "3.13.4"
resolved "https://registry.yarnpkg.com/@formatjs/ts-transformer/-/ts-transformer-3.13.4.tgz#586582ab3aee75b7e24d37f7090ab270062a9bf5"
integrity sha512-AOOHpHBJyjcNLkJcT82zeU7IlaogHI4qBjPlFgyeqcSbGwR4b+LGY7Frf7N5eM8Y9yGnTDVIUA/u3gHUA3SHQg==
dependencies:
"@formatjs/icu-messageformat-parser" "2.6.1"
"@types/json-stable-stringify" "^1.0.32"
"@types/node" "14 || 16 || 17"
chalk "^4.0.0"
json-stable-stringify "^1.0.1"
tslib "^2.4.0"
typescript "^4.7 || 5"
"@gamestdio/websocket@^0.3.2": "@gamestdio/websocket@^0.3.2":
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/@gamestdio/websocket/-/websocket-0.3.2.tgz#321ba0976ee30fd14e51dbf8faa85ce7b325f76a" resolved "https://registry.yarnpkg.com/@gamestdio/websocket/-/websocket-0.3.2.tgz#321ba0976ee30fd14e51dbf8faa85ce7b325f76a"
@ -1718,9 +1747,9 @@
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/source-map@^0.3.3": "@jridgewell/source-map@^0.3.3":
version "0.3.3" version "0.3.5"
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda" resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91"
integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg== integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==
dependencies: dependencies:
"@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/gen-mapping" "^0.3.0"
"@jridgewell/trace-mapping" "^0.3.9" "@jridgewell/trace-mapping" "^0.3.9"
@ -1739,9 +1768,9 @@
"@jridgewell/sourcemap-codec" "^1.4.14" "@jridgewell/sourcemap-codec" "^1.4.14"
"@material-design-icons/svg@^0.14.10": "@material-design-icons/svg@^0.14.10":
version "0.14.11" version "0.14.12"
resolved "https://registry.yarnpkg.com/@material-design-icons/svg/-/svg-0.14.11.tgz#f90a2c8de801523c3b17e606c89313121c8bb3b4" resolved "https://registry.yarnpkg.com/@material-design-icons/svg/-/svg-0.14.12.tgz#b3dd27b4c2a93e0310f51acfb311846b0212f987"
integrity sha512-jpAksWZIVLB5/qTAeqANns7pH/faIQR3jgV2yROUNKZkzpJ428h7e1/byJB+rFZNI0hgZpY9nOVMLhc1J41HtA== integrity sha512-hVEMICFvG26SKDXatPmz+vY5BAqLPCDiyXnw+KN46FXOtY4PcpeAfzFZvwt6D9ywNnVJd4EvmLdlWgLmtOWxbA==
"@nodelib/fs.scandir@2.1.5": "@nodelib/fs.scandir@2.1.5":
version "2.1.5" version "2.1.5"
@ -1822,6 +1851,17 @@
redux-thunk "^2.4.2" redux-thunk "^2.4.2"
reselect "^4.1.8" reselect "^4.1.8"
"@renchap/compression-webpack-plugin@^6.1.4":
version "6.1.4"
resolved "https://registry.yarnpkg.com/@renchap/compression-webpack-plugin/-/compression-webpack-plugin-6.1.4.tgz#5ff528ae9edf83de7447b72f5b52a05f860bb899"
integrity sha512-Ij43bj/jhKiMKOZVT9b3DJvr4R+dNs9ZbH7QV3kLfloavt4GhNo4Jw86tVwmP5d+seZtSwTL1NG8/c6dM1V0vw==
dependencies:
cacache "^15.0.5"
find-cache-dir "^3.3.1"
schema-utils "^3.0.0"
serialize-javascript "^5.0.1"
webpack-sources "^1.4.3"
"@restart/hooks@^0.4.7": "@restart/hooks@^0.4.7":
version "0.4.9" version "0.4.9"
resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.9.tgz#ad858fb39d99e252cccce19416adc18fc3f18fcb" resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.9.tgz#ad858fb39d99e252cccce19416adc18fc3f18fcb"
@ -2913,16 +2953,11 @@ acorn@^6.4.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
acorn@^8.0.4, acorn@^8.1.0, acorn@^8.8.1, acorn@^8.9.0: acorn@^8.0.4, acorn@^8.1.0, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0:
version "8.10.0" version "8.10.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5"
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
acorn@^8.8.2:
version "8.8.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
agent-base@6: agent-base@6:
version "6.0.2" version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@ -3383,17 +3418,17 @@ babel-loader@^8.3.0:
schema-utils "^2.6.5" schema-utils "^2.6.5"
babel-plugin-formatjs@^10.5.1: babel-plugin-formatjs@^10.5.1:
version "10.5.3" version "10.5.4"
resolved "https://registry.yarnpkg.com/babel-plugin-formatjs/-/babel-plugin-formatjs-10.5.3.tgz#718e47f4f3aad663ad4f901274aedd7be0a86380" resolved "https://registry.yarnpkg.com/babel-plugin-formatjs/-/babel-plugin-formatjs-10.5.4.tgz#98837caedcdb64f118048f19e59ad0c94f55b5aa"
integrity sha512-PBeryWyN2HY2VUGNFPQS6+DPNQ/I9zDZ97y38i1+LzIpIyTHBePECq/ehEABE73PvvF2irFiN7TCYBrQQw5+lA== integrity sha512-6JSpDS/YVjMu74NzHkuEduWqDsmUbUm1qc6sNeccgknixW9Z3hbQqv3Fvfjrh4opBzJ+CRaAZaUlQQL+xDTQzw==
dependencies: dependencies:
"@babel/core" "^7.10.4" "@babel/core" "^7.10.4"
"@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-jsx" "7" "@babel/plugin-syntax-jsx" "7"
"@babel/traverse" "7" "@babel/traverse" "7"
"@babel/types" "^7.12.11" "@babel/types" "^7.12.11"
"@formatjs/icu-messageformat-parser" "2.6.0" "@formatjs/icu-messageformat-parser" "2.6.1"
"@formatjs/ts-transformer" "3.13.3" "@formatjs/ts-transformer" "3.13.4"
"@types/babel__core" "^7.1.7" "@types/babel__core" "^7.1.7"
"@types/babel__helper-plugin-utils" "^7.10.0" "@types/babel__helper-plugin-utils" "^7.10.0"
"@types/babel__traverse" "^7.1.7" "@types/babel__traverse" "^7.1.7"
@ -3932,9 +3967,9 @@ caniuse-lite@^1.0.30001502:
integrity sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA== integrity sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA==
caniuse-lite@^1.0.30001517: caniuse-lite@^1.0.30001517:
version "1.0.30001525" version "1.0.30001528"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001525.tgz#d2e8fdec6116ffa36284ca2c33ef6d53612fe1c8" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001528.tgz#479972fc705b996f1114336c0032418a215fd0aa"
integrity sha512-/3z+wB4icFt3r0USMwxujAqRvaD/B7rvGTsKhbhSQErVrJvkZCLhgNLJxU8MevahQVH6hCU9FsHdNUFbiwmE7Q== integrity sha512-0Db4yyjR9QMNlsxh+kKWzQtkyflkG/snYheSzkjmvdEtEXB1+jt7A2HmSEiO6XIJPIbo92lHNGNySvE5pZcs5Q==
caniuse-lite@^1.0.30001520: caniuse-lite@^1.0.30001520:
version "1.0.30001520" version "1.0.30001520"
@ -4248,17 +4283,6 @@ compressible@~2.0.16:
dependencies: dependencies:
mime-db ">= 1.43.0 < 2" mime-db ">= 1.43.0 < 2"
compression-webpack-plugin@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-6.1.1.tgz#ae8e4b2ffdb7396bb776e66918d751a20d8ccf0e"
integrity sha512-BEHft9M6lwOqVIQFMS/YJGmeCYXVOakC5KzQk05TFpMBlODByh1qNsZCWjUBxCQhUP9x0WfGidxTbGkjbWO/TQ==
dependencies:
cacache "^15.0.5"
find-cache-dir "^3.3.1"
schema-utils "^3.0.0"
serialize-javascript "^5.0.1"
webpack-sources "^1.4.3"
compression@^1.7.4: compression@^1.7.4:
version "1.7.4" version "1.7.4"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
@ -4347,9 +4371,9 @@ core-js@^2.5.0:
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
core-js@^3.30.2: core-js@^3.30.2:
version "3.32.1" version "3.32.2"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.1.tgz#a7d8736a3ed9dd05940c3c4ff32c591bb735be77" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.2.tgz#172fb5949ef468f93b4be7841af6ab1f21992db7"
integrity sha512-lqufgNn9NLnESg5mQeYsxQP5w7wrViSj0jr/kv6ECQiByzQkrn1MKvV0L3acttpDqfQrHLwr2KCMgX5b8X+lyQ== integrity sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==
core-util-is@~1.0.0: core-util-is@~1.0.0:
version "1.0.3" version "1.0.3"
@ -5097,9 +5121,9 @@ electron-to-chromium@^1.4.428:
integrity sha512-/g3UyNDmDd6ebeWapmAoiyy+Sy2HyJ+/X8KyvNeHfKRFfHaA2W8oF5fxD5F3tjBDcjpwo0iek6YNgxNXDBoEtA== integrity sha512-/g3UyNDmDd6ebeWapmAoiyy+Sy2HyJ+/X8KyvNeHfKRFfHaA2W8oF5fxD5F3tjBDcjpwo0iek6YNgxNXDBoEtA==
electron-to-chromium@^1.4.477: electron-to-chromium@^1.4.477:
version "1.4.508" version "1.4.510"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz#5641ff2f5ba11df4bd960fe6a2f9f70aa8b9af96" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.510.tgz#446c50d7533c1e71a84b00a3b37ab06dd601d890"
integrity sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg== integrity sha512-xPfLIPFcN/WLXBpQ/K4UgE98oUBO5Tia6BD4rkSR0wE7ep/PwBVlgvPJQrIBpmJGVAmUzwPKuDbVt9XV6+uC2g==
elliptic@^6.5.3: elliptic@^6.5.3:
version "6.5.4" version "6.5.4"
@ -6109,11 +6133,16 @@ fsevents@^1.2.7:
bindings "^1.5.0" bindings "^1.5.0"
nan "^2.12.1" nan "^2.12.1"
fsevents@^2.3.2, fsevents@~2.3.2: fsevents@^2.3.2:
version "2.3.2" version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.1: function-bind@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@ -6815,14 +6844,14 @@ intersection-observer@^0.12.0:
resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.12.2.tgz#4a45349cc0cd91916682b1f44c28d7ec737dc375" resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.12.2.tgz#4a45349cc0cd91916682b1f44c28d7ec737dc375"
integrity sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg== integrity sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==
intl-messageformat@10.5.0, intl-messageformat@^10.3.5: intl-messageformat@10.5.1, intl-messageformat@^10.3.5:
version "10.5.0" version "10.5.1"
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.5.0.tgz#86d11b15913ac954075b25253f5e669359f89538" resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.5.1.tgz#40304cbde01c8cb2236e11ac8c827642bed474d0"
integrity sha512-AvojYuOaRb6r2veOKfTVpxH9TrmjSdc5iR9R5RgBwrDZYSmAAFVT+QLbW3C4V7Qsg0OguMp67Q/EoUkxZzXRGw== integrity sha512-irEmjxHq0f1MHviQr3Q4ToF9EgYbnXDq2/R9MRTTveGKHgy6VZ29hQxswu4trqWaX7T6njKxSoKVG92OSz0U5Q==
dependencies: dependencies:
"@formatjs/ecma402-abstract" "1.17.0" "@formatjs/ecma402-abstract" "1.17.1"
"@formatjs/fast-memoize" "2.2.0" "@formatjs/fast-memoize" "2.2.0"
"@formatjs/icu-messageformat-parser" "2.6.0" "@formatjs/icu-messageformat-parser" "2.6.1"
tslib "^2.4.0" tslib "^2.4.0"
invariant@^2.2.2, invariant@^2.2.4: invariant@^2.2.2, invariant@^2.2.4:
@ -10031,19 +10060,19 @@ react-immutable-pure-component@^2.2.2:
integrity sha512-vkgoMJUDqHZfXXnjVlG3keCxSO/U6WeDQ5/Sl0GK2cH8TOxEzQ5jXqDXHEL/jqk6fsNxV05oH5kD7VNMUE2k+A== integrity sha512-vkgoMJUDqHZfXXnjVlG3keCxSO/U6WeDQ5/Sl0GK2cH8TOxEzQ5jXqDXHEL/jqk6fsNxV05oH5kD7VNMUE2k+A==
react-intl@^6.4.2: react-intl@^6.4.2:
version "6.4.4" version "6.4.5"
resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-6.4.4.tgz#14b45ce046bfbb60c0e6d392d8ddc30e9ead5a4f" resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-6.4.5.tgz#d8f61fce2fb3be08ef58deb35d96b964e1ef013a"
integrity sha512-/C9Sl/5//ohfkNG6AWlJuf4BhTXsbzyk93K62A4zRhSPANyOGpKZ+fWhN+TLfFd5YjDUHy+exU/09y0w1bO4Xw== integrity sha512-nUO2W57Tmdu6ErhsOFpsgkEtfQ4Lo7hPrXn8gGJQx59NRC0FNUxRc6+Ah3G8j6vqBiYRmxsLJpVZtIhLZjhYLA==
dependencies: dependencies:
"@formatjs/ecma402-abstract" "1.17.0" "@formatjs/ecma402-abstract" "1.17.1"
"@formatjs/icu-messageformat-parser" "2.6.0" "@formatjs/icu-messageformat-parser" "2.6.1"
"@formatjs/intl" "2.9.0" "@formatjs/intl" "2.9.1"
"@formatjs/intl-displaynames" "6.5.0" "@formatjs/intl-displaynames" "6.5.1"
"@formatjs/intl-listformat" "7.4.0" "@formatjs/intl-listformat" "7.4.1"
"@types/hoist-non-react-statics" "^3.3.1" "@types/hoist-non-react-statics" "^3.3.1"
"@types/react" "16 || 17 || 18" "@types/react" "16 || 17 || 18"
hoist-non-react-statics "^3.3.2" hoist-non-react-statics "^3.3.2"
intl-messageformat "10.5.0" intl-messageformat "10.5.1"
tslib "^2.4.0" tslib "^2.4.0"
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0: "react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0:
@ -11744,9 +11773,9 @@ tapable@^2.2.0:
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
tar@^6.0.2: tar@^6.0.2:
version "6.1.15" version "6.2.0"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73"
integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A== integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==
dependencies: dependencies:
chownr "^2.0.0" chownr "^2.0.0"
fs-minipass "^2.0.0" fs-minipass "^2.0.0"
@ -11792,7 +11821,7 @@ terser-webpack-plugin@^1.4.3, terser-webpack-plugin@^4.2.3:
terser "^5.3.4" terser "^5.3.4"
webpack-sources "^1.4.3" webpack-sources "^1.4.3"
terser@^5.0.0, terser@^5.3.4: terser@^5.0.0:
version "5.18.0" version "5.18.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.18.0.tgz#dc811fb8e3481a875d545bda247c8730ee4dc76b" resolved "https://registry.yarnpkg.com/terser/-/terser-5.18.0.tgz#dc811fb8e3481a875d545bda247c8730ee4dc76b"
integrity sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA== integrity sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA==
@ -11802,6 +11831,16 @@ terser@^5.0.0, terser@^5.3.4:
commander "^2.20.0" commander "^2.20.0"
source-map-support "~0.5.20" source-map-support "~0.5.20"
terser@^5.3.4:
version "5.19.4"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.19.4.tgz#941426fa482bf9b40a0308ab2b3cd0cf7c775ebd"
integrity sha512-6p1DjHeuluwxDXcuT9VR8p64klWJKo1ILiy19s6C9+0Bh2+NWTX6nD9EPppiER4ICkHDVB1RkVpin/YW2nQn/g==
dependencies:
"@jridgewell/source-map" "^0.3.3"
acorn "^8.8.2"
commander "^2.20.0"
source-map-support "~0.5.20"
tesseract.js-core@^2.2.0: tesseract.js-core@^2.2.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/tesseract.js-core/-/tesseract.js-core-2.2.0.tgz#6ef78051272a381969fac3e45a226e85022cffef" resolved "https://registry.yarnpkg.com/tesseract.js-core/-/tesseract.js-core-2.2.0.tgz#6ef78051272a381969fac3e45a226e85022cffef"
@ -12005,7 +12044,12 @@ tslib@^2.1.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410"
integrity sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig== integrity sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==
tslib@^2.4.0, tslib@^2.5.0: tslib@^2.4.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
tslib@^2.5.0:
version "2.5.3" version "2.5.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913"
integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==
@ -12116,12 +12160,7 @@ typed-array-length@^1.0.4:
for-each "^0.3.3" for-each "^0.3.3"
is-typed-array "^1.1.9" is-typed-array "^1.1.9"
"typescript@^4.7 || 5": "typescript@^4.7 || 5", typescript@^5.0.4:
version "5.1.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826"
integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==
typescript@^5.0.4:
version "5.2.2" version "5.2.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78"
integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==
@ -12603,10 +12642,10 @@ webpack-sources@^1.0, webpack-sources@^1.1.0, webpack-sources@^1.4.1, webpack-so
source-list-map "^2.0.0" source-list-map "^2.0.0"
source-map "~0.6.1" source-map "~0.6.1"
webpack@^4.46.0: webpack@^4.47.0:
version "4.46.0" version "4.47.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.47.0.tgz#8b8a02152d7076aeb03b61b47dad2eeed9810ebc"
integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== integrity sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==
dependencies: dependencies:
"@webassemblyjs/ast" "1.9.0" "@webassemblyjs/ast" "1.9.0"
"@webassemblyjs/helper-module-context" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0"
@ -13017,9 +13056,9 @@ ws@^7.3.1:
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
ws@^8.11.0, ws@^8.12.1, ws@^8.13.0: ws@^8.11.0, ws@^8.12.1, ws@^8.13.0:
version "8.13.0" version "8.14.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.0.tgz#6c5792c5316dc9266ba8e780433fc45e6680aecd"
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== integrity sha512-WR0RJE9Ehsio6U4TuM+LmunEsjQ5ncHlw4sn9ihD6RoJKZrVyH9FWV3dmnwu8B2aNib1OvG2X6adUCyFpQyWcg==
xml-name-validator@^4.0.0: xml-name-validator@^4.0.0:
version "4.0.0" version "4.0.0"