jason-rails 0.4.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.ruby-version +1 -0
  4. data/Gemfile.lock +152 -2
  5. data/README.md +117 -5
  6. data/app/controllers/jason/api/pusher_controller.rb +15 -0
  7. data/app/controllers/jason/api_controller.rb +44 -2
  8. data/client/lib/JasonContext.d.ts +6 -1
  9. data/client/lib/JasonProvider.d.ts +2 -2
  10. data/client/lib/JasonProvider.js +5 -124
  11. data/client/lib/createJasonReducers.js +48 -3
  12. data/client/lib/createOptDis.js +0 -2
  13. data/client/lib/createPayloadHandler.d.ts +9 -1
  14. data/client/lib/createPayloadHandler.js +47 -55
  15. data/client/lib/createServerActionQueue.d.ts +10 -0
  16. data/client/lib/createServerActionQueue.js +48 -0
  17. data/client/lib/createServerActionQueue.test.d.ts +1 -0
  18. data/client/lib/createServerActionQueue.test.js +37 -0
  19. data/client/lib/createTransportAdapter.d.ts +5 -0
  20. data/client/lib/createTransportAdapter.js +20 -0
  21. data/client/lib/index.d.ts +3 -2
  22. data/client/lib/pruneIdsMiddleware.d.ts +2 -0
  23. data/client/lib/pruneIdsMiddleware.js +24 -0
  24. data/client/lib/restClient.d.ts +2 -0
  25. data/client/lib/restClient.js +17 -0
  26. data/client/lib/transportAdapters/actionCableAdapter.d.ts +5 -0
  27. data/client/lib/transportAdapters/actionCableAdapter.js +35 -0
  28. data/client/lib/transportAdapters/pusherAdapter.d.ts +5 -0
  29. data/client/lib/transportAdapters/pusherAdapter.js +68 -0
  30. data/client/lib/useJason.d.ts +5 -0
  31. data/client/lib/useJason.js +94 -0
  32. data/client/lib/useJason.test.d.ts +1 -0
  33. data/client/lib/useJason.test.js +85 -0
  34. data/client/lib/useSub.d.ts +1 -1
  35. data/client/lib/useSub.js +6 -3
  36. data/client/package.json +5 -3
  37. data/client/src/JasonProvider.tsx +5 -123
  38. data/client/src/createJasonReducers.ts +56 -3
  39. data/client/src/createOptDis.ts +0 -2
  40. data/client/src/createPayloadHandler.ts +53 -64
  41. data/client/src/createServerActionQueue.test.ts +42 -0
  42. data/client/src/createServerActionQueue.ts +47 -0
  43. data/client/src/createTransportAdapter.ts +13 -0
  44. data/client/src/pruneIdsMiddleware.ts +24 -0
  45. data/client/src/restClient.ts +14 -0
  46. data/client/src/transportAdapters/actionCableAdapter.ts +38 -0
  47. data/client/src/transportAdapters/pusherAdapter.ts +72 -0
  48. data/client/src/useJason.test.ts +87 -0
  49. data/client/src/useJason.ts +110 -0
  50. data/client/src/useSub.ts +6 -3
  51. data/client/yarn.lock +71 -3
  52. data/config/routes.rb +5 -1
  53. data/jason-rails.gemspec +4 -0
  54. data/lib/jason.rb +61 -1
  55. data/lib/jason/api_model.rb +2 -12
  56. data/lib/jason/broadcaster.rb +19 -0
  57. data/lib/jason/channel.rb +50 -21
  58. data/lib/jason/graph_helper.rb +165 -0
  59. data/lib/jason/includes_helper.rb +108 -0
  60. data/lib/jason/lua_generator.rb +71 -0
  61. data/lib/jason/publisher.rb +82 -37
  62. data/lib/jason/publisher_old.rb +112 -0
  63. data/lib/jason/subscription.rb +349 -97
  64. data/lib/jason/subscription_old.rb +171 -0
  65. data/lib/jason/version.rb +1 -1
  66. metadata +80 -11
  67. data/app/assets/config/jason_engine_manifest.js +0 -1
  68. data/app/assets/images/jason/engine/.keep +0 -0
  69. data/app/assets/stylesheets/jason/engine/application.css +0 -15
  70. data/app/helpers/jason/engine/application_helper.rb +0 -6
  71. data/app/jobs/jason/engine/application_job.rb +0 -6
  72. data/app/mailers/jason/engine/application_mailer.rb +0 -8
  73. data/app/models/jason/engine/application_record.rb +0 -7
  74. data/app/views/layouts/jason/engine/application.html.erb +0 -15
