Replace django-channels with `useWebSocket` from `@vueuse/core` (!1759)
This commit is contained in:
parent
688685a9cf
commit
f21c860985
|
@ -0,0 +1 @@
|
||||||
|
Fix CSP issue caused by django-channels package (#1752)
|
|
@ -0,0 +1 @@
|
||||||
|
Replace django-channels package with web socket implementation from @vueuse/core (#1715)
|
|
@ -17,10 +17,11 @@
|
||||||
"postinstall": "yarn run fix-fomantic-css"
|
"postinstall": "yarn run fix-fomantic-css"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vue/composition-api": "1.4.9",
|
||||||
|
"@vueuse/core": "8.2.5",
|
||||||
"axios": "0.26.1",
|
"axios": "0.26.1",
|
||||||
"axios-auth-refresh": "3.2.2",
|
"axios-auth-refresh": "3.2.2",
|
||||||
"diff": "5.0.0",
|
"diff": "5.0.0",
|
||||||
"django-channels": "2.1.3",
|
|
||||||
"focus-trap": "6.7.3",
|
"focus-trap": "6.7.3",
|
||||||
"fomantic-ui-css": "2.8.8",
|
"fomantic-ui-css": "2.8.8",
|
||||||
"howler": "2.2.3",
|
"howler": "2.2.3",
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { mapState, mapGetters } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import { WebSocketBridge } from 'django-channels'
|
import { useWebSocket, whenever } from '@vueuse/core'
|
||||||
import GlobalEvents from '@/components/utils/global-events.vue'
|
import GlobalEvents from '@/components/utils/global-events.vue'
|
||||||
import locales from './locales'
|
import locales from './locales'
|
||||||
import { getClientOnlyRadio } from '@/radios'
|
import { getClientOnlyRadio } from '@/radios'
|
||||||
|
@ -65,6 +65,7 @@ import SetInstanceModal from '@/components/SetInstanceModal.vue'
|
||||||
import ShortcutsModal from '@/components/ShortcutsModal.vue'
|
import ShortcutsModal from '@/components/ShortcutsModal.vue'
|
||||||
import FilterModal from '@/components/moderation/FilterModal.vue'
|
import FilterModal from '@/components/moderation/FilterModal.vue'
|
||||||
import ReportModal from '@/components/moderation/ReportModal.vue'
|
import ReportModal from '@/components/moderation/ReportModal.vue'
|
||||||
|
import { watch, watchEffect } from '@vue/composition-api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
|
@ -81,9 +82,32 @@ export default {
|
||||||
ReportModal,
|
ReportModal,
|
||||||
GlobalEvents
|
GlobalEvents
|
||||||
},
|
},
|
||||||
|
setup (props, { root }) {
|
||||||
|
const store = root.$store
|
||||||
|
|
||||||
|
const url = store.getters['instance/absoluteUrl']('api/v1/activity')
|
||||||
|
.replace(/^http/, 'ws')
|
||||||
|
|
||||||
|
const { data, status, open, close } = useWebSocket(url, {
|
||||||
|
autoReconnect: true,
|
||||||
|
immediate: false
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => store.state.auth.authenticated, (authenticated) => {
|
||||||
|
if (authenticated) return open()
|
||||||
|
close()
|
||||||
|
})
|
||||||
|
|
||||||
|
whenever(data, () => {
|
||||||
|
store.dispatch('ui/websocketEvent', JSON.parse(data.value))
|
||||||
|
})
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
console.log('Websocket status:', status.value)
|
||||||
|
})
|
||||||
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
bridge: null,
|
|
||||||
instanceUrl: null,
|
instanceUrl: null,
|
||||||
showShortcutsModal: false,
|
showShortcutsModal: false,
|
||||||
showSetInstanceModal: false,
|
showSetInstanceModal: false,
|
||||||
|
@ -172,13 +196,6 @@ export default {
|
||||||
this.setTheme(newValue)
|
this.setTheme(newValue)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'$store.state.auth.authenticated' (newValue) {
|
|
||||||
if (!newValue) {
|
|
||||||
this.disconnect()
|
|
||||||
} else {
|
|
||||||
this.openWebsocket()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'$store.state.ui.currentLanguage': {
|
'$store.state.ui.currentLanguage': {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
handler (newValue) {
|
handler (newValue) {
|
||||||
|
@ -248,7 +265,6 @@ export default {
|
||||||
}
|
}
|
||||||
window.addEventListener('resize', this.handleResize)
|
window.addEventListener('resize', this.handleResize)
|
||||||
this.handleResize()
|
this.handleResize()
|
||||||
this.openWebsocket()
|
|
||||||
const self = this
|
const self = this
|
||||||
if (!this.$store.state.ui.selectedLanguage) {
|
if (!this.$store.state.ui.selectedLanguage) {
|
||||||
this.autodetectLanguage()
|
this.autodetectLanguage()
|
||||||
|
@ -264,7 +280,7 @@ export default {
|
||||||
}
|
}
|
||||||
const url = urlParams.get('_url')
|
const url = urlParams.get('_url')
|
||||||
if (url) {
|
if (url) {
|
||||||
this.$router.replace(url)
|
await this.$router.replace(url)
|
||||||
} else if (!this.$store.state.instance.instanceUrl) {
|
} else if (!this.$store.state.instance.instanceUrl) {
|
||||||
// we have several way to guess the API server url. By order of precedence:
|
// we have several way to guess the API server url. By order of precedence:
|
||||||
// 1. use the url provided in settings.json, if any
|
// 1. use the url provided in settings.json, if any
|
||||||
|
@ -279,11 +295,6 @@ export default {
|
||||||
this.$store.commit('instance/instanceUrl', this.$store.state.instance.instanceUrl)
|
this.$store.commit('instance/instanceUrl', this.$store.state.instance.instanceUrl)
|
||||||
}
|
}
|
||||||
await this.fetchNodeInfo()
|
await this.fetchNodeInfo()
|
||||||
this.$store.dispatch('auth/check')
|
|
||||||
setInterval(() => {
|
|
||||||
// used to refresh profile every now and then (important for refreshing scoped tokens)
|
|
||||||
self.$store.dispatch('auth/check')
|
|
||||||
}, 1000 * 60 * 60 * 8)
|
|
||||||
this.$store.dispatch('instance/fetchSettings')
|
this.$store.dispatch('instance/fetchSettings')
|
||||||
this.$store.commit('ui/addWebsocketEventHandler', {
|
this.$store.commit('ui/addWebsocketEventHandler', {
|
||||||
eventName: 'inbox.item_added',
|
eventName: 'inbox.item_added',
|
||||||
|
@ -354,7 +365,6 @@ export default {
|
||||||
eventName: 'Listen',
|
eventName: 'Listen',
|
||||||
id: 'handleListen'
|
id: 'handleListen'
|
||||||
})
|
})
|
||||||
this.disconnect()
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
incrementNotificationCountInSidebar (event) {
|
incrementNotificationCountInSidebar (event) {
|
||||||
|
@ -400,36 +410,6 @@ export default {
|
||||||
}
|
}
|
||||||
this.$store.commit('ui/currentLanguage', candidate)
|
this.$store.commit('ui/currentLanguage', candidate)
|
||||||
},
|
},
|
||||||
disconnect () {
|
|
||||||
if (!this.bridge) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.bridge.socket.close(1000, 'goodbye', { keepClosed: true })
|
|
||||||
},
|
|
||||||
openWebsocket () {
|
|
||||||
if (!this.$store.state.auth.authenticated) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.disconnect()
|
|
||||||
const self = this
|
|
||||||
const token = this.$store.state.auth.token
|
|
||||||
const bridge = new WebSocketBridge()
|
|
||||||
this.bridge = bridge
|
|
||||||
let url =
|
|
||||||
this.$store.getters['instance/absoluteUrl'](`api/v1/activity?token=${token}`)
|
|
||||||
url = url.replace('http://', 'ws://')
|
|
||||||
url = url.replace('https://', 'wss://')
|
|
||||||
bridge.connect(
|
|
||||||
url,
|
|
||||||
[],
|
|
||||||
{ reconnectInterval: 1000 * 60 })
|
|
||||||
bridge.addEventListener('message', function (event) {
|
|
||||||
self.$store.dispatch('ui/websocketEvent', event.data)
|
|
||||||
})
|
|
||||||
bridge.socket.addEventListener('open', function () {
|
|
||||||
console.log('Connected to WebSocket')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getTrackInformationText (track) {
|
getTrackInformationText (track) {
|
||||||
const trackTitle = track.title
|
const trackTitle = track.title
|
||||||
const albumArtist = (track.album) ? track.album.artist.name : null
|
const albumArtist = (track.album) ? track.album.artist.name : null
|
||||||
|
|
|
@ -14,6 +14,7 @@ import GetTextPlugin from 'vue-gettext'
|
||||||
import { sync } from 'vuex-router-sync'
|
import { sync } from 'vuex-router-sync'
|
||||||
import locales from '@/locales'
|
import locales from '@/locales'
|
||||||
import createAuthRefreshInterceptor from 'axios-auth-refresh'
|
import createAuthRefreshInterceptor from 'axios-auth-refresh'
|
||||||
|
import VueCompositionAPI from '@vue/composition-api'
|
||||||
|
|
||||||
import filters from '@/filters' // eslint-disable-line
|
import filters from '@/filters' // eslint-disable-line
|
||||||
import { parseAPIErrors } from '@/utils'
|
import { parseAPIErrors } from '@/utils'
|
||||||
|
@ -57,6 +58,7 @@ Vue.use(GetTextPlugin, {
|
||||||
silent: true
|
silent: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Vue.use(VueCompositionAPI)
|
||||||
Vue.use(VueLazyload)
|
Vue.use(VueLazyload)
|
||||||
Vue.directive('title', function (el, binding) {
|
Vue.directive('title', function (el, binding) {
|
||||||
store.commit('ui/pageTitle', binding.value)
|
store.commit('ui/pageTitle', binding.value)
|
||||||
|
|
|
@ -54,7 +54,6 @@ export default {
|
||||||
moderation: false
|
moderation: false
|
||||||
},
|
},
|
||||||
profile: null,
|
profile: null,
|
||||||
token: '',
|
|
||||||
oauth: getDefaultOauth(),
|
oauth: getDefaultOauth(),
|
||||||
scopedTokens: getDefaultScopedTokens()
|
scopedTokens: getDefaultScopedTokens()
|
||||||
},
|
},
|
||||||
|
@ -71,7 +70,6 @@ export default {
|
||||||
state.profile = null
|
state.profile = null
|
||||||
state.username = ''
|
state.username = ''
|
||||||
state.fullUsername = ''
|
state.fullUsername = ''
|
||||||
state.token = ''
|
|
||||||
state.scopedTokens = getDefaultScopedTokens()
|
state.scopedTokens = getDefaultScopedTokens()
|
||||||
state.oauth = getDefaultOauth()
|
state.oauth = getDefaultOauth()
|
||||||
state.availablePermissions = {
|
state.availablePermissions = {
|
||||||
|
@ -89,7 +87,6 @@ export default {
|
||||||
if (value === false) {
|
if (value === false) {
|
||||||
state.username = null
|
state.username = null
|
||||||
state.fullUsername = null
|
state.fullUsername = null
|
||||||
state.token = null
|
|
||||||
state.profile = null
|
state.profile = null
|
||||||
state.scopedTokens = getDefaultScopedTokens()
|
state.scopedTokens = getDefaultScopedTokens()
|
||||||
state.availablePermissions = {}
|
state.availablePermissions = {}
|
||||||
|
@ -106,9 +103,6 @@ export default {
|
||||||
state.profile.avatar = value
|
state.profile.avatar = value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
token: (state, value) => {
|
|
||||||
state.token = value
|
|
||||||
},
|
|
||||||
scopedTokens: (state, value) => {
|
scopedTokens: (state, value) => {
|
||||||
state.scopedTokens = { ...value }
|
state.scopedTokens = { ...value }
|
||||||
},
|
},
|
||||||
|
@ -138,7 +132,6 @@ export default {
|
||||||
})
|
})
|
||||||
return axios.post('users/login', form).then(response => {
|
return axios.post('users/login', form).then(response => {
|
||||||
logger.default.info('Successfully logged in as', credentials.username)
|
logger.default.info('Successfully logged in as', credentials.username)
|
||||||
// commit('token', response.data.token)
|
|
||||||
dispatch('fetchProfile').then(() => {
|
dispatch('fetchProfile').then(() => {
|
||||||
// Redirect to a specified route
|
// Redirect to a specified route
|
||||||
import('@/router').then((router) => {
|
import('@/router').then((router) => {
|
||||||
|
@ -169,16 +162,6 @@ export default {
|
||||||
})
|
})
|
||||||
logger.default.info('Log out, goodbye!')
|
logger.default.info('Log out, goodbye!')
|
||||||
},
|
},
|
||||||
async check ({ commit, dispatch, state }) {
|
|
||||||
logger.default.info('Checking authentication…')
|
|
||||||
commit('authenticated', false)
|
|
||||||
const profile = await dispatch('fetchProfile')
|
|
||||||
if (profile) {
|
|
||||||
commit('authenticated', true)
|
|
||||||
} else {
|
|
||||||
logger.default.info('Anonymous user')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fetchProfile ({ commit, dispatch, state }) {
|
fetchProfile ({ commit, dispatch, state }) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios.get('users/me/').then((response) => {
|
axios.get('users/me/').then((response) => {
|
||||||
|
|
|
@ -37,28 +37,15 @@ describe('store/auth', () => {
|
||||||
it('authenticated false', () => {
|
it('authenticated false', () => {
|
||||||
const state = {
|
const state = {
|
||||||
username: 'dummy',
|
username: 'dummy',
|
||||||
token: 'dummy',
|
|
||||||
profile: 'dummy',
|
profile: 'dummy',
|
||||||
availablePermissions: 'dummy'
|
availablePermissions: 'dummy'
|
||||||
}
|
}
|
||||||
store.mutations.authenticated(state, false)
|
store.mutations.authenticated(state, false)
|
||||||
expect(state.authenticated).to.equal(false)
|
expect(state.authenticated).to.equal(false)
|
||||||
expect(state.username).to.equal(null)
|
expect(state.username).to.equal(null)
|
||||||
expect(state.token).to.equal(null)
|
|
||||||
expect(state.profile).to.equal(null)
|
expect(state.profile).to.equal(null)
|
||||||
expect(state.availablePermissions).to.deep.equal({})
|
expect(state.availablePermissions).to.deep.equal({})
|
||||||
})
|
})
|
||||||
it('token null', () => {
|
|
||||||
const state = {}
|
|
||||||
store.mutations.token(state, null)
|
|
||||||
expect(state.token).to.equal(null)
|
|
||||||
})
|
|
||||||
it('token real', () => {
|
|
||||||
const state = {}
|
|
||||||
let token = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJodHRwczovL2p3dC1pZHAuZXhhbXBsZS5jb20iLCJzdWIiOiJtYWlsdG86bWlrZUBleGFtcGxlLmNvbSIsIm5iZiI6MTUxNTUzMzQyOSwiZXhwIjoxNTE1NTM3MDI5LCJpYXQiOjE1MTU1MzM0MjksImp0aSI6ImlkMTIzNDU2IiwidHlwIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9yZWdpc3RlciJ9.'
|
|
||||||
store.mutations.token(state, token)
|
|
||||||
expect(state.token).to.equal(token)
|
|
||||||
})
|
|
||||||
it('permissions', () => {
|
it('permissions', () => {
|
||||||
const state = { availablePermissions: {} }
|
const state = { availablePermissions: {} }
|
||||||
store.mutations.permission(state, {key: 'admin', status: true})
|
store.mutations.permission(state, {key: 'admin', status: true})
|
||||||
|
@ -86,19 +73,6 @@ describe('store/auth', () => {
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('check jwt null', () => {
|
|
||||||
testAction({
|
|
||||||
action: store.actions.check,
|
|
||||||
params: {state: {}},
|
|
||||||
expectedMutations: [
|
|
||||||
{ type: 'authenticated', payload: false },
|
|
||||||
{ type: 'authenticated', payload: true },
|
|
||||||
],
|
|
||||||
expectedActions: [
|
|
||||||
{ type: 'fetchProfile' },
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
it('login success', () => {
|
it('login success', () => {
|
||||||
moxios.stubRequest('token/', {
|
moxios.stubRequest('token/', {
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|
|
@ -17,14 +17,6 @@ export default defineConfig({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'fix-django-channels',
|
|
||||||
transform (src, id) {
|
|
||||||
if (id.includes('django-channels')) {
|
|
||||||
return `var parcelRequire;${src}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
port: process.env.VUE_PORT || '8080',
|
port: process.env.VUE_PORT || '8080',
|
||||||
|
|
|
@ -1009,13 +1009,6 @@
|
||||||
"@babel/types" "^7.4.4"
|
"@babel/types" "^7.4.4"
|
||||||
esutils "^2.0.2"
|
esutils "^2.0.2"
|
||||||
|
|
||||||
"@babel/runtime@^7.5.5":
|
|
||||||
version "7.17.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825"
|
|
||||||
integrity sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==
|
|
||||||
dependencies:
|
|
||||||
regenerator-runtime "^0.13.4"
|
|
||||||
|
|
||||||
"@babel/runtime@^7.8.4":
|
"@babel/runtime@^7.8.4":
|
||||||
version "7.17.8"
|
version "7.17.8"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2"
|
||||||
|
@ -1603,6 +1596,11 @@
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
prettier "^1.18.2 || ^2.0.0"
|
prettier "^1.18.2 || ^2.0.0"
|
||||||
|
|
||||||
|
"@vue/composition-api@^1.4.9":
|
||||||
|
version "1.4.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/composition-api/-/composition-api-1.4.9.tgz#6fa65284f545887b52d421f23b4fa1c41bc0ad4b"
|
||||||
|
integrity sha512-l6YOeg5LEXmfPqyxAnBaCv1FMRw0OGKJ4m6nOWRm6ngt5TuHcj5ZoBRN+LXh3J0u6Ur3C4VA+RiKT+M0eItr/g==
|
||||||
|
|
||||||
"@vue/reactivity-transform@3.2.31":
|
"@vue/reactivity-transform@3.2.31":
|
||||||
version "3.2.31"
|
version "3.2.31"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz#0f5b25c24e70edab2b613d5305c465b50fc00911"
|
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz#0f5b25c24e70edab2b613d5305c465b50fc00911"
|
||||||
|
@ -1628,6 +1626,27 @@
|
||||||
lodash "^4.17.15"
|
lodash "^4.17.15"
|
||||||
pretty "^2.0.0"
|
pretty "^2.0.0"
|
||||||
|
|
||||||
|
"@vueuse/core@^8.2.5":
|
||||||
|
version "8.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-8.2.5.tgz#ca6a59091ecf16e6739c53f3d857b11967a5eb06"
|
||||||
|
integrity sha512-5prZAA1Ji2ltwNUnzreu6WIXYqHYP/9U2BiY5mD/650VYLpVcwVlYznJDFcLCmEWI3o3Vd34oS1FUf+6Mh68GQ==
|
||||||
|
dependencies:
|
||||||
|
"@vueuse/metadata" "8.2.5"
|
||||||
|
"@vueuse/shared" "8.2.5"
|
||||||
|
vue-demi "*"
|
||||||
|
|
||||||
|
"@vueuse/metadata@8.2.5":
|
||||||
|
version "8.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-8.2.5.tgz#51c7d95e04284ea378a5242a2e88b77494e2c117"
|
||||||
|
integrity sha512-Lk9plJjh9cIdiRdcj16dau+2LANxIdFCiTgdfzwYXbflxq0QnMBeOD2qHgKDE7fuVrtPcVWj8VSuZEx1HRfNQA==
|
||||||
|
|
||||||
|
"@vueuse/shared@8.2.5":
|
||||||
|
version "8.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-8.2.5.tgz#1ae200a240c4b8d42d41723b64d8f917aa57ff16"
|
||||||
|
integrity sha512-lNWo+7sk6JCuOj4AiYM+6HZ6fq4xAuVq1sVckMQKgfCJZpZRe4i8es+ZULO5bYTKP+VrOCtqrLR2GzEfrbr3YQ==
|
||||||
|
dependencies:
|
||||||
|
vue-demi "*"
|
||||||
|
|
||||||
abab@^2.0.3, abab@^2.0.5:
|
abab@^2.0.3, abab@^2.0.5:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
|
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
|
||||||
|
@ -2572,15 +2591,6 @@ diff@5.0.0, diff@^5.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
|
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
|
||||||
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
|
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
|
||||||
|
|
||||||
django-channels@2.1.3:
|
|
||||||
version "2.1.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/django-channels/-/django-channels-2.1.3.tgz#4d175b9d8553f3e2b1263b75de0b4f23fc9acac3"
|
|
||||||
integrity sha512-H0jzdw3XvBR3HC4FqMqhoM0M4iUlOJ1TCRpyL51r//8LX2KGAK7lA1+4JeTxvnlaq8xBvZ3mMTf55eaRoDcgKA==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.5.5"
|
|
||||||
event-target-shim "^5.0.1"
|
|
||||||
reconnecting-websocket "^4.1.10"
|
|
||||||
|
|
||||||
doctrine@^2.1.0:
|
doctrine@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
|
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
|
||||||
|
@ -3121,11 +3131,6 @@ esutils@^2.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||||
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
|
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
|
||||||
|
|
||||||
event-target-shim@^5.0.1:
|
|
||||||
version "5.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
|
||||||
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
|
|
||||||
|
|
||||||
execa@^5.0.0:
|
execa@^5.0.0:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
|
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
|
||||||
|
@ -5136,11 +5141,6 @@ readdirp@~3.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.2.1"
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
reconnecting-websocket@^4.1.10:
|
|
||||||
version "4.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783"
|
|
||||||
integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==
|
|
||||||
|
|
||||||
regenerate-unicode-properties@^10.0.1:
|
regenerate-unicode-properties@^10.0.1:
|
||||||
version "10.0.1"
|
version "10.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56"
|
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56"
|
||||||
|
@ -5849,6 +5849,11 @@ void-elements@^3.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
|
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
|
||||||
integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=
|
integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=
|
||||||
|
|
||||||
|
vue-demi@*:
|
||||||
|
version "0.12.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.12.5.tgz#8eeed566a7d86eb090209a11723f887d28aeb2d1"
|
||||||
|
integrity sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==
|
||||||
|
|
||||||
vue-eslint-parser@^7.10.0:
|
vue-eslint-parser@^7.10.0:
|
||||||
version "7.11.0"
|
version "7.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.11.0.tgz#214b5dea961007fcffb2ee65b8912307628d0daf"
|
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.11.0.tgz#214b5dea961007fcffb2ee65b8912307628d0daf"
|
||||||
|
|
Loading…
Reference in New Issue