diff --git a/WORKSPACE b/WORKSPACE index 69ffa190..6a6de475 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -7,6 +7,20 @@ workspace( load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +# TODO: Maybe put in rules? +http_archive( + name = "rules_pkg", + sha256 = "8a298e832762eda1830597d64fe7db58178aa84cd5926d76d5b744d6558941c2", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz", + "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz", + ], +) + +load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") + +rules_pkg_dependencies() + http_archive( name = "rules_player", strip_prefix = "rules_player-0.10.0", diff --git a/devtools/client/README.md b/devtools/client/README.md index ae79c9c3..8979ac27 100644 --- a/devtools/client/README.md +++ b/devtools/client/README.md @@ -1,3 +1,5 @@ # devtools-client -Package responsible for providing the common constructs (TODO: maybe even including redux) responsible for managing state and consuming events and RPCs. +Package responsible for providing the common constructs responsible for managing Redux state and consuming events and methods from a devtools client, i.e. flipper plugin or web extension. + +TODO: Usage instructions - createDevtoolsStore diff --git a/devtools/client/src/index.ts b/devtools/client/src/index.ts index 52a13430..9686012a 100644 --- a/devtools/client/src/index.ts +++ b/devtools/client/src/index.ts @@ -1,3 +1 @@ export * from './redux'; -export * from './rpc'; -export * from '@player-tools/devtools-common'; diff --git a/devtools/client/src/redux/actions.ts b/devtools/client/src/redux/actions.ts deleted file mode 100644 index 58a1ac64..00000000 --- a/devtools/client/src/redux/actions.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { type AsyncThunk, createAsyncThunk } from '@reduxjs/toolkit'; -import { - Runtime, - createLogger, - BACKGROUND_SOURCE, -} from '@player-tools/devtools-common'; -import type { RuntimeRPCRequestHandlers } from '../rpc'; - -const logger = createLogger(BACKGROUND_SOURCE); - -export type AsyncRPCActions = { - [key in Runtime.RuntimeRPCTypes]: AsyncThunk< - Extract['result'], - Extract['params'], - any - >; -}; - -export const buildRPCActions = ( - handlers: RuntimeRPCRequestHandlers -): AsyncRPCActions => - Runtime.RuntimeRPCTypes.reduce((acc, rpcType) => { - // TODO: Fix this - // @ts-ignore - acc[rpcType] = createAsyncThunk< - Extract['result'], - Extract['params'] - >(rpcType, async (params) => { - logger.log(`Requesting ${rpcType}`, params); - const data = (await handlers[rpcType].call(params)) as Extract< - Runtime.RuntimeRPC, - { type: typeof rpcType } - >['result']; - logger.log(`Response from ${rpcType}`, data); - return data; - }); - return acc; - }, {} as AsyncRPCActions); diff --git a/devtools/client/src/redux/actions/events.ts b/devtools/client/src/redux/actions/events.ts new file mode 100644 index 00000000..be5b6140 --- /dev/null +++ b/devtools/client/src/redux/actions/events.ts @@ -0,0 +1,16 @@ +import { Events } from '@player-tools/devtools-common'; +import { ActionCreatorWithPayload, createAction } from '@reduxjs/toolkit'; +import { AnyAction } from 'redux'; + +/** Redux actions associated against all possible event types */ +type EventActions = { + [key in Events.EventTypes]: ActionCreatorWithPayload, key>; +}; + +/** Redux actions associated against all defined event types */ +export const Actions: EventActions = Object.fromEntries( + Events.EventTypes.map((event) => [ + event, + createAction>(event), + ]) +) as EventActions; diff --git a/devtools/client/src/redux/actions/index.ts b/devtools/client/src/redux/actions/index.ts new file mode 100644 index 00000000..b77090be --- /dev/null +++ b/devtools/client/src/redux/actions/index.ts @@ -0,0 +1,20 @@ +import { createAction } from '@reduxjs/toolkit'; +import type { Events } from '@player-tools/devtools-common'; + +export { Actions as EventActions } from './events'; +export * from './methods'; + +/** Explicit actions that don't correspond to a specific event or method */ +export const Actions = { + // Explicit actions TODO: Is this level of redundancy okay? + 'selected-player': createAction('selected-player'), + 'player-timeline-event': createAction( + 'player-timeline-event' + ), + + // Reset actions + 'clear-selected-data-details': createAction('clear-selected-data-details'), + 'clear-console': createAction('clear-console'), + 'clear-logs': createAction('clear-logs'), + 'clear-store': createAction('clear-store'), +}; diff --git a/devtools/client/src/redux/actions/methods.ts b/devtools/client/src/redux/actions/methods.ts new file mode 100644 index 00000000..24970072 --- /dev/null +++ b/devtools/client/src/redux/actions/methods.ts @@ -0,0 +1,43 @@ +import { + BACKGROUND_SOURCE, + createLogger, + Methods, +} from '@player-tools/devtools-common'; +import { createAsyncThunk, type AsyncThunk } from '@reduxjs/toolkit'; + +const logger = createLogger(BACKGROUND_SOURCE); + +/** Type describing an object containing async thunks for each Method defined */ +export type MethodThunks = { + [key in Methods.Method['type']]: AsyncThunk< + Methods.ByType['result'], + Methods.ByType, + any + >; +}; + +/** Signature for handling method requests */ +export type MethodHandler = ( + method: Methods.ByType +) => Promise['result']>; + +/** Utility for building async thunks for all known method types */ +export const buildMethodThunks = ( + onMethodRequest: MethodHandler +): MethodThunks => + Object.fromEntries( + Methods.MethodTypes.map((method) => [ + method, + createAsyncThunk< + Methods.ByType['result'], + Methods.ByType + >(method, async (method) => { + logger.log(`Requesting ${method.type}`, method.params); + const data = (await onMethodRequest(method)) as Methods.ByType< + typeof method.type + >['result']; + logger.log(`Response from ${method.type}`, data); + return data; + }), + ]) + ) as MethodThunks; diff --git a/devtools/client/src/redux/aliases.ts b/devtools/client/src/redux/aliases.ts index 2eb1305d..b8260cb6 100644 --- a/devtools/client/src/redux/aliases.ts +++ b/devtools/client/src/redux/aliases.ts @@ -1,12 +1,5 @@ -import type { - AliasAction, - ConfigAction, - DataBindingAction, - ExpressionAction, - StartProfilerAction, - StopProfilerAction, -} from '@player-tools/devtools-common'; -import type { AsyncRPCActions } from './actions'; +import { Methods as Methods, RUNTIME_SOURCE } from '@player-tools/devtools-common'; +import { MethodThunks } from './actions/methods'; export const GET_INFO_DETAILS = 'GET_INFO_DETAILS'; export const GET_CONFIG_DETAILS = 'GET_CONFIG_DETAILS'; @@ -16,6 +9,12 @@ export const GET_CONSOLE_EVAL = 'GET_CONSOLE_EVAL'; export const START_PROFILER = 'START_PROFILER'; export const STOP_PROFILER = 'STOP_PROFILER'; + +interface MethodAction { + payload: Methods.ByType['params']; +} + +// Copied from webext redux library not allowed in flipper const _alias = (aliases: any) => () => (next: any) => (action: any) => { const alias = aliases[action.type]; @@ -26,20 +25,20 @@ const _alias = (aliases: any) => () => (next: any) => (action: any) => { return next(action); }; -export const buildAliases = (actions: AsyncRPCActions) => +/** Helper for building corresponding method action via supplied thunks */ +const alias = (type: T, methods: MethodThunks) => (action: MethodAction) => methods[type]({ + type, + params: action.payload, + source: RUNTIME_SOURCE, +} as Methods.ByType) + +export const buildAliases = (methods: MethodThunks) => _alias({ - GET_INFO_DETAILS: (action: AliasAction) => - actions['player-runtime-info-request'](action.payload), - GET_CONFIG_DETAILS: (action: ConfigAction) => - actions['player-config-request'](action.payload), - GET_VIEW_DETAILS: (action: AliasAction) => - actions['player-view-details-request'](action.payload), - GET_DATA_BINDING_DETAILS: (action: DataBindingAction) => - actions['player-data-binding-details'](action.payload), - GET_CONSOLE_EVAL: (action: ExpressionAction) => - actions['player-execute-expression'](action.payload), - START_PROFILER: (action: StartProfilerAction) => - actions['player-start-profiler-request'](action.payload), - STOP_PROFILER: (action: StopProfilerAction) => - actions['player-stop-profiler-request'](action.payload), + GET_INFO_DETAILS: alias('player-runtime-info-request', methods), + GET_CONFIG_DETAILS: alias('player-config-request', methods), + GET_VIEW_DETAILS: alias('player-view-details-request', methods), + GET_DATA_BINDING_DETAILS: alias('player-data-binding-details', methods), + GET_CONSOLE_EVAL: alias('player-execute-expression', methods), + START_PROFILER: alias('player-start-profiler-request', methods), + STOP_PROFILER: alias('player-stop-profiler-request', methods), }); diff --git a/devtools/client/src/redux/index.ts b/devtools/client/src/redux/index.ts index ba2d9369..d8d1ed24 100644 --- a/devtools/client/src/redux/index.ts +++ b/devtools/client/src/redux/index.ts @@ -1,70 +1,8 @@ -import { - type Message, - clearStore, - playerFlowStartAction, - playerInitAction, - playerRemoveAction, - playerTimelineAction, - playerViewUpdateAction, - selectedPlayerAction, -} from '@player-tools/devtools-common'; -import type { Store } from 'redux'; -import { GET_DATA_BINDING_DETAILS } from './aliases'; - export * from './actions'; export * from './aliases'; +export * from './listeners'; +export * from './middleware'; export * from './reducers'; - -export function handleMessage(store: Store, message: Message) { - switch (message.type) { - case 'runtime-init': - store.dispatch(clearStore()); - break; - case 'player-init': - store.dispatch(playerInitAction(message)); - store.dispatch(selectedPlayerAction()); - break; - case 'player-removed': - store.dispatch(playerRemoveAction(message.playerID)); - store.dispatch(selectedPlayerAction()); - break; - case 'player-flow-start': - store.dispatch(playerFlowStartAction(message)); - store.dispatch(playerTimelineAction(message)); - store.dispatch({ - type: GET_DATA_BINDING_DETAILS, - payload: { playerID: message.playerID, binding: '' }, - }); - break; - case 'player-log-event': - store.dispatch(playerTimelineAction(message)); - break; - case 'player-view-update-event': - store.dispatch(playerViewUpdateAction(message)); - break; - case 'player-data-change-event': { - const { players } = store.getState(); - - if ( - players.activePlayers[message.playerID] && - players.activePlayers[message.playerID].dataState.selectedBinding - ) { - store.dispatch({ - type: GET_DATA_BINDING_DETAILS, - payload: message, - }); - } - - store.dispatch({ - type: GET_DATA_BINDING_DETAILS, - payload: { playerID: message.playerID, binding: '' }, - }); - store.dispatch(playerTimelineAction(message)); - break; - } - - default: - console.warn(`Unhandled event: ${JSON.stringify(message)}`); - break; - } -} +export * from './selectors'; +export * from './state'; +export * from './store'; \ No newline at end of file diff --git a/devtools/client/src/redux/listeners.ts b/devtools/client/src/redux/listeners.ts new file mode 100644 index 00000000..3d757f02 --- /dev/null +++ b/devtools/client/src/redux/listeners.ts @@ -0,0 +1,8 @@ +import { Events } from "@player-tools/devtools-common"; +import { Dispatch } from "redux"; +import { EventActions } from "./actions"; + +/** Utility method to filter known events from a supplied message and dispatch the corresponding action */ +export const dispatchEvents = (dispatch: Dispatch) => (message: any) => { + if (Events.isEvent(message)) dispatch(EventActions[message.type](message as any)) +} diff --git a/devtools/client/src/redux/middleware.ts b/devtools/client/src/redux/middleware.ts new file mode 100644 index 00000000..632b4049 --- /dev/null +++ b/devtools/client/src/redux/middleware.ts @@ -0,0 +1,64 @@ +import { createListenerMiddleware, isAnyOf } from "@reduxjs/toolkit"; +import { Actions, EventActions } from "./actions"; +import { GET_DATA_BINDING_DETAILS } from "./aliases"; +import { type StoreState } from './state'; + +/** + * Listener middleware that will be consumed by default when creating the devtools store. + * Exported such that clients can configure additional side effects. + */ +export const listenerMiddleware = createListenerMiddleware(); + +listenerMiddleware.startListening({ + matcher: isAnyOf( + EventActions['player-data-change-event'], + EventActions['player-log-event'], + EventActions['player-flow-start'], + ), + effect: (action, api) => { + api.dispatch(Actions['player-timeline-event'](action.payload)); + }, +}); + +listenerMiddleware.startListening({ + actionCreator: EventActions['runtime-init'], + effect: (_, api) => { + api.dispatch(Actions["clear-store"]()) + } +}) + +listenerMiddleware.startListening({ + matcher: isAnyOf( + EventActions["player-init"], + EventActions["player-removed"], + ), + effect: (_, api) => { + api.dispatch(Actions["selected-player"]()) + } +}); + +listenerMiddleware.startListening({ + matcher: isAnyOf( + EventActions["player-flow-start"], + EventActions["player-data-change-event"], + ), + effect: (action, api) => { + const { players } = api.getState(); + const { playerID } = action.payload; + + if ( + players.activePlayers[playerID] && + players.activePlayers[playerID].dataState.selectedBinding + ) { + api.dispatch({ + type: GET_DATA_BINDING_DETAILS, + payload: { playerID, binding: players.activePlayers[playerID].dataState.selectedBinding }, + }); + } + + api.dispatch({ + type: GET_DATA_BINDING_DETAILS, + payload: { playerID, binding: '' }, + }) + } +}) diff --git a/devtools/client/src/redux/reducers.ts b/devtools/client/src/redux/reducers.ts index e1a6b997..91d2279a 100644 --- a/devtools/client/src/redux/reducers.ts +++ b/devtools/client/src/redux/reducers.ts @@ -1,118 +1,247 @@ -import type { ActionReducerMapBuilder } from '@reduxjs/toolkit'; -import type { PlayersState } from '@player-tools/devtools-common'; -import type { AsyncRPCActions } from './actions'; - -/** - * Callback function that adds cases for async actions for the player. - * @param playerReducerCallback - * @returns - */ -export const buildPlayerReducerCallback = - (actions: AsyncRPCActions) => - (builder: ActionReducerMapBuilder) => { - builder.addCase( - actions['player-runtime-info-request'].fulfilled, - (state, action) => { - const { activePlayers, selectedPlayerId } = state; - - if (!selectedPlayerId) { - return; - } - - const data = - action.payload && Object.keys(action.payload).length > 0 - ? action.payload - : null; - activePlayers[selectedPlayerId].flowInfo = data; +import { type ActionReducerMapBuilder, createReducer } from '@reduxjs/toolkit'; +import type { PlayersState } from './state'; +import { Actions, EventActions, type MethodThunks } from './actions'; + +const initialState = { + selectedPlayerId: null, + activePlayers: {}, +} as PlayersState; + +const methodsReducer = (methods: MethodThunks) => (builder: ActionReducerMapBuilder) => { + builder.addCase( + methods['player-runtime-info-request'].fulfilled, + (state, action) => { + const { activePlayers, selectedPlayerId } = state; + + if (!selectedPlayerId) { + return; } - ); - builder.addCase( - actions['player-config-request'].fulfilled, - (state, action) => { - const { activePlayers, selectedPlayerId } = state; - - if (!selectedPlayerId) { - return; - } - - activePlayers[selectedPlayerId].configState = action.payload; + const data = + action.payload && Object.keys(action.payload).length > 0 + ? action.payload + : null; + activePlayers[selectedPlayerId].flowInfo = data; + } + ); + + builder.addCase( + methods['player-config-request'].fulfilled, + (state, action) => { + const { activePlayers, selectedPlayerId } = state; + + if (!selectedPlayerId) { + return; } - ); - builder.addCase( - actions['player-view-details-request'].fulfilled, - (state, action) => { - const { activePlayers, selectedPlayerId } = state; + activePlayers[selectedPlayerId].configState = action.payload; + } + ); - if (!selectedPlayerId) { - return; - } + builder.addCase( + methods['player-view-details-request'].fulfilled, + (state, action) => { + const { activePlayers, selectedPlayerId } = state; - activePlayers[selectedPlayerId].view = action.payload?.lastViewUpdate; - } - ); - - builder.addCase( - actions['player-data-binding-details'].fulfilled, - (state, action) => { - const { - meta: { - arg: { binding, playerID }, - }, - payload, - } = action; - const { activePlayers } = state; - - if (!playerID || !activePlayers[playerID]) { - return; - } - - if (binding === '') { - activePlayers[playerID].dataState.allBindings = payload; - return; - } - - activePlayers[playerID].dataState.selectedBinding = payload; - } - ); - - builder.addCase( - actions['player-execute-expression'].fulfilled, - (state, action) => { - const { activePlayers, selectedPlayerId } = state; - - if (!selectedPlayerId) { - return; - } - - activePlayers[selectedPlayerId].consoleState?.history?.push({ - id: action.meta.requestId, - result: action.payload, - expression: action.payload?.exp ?? '', - }); + if (!selectedPlayerId) { + return; } - ); - builder.addCase( - actions['player-start-profiler-request'].fulfilled, - (state, action) => { - const { activePlayers, selectedPlayerId } = state; - - if (!selectedPlayerId) return; + activePlayers[selectedPlayerId].view = action.payload?.lastViewUpdate; + } + ); + + builder.addCase( + methods['player-data-binding-details'].fulfilled, + (state, action) => { + const { + meta: { + arg: { params: { binding, playerID } }, + }, + payload, + } = action; + const { activePlayers } = state; + + if (!playerID || !activePlayers[playerID]) { + return; + } - activePlayers[selectedPlayerId].profilerInfo = action.payload?.data; + if (binding === '') { + activePlayers[playerID].dataState.allBindings = payload; + return; } - ); - builder.addCase( - actions['player-stop-profiler-request'].fulfilled, - (state, action) => { - const { activePlayers, selectedPlayerId } = state; + activePlayers[playerID].dataState.selectedBinding = payload; + } + ); - if (!selectedPlayerId) return; + builder.addCase( + methods['player-execute-expression'].fulfilled, + (state, action) => { + const { activePlayers, selectedPlayerId } = state; - activePlayers[selectedPlayerId].profilerInfo = action.payload?.data; + if (!selectedPlayerId) { + return; } - ); - }; + + activePlayers[selectedPlayerId].consoleState?.history?.push({ + id: action.meta.requestId, + result: action.payload, + expression: action.payload?.exp ?? '', + }); + } + ); + + builder.addCase( + methods['player-start-profiler-request'].fulfilled, + (state, action) => { + const { activePlayers, selectedPlayerId } = state; + + if (!selectedPlayerId) return; + + activePlayers[selectedPlayerId].profilerInfo = action.payload?.data; + } + ); + + builder.addCase( + methods['player-stop-profiler-request'].fulfilled, + (state, action) => { + const { activePlayers, selectedPlayerId } = state; + + if (!selectedPlayerId) return; + + activePlayers[selectedPlayerId].profilerInfo = action.payload?.data; + } + ); +}; + +const eventsReducer = (builder: ActionReducerMapBuilder) => { + builder.addCase(EventActions['player-init'], (state, action) => { + const { + payload: { version, playerID }, + } = action; + state.activePlayers[playerID] = { + timelineEvents: [], + dataState: {}, + consoleState: { history: [] }, + }; + state.version = version; + }); + + builder.addCase(EventActions['player-removed'], (state, action) => { + delete state.activePlayers[action.payload.playerID]; + }); + + builder.addCase(EventActions['player-flow-start'], (state, action) => { + const { + payload: { flow, playerID }, + } = action; + + if (!state.activePlayers[playerID]) { + state.activePlayers[playerID] = { + flowInfo: { currentFlow: flow }, + timelineEvents: [], + dataState: {}, + consoleState: { history: [] }, + }; + state.selectedPlayerId = playerID; + return; + } + + state.activePlayers[playerID].flowInfo = { + ...state.activePlayers[playerID].flowInfo, + currentFlow: flow, + }; + }); + + builder.addCase(EventActions['player-view-update-event'], (state, action) => { + const { + payload: { playerID, update }, + } = action; + + if (!state.activePlayers[playerID]) { + state.activePlayers[playerID] = { + view: update, + timelineEvents: [], + dataState: {}, + consoleState: { history: [] }, + }; + state.selectedPlayerId = playerID; + return; + } + + state.activePlayers[playerID].view = update; + }); +}; + +const actionsReducer = (builder: ActionReducerMapBuilder) => { + builder.addCase(Actions['selected-player'], (state, action) => { + if (action.payload) { + state.selectedPlayerId = action.payload; + return; + } + + state.selectedPlayerId = Object.keys(state.activePlayers)[0] || null; + }); + + builder.addCase(Actions['player-timeline-event'], (state, action) => { + const { + payload: { playerID }, + } = action; + + if (!state.activePlayers[playerID]) { + state.activePlayers[playerID] = { + timelineEvents: [action.payload], + dataState: {}, + consoleState: { history: [] }, + }; + state.selectedPlayerId = playerID; + return; + } + + state.activePlayers[playerID].timelineEvents.push(action.payload); + }); + + builder.addCase(Actions['clear-selected-data-details'], (state) => { + const { activePlayers, selectedPlayerId } = state; + + if (!selectedPlayerId || !activePlayers[selectedPlayerId]) { + return; + } + + activePlayers[selectedPlayerId].dataState.selectedBinding = undefined; + }); + + builder.addCase(Actions['clear-console'], (state) => { + const { activePlayers, selectedPlayerId } = state; + + if (!selectedPlayerId) { + return; + } + + activePlayers[selectedPlayerId].consoleState = { + history: [], + }; + }); + + builder.addCase(Actions['clear-logs'], (state) => { + const { activePlayers, selectedPlayerId } = state; + + if (!selectedPlayerId) { + return; + } + + activePlayers[selectedPlayerId].timelineEvents = []; + }); + + builder.addCase(Actions['clear-store'], () => { + return initialState; + }); +}; + +export const playersReducer = (methods: MethodThunks) => + createReducer(initialState, (builder) => { + actionsReducer(builder) + eventsReducer(builder) + methodsReducer(methods)(builder) + }); diff --git a/devtools/common/src/redux/selectors.ts b/devtools/client/src/redux/selectors.ts similarity index 66% rename from devtools/common/src/redux/selectors.ts rename to devtools/client/src/redux/selectors.ts index a634f27c..281d4323 100644 --- a/devtools/common/src/redux/selectors.ts +++ b/devtools/client/src/redux/selectors.ts @@ -1,5 +1,5 @@ import { createSelector } from '@reduxjs/toolkit'; -import type { StoreState, PlayersState } from '../types/state'; +import type { StoreState, PlayersState } from './state'; /** * Selects the player state @@ -18,12 +18,12 @@ const selectActivePlayers = createSelector( (players: PlayersState) => players.activePlayers ); -export const selectPlayerVersion: any = createSelector( +export const selectPlayerVersion = createSelector( selectPlayers, (players: PlayersState) => players.version ); -export const selectPlayerIds: any = createSelector( +export const selectPlayerIds = createSelector( selectActivePlayers, (activePlayers) => Object.keys(activePlayers) || [] ); @@ -31,12 +31,12 @@ export const selectPlayerIds: any = createSelector( /** * Selects the selected/currently active player Id. */ -export const selectSelectedPlayerId: any = createSelector( +export const selectSelectedPlayerId = createSelector( selectPlayers, (players: PlayersState) => players.selectedPlayerId ); -export const selectCurrentPlayer: any = createSelector( +export const selectCurrentPlayer = createSelector( selectActivePlayers, selectSelectedPlayerId, (activePlayers, selectedPlayerId) => { @@ -48,18 +48,18 @@ export const selectCurrentPlayer: any = createSelector( } ); -export const selectConfig: any = createSelector( +export const selectConfig = createSelector( selectCurrentPlayer, (currentPlayer) => { return currentPlayer?.configState ?? null; } ); -const selectData: any = createSelector(selectCurrentPlayer, (currentPlayer) => { +const selectData = createSelector(selectCurrentPlayer, (currentPlayer) => { return currentPlayer?.dataState; }); -export const selectFlowInfo: any = createSelector( +export const selectFlowInfo = createSelector( selectCurrentPlayer, (currentPlayer) => { if (!currentPlayer) { @@ -70,21 +70,21 @@ export const selectFlowInfo: any = createSelector( } ); -export const selectCurrentFlow: any = createSelector( +export const selectCurrentFlow = createSelector( selectFlowInfo, (flowInfo) => { return flowInfo?.currentFlow; } ); -export const selectCurrentTopic: any = createSelector( +export const selectCurrentTopic = createSelector( selectCurrentFlow, (currentFlow) => { return currentFlow?.topic; } ); -export const selectEvents: any = createSelector( +export const selectEvents = createSelector( selectCurrentPlayer, (currentPlayer) => { if (!currentPlayer) { @@ -95,7 +95,7 @@ export const selectEvents: any = createSelector( } ); -export const selectView: any = createSelector( +export const selectView = createSelector( selectCurrentPlayer, (currentPlayer) => { if (!currentPlayer) { @@ -106,15 +106,15 @@ export const selectView: any = createSelector( } ); -export const selectAllBindings: any = createSelector(selectData, (data) => { +export const selectAllBindings = createSelector(selectData, (data) => { return data?.allBindings; }); -export const selectSelectedBinding: any = createSelector(selectData, (data) => { +export const selectSelectedBinding = createSelector(selectData, (data) => { return data?.selectedBinding; }); -export const selectConsole: any = createSelector( +export const selectConsole = createSelector( selectCurrentPlayer, (currentPlayer) => { if (!currentPlayer) { @@ -125,7 +125,7 @@ export const selectConsole: any = createSelector( } ); -export const selectProfiler: any = createSelector( +export const selectProfiler = createSelector( selectCurrentPlayer, (currentPlayer) => { return currentPlayer?.profilerInfo; diff --git a/devtools/common/src/types/state.ts b/devtools/client/src/redux/state.ts similarity index 66% rename from devtools/common/src/types/state.ts rename to devtools/client/src/redux/state.ts index 5e8efd47..f6c5820f 100644 --- a/devtools/common/src/types/state.ts +++ b/devtools/client/src/redux/state.ts @@ -1,23 +1,19 @@ -import type { Flow, View } from '@player-ui/types'; -import type { Runtime } from '.'; +import { Flow, View } from '@player-ui/types'; +import { Methods, Events, ProfilerNode } from '@player-tools/devtools-common'; export type ActivePlayerState = { /** * state associated with player config */ - configState?: Runtime.PlayerConfigRPC['result'] | null; + configState?: Methods.PlayerConfigMethod['result'] | null; /** * Flow related Information of the player. */ - flowInfo?: Runtime.PlayerRuntimeInfoRPC['result'] | null; + flowInfo?: Methods.PlayerRuntimeInfoMethod['result'] | null; /** * A collection of all the events associated with the running player instance. */ - timelineEvents: Array< - | Runtime.PlayerDataChangeEvent - | Runtime.PlayerLogEvent - | Runtime.PlayerFlowStartEvent - >; + timelineEvents: Array; /** * View related information of the player. */ @@ -81,11 +77,11 @@ export type DataState = { /** * The binding selected on the Data panel. */ - selectedBinding?: Runtime.PlayerDataBindingRPC['result']; + selectedBinding?: Methods.PlayerDataBindingMethod['result']; /** * All the bindings in the data state. */ - allBindings?: Runtime.PlayerDataBindingRPC['result']; + allBindings?: Methods.PlayerDataBindingMethod['result']; }; export interface ConsoleState { @@ -104,34 +100,6 @@ export interface ConsoleState { /** * Result of the console evaluation. */ - result: Runtime.PlayerExpressionRPC['result']; + result: Methods.PlayerExpressionMethod['result']; }>; } - -export type ProfilerNode = { - /** - * hook name - */ - name: string; - /** - * startTime of the hook - */ - startTime?: number; - /** - * endTime of the hook - */ - endTime?: number; - /** - * duration of hook resolution times - * unit: ms - */ - value?: number; - /** - * tooltip to be shown on hover - */ - tooltip?: string; - /** - * subhook profiler nodes - */ - children: ProfilerNode[]; -}; diff --git a/devtools/client/src/redux/store.ts b/devtools/client/src/redux/store.ts new file mode 100644 index 00000000..9dfc2fcb --- /dev/null +++ b/devtools/client/src/redux/store.ts @@ -0,0 +1,65 @@ +import { + type AnyAction, + type Dispatch, + type Middleware, + type EnhancedStore, + configureStore, + ReducersMapObject, +} from '@reduxjs/toolkit'; +import { StoreState } from './state'; +import { + type MethodThunks, + type MethodHandler, + buildMethodThunks, +} from './actions'; +import { playersReducer } from './reducers'; +import { listenerMiddleware } from './middleware'; +import { buildAliases } from './aliases'; + +const createStore = ( + methodThunks: MethodThunks, + middleware?: Middleware>, + reducers?: ReducersMapObject +): EnhancedStore< + StoreState, + any, + Middleware>[] +> => + configureStore({ + reducer: { + // TODO: Look into slices + players: playersReducer(methodThunks), + ...reducers, + }, + middleware: (getDefaultMiddleware) => { + const m = getDefaultMiddleware() + .prepend(buildAliases(methodThunks)) + .concat(listenerMiddleware.middleware); + + if (middleware) m.concat(middleware); + + return m; + }, + }); + +/** + * This function returns the players store. Accepts optional middleware and callback to enhance the store. + * @param middleware Additional middleware to be added to the store. Optional. + * @param reducers Additional reducers to be added to the store. Optional + * @returns + */ +// TODO: Turn into store enhancer? Maybe remove middleware and additionalReducers? +export const createDevtoolsStore = ( + onMethodRequest: MethodHandler, + middleware?: Middleware>, + additionalReducers?: any +): EnhancedStore< + StoreState, + any, + Middleware>[] +> => + createStore( + buildMethodThunks(onMethodRequest), + middleware, + additionalReducers + ); diff --git a/devtools/client/src/rpc/index.ts b/devtools/client/src/rpc/index.ts deleted file mode 100644 index f1140847..00000000 --- a/devtools/client/src/rpc/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { - type RPCRequestMessageEvent, - type RPCRequestHandler, - createRPCRequest, - Runtime, - PANEL_SOURCE, -} from '@player-tools/devtools-common'; - -export type RuntimeRPCRequestHandlers = { - [key in Runtime.RuntimeRPCTypes]: RPCRequestHandler; -}; - -export const buildRPCRequests = ( - onRequestMessage: ( - message: RPCRequestMessageEvent - ) => void -): RuntimeRPCRequestHandlers => - Runtime.RuntimeRPCTypes.reduce((acc, rpcType) => { - acc[rpcType] = createRPCRequest(rpcType, PANEL_SOURCE, onRequestMessage); - return acc; - }, {} as RuntimeRPCRequestHandlers); diff --git a/devtools/common/BUILD b/devtools/common/BUILD index b8d92bda..3bf495d4 100644 --- a/devtools/common/BUILD +++ b/devtools/common/BUILD @@ -3,7 +3,7 @@ load("//:index.bzl", "javascript_pipeline") javascript_pipeline( name = "@player-tools/devtools-common", dependencies = [ - "@npm//@player-ui/logger", + "@npm//@player-ui/player", "@npm//@player-ui/types", "@npm//@reduxjs/toolkit", "@npm//@types/uuid", diff --git a/devtools/common/README.md b/devtools/common/README.md new file mode 100644 index 00000000..960192a7 --- /dev/null +++ b/devtools/common/README.md @@ -0,0 +1,3 @@ +# devtools-common + +Patternized definition of unidirectional events and bidirectional methods such that adding or modifying the types should result in respective changes within the consumer libs. diff --git a/devtools/common/src/rpc/bridge.ts b/devtools/common/src-old/rpc/bridge.ts similarity index 100% rename from devtools/common/src/rpc/bridge.ts rename to devtools/common/src-old/rpc/bridge.ts diff --git a/devtools/common/src/README.md b/devtools/common/src/README.md deleted file mode 100644 index 85a5f951..00000000 --- a/devtools/common/src/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# devtools-common - -Common definition of events and RPC actions diff --git a/devtools/common/src/constants.ts b/devtools/common/src/constants.ts deleted file mode 100644 index 0c60f6fe..00000000 --- a/devtools/common/src/constants.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const RUNTIME_SOURCE = '__PLAYER_RUNTIME__'; -export const CONTENT_SOURCE = '__PLAYER_CONTENT__'; -export const PANEL_SOURCE = '__PLAYER_PANEL__'; -export const POPUP_SOURCE = '__PLAYER__POPUP__'; -export const BACKGROUND_SOURCE = '__PLAYER_BACKGROUND__'; - -export type MESSAGE_SOURCE = - | typeof RUNTIME_SOURCE - | typeof CONTENT_SOURCE - | typeof PANEL_SOURCE - | typeof POPUP_SOURCE - | typeof BACKGROUND_SOURCE; diff --git a/devtools/common/src/events.ts b/devtools/common/src/events.ts new file mode 100644 index 00000000..1bb7efaa --- /dev/null +++ b/devtools/common/src/events.ts @@ -0,0 +1,156 @@ +import type { Severity } from "@player-ui/player"; +import type { Binding, Flow, View } from "@player-ui/types"; +import { DiscriminateByType, DiscriminatedType, isKnownType } from "./utils"; +import { RUNTIME_SOURCE } from "./logger"; + +// TODO: Maybe reverse the pluralness +// Unidirectional events originating from the Player +export namespace Events { + interface BaseEvent extends DiscriminatedType { + /** Source of the method type */ + source: string; + } + + interface BaseEventWithPlayerID extends BaseEvent { + /** + * Unique Player Id associated with the message. + */ + playerID: string; + } + + interface PlayerTimelineEvent extends BaseEventWithPlayerID { + /** + * The time in milliseconds when the event was received. + */ + timestamp: number; + } + + export interface RuntimeInitEvent extends BaseEvent<'runtime-init'> { + /** + * Source of the event. + */ + source: typeof RUNTIME_SOURCE; + } + + export interface PlayerInitEvent extends BaseEventWithPlayerID<'player-init'> { + /** + * Source of the event. + */ + source: string; + /** + * Web player version. + */ + version: string; + } + + export interface PlayerRemovedEvent extends BaseEventWithPlayerID<'player-removed'> { + /** + * Source of the event. + */ + source: string; + } + + export interface PlayerLogEvent extends PlayerTimelineEvent<'player-log-event'> { + /** + * Source of the event. + */ + source: string; + /** + * Severity of the event + */ + severity: Severity; + /** + * Collection of messages associated with the log event. + */ + message: Array; + } + + export interface PlayerDataChangeEvent extends PlayerTimelineEvent<'player-data-change-event'> { + /** + * Source of the event. + */ + source: typeof RUNTIME_SOURCE; + /** + * Binding associated with the Data change event. + */ + binding: Binding; + /** + * Old value of the binding. + */ + oldValue: any; + /** + * New value of the binding. + */ + newValue: any; + } + + export interface PlayerViewUpdateEvent extends PlayerTimelineEvent<'player-view-update-event'> { + /** + * Source of the event. + */ + source: typeof RUNTIME_SOURCE; + /** + * View update. + */ + update: View; + } + + export interface PlayerFlowTransitionEvent extends PlayerTimelineEvent<'player-flow-transition-event'> { + /** + * The state from which the transition has taken place. + */ + fromState: string; + /** + * The state to which the transition has taken place. + */ + toState: string; + } + + export interface PlayerFlowStartEvent + extends PlayerTimelineEvent<'player-flow-start'> { + /** + * Flow information that has been started. + */ + flow: Flow; + } + + export interface PlayerFlowEndEvent + extends PlayerTimelineEvent<'player-flow-end'> { + /** + * Flow information that has been ended. + */ + id: string; + } + + export type TimelineEvents = + | PlayerDataChangeEvent + | PlayerLogEvent + | PlayerFlowStartEvent + | PlayerViewUpdateEvent; + + export type Event = + | TimelineEvents + | PlayerInitEvent + | PlayerRemovedEvent + | PlayerFlowTransitionEvent + | PlayerFlowEndEvent + | RuntimeInitEvent; + + export const EventTypes = [ + 'runtime-init', + 'player-init', + 'player-removed', + 'player-log-event', + 'player-data-change-event', + 'player-view-update-event', + 'player-flow-transition-event', + 'player-flow-start', + 'player-flow-end', + ] as const; + + export type EventTypes = typeof EventTypes[number]; + + export type ByType = DiscriminateByType; + + export const isEvent = isKnownType(EventTypes); +} diff --git a/devtools/common/src/index.ts b/devtools/common/src/index.ts index 87548389..b8bd39bd 100644 --- a/devtools/common/src/index.ts +++ b/devtools/common/src/index.ts @@ -1,8 +1,5 @@ -export * from './redux'; export * from './types'; -export * from './types/alias'; -export * from './types/state'; -export * from './rpc'; -export * from './constants'; export * from './logger'; -export * from './runtime/message'; + +export { Events } from './events'; +export { Methods } from './methods'; diff --git a/devtools/common/src/logger.ts b/devtools/common/src/logger.ts index 4f5a20e3..0cb68d32 100644 --- a/devtools/common/src/logger.ts +++ b/devtools/common/src/logger.ts @@ -11,3 +11,17 @@ export function createLogger(source: string): Logger { }, }; } + +// Common logger sources +export const RUNTIME_SOURCE = '__PLAYER_RUNTIME__'; +export const CONTENT_SOURCE = '__PLAYER_CONTENT__'; +export const PANEL_SOURCE = '__PLAYER_PANEL__'; +export const POPUP_SOURCE = '__PLAYER__POPUP__'; +export const BACKGROUND_SOURCE = '__PLAYER_BACKGROUND__'; + +export type MESSAGE_SOURCE = + | typeof RUNTIME_SOURCE + | typeof CONTENT_SOURCE + | typeof PANEL_SOURCE + | typeof POPUP_SOURCE + | typeof BACKGROUND_SOURCE; diff --git a/devtools/common/src/methods.ts b/devtools/common/src/methods.ts new file mode 100644 index 00000000..741deb68 --- /dev/null +++ b/devtools/common/src/methods.ts @@ -0,0 +1,276 @@ +import { Binding, Flow, Schema, View } from '@player-ui/types'; +import { ProfilerNode } from './types'; +import { DiscriminateByType, DiscriminatedType, isKnownType } from './utils'; + +// Bidirectional methods originating from the devtools client +export namespace Methods { + interface BaseMethod extends DiscriminatedType { + // TODO: I don't think I need a source -- esp for methods? unless i use that to determine if the event has been responded to or not? + /** + * Source of the method type + */ + source: string; + /** + * Parameters associated with the method request. + */ + params?: unknown; + /** + * Result of the method response. + */ + result?: unknown; + } + + export interface PlayerConfigMethod extends BaseMethod<'player-config-request'> { + /** + * Parameters associated with the Runtime Info method request. + */ + params: { + /** + * Unique Player Id associated with the message. + */ + playerID: string; + }; + /** + * Result of the method response. + */ + result?: { + /** + * currently registered plugins + */ + plugins?: string[]; + /** + * data types and validations + */ + schema?: any; + /** + * unary, binary, operators + */ + expressions?: any; + }; + } + + export interface PlayerDataBindingMethod extends BaseMethod<'player-data-binding-details'> { + /** + * Parameters associated with the Data Binding method request. + */ + params: { + /** + * Unique Player Id associated with the message. + */ + playerID: string; + /** + * Binding associated with the Data binding method request. + */ + binding: Binding; + }; + /** + * Result of the method response. + */ + result?: { + /** + * Binding associated with the Data binding method response. + */ + binding: Binding; + /** + * Value associated with the binding + */ + value: { + /** + * The current (user) value of the binding. + * It may or may not be valid + * */ + currentValue?: any; + + /** + * The formatted version of the currentValue. + * This will be the same if there's no formatted for the data-type + */ + formattedValue?: any; + + /** + * The value of the binding stored in the model. + * This will be the last known good value for the binding + */ + modelValue?: any; + }; + /** + * Data binding type + */ + type?: Schema.DataType; + /** + * Validation associated with the data binding + */ + validation?: any; + }; + } + + export interface PlayerViewDetailsMethod extends BaseMethod<'player-view-details-request'> { + /** + * Parameters associated with the View Details method request. + */ + params: { + /** + * Unique Player Id associated with the message. + */ + playerID: string; + }; + /** + * Result of the method response. + */ + result?: { + /** + * Last update of the view. + */ + lastViewUpdate?: View; + }; + } + + export interface PlayerRuntimeInfoMethod extends BaseMethod<'player-runtime-info-request'> { + /** + * Parameters associated with the Runtime Info method request. + */ + params: { + /** + * Unique Player Id associated with the message. + */ + playerID: string; + }; + /** + * Result of the method response. + */ + result?: { + /** + * The unique ID associated with the current flow. + */ + currentFlowID?: string; + /** + * The state of the current flow. + */ + currentFlowState?: string; + /** + * The unique ID associated with the current view. + */ + currentViewID?: string; + /** + * The flow information associated with the current flow. + */ + currentFlow?: Flow; + }; + } + + export interface PlayerExpressionMethod extends BaseMethod<'player-execute-expression'> { + /** + * Parameters associated with the Expression method request. + */ + params: { + /** + * Unique Player Id associated with the message. + */ + playerID: string; + /** + * Expression to be evaluated. + */ + expression: string; + }; + /** + * Result of the method response. + */ + result?: + | { + /** + * Error Status of the evaluation + */ + status: 'error'; + /** + * Message associated with the evaluation. + */ + message: string; + /** + * Expression associated with this evaluation. + */ + exp: string; + } + | { + /** + * Sucess Status of the evaluation + */ + status: 'success'; + /** + * Data associated with the evaluation + */ + data: any; + /** + * Expression associated with this evaluation. + */ + exp: string; + }; + } + + export interface PlayerStartProfilerMethod extends BaseMethod<'player-start-profiler-request'> { + /** + * Parameters associated with the Profiler Details method request. + */ + params: { + /** + * Unique Player Id associated with the message. + */ + playerID: string; + }; + /** + * Result of the method response. + */ + result?: { + /** + * initial rootnode of the profiler node tree + */ + data: ProfilerNode; + }; + } + + export interface PlayerStopProfilerMethod extends BaseMethod<'player-stop-profiler-request'> { + /** + * Parameters associated with the Profiler Details method request. + */ + params: { + /** + * Unique Player Id associated with the message. + */ + playerID: string; + }; + /** + * Result of the method response. + */ + result?: { + /** + * final rootnode of the profiler node tree + */ + data: ProfilerNode; + }; + } + + export type Method = + | PlayerConfigMethod + | PlayerDataBindingMethod + | PlayerViewDetailsMethod + | PlayerRuntimeInfoMethod + | PlayerExpressionMethod + | PlayerStartProfilerMethod + | PlayerStopProfilerMethod; + + // TODO: Generate this from `Method['type']` if I ever can + export const MethodTypes = [ + 'player-config-request', + 'player-data-binding-details', + 'player-view-details-request', + 'player-runtime-info-request', + 'player-execute-expression', + 'player-start-profiler-request', + 'player-stop-profiler-request', + ] as const; + + export type MethodTypes = typeof MethodTypes[number]; + + export type ByType = DiscriminateByType; + + export const isMethod = isKnownType(MethodTypes); +} diff --git a/devtools/common/src/redux/actions.ts b/devtools/common/src/redux/actions.ts deleted file mode 100644 index e45f8daf..00000000 --- a/devtools/common/src/redux/actions.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createAction } from '@reduxjs/toolkit'; -import type { Runtime } from '../types'; - -// Actions -export const playerInitAction = - createAction('player-init'); -export const playerRemoveAction = createAction('player-removed'); -export const selectedPlayerAction = createAction( - 'selected-player' -); -export const playerFlowStartAction = - createAction('player-flow-start'); -export const playerTimelineAction = createAction< - | Runtime.PlayerDataChangeEvent - | Runtime.PlayerLogEvent - | Runtime.PlayerFlowStartEvent ->('player-timeline-event'); -export const playerViewUpdateAction = - createAction('player-view-update-event'); -export const clearSelectedDataDetails = createAction( - 'clear-selected-data-details' -); -export const consoleClearAction = createAction('console-clear'); -export const clearStore = createAction('clear-store'); -export const logsClearAction = createAction('logs-clear'); diff --git a/devtools/common/src/redux/index.ts b/devtools/common/src/redux/index.ts deleted file mode 100644 index 1f364263..00000000 --- a/devtools/common/src/redux/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './actions'; -export * from './reducers'; -export * from './selectors'; diff --git a/devtools/common/src/redux/reducers.ts b/devtools/common/src/redux/reducers.ts deleted file mode 100644 index 971832bf..00000000 --- a/devtools/common/src/redux/reducers.ts +++ /dev/null @@ -1,199 +0,0 @@ -/* eslint-disable no-param-reassign */ -import { - type AnyAction, - type Dispatch, - type Middleware, - type MiddlewareArray, - type ActionReducerMapBuilder, - type EnhancedStore, - configureStore, - createReducer, -} from '@reduxjs/toolkit'; -import { - clearSelectedDataDetails, - clearStore, - consoleClearAction, - logsClearAction, - playerFlowStartAction, - playerInitAction, - playerRemoveAction, - playerTimelineAction, - playerViewUpdateAction, - selectedPlayerAction, -} from './actions'; -import type { PlayersState, StoreState } from '../types/state'; - -const initialState = { - selectedPlayerId: null, - activePlayers: {}, -} as PlayersState; - -/** - * This function returns the players reducer. Optionally accepts a callback that receives builder object to add cases upon - * @param playerReducerCallback: A callback that receives builder object to add cases upon - * @returns - */ -const playersReducer = ( - playerReducerCallback?: ( - mapBuilder: ActionReducerMapBuilder - ) => void -) => { - return createReducer(initialState, (builder) => { - builder.addCase(playerInitAction, (state, action) => { - const { - payload: { version, playerID }, - } = action; - state.activePlayers[playerID] = { - timelineEvents: [], - dataState: {}, - consoleState: { history: [] }, - }; - state.version = version; - }); - - builder.addCase(playerRemoveAction, (state, action) => { - delete state.activePlayers[action.payload]; - }); - - builder.addCase(selectedPlayerAction, (state, action) => { - if (action.payload) { - state.selectedPlayerId = action.payload; - return; - } - - state.selectedPlayerId = Object.keys(state.activePlayers)[0] || null; - }); - - builder.addCase(playerFlowStartAction, (state, action) => { - const { - payload: { flow, playerID }, - } = action; - - if (!state.activePlayers[playerID]) { - state.activePlayers[playerID] = { - flowInfo: { currentFlow: flow }, - timelineEvents: [], - dataState: {}, - consoleState: { history: [] }, - }; - state.selectedPlayerId = playerID; - return; - } - - state.activePlayers[playerID].flowInfo = { - ...state.activePlayers[playerID].flowInfo, - currentFlow: flow, - }; - }); - - builder.addCase(playerTimelineAction, (state, action) => { - const { - payload: { playerID }, - } = action; - - if (!state.activePlayers[playerID]) { - state.activePlayers[playerID] = { - timelineEvents: [action.payload], - dataState: {}, - consoleState: { history: [] }, - }; - state.selectedPlayerId = playerID; - return; - } - - state.activePlayers[playerID].timelineEvents.push(action.payload); - }); - - builder.addCase(playerViewUpdateAction, (state, action) => { - const { - payload: { playerID, update }, - } = action; - - if (!state.activePlayers[playerID]) { - state.activePlayers[playerID] = { - view: update, - timelineEvents: [], - dataState: {}, - consoleState: { history: [] }, - }; - state.selectedPlayerId = playerID; - return; - } - - state.activePlayers[playerID].view = update; - }); - - builder.addCase(clearSelectedDataDetails, (state) => { - const { activePlayers, selectedPlayerId } = state; - - if (!selectedPlayerId || !activePlayers[selectedPlayerId]) { - return; - } - - activePlayers[selectedPlayerId].dataState.selectedBinding = undefined; - }); - - builder.addCase(consoleClearAction, (state) => { - const { activePlayers, selectedPlayerId } = state; - - if (!selectedPlayerId) { - return; - } - - activePlayers[selectedPlayerId].consoleState = { - history: [], - }; - }); - builder.addCase(clearStore, () => { - return initialState; - }); - builder.addCase(logsClearAction, (state) => { - const { activePlayers, selectedPlayerId } = state; - - if (!selectedPlayerId) { - return; - } - - activePlayers[selectedPlayerId].timelineEvents = []; - }); - playerReducerCallback?.(builder); - }); -}; - -/** - * This function returns the players store. Accepts optional middleware and callback to enhance the store. - * @param middleware : Middleware to be added to the store. Optional. - * @param playerReducerCallback A callback that receives builder object to add cases upon. Optional. - * @param additionalReducers Additional reducers to be added to the store. Optional - * @returns - */ -export const createStore = ( - middleware?: Middleware>, - playerReducerCallback?: ( - mapBuilder: ActionReducerMapBuilder - ) => void, - additionalReducers?: any -): EnhancedStore< - StoreState, - any, - Middleware>[] -> => { - return configureStore< - StoreState, - any, - Middleware>[] - >({ - reducer: { - players: playersReducer(playerReducerCallback), - ...additionalReducers, - }, - middleware: ( - getDefaultMiddleware: () => MiddlewareArray< - Middleware>[] - > - ) => - middleware - ? getDefaultMiddleware().prepend(middleware) - : getDefaultMiddleware(), - }); -}; diff --git a/devtools/common/src/rpc/index.ts b/devtools/common/src/rpc/index.ts deleted file mode 100644 index ac69706c..00000000 --- a/devtools/common/src/rpc/index.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { v4 as uuid } from 'uuid'; -import type { - BaseRPCType, - Message, - RPCRequestMessageEvent, - RPCResponseMessageEvent, -} from '../types'; - -export type RPCCallFunction> = ( - params?: T['params'] -) => Promise; - -export interface RPCRequestHandler> { - /** - * On call function that is called when a response from the RPC call is received. - */ - onMessage: (message: Message) => void; - /** - * Makes an RPC request/call to the running player instance. - */ - call: RPCCallFunction; -} - -/** - * Utility function that creates an RPC Request object based on provided parameters. - * @param type - * @param source - * @param sendMessage - * @returns - */ -// TODO: This might have to be a bit more sophisticated b/c some RPC connections are more structure -- i.e. setting up listeners for specific events -export function createRPCRequest>( - type: T['type'], - source: string, - sendMessage: (message: RPCRequestMessageEvent) => void -): RPCRequestHandler { - const requestsInFlight = new Map void>(); - - /** - * On call function to be called when a response from the RPC call is received. - * @param message - */ - const onMessage = (message: Message) => { - if (message.type === 'rpc-response') { - const handler = requestsInFlight.get(message.id); - requestsInFlight.delete(message.id); - handler?.(message.result); - } - }; - - /** - * Makes an RPC request/call to the running web player instance. - * @param params - * @returns - */ - const callRPCMethod: RPCCallFunction = (params: T['params']) => { - return new Promise((resolve, reject) => { - // Construct the message to send - const id = uuid(); - - const message: RPCRequestMessageEvent = { - id, - params, - type: 'rpc-request', - rpcType: type, - source, - }; - - // Register the UUID for when we get a response - requestsInFlight.set(id, resolve); - sendMessage(message); - }); - }; - - return { - onMessage, - call: callRPCMethod, - }; -} - -export interface RPCResponder { - /** - * On call function that is called when a response from the RPC call is received. - */ - onMessage: (message: Message) => void; -} - -/** Construct for proxying generic RPC events to event specific responders */ -export interface RPCController extends RPCResponder { - /** Collection of underlying, event specific responders */ - rpcHandlers: (RPCResponder & { type: string })[]; -} - -export type RPCFunctionCallback> = ( - params: T['params'] -) => T['result'] | Promise; - -/** - * Utility function that creates an RPC Responder object based on provided parameters. - * @param type - * @param source - * @param sendResponse - * @param messageHandler - * @returns - */ -// TODO: This might have to be a bit more sophisticated b/c some RPC connections are more structure -- i.e. setting up listeners for specific events -export function createRPCResponder>( - type: T['type'], - source: string, - sendResponse: ( - response: RPCResponseMessageEvent, - request: RPCRequestMessageEvent - ) => void, - messageHandler: RPCFunctionCallback -): RPCResponder & { type: T['type'] } { - /** - * On call function to be called when a request from the RPC call is received. - * @param message - */ - const onMessage = async (message: Message) => { - if (message.type === 'rpc-request' && message.rpcType === type) { - const result = await messageHandler(message.params); - - if (result) { - const response: RPCResponseMessageEvent = { - type: 'rpc-response', - id: message.id, - rpcType: type, - source, - result, - params: message.params, - }; - sendResponse(response, message); - } - } - }; - - return { - onMessage, - type, - }; -} - -export * from './bridge'; diff --git a/devtools/common/src/runtime/message.ts b/devtools/common/src/runtime/message.ts deleted file mode 100644 index d4eb8f31..00000000 --- a/devtools/common/src/runtime/message.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { RPCFunctionCallback } from '../rpc'; -import type { Runtime } from '../types'; - -export type RuntimeEventWithoutSource = - | Omit - | Omit - | Omit - | Omit - | Omit - | Omit - | Omit; - -export type RuntimeEventPublisher = ( - message: RuntimeEventWithoutSource -) => void; - -export type ConfigRequestHandler = RPCFunctionCallback; -export type DataBindingRequestHandler = - RPCFunctionCallback; -export type RuntimeInfoRequestHandler = - RPCFunctionCallback; -export type ViewInfoRequestHandler = - RPCFunctionCallback; -export type ExpressionEvalHandler = - RPCFunctionCallback; -export type StartProfilerRequestHandler = - RPCFunctionCallback; -export type StopProfilerRequestHandler = - RPCFunctionCallback; - -export interface PlayerRuntimeRPCCallbacks { - /** - * Config Callback - */ - config: ConfigRequestHandler; - /** - * Data Binding Callback - */ - dataBinding: DataBindingRequestHandler; - /** - * Info Callback - */ - info: RuntimeInfoRequestHandler; - /** - * View Callback - */ - view: ViewInfoRequestHandler; - /** - * Evaluate Callback - */ - evaluate: ExpressionEvalHandler; - /** - * Start Profiler Request Callback - */ - startProfiler: StartProfilerRequestHandler; - /** - * End Profiler Request Callback - */ - stopProfiler: StopProfilerRequestHandler; -} diff --git a/devtools/common/src/types.ts b/devtools/common/src/types.ts new file mode 100644 index 00000000..707bc5a1 --- /dev/null +++ b/devtools/common/src/types.ts @@ -0,0 +1,28 @@ +// TODO: export via plugins +export type ProfilerNode = { + /** + * hook name + */ + name: string; + /** + * startTime of the hook + */ + startTime?: number; + /** + * endTime of the hook + */ + endTime?: number; + /** + * duration of hook resolution times + * unit: ms + */ + value?: number; + /** + * tooltip to be shown on hover + */ + tooltip?: string; + /** + * subhook profiler nodes + */ + children: ProfilerNode[]; +}; diff --git a/devtools/common/src/types/alias.ts b/devtools/common/src/types/alias.ts deleted file mode 100644 index f37001eb..00000000 --- a/devtools/common/src/types/alias.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { Runtime } from './index'; - -interface Action { - /** - * Type associated with the action - */ - type: string; -} - -export interface AliasAction extends Action { - /** - * RuntimeRPC Payload associated with the Alias Action. - */ - payload: Runtime.RuntimeRPC['params']; -} - -export interface ConfigAction extends Action { - /** - * PlayerConfigRPC Payload associated with the Alias Action - */ - payload: Runtime.PlayerConfigRPC['params']; -} - -export interface DataBindingAction extends Action { - /** - * PlayerDataBindingRPC Payload associated with the Alias Action. - */ - payload: Runtime.PlayerDataBindingRPC['params']; -} - -export interface ExpressionAction extends Action { - /** - * PlayerExpressionRPC Payload associated with the Alias Action. - */ - payload: Runtime.PlayerExpressionRPC['params']; -} - -export interface StartProfilerAction extends Action { - /** - * PlayerStartProfilerRPC associated with the Alias Action - */ - payload: Runtime.PlayerStartProfilerRPC['params']; -} - -export interface StopProfilerAction extends Action { - /** - * PlayerStartProfilerRPC associated with the Alias Action - */ - payload: Runtime.PlayerStopProfilerRPC['params']; -} diff --git a/devtools/common/src/types/index.ts b/devtools/common/src/types/index.ts deleted file mode 100644 index 2a2ce3e9..00000000 --- a/devtools/common/src/types/index.ts +++ /dev/null @@ -1,478 +0,0 @@ -import type { Severity } from '@player-ui/logger'; -import type { Binding, Flow, Schema, View } from '@player-ui/types'; -import type { RUNTIME_SOURCE } from '../constants'; -import type { ProfilerNode } from './state'; - -export interface BaseEventMessage< - T extends Runtime.RuntimeEventTypes | 'rpc-request' | 'rpc-response' -> { - /** - * Source of the Message - */ - source: string; - /** - * Unique type associated with the message. - */ - type: T; -} - -export interface BaseRPCType { - /** - * Source of the RPC type - */ - source: string; - /** - * Unique type associated with the RPC type. - */ - type: T; - /** - * Parameters associated with the RPC type. - */ - params?: unknown; - /** - * Result of the RPC call. - */ - result?: unknown; -} - -export interface BaseMessageWithPlayerID - extends BaseEventMessage { - /** - * Unique Player Id associated with the message. - */ - playerID: string; -} - -export interface RPCRequestMessageEvent> - extends BaseEventMessage<'rpc-request'> { - /** - * Unique Player Id associated with the RPC Request message. - */ - id: string; - /** - * RPC type associated with the RPC Request message. - */ - rpcType: T['type']; - /** - * Parameters associated with the RPC Request message. - */ - params: T['params']; -} - -export interface RPCResponseMessageEvent> - extends BaseEventMessage<'rpc-response'> { - /** - * Unique Id associated with the RPC Response message. - */ - id: string; - /** - * RPC type associated with the RPC Response message. - */ - rpcType: T['type']; - /** - * Result associated with the RPC Response message. - */ - result: T['result']; - /** - * Parameters associated with the RPC Response message. - */ - params: T['params']; -} -export namespace Runtime { - interface PlayerTimelineEvent - extends BaseMessageWithPlayerID { - /** - * The time in milliseconds when the event was received. - */ - timestamp: number; - } - - export interface RuntimeInitEvent extends BaseEventMessage<'runtime-init'> { - /** - * Source of the event. - */ - source: typeof RUNTIME_SOURCE; - } - - export interface PlayerInitEvent - extends BaseMessageWithPlayerID<'player-init'> { - /** - * Source of the event. - */ - source: string; - /** - * Web player version. - */ - version: string; - } - - export interface PlayerRemovedEvent - extends BaseMessageWithPlayerID<'player-removed'> { - /** - * Source of the event. - */ - source: string; - } - - export interface PlayerLogEvent - extends PlayerTimelineEvent<'player-log-event'> { - /** - * Source of the event. - */ - source: string; - /** - * Severity of the event - */ - severity: Severity; - /** - * Collection of messages associated with the log event. - */ - message: Array; - } - - export interface PlayerDataChangeEvent - extends PlayerTimelineEvent<'player-data-change-event'> { - /** - * Source of the event. - */ - source: typeof RUNTIME_SOURCE; - /** - * Binding associated with the Data change event. - */ - binding: Binding; - /** - * Old value of the binding. - */ - oldValue: any; - /** - * New value of the binding. - */ - newValue: any; - } - - export interface PlayerViewUpdateEvent - extends PlayerTimelineEvent<'player-view-update-event'> { - /** - * Source of the event. - */ - source: typeof RUNTIME_SOURCE; - /** - * View update. - */ - update: View; - } - - export interface PlayerFlowTransitionEvent - extends PlayerTimelineEvent<'player-flow-transition-event'> { - /** - * The state from which the transition has taken place. - */ - fromState: string; - /** - * The state to which the transition has taken place. - */ - toState: string; - } - - export interface PlayerFlowStartEvent - extends PlayerTimelineEvent<'player-flow-start'> { - /** - * Flow information that has been started. - */ - flow: Flow; - } - - export interface PlayerFlowEndEvent - extends PlayerTimelineEvent<'player-flow-end'> { - /** - * Flow information that has been ended. - */ - id: string; - } - - export interface PlayerConfigRPC - extends BaseRPCType<'player-config-request'> { - /** - * Parameters associated with the Runtime Info RPC. - */ - params: { - /** - * Unique Player Id associated with the message. - */ - playerID: string; - }; - /** - * Result of the RPC call. - */ - result?: { - /** - * currently registered plugins - */ - plugins?: string[]; - /** - * data types and validations - */ - schema?: any; - /** - * unary, binary, operators - */ - expressions?: any; - }; - } - - export interface PlayerDataBindingRPC - extends BaseRPCType<'player-data-binding-details'> { - /** - * Parameters associated with the Data Binding RPC. - */ - params: { - /** - * Unique Player Id associated with the message. - */ - playerID: string; - /** - * Binding associated with the Data binding RPC. - */ - binding: Binding; - }; - /** - * Result of the RPC call. - */ - result?: { - /** - * Binding associated with the Data binding RPC result. - */ - binding: Binding; - /** - * Value associated with the binding - */ - value: { - /** - * The current (user) value of the binding. - * It may or may not be valid - * */ - currentValue?: any; - - /** - * The formatted version of the currentValue. - * This will be the same if there's no formatted for the data-type - */ - formattedValue?: any; - - /** - * The value of the binding stored in the model. - * This will be the last known good value for the binding - */ - modelValue?: any; - }; - /** - * Data binding type - */ - type?: Schema.DataType; - /** - * Validation associated with the data binding - */ - validation?: any; - }; - } - - export interface PlayerViewDetailsRPC - extends BaseRPCType<'player-view-details-request'> { - /** - * Parameters associated with the View Details RPC. - */ - params: { - /** - * Unique Player Id associated with the message. - */ - playerID: string; - }; - /** - * Result of the RPC call. - */ - result?: { - /** - * Last update of the view. - */ - lastViewUpdate?: View; - }; - } - - export interface PlayerRuntimeInfoRPC - extends BaseRPCType<'player-runtime-info-request'> { - /** - * Parameters associated with the Runtime Info RPC. - */ - params: { - /** - * Unique Player Id associated with the message. - */ - playerID: string; - }; - /** - * Result of the RPC call. - */ - result?: { - /** - * The unique ID associated with the current flow. - */ - currentFlowID?: string; - /** - * The state of the current flow. - */ - currentFlowState?: string; - /** - * The unique ID associated with the current view. - */ - currentViewID?: string; - /** - * The flow information associated with the current flow. - */ - currentFlow?: Flow; - }; - } - - export interface PlayerExpressionRPC - extends BaseRPCType<'player-execute-expression'> { - /** - * Parameters associated with the Expression RPC. - */ - params: { - /** - * Unique Player Id associated with the message. - */ - playerID: string; - /** - * Expression to be evaluated. - */ - expression: string; - }; - /** - * Result of the RPC call. - */ - result?: - | { - /** - * Error Status of the evaluation - */ - status: 'error'; - /** - * Message associated with the evaluation. - */ - message: string; - /** - * Expression associated with this evaluation. - */ - exp: string; - } - | { - /** - * Sucess Status of the evaluation - */ - status: 'success'; - /** - * Data associated with the evaluation - */ - data: any; - /** - * Expression associated with this evaluation. - */ - exp: string; - }; - } - - export interface PlayerStartProfilerRPC - extends BaseRPCType<'player-start-profiler-request'> { - /** - * Parameters associated with the Profiler Details RPC. - */ - params: { - /** - * Unique Player Id associated with the message. - */ - playerID: string; - }; - /** - * Result of the RPC call. - */ - result?: { - /** - * initial rootnode of the profiler node tree - */ - data: ProfilerNode; - }; - } - - export interface PlayerStopProfilerRPC - extends BaseRPCType<'player-stop-profiler-request'> { - /** - * Parameters associated with the Profiler Details RPC. - */ - params: { - /** - * Unique Player Id associated with the message. - */ - playerID: string; - }; - /** - * Result of the RPC call. - */ - result?: { - /** - * final rootnode of the profiler node tree - */ - data: ProfilerNode; - }; - } - - export type RuntimeEvent = - | PlayerInitEvent - | PlayerRemovedEvent - | PlayerDataChangeEvent - | PlayerLogEvent - | PlayerViewUpdateEvent - | PlayerFlowStartEvent - | RuntimeInitEvent; - - export const RuntimeEventTypes = [ - 'runtime-init', - 'player-init', - 'player-removed', - 'player-log-event', - 'player-data-change-event', - 'player-view-update-event', - 'player-flow-transition-event', - 'player-flow-start', - 'player-flow-end', - ] as const; - - export type RuntimeEventTypes = typeof RuntimeEventTypes[number]; - - export type RuntimeRPC = - | PlayerConfigRPC - | PlayerDataBindingRPC - | PlayerViewDetailsRPC - | PlayerRuntimeInfoRPC - | PlayerExpressionRPC - | PlayerStartProfilerRPC - | PlayerStopProfilerRPC; - - // TODO: Generate this from `RuntimeRPC['type']` if I ever can - export const RuntimeRPCTypes = [ - 'player-config-request', - 'player-data-binding-details', - 'player-view-details-request', - 'player-runtime-info-request', - 'player-execute-expression', - 'player-start-profiler-request', - 'player-stop-profiler-request', - ] as const; - - export type RuntimeRPCTypes = typeof RuntimeRPCTypes[number]; -} - -// TODO: Convert to RPC -export type Message = - | RPCRequestMessageEvent - | RPCResponseMessageEvent - | Runtime.RuntimeEvent; diff --git a/devtools/common/src/utils.ts b/devtools/common/src/utils.ts new file mode 100644 index 00000000..00afd6bd --- /dev/null +++ b/devtools/common/src/utils.ts @@ -0,0 +1,18 @@ +/** Utility structure defining the base interface for a discriminated union */ +export interface DiscriminatedType { + /** Unique type associated with the type */ + type: T; +} + +/** Utility for patternizing union discrimination */ +export type DiscriminateByType = Extract< + Types, + { type: T } +>; + +/** Higher order utility for determining what types things are */ +export const isKnownType = , T extends string>(types: readonly T[]) => + (value: any, type?: Provided): value is DiscriminateByType => + typeof value === 'object' && + types.includes(value.type) && + (!type || value.type === type); diff --git a/devtools/flipper/BUILD b/devtools/flipper/BUILD index 2769cc3f..2d9f1bc8 100644 --- a/devtools/flipper/BUILD +++ b/devtools/flipper/BUILD @@ -1,5 +1,6 @@ load(":build.bzl", "flipper_pkg") +# TODO: probably try to bundle w/ CSS first and then feed into flipper_pkg flipper_pkg( name = "flipper", dependencies = [ diff --git a/devtools/flipper/src/index.tsx b/devtools/flipper/src/index.tsx index 108493e7..4471da4c 100644 --- a/devtools/flipper/src/index.tsx +++ b/devtools/flipper/src/index.tsx @@ -1,58 +1,46 @@ import React from 'react'; import { PluginClient, usePlugin } from 'flipper-plugin'; import { - createStore, Runtime, - RPCRequestMessageEvent, - RPCResponseMessageEvent, + Events, + Methods, } from '@player-tools/devtools-common'; import { - handleMessage, - buildAliases, - buildPlayerReducerCallback, - buildRPCActions, - buildRPCRequests, - RuntimeRPCRequestHandlers, + createDevtoolsStore, + dispatchEvents, } from '@player-tools/devtools-client'; // TODO: Fix import lol -- maybe try to bundle this package _before_ it hits flipper-pkg? i'm so tired of monkeying with this import { App } from '@player-tools/devtools-ui/dist/devtools-ui.prod'; type Events = { - [key in Runtime.RuntimeEventTypes]: Extract< - Runtime.RuntimeEvent, - { type: key } - >; -} & { - 'rpc-response': RPCResponseMessageEvent; + [type in Events.Event['type']]: Events.ByType; }; type Methods = { - [key in Runtime.RuntimeRPCTypes]: ( - payload: RPCRequestMessageEvent - ) => Promise; + [type in Methods.Method["type"]]: ( + payload: Methods.ByType["params"] + ) => Promise["result"]>; }; export function plugin(client: PluginClient) { - const rpcHandlers: RuntimeRPCRequestHandlers = buildRPCRequests( - (message: RPCRequestMessageEvent) => { - // TODO: Do `send('rpc-request', message)` - client.send(message.rpcType, message); + // Delegate to plugin for how to handle communications + const methodHandler = async ( + method: Methods.ByType + ): Promise['result']> => { + if (await client.supportsMethod(method.type)) { + return (await client.send( + method.type, + method.params as any + )) as Methods.ByType['result']; } - ); - const actions = buildRPCActions(rpcHandlers); - const store = createStore( - buildAliases(actions), - buildPlayerReducerCallback(actions) - ); + // TODO: What do we do when things aren't supported? + }; - Runtime.RuntimeEventTypes.forEach((event) => { - client.onMessage(event, (message) => { - handleMessage(store, message); - }); - }); + const store = createDevtoolsStore(methodHandler) - client.onMessage('rpc-response', (params) => { - rpcHandlers[params.rpcType].onMessage(params); + // Listen for events + Events.EventTypes.forEach((event) => { + client.onMessage(event, dispatchEvents(store.dispatch)); }); return { store }; diff --git a/devtools/plugins/README.md b/devtools/plugins/README.md new file mode 100644 index 00000000..53f7ade6 --- /dev/null +++ b/devtools/plugins/README.md @@ -0,0 +1,4 @@ +# Plugins + +Belongs in `player-ui/player` repo, but keeping here until most of the heavy lifting is done. + diff --git a/devtools/ui/src/components/config/Config.tsx b/devtools/ui/src/components/config/Config.tsx index ceab6740..dcbc4c43 100644 --- a/devtools/ui/src/components/config/Config.tsx +++ b/devtools/ui/src/components/config/Config.tsx @@ -1,13 +1,13 @@ import React from 'react'; -import type { Runtime } from '@player-tools/devtools-client'; +import type { ActivePlayerState } from '@player-tools/devtools-client'; import { ObjectInspector } from '@devtools-ds/object-inspector'; import { Controls, Navigation, Tab, TabList } from '@devtools-ds/navigation'; import { headerCase } from 'change-case'; -export const SUB_PANEL_IDS = ['plugins', 'schema', 'expressions'] as const; +const SUB_PANEL_IDS = ['plugins', 'schema', 'expressions'] as const; interface ConfigProps { - configState: Runtime.PlayerConfigRPC['result']; + configState: ActivePlayerState["configState"] } export const Config = ({ configState }: ConfigProps) => { diff --git a/devtools/ui/src/components/config/ConfigPanel.tsx b/devtools/ui/src/components/config/ConfigPanel.tsx index bd9f451e..ebe2db2c 100644 --- a/devtools/ui/src/components/config/ConfigPanel.tsx +++ b/devtools/ui/src/components/config/ConfigPanel.tsx @@ -1,7 +1,5 @@ import React, { useEffect } from 'react'; import { - type Runtime, - type StoreState, selectConfig, selectSelectedPlayerId, GET_CONFIG_DETAILS, @@ -10,16 +8,9 @@ import { useDispatch, useSelector } from 'react-redux'; import styles from '../app.css'; import { Config } from './Config'; -export const SUB_PANEL_IDS = ['plugins', 'schema', 'expressions'] as const; - export const ConfigPanel = () => { - const currentPlayerID = useSelector( - selectSelectedPlayerId - ); - const configState = useSelector< - StoreState, - Runtime.PlayerConfigRPC['result'] - >(selectConfig); + const currentPlayerID = useSelector(selectSelectedPlayerId); + const configState = useSelector(selectConfig); const dispatch = useDispatch(); diff --git a/devtools/ui/src/components/console/ConsolePanel.tsx b/devtools/ui/src/components/console/ConsolePanel.tsx index 1aed73ea..299df3f8 100644 --- a/devtools/ui/src/components/console/ConsolePanel.tsx +++ b/devtools/ui/src/components/console/ConsolePanel.tsx @@ -1,23 +1,17 @@ import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { - type ConsoleState, - type StoreState, selectConsole, selectSelectedPlayerId, - consoleClearAction, GET_CONSOLE_EVAL, + Actions, } from '@player-tools/devtools-client'; import { Console } from './Console'; import styles from '../app.css'; export const ConsolePanel = () => { - const consoleState = useSelector( - selectConsole - ); - const currentPlayerID = useSelector( - selectSelectedPlayerId - ); + const consoleState = useSelector(selectConsole); + const currentPlayerID = useSelector(selectSelectedPlayerId); const dispatch = useDispatch(); if (!currentPlayerID || !consoleState) { @@ -28,7 +22,7 @@ export const ConsolePanel = () => { { - dispatch(consoleClearAction()); + dispatch(Actions['clear-console']()); }} onExecute={(expression) => { dispatch({ diff --git a/devtools/ui/src/components/data/Data.tsx b/devtools/ui/src/components/data/Data.tsx index 72f56e62..a9e62be3 100644 --- a/devtools/ui/src/components/data/Data.tsx +++ b/devtools/ui/src/components/data/Data.tsx @@ -2,19 +2,17 @@ import React from 'react'; import { ObjectInspector } from '@devtools-ds/object-inspector'; import Split from 'react-split'; import type { ResolvedASTNode, ASTNode } from '@devtools-ds/object-parser'; -import type { Runtime } from '@player-tools/devtools-common'; +import type { DataState } from '@player-tools/devtools-client'; import styles from './data.css'; import commonStyles from '../app.css'; interface DataDetailsPanelProps { - details?: Runtime.PlayerDataBindingRPC['result']; + details?: DataState["selectedBinding"]; } -export const DataDetailsPanel = (props: DataDetailsPanelProps) => { - const { details } = props; - +export const DataDetailsPanel = ({ details }: DataDetailsPanelProps) => { return ( -
+

Details

{details ? (
@@ -96,12 +94,10 @@ export const DataDetailsPanel = (props: DataDetailsPanelProps) => { }; interface DataProps { - allBindings: Runtime.PlayerDataBindingRPC['result']; - selectedBinding: Runtime.PlayerDataBindingRPC['result']; onSelect: (astNode: ASTNode | ResolvedASTNode | undefined) => Promise; } -export const Data = ({ allBindings, selectedBinding, onSelect }: DataProps) => ( +export const Data = ({ allBindings, selectedBinding, onSelect }: DataProps & DataState) => ( { - const allBindings: Runtime.PlayerDataBindingRPC['result'] = - useSelector(selectAllBindings); - const selectedBinding: Runtime.PlayerDataBindingRPC['result'] = useSelector( - selectSelectedBinding - ); + const allBindings = useSelector(selectAllBindings); + const selectedBinding = useSelector(selectSelectedBinding); const currentPlayerID = useSelector(selectSelectedPlayerId); const dispatch = useDispatch(); @@ -59,7 +55,7 @@ export const DataPanel = () => { } if (!binding) { - dispatch(clearSelectedDataDetails()); + dispatch(Actions['clear-selected-data-details']()); return; } diff --git a/devtools/ui/src/components/events/Events.tsx b/devtools/ui/src/components/events/Events.tsx index a53c7d29..fc503040 100644 --- a/devtools/ui/src/components/events/Events.tsx +++ b/devtools/ui/src/components/events/Events.tsx @@ -1,18 +1,17 @@ import React from 'react'; import { ObjectInspector } from '@devtools-ds/object-inspector'; import { Table } from '@devtools-ds/table'; -import type { Runtime } from '@player-tools/devtools-common'; +import type { ActivePlayerState } from '@player-tools/devtools-client'; import styles from './events.css'; const EVENT_NAME_MAP: Record< - | Runtime.PlayerDataChangeEvent['type'] - | Runtime.PlayerLogEvent['type'] - | Runtime.PlayerFlowStartEvent['type'], + ActivePlayerState['timelineEvents'][number]['type'], string > = { 'player-data-change-event': 'Data Change', 'player-log-event': 'Log', 'player-flow-start': 'Flow Start', + 'player-view-update-event': 'View Update', }; function createEmptyCells( @@ -29,11 +28,7 @@ function createEmptyCells( } interface EventsProps { - events: Array< - | Runtime.PlayerDataChangeEvent - | Runtime.PlayerLogEvent - | Runtime.PlayerFlowStartEvent - >; + events: ActivePlayerState['timelineEvents']; } export const Events = ({ events }: EventsProps) => { diff --git a/devtools/ui/src/components/events/EventsPanel.tsx b/devtools/ui/src/components/events/EventsPanel.tsx index 192ea3c5..dfe66b29 100644 --- a/devtools/ui/src/components/events/EventsPanel.tsx +++ b/devtools/ui/src/components/events/EventsPanel.tsx @@ -1,15 +1,11 @@ import React from 'react'; import { useSelector } from 'react-redux'; -import { type Runtime, selectEvents } from '@player-tools/devtools-common'; +import { selectEvents } from '@player-tools/devtools-client'; import { Events } from './Events'; import styles from '../app.css'; export const EventsPanel = () => { - const events: Array< - | Runtime.PlayerDataChangeEvent - | Runtime.PlayerLogEvent - | Runtime.PlayerFlowStartEvent - > = useSelector(selectEvents); + const events = useSelector(selectEvents); if (!events || !events.length) { return
No events available
; diff --git a/devtools/ui/src/components/flow/FlowPanel.tsx b/devtools/ui/src/components/flow/FlowPanel.tsx index cfe99325..da34ce01 100644 --- a/devtools/ui/src/components/flow/FlowPanel.tsx +++ b/devtools/ui/src/components/flow/FlowPanel.tsx @@ -1,15 +1,11 @@ import React from 'react'; import { useSelector } from 'react-redux'; -import type { Flow as FlowType } from '@player-ui/types'; -import { - type StoreState, - selectCurrentFlow, -} from '@player-tools/devtools-common'; +import { selectCurrentFlow } from '@player-tools/devtools-client'; import styles from '../app.css'; import { Flow } from './Flow'; export const FlowPanel = () => { - const flow = useSelector(selectCurrentFlow); + const flow = useSelector(selectCurrentFlow); if (!flow) { return
No flow details available
; diff --git a/devtools/ui/src/components/info/Info.tsx b/devtools/ui/src/components/info/Info.tsx index beb9a99d..fb874491 100644 --- a/devtools/ui/src/components/info/Info.tsx +++ b/devtools/ui/src/components/info/Info.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import type { Runtime } from '@player-tools/devtools-common'; import { Button } from '@chakra-ui/react'; import copy from 'copy-to-clipboard'; +import { ActivePlayerState } from '@player-tools/devtools-client'; import styles from '../sidebar.css'; interface InfoProps { - info: Runtime.PlayerRuntimeInfoRPC['result']; + info: ActivePlayerState["flowInfo"]; } /** diff --git a/devtools/ui/src/components/info/InfoPanel.tsx b/devtools/ui/src/components/info/InfoPanel.tsx index 8dad43a5..8c2562da 100644 --- a/devtools/ui/src/components/info/InfoPanel.tsx +++ b/devtools/ui/src/components/info/InfoPanel.tsx @@ -1,9 +1,6 @@ import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; -import type { Flow } from '@player-ui/types'; import { - type Runtime, - type StoreState, selectCurrentFlow, selectFlowInfo, selectSelectedPlayerId, @@ -19,16 +16,9 @@ import styles from '../app.css'; export const InfoPanel = () => { const dispatch = useDispatch(); - const currentPlayerID = useSelector( - selectSelectedPlayerId - ); - - const info = useSelector< - StoreState, - Runtime.PlayerRuntimeInfoRPC['result'] | null - >(selectFlowInfo); - - const flow = useSelector(selectCurrentFlow); + const currentPlayerID = useSelector(selectSelectedPlayerId); + const info = useSelector(selectFlowInfo); + const flow = useSelector(selectCurrentFlow); React.useEffect(() => { if ( diff --git a/devtools/ui/src/components/profiler/Profiler.tsx b/devtools/ui/src/components/profiler/Profiler.tsx index cab54cc5..632b0e18 100644 --- a/devtools/ui/src/components/profiler/Profiler.tsx +++ b/devtools/ui/src/components/profiler/Profiler.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { FlameGraph } from 'react-flame-graph'; import { ObjectInspector } from '@devtools-ds/object-inspector'; -import type { ProfilerNode } from '@player-tools/devtools-common'; +import { ActivePlayerState } from '@player-tools/devtools-client'; import styles from '../app.css'; interface ProfilerProps { - profiler?: ProfilerNode; + profiler?: ActivePlayerState['profilerInfo']; onStart: () => void; onStop: () => void; } diff --git a/devtools/ui/src/components/profiler/ProfilerPanel.tsx b/devtools/ui/src/components/profiler/ProfilerPanel.tsx index a087ef76..7197d9c1 100644 --- a/devtools/ui/src/components/profiler/ProfilerPanel.tsx +++ b/devtools/ui/src/components/profiler/ProfilerPanel.tsx @@ -1,7 +1,5 @@ import React from 'react'; import { - type ProfilerNode, - type StoreState, selectProfiler, selectSelectedPlayerId, START_PROFILER, @@ -16,12 +14,8 @@ import styles from '../app.css'; * @returns */ export const ProfilerPanel = () => { - const currentPlayerID = useSelector( - selectSelectedPlayerId - ); - const profiler = useSelector( - selectProfiler - ); + const currentPlayerID = useSelector(selectSelectedPlayerId); + const profiler = useSelector(selectProfiler); const dispatch = useDispatch(); const StartProfiler = () => { diff --git a/devtools/ui/src/components/sidebar.tsx b/devtools/ui/src/components/sidebar.tsx index 48c2bf65..2c041c9a 100644 --- a/devtools/ui/src/components/sidebar.tsx +++ b/devtools/ui/src/components/sidebar.tsx @@ -1,12 +1,11 @@ import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { - type StoreState, - selectedPlayerAction, + Actions, selectPlayerIds, selectSelectedPlayerId, selectPlayerVersion, -} from '@player-tools/devtools-common'; +} from '@player-tools/devtools-client'; import logo from '../media/player-logo.png'; import { InfoPanel } from './info'; import styles from './sidebar.css'; @@ -17,13 +16,9 @@ import styles from './sidebar.css'; */ export const Sidebar = () => { const dispatch = useDispatch(); - const playerIDs = useSelector>(selectPlayerIds); - const selectedPlayerID = useSelector( - selectSelectedPlayerId - ); - const playerVersion = useSelector( - selectPlayerVersion - ); + const playerIDs = useSelector(selectPlayerIds); + const selectedPlayerID = useSelector(selectSelectedPlayerId); + const playerVersion = useSelector(selectPlayerVersion); const details = selectedPlayerID ? (
@@ -37,7 +32,7 @@ export const Sidebar = () => { id="player-select" value={selectedPlayerID} onChange={(event) => { - dispatch(selectedPlayerAction(event.target.value)); + dispatch(Actions['selected-player'](event.target.value)); }} > {playerIDs.map((playerID) => ( diff --git a/devtools/ui/src/components/view/ViewInspector.tsx b/devtools/ui/src/components/view/ViewInspector.tsx index 36cf6192..92c6e508 100644 --- a/devtools/ui/src/components/view/ViewInspector.tsx +++ b/devtools/ui/src/components/view/ViewInspector.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import type { View } from '@player-ui/types'; import { ObjectInspector } from '@devtools-ds/object-inspector'; +import { ActivePlayerState } from '@player-tools/devtools-client'; interface ViewInspectorProps { - currentView: View; + currentView: ActivePlayerState['view']; } /** diff --git a/devtools/ui/src/components/view/ViewPanel.tsx b/devtools/ui/src/components/view/ViewPanel.tsx index 6036c740..517321b3 100644 --- a/devtools/ui/src/components/view/ViewPanel.tsx +++ b/devtools/ui/src/components/view/ViewPanel.tsx @@ -1,8 +1,6 @@ import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; -import type { View } from '@player-ui/types'; import { - type StoreState, selectSelectedPlayerId, selectView, GET_VIEW_DETAILS, @@ -15,12 +13,8 @@ import styles from '../app.css'; * @returns */ export const ViewPanel = () => { - const currentPlayerID = useSelector( - selectSelectedPlayerId - ); - const currentView = useSelector( - selectView - ); + const currentPlayerID = useSelector(selectSelectedPlayerId); + const currentView = useSelector(selectView); const dispatch = useDispatch(); React.useEffect(() => { diff --git a/devtools/web-ext/BUILD b/devtools/web-ext/BUILD new file mode 100644 index 00000000..71f2b153 --- /dev/null +++ b/devtools/web-ext/BUILD @@ -0,0 +1,77 @@ +load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin") +load("@npm//webpack-cli:index.bzl", webpack = "webpack_cli") +load("@rules_pkg//:pkg.bzl", "pkg_zip") + +genrule( + name = "version", + srcs = [ + "//:VERSION", + ], + outs = ["version.json"], + cmd = """echo {'"'version'"': '"'`cat $(location //:VERSION)`'"'} > $@""", +) + +# Necessary to ensure files are available in same context where output will be created +# See [chdir] option on [webpack] target +copy_to_bin( + name = "srcs", + srcs = glob(["src/**"]) + [ + ":webpack.config.js", + ":postcss.config.js", + ":tsconfig.json", + ], +) + +webpack( + # The name controls the output directory, which I tried to make dynamic based on + # target name, but somewhere, the link was missing, so this should stay [dist] + # unless you figure out how to correctly link the output directory with the files + # created by webpack + name = "dist", + chdir = "$(RULEDIR)", + data = [ + ":srcs", + ":version.json", + + # Webpack deps + "@npm//@babel/preset-env", + "@npm//@babel/plugin-proposal-nullish-coalescing-operator", + "@npm//babel-loader", + "@npm//copy-webpack-plugin", + "@npm//css-loader", + "@npm//html-webpack-plugin", + "@npm//postcss-loader", + "@npm//postcss-nested", + "@npm//postcss-mixins", + "@npm//postcss-simple-vars", + "@npm//style-loader", + "@npm//ts-loader", + "@npm//url-loader", + "@npm//webpack-extension-reloader", + + # Real deps + # "//devtools/common", + # "//devtools/window-rpc-bridge", + "//devtools/ui", + "@npm//@types/react-dom", + "@npm//react", + "@npm//react-dom", + "@npm//uuid", + "@npm//webextension-polyfill-ts", + "@npm//webext-redux", + ], + output_dir = True, +) + +alias( + name = "web-ext", + actual = "dist", +) + +pkg_zip( + name = "chrome", + srcs = [ + ":dist", + ], + strip_prefix = "dist", +) diff --git a/devtools/web-ext/README.md b/devtools/web-ext/README.md new file mode 100644 index 00000000..31e69f7f --- /dev/null +++ b/devtools/web-ext/README.md @@ -0,0 +1,7 @@ +# Web extension + +### Local development + +1. Enable development mode: `chrome://extensions` +2. Load unpacked extension from `path/to/tools/bazel-bin/devtools/web-ext/dist` + diff --git a/devtools/web-ext/postcss.config.js b/devtools/web-ext/postcss.config.js new file mode 100644 index 00000000..7f777b59 --- /dev/null +++ b/devtools/web-ext/postcss.config.js @@ -0,0 +1,28 @@ +// const variables = require('@cgds/styles').default; +const variables = {}; + +const simpleVars = require('postcss-simple-vars'); +const mixins = require('postcss-mixins'); + +// const typography = require.resolve('@cgds/styles/mixins/typography.css'); +// const elevation = require.resolve('@cgds/styles/mixins/elevation.css'); +// const motion = require.resolve('@cgds/styles/mixins/motion.css'); +// const focus = require.resolve('@cgds/styles/mixins/focus.css'); + +/* eslint-disable global-require */ +module.exports = { + plugins: [ + mixins({ + mixinsFiles: [ + // typography, elevation, motion, focus + ], + }), + require('postcss-nested'), + simpleVars({ + variables: Object.entries(variables).reduce( + (all, [name, value]) => ({ ...all, [`$${name}`]: value }), + {} + ), + }), + ], +}; diff --git a/devtools/web-ext/src/background/background.ts b/devtools/web-ext/src/background/background.ts new file mode 100644 index 00000000..b20602d3 --- /dev/null +++ b/devtools/web-ext/src/background/background.ts @@ -0,0 +1,38 @@ +import { wrapStore } from 'webext-redux'; +import { + createLogger, + BACKGROUND_SOURCE, + type Methods, +} from '@player-tools/devtools-common'; +import { createDevtoolsStore, dispatchEvents, Actions, listenerMiddleware } from '@player-tools/devtools-client'; +import { browser } from 'webextension-polyfill-ts'; + +const logger = createLogger(BACKGROUND_SOURCE); + +async function sendMessage(message: any): Promise { + const [{ id: tabId }] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + + if (!tabId) return Promise.reject('could not find active tab'); + + return await browser.tabs.sendMessage(tabId, message); +} + +// Configure middleware to forward 'clear-store' actions +// to the content script so it can clear any caches when +// we reset the store! This also serves as an example for +// how to configure other side effects, if necessary. +listenerMiddleware.startListening({ + actionCreator: Actions['clear-store'], + effect: sendMessage, +}) + +const store = createDevtoolsStore(sendMessage); + +wrapStore(store); +logger.log('Wrapped Redux Store'); + +// Connect events to store +browser.runtime.onMessage.addListener(dispatchEvents(store.dispatch)); diff --git a/devtools/web-ext/src/constants.ts b/devtools/web-ext/src/constants.ts new file mode 100644 index 00000000..ed501d00 --- /dev/null +++ b/devtools/web-ext/src/constants.ts @@ -0,0 +1 @@ +export const INJECTED_SCRIPT_ID = '__PLAYER_UI_RUNTIME__'; diff --git a/devtools/web-ext/src/content/content.ts b/devtools/web-ext/src/content/content.ts new file mode 100644 index 00000000..a519de60 --- /dev/null +++ b/devtools/web-ext/src/content/content.ts @@ -0,0 +1,59 @@ +import { browser } from 'webextension-polyfill-ts'; +import { v4 as uuid } from 'uuid'; +import { + CONTENT_SOURCE, + createLogger, + Methods, + Events, +} from '@player-tools/devtools-common'; +import { INJECTED_SCRIPT_ID } from '../constants'; + +const logger = createLogger(CONTENT_SOURCE); + +/** Create a script element to be injected into the dom */ +function getScript() { + const injectedScript = document.createElement('script'); + injectedScript.src = browser.extension.getURL('../runtime/runtime.js'); + injectedScript.id = INJECTED_SCRIPT_ID; + return injectedScript; +} + +document.documentElement.append(getScript()); + +/** Method type with unique identifier for tracking incoming method responses */ +type MethodWithId = Methods.ByType & { id: string }; + +/** Tracker for method requests sent to the frontend */ +const requestsInFlight = new Map void>(); + +/** Handle messages sent from the background to the content script */ +browser.runtime.onMessage.addListener((request) => { + // Forward method requests to Player context + if (Methods.isMethod(request) && !request.result) { + const id = uuid(); + window.postMessage({ + ...request, + id, + }, '*'); + + return new Promise((resolve) => { + requestsInFlight.set(id, resolve) + }) + } else if (request.type === 'clear-store') { + // If we clear the store, we don't care about any pending information + requestsInFlight.clear() + } +}); + +/** Send window events from Player to background */ +window.addEventListener('message', ({ data, source }: MessageEvent) => { + if (source !== window) return; + + if (Methods.isMethod(data) && data.result) { + // TODO: Should this return the full method request? + requestsInFlight.get(data.id)?.(data.result) + } else if (Events.isEvent(data)) { + // Forward events to background + browser.runtime.sendMessage(data); + } +}); diff --git a/devtools/web-ext/src/manifest.json b/devtools/web-ext/src/manifest.json new file mode 100644 index 00000000..d5e9d287 --- /dev/null +++ b/devtools/web-ext/src/manifest.json @@ -0,0 +1,37 @@ +{ + "name": "Player UI Developer Tools", + "description": "Developer tooling for debugging active Player instances", + "devtools_page": "panel/startup.html", + "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", + "version": "0.0.0-PLACEHOLDER", + "manifest_version": 2, + "background": { + "scripts": ["background/background.js"] + }, + "content_scripts": [ + { + "matches": [""], + "js": ["content/content.js"], + "all_frames": true, + "run_at": "document_start" + } + ], + "permissions": [ + "tabs", + "storage", + "webNavigation", + "clipboardRead", + "clipboardWrite", + "http://*/*", + "https://*/*" + ], + "web_accessible_resources": [ + "runtime/runtime.js", + "popup/assets/*", + "panel/assets/*" + ], + "options_ui": { + "page": "options/options.html", + "chrome_style": true + } +} diff --git a/devtools/web-ext/src/media/player-logo.png b/devtools/web-ext/src/media/player-logo.png new file mode 100644 index 00000000..726b21bb Binary files /dev/null and b/devtools/web-ext/src/media/player-logo.png differ diff --git a/devtools/web-ext/src/options/options.ts b/devtools/web-ext/src/options/options.ts new file mode 100644 index 00000000..e69de29b diff --git a/devtools/web-ext/src/panel/global.css b/devtools/web-ext/src/panel/global.css new file mode 100644 index 00000000..60925395 --- /dev/null +++ b/devtools/web-ext/src/panel/global.css @@ -0,0 +1,3 @@ +body { + margin: 0; +} \ No newline at end of file diff --git a/devtools/web-ext/src/panel/panel.tsx b/devtools/web-ext/src/panel/panel.tsx new file mode 100644 index 00000000..8b5a0d60 --- /dev/null +++ b/devtools/web-ext/src/panel/panel.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import ReactDom from 'react-dom'; +import { AutoThemeProvider } from '@devtools-ds/themes'; +import { App } from '@player-tools/devtools-ui'; +import { proxyStore } from '../redux/proxy-store'; +import './global.css'; + +const ele = document.createElement('div'); +document.body.appendChild(ele); + +proxyStore + .ready() + .then(() => ReactDom.render( + + + , ele)); diff --git a/devtools/web-ext/src/panel/startup.ts b/devtools/web-ext/src/panel/startup.ts new file mode 100644 index 00000000..17278226 --- /dev/null +++ b/devtools/web-ext/src/panel/startup.ts @@ -0,0 +1,7 @@ +import { browser } from 'webextension-polyfill-ts'; + +browser.devtools.panels.create( + 'Player', + '../media/player-logo.png', + '/panel/panel.html' +); diff --git a/devtools/web-ext/src/popup/popup.ts b/devtools/web-ext/src/popup/popup.ts new file mode 100644 index 00000000..e69de29b diff --git a/devtools/web-ext/src/redux/proxy-store.ts b/devtools/web-ext/src/redux/proxy-store.ts new file mode 100644 index 00000000..9acf0357 --- /dev/null +++ b/devtools/web-ext/src/redux/proxy-store.ts @@ -0,0 +1,3 @@ +import { Store } from 'webext-redux'; + +export const proxyStore = new Store(); diff --git a/devtools/web-ext/src/runtime/runtime.tsx b/devtools/web-ext/src/runtime/runtime.tsx new file mode 100644 index 00000000..a3bee868 --- /dev/null +++ b/devtools/web-ext/src/runtime/runtime.tsx @@ -0,0 +1,3 @@ +import { RUNTIME_SOURCE } from "@player-tools/devtools-common"; + +window.postMessage({type: 'runtime-init', source: RUNTIME_SOURCE}, '*') diff --git a/devtools/web-ext/tsconfig.json b/devtools/web-ext/tsconfig.json new file mode 100644 index 00000000..fc44378a --- /dev/null +++ b/devtools/web-ext/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES6", + "lib": ["ESNext", "DOM"], + "outDir": "dist", + "jsx": "react", + "sourceMap": true, + "strict": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "importHelpers": true + }, + "exclude": ["node_modules"] +} diff --git a/devtools/web-ext/webpack.config.js b/devtools/web-ext/webpack.config.js new file mode 100644 index 00000000..a4758c2e --- /dev/null +++ b/devtools/web-ext/webpack.config.js @@ -0,0 +1,138 @@ +const path = require('path'); +const webpack = require('webpack'); +const { version } = require('./version.json'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const ExtensionReloader = require('webpack-extension-reloader'); +const HTMLWebpackPlugin = require('html-webpack-plugin'); + +const config = () => { + return { + mode: process.env.NODE_ENV ?? 'development', + context: path.join(__dirname, 'src'), + devtool: false, + entry: { + 'background/background': './background/background.ts', + 'popup/popup': './popup/popup.ts', + 'panel/panel': './panel/panel.tsx', + 'panel/startup': './panel/startup.ts', + 'runtime/runtime': './runtime/runtime.tsx', + 'content/content': './content/content.ts', + 'options/options': './options/options.ts', + }, + output: { + path: path.join(__dirname, 'dist'), + filename: '[name].js', + }, + resolve: { + extensions: ['.ts', '.tsx', '.js', '.json'], + }, + module: { + rules: [ + { + test: /\.tsx?$/, + loader: 'ts-loader', + exclude: /node_modules/, + }, + { + test: /\.m?js$/, + use: { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env'], + plugins: ['@babel/plugin-proposal-nullish-coalescing-operator'], + }, + }, + }, + { + test: /\.(png|jpg|jpeg|gif)$/i, + use: [ + { + loader: 'url-loader', + options: { + limit: false, + }, + }, + ], + }, + { + test: /\.css$/, + oneOf: [ + { + exclude: /node_modules/, + use: [ + { loader: 'style-loader' }, + { + loader: 'css-loader', + options: { + sourceMap: true, + localsConvention: 'camelCase', + importLoaders: 1, + modules: { + localIdentName: '[name]-[local]', + }, + }, + }, + 'postcss-loader', + ], + }, + { include: /node_modules/, use: ['style-loader', 'css-loader'] }, + ], + }, + ], + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'production'), + }, + }), + new CopyWebpackPlugin({ + patterns: [ + { from: 'media', to: 'media' }, + { + from: 'manifest.json', + to: 'manifest.json', + transform: (content) => { + const jsonContent = JSON.parse(content); + jsonContent['version'] = version; + + if (config.mode === 'development') { + jsonContent['content_security_policy'] = + "script-src 'self' 'unsafe-eval'; object-src 'self'"; + } + + return JSON.stringify(jsonContent, null, 2); + }, + }, + ], + }), + new HTMLWebpackPlugin({ + filename: 'panel/panel.html', + chunks: ['panel/panel'], + }), + new HTMLWebpackPlugin({ + filename: 'panel/startup.html', + + chunks: ['panel/startup'], + }), + new HTMLWebpackPlugin({ + filename: 'popup/popup.html', + chunks: ['popup/popup'], + }), + new HTMLWebpackPlugin({ + filename: 'options/options.html', + chunks: ['options/options'], + }), + process.env.HMR === 'true' && + new ExtensionReloader({ + entries: { + contentScript: ['content/content'], + background: 'background/background', + extensionPage: ['popup/popup', 'options/options', 'panel/panel'], + }, + }), + ].filter(Boolean), + }; +}; + +module.exports = config; diff --git a/package.json b/package.json index 09d05105..fae20547 100644 --- a/package.json +++ b/package.json @@ -18,18 +18,14 @@ "dependencies": { "@auto-it/version-file": "^10.37.2", "@babel/cli": "^7.15.7", - "duplicate-package-checker-webpack-plugin": "^3.0.0", - "uuid": "^8.3.2", "@babel/core": "^7.15.5", "@babel/eslint-parser": "^7.15.8", "@babel/plugin-transform-react-jsx-source": "^7.18.6", "@babel/plugin-transform-runtime": "^7.15.8", "@babel/preset-env": "^7.15.6", - "style-loader": "^1.3.0", "@babel/preset-react": "^7.14.5", "@babel/preset-typescript": "^7.15.0", "@babel/register": "^7.17.7", - "babel-loader": "^8.2.5", "@babel/runtime": "7.15.4", "@bazel/typescript": "^4.4.2", "@chakra-ui/react": "^1.0.0", @@ -43,8 +39,8 @@ "@kendallgassner/eslint-plugin-package-json": "^0.2.1", "@oclif/core": "1.9.0", "@oclif/plugin-legacy": "^1.2.7", - "@player-ui/logger": "^0.2.0", - "@player-ui/types": "^0.2.0", + "@player-ui/player": "^0.3.0", + "@player-ui/types": "^0.3.0", "@reduxjs/toolkit": "^1.6.1", "@rollup/plugin-image": "^2.1.1", "@rollup/plugin-json": "^4.1.0", @@ -69,17 +65,20 @@ "@typescript/vfs": "^1.4.0", "all-contributors-cli": "^6.20.0", "auto": "^10.37.2", + "babel-loader": "^8.2.5", "c8": "^7.10.0", "case-sensitive-paths-webpack-plugin": "^2.3.0", "chalk": "^4.0.1", "change-case": "^4.1.1", "command-line-application": "^0.10.1", "copy-to-clipboard": "^3.3.2", + "copy-webpack-plugin": "^6.2.1", "cosmiconfig": "^7.0.1", "cross-fetch": "^3.0.5", "css-loader": "^3.6.0", "dequal": "^2.0.2", "detect-indent": "^6.0.0", + "duplicate-package-checker-webpack-plugin": "^3.0.0", "elegant-spinner": "^2.0.0", "esbuild": "^0.13.15", "eslint": "^8.0.1", @@ -105,6 +104,7 @@ "framer-motion": "^4", "fs-extra": "^10.0.0", "globby": "^11.0.1", + "html-webpack-plugin": "^4.0.0", "husky": "^7.0.2", "is-ci-cli": "^2.2.0", "jest": "^27.3.1", @@ -120,6 +120,9 @@ "modify-source-webpack-plugin": "^3.0.0", "oclif": "3.0.1", "patch-package": "^6.4.7", + "postcss-loader": "^3.0.0", + "postcss-mixins": "6.2.3", + "postcss-nested": "^4.2.1", "prettier": "^2.4.1", "react": "^17.0.2", "react-docgen-typescript": "^2.1.1", @@ -142,17 +145,23 @@ "signale": "^1.4.0", "source-map-js": "^1.0.2", "std-mocks": "^1.0.1", + "style-loader": "^1.3.0", "tapable-ts": "^0.1.0", "timm": "^1.7.1", - "ts-loader": "^5.3.3", + "ts-loader": "^8.4.0", "ts-node": "^10.4.0", "typescript": "4.8.4", + "url-loader": "4.1.1", + "uuid": "^8.3.2", "vsce": "^1.93.0", "vscode-languageserver": "^6.1.1", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.15.1", + "webext-redux": "^2.1.7", + "webextension-polyfill-ts": "^0.21.0", "webpack": "^4.41.0", "webpack-cli": "^3.3.0", + "webpack-extension-reloader": "^1.1.4", "zip-folder": "^1.0.0" }, "volta": { diff --git a/yarn.lock b/yarn.lock index 2c201433..5a0450d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2621,6 +2621,14 @@ package-json-validator "^0.6.3" requireindex "^1.2.0" +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": version "2.1.8-no-fsevents.3" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" @@ -2646,6 +2654,11 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + "@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" @@ -3204,18 +3217,40 @@ dependencies: "@octokit/openapi-types" "^12.11.0" -"@player-ui/logger@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@player-ui/logger/-/logger-0.2.0.tgz#8cda9d07922a2f1a4090cb849a42f7e9499fbda9" - integrity sha512-rwWOuWDRCEsLv8SpHJgpVei18PcXLzr/F6ZS8SPR2UIlZ4y6T1TxPd/qjIdPMs6CeP5pzOVeZVIevhDQNHO+lQ== +"@player-ui/partial-match-registry@0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@player-ui/partial-match-registry/-/partial-match-registry-0.3.0.tgz#cd102b947ccba754e390a1a022d8ccd8698053c4" + integrity sha512-jssryLQrPMz/A7KmtDH4M3/i2aWSVF4K6WCLZWFCxWhXKF0DXV/kdCH+8TIWMZ2mBjPwh3DZ3H6yFEhfU7DGCg== + dependencies: + "@babel/runtime" "7.15.4" + "@types/dlv" "^1.1.2" + dlv "^1.1.3" + sorted-array "^2.0.4" + +"@player-ui/player@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@player-ui/player/-/player-0.3.0.tgz#6cfbed27577e86c94ff0281be2c908081fe760a2" + integrity sha512-Tdd/g0yTLlCC/egXXt/DRypH/STtQYb4ZQGVvMPmonWCGo/f86DzygHUwsDEVeJCceme0nxw8gTaTlBYpAw/vQ== dependencies: "@babel/runtime" "7.15.4" + "@player-ui/partial-match-registry" "0.3.0" + "@player-ui/types" "0.3.0" + "@types/nested-error-stacks" "^2.1.0" + "@types/parsimmon" "^1.10.0" + arr-flatten "^1.1.0" + dequal "^2.0.2" + ebnf "^1.9.0" + nested-error-stacks "^2.1.1" + p-defer "^3.0.0" + parsimmon "^1.12.0" + queue-microtask "^1.2.3" tapable-ts "^0.1.0" + timm "^1.6.2" -"@player-ui/types@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@player-ui/types/-/types-0.2.0.tgz#72990b70621d65ceebf02da9f813481e8eb17692" - integrity sha512-Lj7XNWQ9bHeVQbPJQwObXhYJddvr5iEhFCivCdbREuqiuF7RhG6Sodn3nT88L8EQPr78n88I7oxD6G8fQ/iEjw== +"@player-ui/types@0.3.0", "@player-ui/types@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@player-ui/types/-/types-0.3.0.tgz#821a78b59363bcd81f283f7d2882d5446937f000" + integrity sha512-feq7R8Pdm5yhSJmR7LHK+69IZQ1s0XAEN1HEiSyqqKP4x4IjROH9iRZ2x9lXjC6vh1Qi8uK5JSOht8ZxRYcm2Q== dependencies: "@babel/runtime" "7.15.4" @@ -3583,6 +3618,11 @@ dependencies: "@types/ms" "*" +"@types/dlv@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@types/dlv/-/dlv-1.1.2.tgz#02d4fcc41c5f707753427867c64fdae543031fb9" + integrity sha512-OyiZ3jEKu7RtGO1yp9oOdK0cTwZ/10oE9PDJ6fyN3r9T5wkyOcvr6awdugjYdqF6KVO5eUvt7jx7rk2Eylufow== + "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -3630,6 +3670,11 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" +"@types/html-minifier-terser@^5.0.0": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57" + integrity sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" @@ -3725,6 +3770,11 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== +"@types/nested-error-stacks@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0e4caf703b874c3c4e6ba74dbc0c6acf88b8ce05" + integrity sha512-7+la7jn6iA603lBgyASoaW5Nk/R5G3+hkJ4Y0gtc9VYHlcixvm/YBV2KV92dTBpeCQJYjpN2owb5jVuKCqxOaA== + "@types/node-fetch@^2.5.12": version "2.6.2" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" @@ -3763,6 +3813,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/parsimmon@^1.10.0": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@types/parsimmon/-/parsimmon-1.10.6.tgz#8fcf95990514d2a7624aa5f630c13bf2427f9cdd" + integrity sha512-FwAQwMRbkhx0J6YELkwIpciVzCcgEqXEbIrIn3a2P5d3kGEHQ3wVhlN3YdVepYP+bZzCYO6OjmD4o9TGOZ40rA== + "@types/prettier@^2.1.5": version "2.7.1" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e" @@ -3883,7 +3938,7 @@ resolved "https://registry.yarnpkg.com/@types/std-mocks/-/std-mocks-1.0.1.tgz#a5350e9e9c16af0157ab644337152fd6befe8baa" integrity sha512-pwOrGJ6jnqb1bw9TxrmbbHTI+7ngY8yEEkrTG7lJuwplvFT9XSwOCQC3Zb4E+e6g9yDzrFX+S1ysLZuT/07WYg== -"@types/tapable@^1": +"@types/tapable@^1", "@types/tapable@^1.0.5": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== @@ -3947,6 +4002,27 @@ "@types/source-list-map" "*" source-map "^0.7.3" +"@types/webpack-sources@^0.1.5": + version "0.1.9" + resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.9.tgz#da69b06eb34f6432e6658acb5a6893c55d983920" + integrity sha512-bvzMnzqoK16PQIC8AYHNdW45eREJQMd6WG/msQWX5V2+vZmODCOPb4TJcbgRljTZZTwTM4wUMcsI8FftNA7new== + dependencies: + "@types/node" "*" + "@types/source-list-map" "*" + source-map "^0.6.1" + +"@types/webpack@^4.39.8", "@types/webpack@^4.41.8": + version "4.41.33" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.33.tgz#16164845a5be6a306bcbe554a8e67f9cac215ffc" + integrity sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g== + dependencies: + "@types/node" "*" + "@types/tapable" "^1" + "@types/uglify-js" "*" + "@types/webpack-sources" "*" + anymatch "^3.0.0" + source-map "^0.6.0" + "@types/webpack@^4.41.0": version "4.41.32" resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.32.tgz#a7bab03b72904070162b2f169415492209e94212" @@ -4667,6 +4743,22 @@ array.prototype.flatmap@^1.3.0: es-abstract "^1.19.2" es-shim-unscopables "^1.0.0" +array.prototype.reduce@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" + integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== + arrify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" @@ -5385,6 +5477,11 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-me-maybe@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" + integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ== + caller-callsite@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" @@ -5409,7 +5506,7 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camel-case@^4.1.2: +camel-case@^4.1.1, camel-case@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== @@ -5417,6 +5514,11 @@ camel-case@^4.1.2: pascal-case "^3.1.2" tslib "^2.0.3" +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -5650,6 +5752,13 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-css@^4.2.3: + version "4.2.4" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" + integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A== + dependencies: + source-map "~0.6.0" + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -5927,6 +6036,11 @@ colors@1.0.3: resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" integrity sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw== +colors@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -5983,7 +6097,7 @@ commander@^2.20.0, commander@^2.8.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^4.0.1: +commander@^4.0.1, commander@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== @@ -6159,6 +6273,23 @@ copy-to-clipboard@^3.3.2: dependencies: toggle-selection "^1.0.6" +copy-webpack-plugin@^6.2.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.4.1.tgz#138cd9b436dbca0a6d071720d5414848992ec47e" + integrity sha512-MXyPCjdPVx5iiWyl40Va3JGh27bKzOTNY3NjUTrosD2q7dR/cLD0013uqJ3BpFbUjyONINjb6qI7nDIJujrMbA== + dependencies: + cacache "^15.0.5" + fast-glob "^3.2.4" + find-cache-dir "^3.3.1" + glob-parent "^5.1.1" + globby "^11.0.1" + loader-utils "^2.0.0" + normalize-path "^3.0.0" + p-limit "^3.0.2" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" + webpack-sources "^1.4.3" + core-js-compat@^3.25.1: version "3.25.5" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.5.tgz#0016e8158c904f7b059486639e6e82116eafa7d9" @@ -6187,7 +6318,7 @@ cosmiconfig@7.0.0: path-type "^4.0.0" yaml "^1.10.0" -cosmiconfig@^5.0.5: +cosmiconfig@^5.0.0, cosmiconfig@^5.0.5: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== @@ -6617,7 +6748,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-properties@^1.1.3, define-properties@^1.1.4: +define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== @@ -6752,6 +6883,14 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +dir-glob@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== + dependencies: + arrify "^1.0.1" + path-type "^3.0.0" + dir-glob@^2.0.0: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -6774,6 +6913,11 @@ disparity@^3.1.0: ansi-styles "^4.2.1" diff "^4.0.2" +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -6793,6 +6937,13 @@ dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56" integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg== +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" @@ -6828,7 +6979,7 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" -domhandler@^4.2.0, domhandler@^4.3.1: +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== @@ -6842,7 +6993,7 @@ domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3: dependencies: domelementtype "^2.3.0" -domutils@^2.8.0: +domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== @@ -6902,6 +7053,11 @@ duplicate-package-checker-webpack-plugin@^3.0.0: lodash "^4.17.4" semver "^5.4.1" +ebnf@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/ebnf/-/ebnf-1.9.0.tgz#9c2dd6052f3ed43a69c1f0b07b15bd03cefda764" + integrity sha512-LKK899+j758AgPq00ms+y90mo+2P86fMKUWD28sH0zLKUj7aL6iIH2wy4jejAMM9I2BawJ+2kp6C3mMXj+Ii5g== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -7096,6 +7252,42 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19 string.prototype.trimstart "^1.0.5" unbox-primitive "^1.0.2" +es-abstract@^1.20.4: + version "1.20.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.5.tgz#e6dc99177be37cacda5988e692c3fa8b218e95d2" + integrity sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.3" + get-symbol-description "^1.0.0" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.2" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + unbox-primitive "^1.0.2" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + es-module-lexer@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" @@ -7890,7 +8082,19 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.9: +fast-glob@^2.0.2: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.4, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -8632,7 +8836,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.1.2, glob-parent@~5.1.2: +glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -8662,6 +8866,11 @@ glob-stream@^6.1.0: to-absolute-glob "^2.0.0" unique-stream "^2.0.2" +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig== + glob@^7.0.0, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -8779,11 +8988,31 @@ globby@^7.1.1: pify "^3.0.0" slash "^1.0.0" +globby@^8.0.1: + version "8.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" + integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== + dependencies: + array-union "^1.0.1" + dir-glob "2.0.0" + fast-glob "^2.0.2" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + google-protobuf@^3.6.1: version "3.21.0" resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.21.0.tgz#8dfa3fca16218618d373d414d3c1139e28034d6e" integrity sha512-byR7MBTK4tZ5PZEb+u5ZTzpt4SfrTxv5682MjPlHN16XeqgZE2/8HOIWeiXe8JKnT9OVbtBGhbq8mtvkK8cd5g== +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -8921,6 +9150,11 @@ hastscript@^6.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + header-case@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" @@ -9006,6 +9240,44 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +html-minifier-terser@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#922e96f1f3bb60832c2634b79884096389b1f054" + integrity sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg== + dependencies: + camel-case "^4.1.1" + clean-css "^4.2.3" + commander "^4.1.1" + he "^1.2.0" + param-case "^3.0.3" + relateurl "^0.2.7" + terser "^4.6.3" + +html-webpack-plugin@^4.0.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz#76fc83fa1a0f12dd5f7da0404a54e2699666bc12" + integrity sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A== + dependencies: + "@types/html-minifier-terser" "^5.0.0" + "@types/tapable" "^1.0.5" + "@types/webpack" "^4.41.8" + html-minifier-terser "^5.0.1" + loader-utils "^1.2.3" + lodash "^4.17.20" + pretty-error "^2.1.1" + tapable "^1.1.3" + util.promisify "1.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + htmlparser2@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010" @@ -9154,6 +9426,13 @@ immer@^9.0.12, immer@^9.0.7: resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.15.tgz#0b9169e5b1d22137aba7d43f8a81a495dd1b62dc" integrity sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ== +import-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" + integrity sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg== + dependencies: + import-from "^2.1.0" + import-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" @@ -9177,6 +9456,13 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" +import-from@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" + integrity sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w== + dependencies: + resolve-from "^3.0.0" + import-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" @@ -9408,7 +9694,7 @@ is-builtin-module@^3.1.0: dependencies: builtin-modules "^3.3.0" -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.6: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.6, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== @@ -10761,7 +11047,16 @@ loader-runner@^2.4.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== -loader-utils@^1.0.2, loader-utils@^1.2.3, loader-utils@^1.4.0: +loader-utils@^1.1.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" + integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +loader-utils@^1.2.3, loader-utils@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== @@ -10821,6 +11116,11 @@ lodash-es@^4.17.15: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== +lodash.assignin@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" + integrity sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg== + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -10831,6 +11131,11 @@ lodash.chunk@^4.2.0: resolved "https://registry.yarnpkg.com/lodash.chunk/-/lodash.chunk-4.2.0.tgz#66e5ce1f76ed27b4303d8c6512e8d1216e8106bc" integrity sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w== +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -10866,7 +11171,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@^4.0.1, lodash@^4.11.1, lodash@^4.11.2, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: +lodash@^4.0.1, lodash@^4.11.1, lodash@^4.11.2, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -10926,6 +11231,14 @@ lru-cache@2: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" integrity sha512-WpibWJ60c3AgAz8a2iYErDrcT2C7OmKnsWhIcHOjkUHFjkXncJhtLxNSqUmxRxRunpb5I8Vprd7aNSd2NtksJQ== +lru-cache@4.1.x: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -11412,7 +11725,7 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -11750,6 +12063,11 @@ neo-async@^2.5.0, neo-async@^2.6.1: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +nested-error-stacks@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5" + integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw== + nested-error-stacks@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz#d2cc9fc5235ddb371fc44d506234339c8e4b0a4b" @@ -12113,6 +12431,16 @@ object.fromentries@^2.0.5: define-properties "^1.1.3" es-abstract "^1.19.1" +object.getownpropertydescriptors@^2.0.3: + version "2.1.5" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz#db5a9002489b64eef903df81d6623c07e5b4b4d3" + integrity sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw== + dependencies: + array.prototype.reduce "^1.0.5" + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + object.hasown@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" @@ -12279,6 +12607,11 @@ osenv@^0.1.3: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +p-defer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-3.0.0.tgz#d1dceb4ee9b2b604b1d94ffec83760175d4e6f83" + integrity sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw== + p-filter@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-filter/-/p-filter-2.1.0.tgz#1b1472562ae7a0f742f0f3d3d3718ea66ff9c09c" @@ -12443,7 +12776,7 @@ parallel-transform@^1.1.0: inherits "^2.0.3" readable-stream "^2.1.5" -param-case@^3.0.4: +param-case@^3.0.3, param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== @@ -12562,6 +12895,11 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +parsimmon@^1.12.0: + version "1.18.1" + resolved "https://registry.yarnpkg.com/parsimmon/-/parsimmon-1.18.1.tgz#d8dd9c28745647d02fc6566f217690897eed7709" + integrity sha512-u7p959wLfGAhJpSDJVYXoyMCXWYwHia78HhRBWqk7AIbxdmlrfdp5wX0l3xv/iTSH5HvhN9K7o26hwwpgS5Nmw== + pascal-case@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" @@ -12825,6 +13163,32 @@ postcss-discard-overridden@^5.1.0: resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e" integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw== +postcss-js@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-2.0.3.tgz#a96f0f23ff3d08cec7dc5b11bf11c5f8077cdab9" + integrity sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w== + dependencies: + camelcase-css "^2.0.1" + postcss "^7.0.18" + +postcss-load-config@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz#c5ea504f2c4aef33c7359a34de3573772ad7502a" + integrity sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw== + dependencies: + cosmiconfig "^5.0.0" + import-cwd "^2.0.0" + +postcss-loader@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" + integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== + dependencies: + loader-utils "^1.1.0" + postcss "^7.0.0" + postcss-load-config "^2.0.0" + schema-utils "^1.0.0" + postcss-merge-longhand@^5.1.6: version "5.1.6" resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz#f378a8a7e55766b7b644f48e5d8c789ed7ed51ce" @@ -12875,6 +13239,17 @@ postcss-minify-selectors@^5.2.1: dependencies: postcss-selector-parser "^6.0.5" +postcss-mixins@6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/postcss-mixins/-/postcss-mixins-6.2.3.tgz#021893ba455d04b5baa052bf196297ddd70e4af1" + integrity sha512-gfH5d09YilzDn/CLGFA9Lwv7GTezuyHgnAyXC8AfvhUMpl67ZTewhcpNuOgawClCOD+76XePE2IHO1xMgsOlvA== + dependencies: + globby "^8.0.1" + postcss "^7.0.21" + postcss-js "^2.0.3" + postcss-simple-vars "^5.0.2" + sugarss "^2.0.0" + postcss-modules-extract-imports@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" @@ -12936,6 +13311,14 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" +postcss-nested@^4.2.1: + version "4.2.3" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-4.2.3.tgz#c6f255b0a720549776d220d00c4b70cd244136f6" + integrity sha512-rOv0W1HquRCamWy2kFl3QazJMMe1ku6rCFoAAH+9AcxdbpDeBr6k968MLWuLjvjMcGEip01ak09hKOEgpK9hvw== + dependencies: + postcss "^7.0.32" + postcss-selector-parser "^6.0.2" + postcss-normalize-charset@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed" @@ -13030,6 +13413,13 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-simple-vars@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-simple-vars/-/postcss-simple-vars-5.0.2.tgz#e2f81b3d0847ddd4169816b6d141b91d51e6e22e" + integrity sha512-xWIufxBoINJv6JiLb7jl5oElgp+6puJwvT5zZHliUSydoLz4DADRB3NDDsYgfKVwojn4TDLiseoC65MuS8oGGg== + dependencies: + postcss "^7.0.14" + postcss-svgo@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" @@ -13050,7 +13440,7 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^7.0.14, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: +postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.18, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: version "7.0.39" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== @@ -13122,6 +13512,14 @@ pretty-bytes@^5.3.0: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== +pretty-error@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6" + integrity sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw== + dependencies: + lodash "^4.17.20" + renderkid "^2.0.4" + pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" @@ -13253,6 +13651,11 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== + psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -13361,7 +13764,7 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== -queue-microtask@^1.2.2: +queue-microtask@^1.2.2, queue-microtask@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== @@ -13860,6 +14263,11 @@ regjsparser@^0.9.1: dependencies: jsesc "~0.5.0" +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + remove-bom-buffer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" @@ -13887,6 +14295,17 @@ remove-trailing-separator@^1.0.1: resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== +renderkid@^2.0.4: + version "2.0.7" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.7.tgz#464f276a6bdcee606f4a15993f9b29fc74ca8609" + integrity sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^3.0.1" + repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" @@ -14273,7 +14692,7 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== -"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -14323,6 +14742,13 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -14559,6 +14985,11 @@ sort-keys@^4.0.0, sort-keys@^4.2.0: dependencies: is-plain-obj "^2.0.0" +sorted-array@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sorted-array/-/sorted-array-2.0.4.tgz#5d62bbfe64d1bde3cf4b6b79530a6feb95afb5ae" + integrity sha512-58INzrX0rL6ttCfsGoFmOuQY5AjR6A5E/MmGKJ5JvWHOey6gOEOC6vO8K6C0Y2bQR6KJ8o8aFwHjp/mJ/HcYsQ== + source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -14606,7 +15037,7 @@ source-map@^0.5.6, source-map@^0.5.7: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -14850,6 +15281,15 @@ string.prototype.trimend@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimstart@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" @@ -14859,6 +15299,15 @@ string.prototype.trimstart@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -15010,6 +15459,13 @@ stylis@4.0.13: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91" integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag== +sugarss@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-2.0.0.tgz#ddd76e0124b297d40bf3cca31c8b22ecb43bc61d" + integrity sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ== + dependencies: + postcss "^7.0.2" + supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" @@ -15202,7 +15658,7 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@^4.1.2: +terser@^4.1.2, terser@^4.6.3: version "4.8.1" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f" integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw== @@ -15278,7 +15734,7 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" -timm@^1.7.1: +timm@^1.6.2, timm@^1.7.1: version "1.7.1" resolved "https://registry.yarnpkg.com/timm/-/timm-1.7.1.tgz#96bab60c7d45b5a10a8a4d0f0117c6b7e5aff76f" integrity sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw== @@ -15293,7 +15749,7 @@ tinycolor2@^1.4.1: resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803" integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA== -tmp@^0.0.33: +tmp@0.0.x, tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== @@ -15423,16 +15879,16 @@ treeverse@^1.0.4: resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-1.0.4.tgz#a6b0ebf98a1bca6846ddc7ecbc900df08cb9cd5f" integrity sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g== -ts-loader@^5.3.3: - version "5.4.5" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-5.4.5.tgz#a0c1f034b017a9344cef0961bfd97cc192492b8b" - integrity sha512-XYsjfnRQCBum9AMRZpk2rTYSVpdZBpZK+kDh0TeT3kxmQNBDVIeUjdPjY5RZry4eIAb8XHc4gYSUiUWPYvzSRw== +ts-loader@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.4.0.tgz#e845ea0f38d140bdc3d7d60293ca18d12ff2720f" + integrity sha512-6nFY3IZ2//mrPc+ImY3hNWx1vCHyEhl6V+wLmL4CZcm6g1CqX7UKrkc6y0i4FwcfOhxyMPCfaEvh20f4r9GNpw== dependencies: - chalk "^2.3.0" + chalk "^4.1.0" enhanced-resolve "^4.0.0" - loader-utils "^1.0.2" - micromatch "^3.1.4" - semver "^5.0.1" + loader-utils "^2.0.0" + micromatch "^4.0.0" + semver "^7.3.4" ts-node@^10.4.0, ts-node@^10.9.1: version "10.9.1" @@ -15796,6 +16252,15 @@ url-join@^4.0.0, url-join@^4.0.1: resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== +url-loader@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" + integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== + dependencies: + loader-utils "^2.0.0" + mime-types "^2.1.27" + schema-utils "^3.0.0" + url-parse@^1.5.3: version "1.5.10" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" @@ -15854,11 +16319,27 @@ user-home@^2.0.0: dependencies: os-homedir "^1.0.0" +useragent@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.3.0.tgz#217f943ad540cb2128658ab23fc960f6a88c9972" + integrity sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw== + dependencies: + lru-cache "4.1.x" + tmp "0.0.x" + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +util.promisify@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" @@ -15885,6 +16366,11 @@ util@^0.12.4: safe-buffer "^5.1.2" which-typed-array "^1.1.2" +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -16134,6 +16620,31 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +webext-redux@^2.1.7: + version "2.1.9" + resolved "https://registry.yarnpkg.com/webext-redux/-/webext-redux-2.1.9.tgz#f7fd01ea4b93191d07bcdd0db5966955766ef634" + integrity sha512-z7frkQ/avFgnMxUH6Q955hU8SDsHT9Zlq9az3OpY891RXw9nKODOTnUhNo9ZAlDFUPHhX2A+z6j74BCaYwEsfQ== + dependencies: + lodash.assignin "^4.2.0" + lodash.clonedeep "^4.5.0" + +webextension-polyfill-ts@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/webextension-polyfill-ts/-/webextension-polyfill-ts-0.21.0.tgz#de07984177b585d12e7189abfab9fb1e3fbfa048" + integrity sha512-x1Bbr4XVYcIUIQRNWulWHBIgKTJOhzfkcaaVraTOlvVqYiIz+pMAamrK6XKnOlWNZEybDa2DkZgidpriPSXFGw== + dependencies: + webextension-polyfill "^0.6.0" + +webextension-polyfill@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.5.0.tgz#795e0bf6a2b8eadcdb6edaecd169e9228c747519" + integrity sha512-aFrl38x43t1bTboX/paCT8I97+idzX/TY0+fuM52hrIkCpYfROEF9kSn0BXuEIi3J9LTYt2ZZKkhx9NB1qF3nA== + +webextension-polyfill@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.6.0.tgz#1afd925f3274a0d4848083579b9c0b649a5c6763" + integrity sha512-PlYwiX8e4bNZrEeBFxbFFsLtm0SMPxJliLTGdNCA0Bq2XkWrAn2ejUd+89vZm+8BnfFB1BclJyCz3iKsm2atNg== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -16166,7 +16677,22 @@ webpack-cli@^3.3.0: v8-compile-cache "^2.1.1" yargs "^13.3.2" -webpack-sources@^1.4.0, webpack-sources@^1.4.1: +webpack-extension-reloader@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/webpack-extension-reloader/-/webpack-extension-reloader-1.1.4.tgz#f5e5fa580e617c114cc45ddb6eb25c5d6a4dd2f6" + integrity sha512-PyssJvAiKhztc//QmhpU8yfg7LBR7Bn/cjSM7jadfQJPIDNN1Djxc+SJQRk8uHQ3GQbyWhsWu2DLCMBRcWHIPA== + dependencies: + "@types/webpack" "^4.39.8" + "@types/webpack-sources" "^0.1.5" + colors "^1.4.0" + lodash "^4.17.15" + minimist "^1.2.0" + useragent "^2.3.0" + webextension-polyfill "^0.5.0" + webpack-sources "^1.4.3" + ws "^7.2.0" + +webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== @@ -16391,7 +16917,7 @@ write-json-file@^4.1.1: sort-keys "^4.0.0" write-file-atomic "^3.0.0" -ws@^7.4.6, ws@^7.5.1: +ws@^7.2.0, ws@^7.4.6, ws@^7.5.1: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== @@ -16452,6 +16978,11 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"