@@ -0,0 +1,13 @@
1
+ import actionCableAdapter from './transportAdapters/actionCableAdapter'
2
+ import pusherAdapter from './transportAdapters/pusherAdapter'
3
+
4
+ export default function createTransportAdapter(jasonConfig, handlePayload, dispatch, onConnect) {
5
+ const { transportService } = jasonConfig
6
+ if (transportService === 'action_cable') {
7
+ return actionCableAdapter(jasonConfig, handlePayload, dispatch, onConnect)
8
+ } else if (transportService === 'pusher') {
9
+ return pusherAdapter(jasonConfig, handlePayload, dispatch)
10
+ } else {
11
+ throw(`Transport adapter does not exist for ${transportService}`)
12
+ }
13
+ }
@@ -0,0 +1,24 @@
1
+ import _ from 'lodash'
2
+ import pluralize from 'pluralize'
3
+
4
+ const pruneIdsMiddleware = schema => store => next => action => {
5
+ const { type, payload } = action
6
+ const result = next(action)
7
+
8
+ const state = store.getState()
9
+ if (type === 'jasonModels/setSubscriptionIds' || type === 'jasonModels/removeSubscriptionIds') {
10
+ const { model, ids } = payload
11
+
12
+ let idsInSubs = []
13
+ _.map(state.jasonModels[model], (subscribedIds, k) => {
14
+ idsInSubs = _.union(idsInSubs, subscribedIds)
15
+ })
16
+ // Find IDs currently in Redux that aren't in any subscription
17
+ const idsToRemove = _.difference(state[pluralize(model)].ids, idsInSubs)
18
+ store.dispatch({ type: `${pluralize(model)}/removeMany`, payload: idsToRemove })
19
+ }
20
+
21
+ return result
22
+ }
23
+
24
+ export default pruneIdsMiddleware
@@ -0,0 +1,14 @@
1
+ import axios from 'axios'
2
+ import applyCaseMiddleware from 'axios-case-converter'
3
+ import { validate as isUuid } from 'uuid'
4
+
5
+ const csrfToken = (document?.querySelector("meta[name=csrf-token]") as any)?.content
6
+ axios.defaults.headers.common['X-CSRF-Token'] = csrfToken
7
+
8
+ const restClient = applyCaseMiddleware(axios.create() as any, {
9
+ preservedKeys: (key) => {
10
+ return isUuid(key)
11
+ }
12
+ }) as any
13
+
14
+ export default restClient
@@ -0,0 +1,38 @@
1
+ import { createConsumer } from "@rails/actioncable"
2
+
3
+ export default function actionCableAdapter(jasonConfig, handlePayload, dispatch, onConnected) {
4
+ const consumer = createConsumer()
5
+ const subscription = (consumer.subscriptions.create({
6
+ channel: 'Jason::Channel'
7
+ }, {
8
+ connected: () => {
9
+ dispatch({ type: 'jason/upsert', payload: { connected: true } })
10
+ console.debug('Connected to ActionCable')
11
+
12
+ // When AC loses connection - all state is lost, so we need to re-initialize all subscriptions
13
+ onConnected()
14
+ },
15
+ received: payload => {
16
+ handlePayload(payload)
17
+ console.debug("ActionCable Payload received: ", payload)
18
+ },
19
+ disconnected: () => {
20
+ dispatch({ type: 'jason/upsert', payload: { connected: false } })
21
+ console.warn('Disconnected from ActionCable')
22
+ }
23
+ }));
24
+
25
+ function getPayload(config, options) {
26
+ subscription.send({ getPayload: config, ...options })
27
+ }
28
+
29
+ function createSubscription(config) {
30
+ subscription.send({ createSubscription: config })
31
+ }
32
+
33
+ function removeSubscription(config) {
34
+ subscription.send({ removeSubscription: config })
35
+ }
36
+
37
+ return { getPayload, createSubscription, removeSubscription }
38
+ }
@@ -0,0 +1,72 @@
1
+ import Pusher from 'pusher-js'
2
+ import { createConsumer } from "@rails/actioncable"
3
+ import restClient from '../restClient'
4
+ import { v4 as uuidv4 } from 'uuid'
5
+ import _ from 'lodash'
6
+
7
+ export default function pusherAdapter(jasonConfig, handlePayload, dispatch) {
8
+ let consumerId = uuidv4()
9
+
10
+ const { pusherKey, pusherRegion, pusherChannelPrefix } = jasonConfig
11
+ const pusher = new Pusher(pusherKey, {
12
+ cluster: 'eu',
13
+ forceTLS: true,
14
+ authEndpoint: '/jason/api/pusher/auth'
15
+ })
16
+ pusher.connection.bind('state_change', ({ current }) => {
17
+ if (current === 'connected') {
18
+ dispatch({ type: 'jason/upsert', payload: { connected: true } })
19
+ } else {
20
+ dispatch({ type: 'jason/upsert', payload: { connected: false } })
21
+ }
22
+ })
23
+ pusher.connection.bind( 'error', error => {
24
+ dispatch({ type: 'jason/upsert', payload: { connected: false } })
25
+ });
26
+
27
+ const configToChannel = {}
28
+
29
+ function createSubscription(config) {
30
+ restClient.post('/jason/api/create_subscription', { config, consumerId })
31
+ .then(({ data: { channelName } }) => {
32
+ configToChannel[JSON.stringify(config)] = channelName
33
+ subscribeToChannel(channelName)
34
+ })
35
+ .catch(e => console.error(e))
36
+ }
37
+
38
+ function removeSubscription(config) {
39
+ const channelName = configToChannel[JSON.stringify(config)]
40
+ unsubscribeFromChannel(fullChannelName(channelName))
41
+ restClient.post('/jason/api/remove_subscription', { config, consumerId })
42
+ .catch(e => console.error(e))
43
+ }
44
+
45
+ function getPayload(config, options) {
46
+ restClient.post('/jason/api/get_payload', {
47
+ config,
48
+ options
49
+ })
50
+ .then(({ data }) => {
51
+ _.map(data, (payload, modelName) => {
52
+ handlePayload(payload)
53
+ })
54
+ })
55
+ .catch(e => console.error(e))
56
+ }
57
+
58
+ function subscribeToChannel(channelName) {
59
+ const channel = pusher.subscribe(fullChannelName(channelName))
60
+ channel.bind('changed', message => handlePayload(message))
61
+ }
62
+
63
+ function unsubscribeFromChannel(channelName) {
64
+ const channel = pusher.unsubscribe(fullChannelName(channelName))
65
+ }
66
+
67
+ function fullChannelName(channelName) {
68
+ return `private-${pusherChannelPrefix}-${channelName}`
69
+ }
70
+
71
+ return { getPayload, createSubscription, removeSubscription }
72
+ }
@@ -0,0 +1,87 @@
1
+ import { renderHook, act } from '@testing-library/react-hooks'
2
+ import useJason from './useJason'
3
+ import restClient from './restClient'
4
+
5
+ jest.mock('./restClient')
6
+
7
+ test('it works', async () => {
8
+ const resp = { data: {
9
+ schema: { post: {} },
10
+ transportService: 'action_cable'
11
+ } };
12
+ // @ts-ignore
13
+ restClient.get.mockResolvedValue(resp);
14
+
15
+ const { result, waitForNextUpdate } = renderHook(() => useJason({ reducers: {
16
+ test: (s,a) => s || {}
17
+ }}));
18
+
19
+ await waitForNextUpdate()
20
+ const [store, value, connected] = result.current
21
+ const { handlePayload, subscribe } = value
22
+
23
+ const subscription = subscribe({ post: {} })
24
+
25
+ handlePayload({
26
+ type: 'payload',
27
+ model: 'post',
28
+ payload: [{ id: 4, name: 'test' }],
29
+ md5Hash: subscription.md5Hash,
30
+ idx: 1
31
+ })
32
+
33
+ handlePayload({
34
+ id: 4,
35
+ model: 'post',
36
+ destroy: true,
37
+ md5Hash: subscription.md5Hash,
38
+ idx: 2
39
+ })
40
+
41
+ handlePayload({
42
+ id: 5,
43
+ model: 'post',
44
+ payload: { id: 5, name: 'test2' },
45
+ md5Hash: subscription.md5Hash,
46
+ idx: 3
47
+ })
48
+ })
49
+
50
+ test('pruning IDs', async () => {
51
+ const resp = { data: {
52
+ schema: { post: {} },
53
+ transportService: 'action_cable'
54
+ } };
55
+
56
+ // @ts-ignore
57
+ restClient.get.mockResolvedValue(resp);
58
+
59
+ const { result, waitForNextUpdate } = renderHook(() => useJason({ reducers: {
60
+ test: (s,a) => s || {}
61
+ }}));
62
+
63
+ await waitForNextUpdate()
64
+ const [store, value, connected] = result.current
65
+ const { handlePayload, subscribe } = value
66
+
67
+ const subscription = subscribe({ post: {} })
68
+
69
+ handlePayload({
70
+ type: 'payload',
71
+ model: 'post',
72
+ payload: [{ id: 4, name: 'test' }],
73
+ md5Hash: subscription.md5Hash,
74
+ idx: 1
75
+ })
76
+
77
+ handlePayload({
78
+ type: 'payload',
79
+ model: 'post',
80
+ payload: [{ id: 5, name: 'test it out' }],
81
+ md5Hash: subscription.md5Hash,
82
+ idx: 2
83
+ })
84
+
85
+ // The ID 4 should have been pruned
86
+ expect(store.getState().posts.ids).toStrictEqual([5])
87
+ })
@@ -0,0 +1,110 @@
1
+ import createActions from './createActions'
2
+ import createJasonReducers from './createJasonReducers'
3
+ import createPayloadHandler from './createPayloadHandler'
4
+ import createOptDis from './createOptDis'
5
+ import createServerActionQueue from './createServerActionQueue'
6
+ import restClient from './restClient'
7
+ import pruneIdsMiddleware from './pruneIdsMiddleware'
8
+ import createTransportAdapater from './createTransportAdapter'
9
+
10
+ import { createEntityAdapter, createSlice, createReducer, configureStore } from '@reduxjs/toolkit'
11
+
12
+ import makeEager from './makeEager'
13
+ import { camelizeKeys } from 'humps'
14
+ import md5 from 'blueimp-md5'
15
+ import _ from 'lodash'
16
+ import React, { useState, useEffect } from 'react'
17
+
18
+ export default function useJason({ reducers, middleware = [], extraActions }: { reducers?: any, middleware?: any[], extraActions?: any }) {
19
+ const [store, setStore] = useState(null as any)
20
+ const [value, setValue] = useState(null as any)
21
+
22
+ useEffect(() => {
23
+ restClient.get('/jason/api/config')
24
+ .then(({ data: jasonConfig }) => {
25
+ const { schema: snakey_schema } = jasonConfig
26
+ const schema = camelizeKeys(snakey_schema)
27
+ console.debug({ schema })
28
+
29
+ const serverActionQueue = createServerActionQueue()
30
+
31
+
32
+ const allReducers = {
33
+ ...reducers,
34
+ ...createJasonReducers(schema)
35
+ }
36
+
37
+ console.debug({ allReducers })
38
+
39
+ const store = configureStore({ reducer: allReducers, middleware: [...middleware, pruneIdsMiddleware(schema)] })
40
+ const dispatch = store.dispatch
41
+
42
+ const optDis = createOptDis(schema, dispatch, restClient, serverActionQueue)
43
+ const actions = createActions(schema, store, restClient, optDis, extraActions)
44
+ const eager = makeEager(schema)
45
+
46
+ let payloadHandlers = {}
47
+ let configs = {}
48
+ let subOptions = {}
49
+
50
+ function handlePayload(payload) {
51
+ const { md5Hash } = payload
52
+
53
+ const { handlePayload } = payloadHandlers[md5Hash]
54
+ if (handlePayload) {
55
+ handlePayload(payload)
56
+ } else {
57
+ console.warn("Payload arrived with no handler", payload, payloadHandlers)
58
+ }
59
+ }
60
+
61
+ const transportAdapter = createTransportAdapater(jasonConfig, handlePayload, dispatch, () => _.keys(configs).forEach(md5Hash => createSubscription(configs[md5Hash], subOptions[md5Hash])))
62
+
63
+ function createSubscription(config, options = {}) {
64
+ // We need the hash to be consistent in Ruby / Javascript
65
+ const hashableConfig = _({ conditions: {}, includes: {}, ...config }).toPairs().sortBy(0).fromPairs().value()
66
+ const md5Hash = md5(JSON.stringify(hashableConfig))
67
+ payloadHandlers[md5Hash] = createPayloadHandler({ dispatch, serverActionQueue, transportAdapter, config })
68
+ configs[md5Hash] = hashableConfig
69
+ subOptions[md5Hash] = options
70
+
71
+ setTimeout(() => transportAdapter.createSubscription(hashableConfig), 500)
72
+ let pollInterval = null as any;
73
+
74
+ // This is only for debugging / dev - not prod!
75
+ // @ts-ignore
76
+ if (options.pollInterval) {
77
+ // @ts-ignore
78
+ pollInterval = setInterval(() => transportAdapter.getPayload(hashableConfig, { forceRefresh: true }), options.pollInterval)
79
+ }
80
+
81
+ return {
82
+ remove() {
83
+ removeSubscription(hashableConfig)
84
+ if (pollInterval) clearInterval(pollInterval)
85
+ },
86
+ md5Hash
87
+ }
88
+ }
89
+
90
+ function removeSubscription(config) {
91
+ transportAdapter.removeSubscription(config)
92
+ const md5Hash = md5(JSON.stringify(config))
93
+ payloadHandlers[md5Hash].tearDown()
94
+ delete payloadHandlers[md5Hash]
95
+ delete configs[md5Hash]
96
+ delete subOptions[md5Hash]
97
+ }
98
+
99
+ setValue({
100
+ actions: actions,
101
+ subscribe: createSubscription,
102
+ eager,
103
+ handlePayload
104
+ })
105
+ setStore(store)
106
+ })
107
+ }, [])
108
+
109
+ return [store, value]
110
+ }
data/client/src/useSub.ts CHANGED
@@ -1,10 +1,13 @@
1
1
  import JasonContext from './JasonContext'
2
2
  import { useContext, useEffect } from 'react'
3
3
 
4
- export default function useSub(config) {
4
+ export default function useSub(config, options = {}) {
5
+ // useEffect uses strict equality
6
+ const configJson = JSON.stringify(config)
5
7
  const subscribe = useContext(JasonContext).subscribe
6
8
 
7
9
  useEffect(() => {
8
- return subscribe(config)
9
- }, [])
10
+ // @ts-ignore
11
+ return subscribe(config, options).remove
12
+ }, [configJson])
10
13
  }
data/client/yarn.lock CHANGED
@@ -813,7 +813,7 @@
813
813
  "@babel/helper-validator-option" "^7.12.1"
814
814
  "@babel/plugin-transform-typescript" "^7.12.1"
815
815
 
816
- "@babel/runtime@^7.12.1", "@babel/runtime@^7.8.4":
816
+ "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4":
817
817
  version "7.12.5"
818
818
  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
819
819
  integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
@@ -1082,6 +1082,18 @@
1082
1082
  dependencies:
1083
1083
  "@sinonjs/commons" "^1.7.0"
1084
1084
 
1085
+ "@testing-library/react-hooks@^5.0.3":
1086
+ version "5.0.3"
1087
+ resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-5.0.3.tgz#dd0d2048817b013b266d35ca45e3ea48a19fd87e"
1088
+ integrity sha512-UrnnRc5II7LMH14xsYNm/WRch/67cBafmrSQcyFh0v+UUmSf1uzfB7zn5jQXSettGwOSxJwdQUN7PgkT0w22Lg==
1089
+ dependencies:
1090
+ "@babel/runtime" "^7.12.5"
1091
+ "@types/react" ">=16.9.0"
1092
+ "@types/react-dom" ">=16.9.0"
1093
+ "@types/react-test-renderer" ">=16.9.0"
1094
+ filter-console "^0.1.1"
1095
+ react-error-boundary "^3.1.0"
1096
+
1085
1097
  "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7":
1086
1098
  version "7.1.12"
1087
1099
  resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz#4d8e9e51eb265552a7e4f1ff2219ab6133bdfb2d"
@@ -1164,6 +1176,33 @@
1164
1176
  resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.5.tgz#b6ab3bba29e16b821d84e09ecfaded462b816b00"
1165
1177
  integrity sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ==
1166
1178
 
1179
+ "@types/prop-types@*":
1180
+ version "15.7.3"
1181
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
1182
+ integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
1183
+
1184
+ "@types/react-dom@>=16.9.0":
1185
+ version "17.0.0"
1186
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.0.tgz#b3b691eb956c4b3401777ee67b900cb28415d95a"
1187
+ integrity sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==
1188
+ dependencies:
1189
+ "@types/react" "*"
1190
+
1191
+ "@types/react-test-renderer@>=16.9.0":
1192
+ version "17.0.0"
1193
+ resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.0.tgz#9be47b375eeb906fced37049e67284a438d56620"
1194
+ integrity sha512-nvw+F81OmyzpyIE1S0xWpLonLUZCMewslPuA8BtjSKc5XEbn8zEQBXS7KuOLHTNnSOEM2Pum50gHOoZ62tqTRg==
1195
+ dependencies:
1196
+ "@types/react" "*"
1197
+
1198
+ "@types/react@*", "@types/react@>=16.9.0":
1199
+ version "17.0.0"
1200
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.0.tgz#5af3eb7fad2807092f0046a1302b7823e27919b8"
1201
+ integrity sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==
1202
+ dependencies:
1203
+ "@types/prop-types" "*"
1204
+ csstype "^3.0.2"
1205
+
1167
1206
  "@types/stack-utils@^2.0.0":
1168
1207
  version "2.0.0"
1169
1208
  resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff"
@@ -1742,6 +1781,11 @@ cssstyle@^2.2.0:
1742
1781
  dependencies:
1743
1782
  cssom "~0.3.6"
1744
1783
 
1784
+ csstype@^3.0.2:
1785
+ version "3.0.6"
1786
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.6.tgz#865d0b5833d7d8d40f4e5b8a6d76aea3de4725ef"
1787
+ integrity sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==
1788
+
1745
1789
  dashdash@^1.12.0:
1746
1790
  version "1.14.1"
1747
1791
  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
@@ -2081,6 +2125,11 @@ fill-range@^7.0.1:
2081
2125
  dependencies:
2082
2126
  to-regex-range "^5.0.1"
2083
2127
 
2128
+ filter-console@^0.1.1:
2129
+ version "0.1.1"
2130
+ resolved "https://registry.yarnpkg.com/filter-console/-/filter-console-0.1.1.tgz#6242be28982bba7415bcc6db74a79f4a294fa67c"
2131
+ integrity sha512-zrXoV1Uaz52DqPs+qEwNJWJFAWZpYJ47UNmpN9q4j+/EYsz85uV0DC9k8tRND5kYmoVzL0W+Y75q4Rg8sRJCdg==
2132
+
2084
2133
  find-up@^4.0.0, find-up@^4.1.0:
2085
2134
  version "4.1.0"
2086
2135
  resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
@@ -3596,12 +3645,19 @@ punycode@^2.1.0, punycode@^2.1.1:
3596
3645
  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
3597
3646
  integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
3598
3647
 
3648
+ pusher-js@^7.0.3:
3649
+ version "7.0.3"
3650
+ resolved "https://registry.yarnpkg.com/pusher-js/-/pusher-js-7.0.3.tgz#f81c78cdf2ad32f546caa7532ec7f9081ef00b8d"
3651
+ integrity sha512-HIfCvt00CAqgO4W0BrdpPsDcAwy51rB6DN0VMC+JeVRRbo8mn3XTeUeIFjmmlRLZLX8rPhUtLRo7vPag6b8GCw==
3652
+ dependencies:
3653
+ tweetnacl "^1.0.3"
3654
+
3599
3655
  qs@~6.5.2:
3600
3656
  version "6.5.2"
3601
3657
  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
3602
3658
  integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
3603
3659
 
3604
- react-dom@^16.8.3:
3660
+ react-dom@^16.9.x:
3605
3661
  version "16.14.0"
3606
3662
  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
3607
3663
  integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==
@@ -3611,6 +3667,13 @@ react-dom@^16.8.3:
3611
3667
  prop-types "^15.6.2"
3612
3668
  scheduler "^0.19.1"
3613
3669
 
3670
+ react-error-boundary@^3.1.0:
3671
+ version "3.1.0"
3672
+ resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.0.tgz#9487443df2f9ba1db90d8ab52351814907ea4af3"
3673
+ integrity sha512-lmPrdi5SLRJR+AeJkqdkGlW/CRkAUvZnETahK58J4xb5wpbfDngasEGu+w0T1iXEhVrYBJZeW+c4V1hILCnMWQ==
3674
+ dependencies:
3675
+ "@babel/runtime" "^7.12.5"
3676
+
3614
3677
  react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1:
3615
3678
  version "16.13.1"
3616
3679
  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -3632,7 +3695,7 @@ react-redux@^7.2.2:
3632
3695
  prop-types "^15.7.2"
3633
3696
  react-is "^16.13.1"
3634
3697
 
3635
- react@^16.8.3:
3698
+ react@^16.9.x:
3636
3699
  version "16.14.0"
3637
3700
  resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
3638
3701
  integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
@@ -4302,6 +4365,11 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
4302
4365
  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
4303
4366
  integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
4304
4367
 
4368
+ tweetnacl@^1.0.3:
4369
+ version "1.0.3"
4370
+ resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
4371
+ integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
4372
+
4305
4373
  type-check@~0.3.2:
4306
4374
  version "0.3.2"
4307
4375
  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"