From 2fd9ad903c29dd1b18ba0c1e165e70f006eaaad4 Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Wed, 9 Nov 2022 16:21:22 -0500 Subject: [PATCH 01/10] wip --- devtools/client/README.md | 2 +- devtools/client/src/index.ts | 2 +- devtools/client/src/redux/actions.ts | 68 +++++ devtools/client/src/redux/index.ts | 69 +---- devtools/client/src/redux/listeners.ts | 58 ++++ devtools/client/src/redux/state.ts | 109 ++++++++ devtools/client/src/rpc/index.ts | 1 + devtools/common/{src => }/README.md | 0 devtools/common/{src => src-old}/constants.ts | 0 devtools/common/src-old/index.ts | 8 + devtools/common/src-old/logger.ts | 13 + devtools/common/src-old/redux/actions.ts | 4 + .../common/{src => src-old}/redux/index.ts | 0 .../common/{src => src-old}/redux/reducers.ts | 0 .../{src => src-old}/redux/selectors.ts | 0 .../common/{src => src-old}/rpc/bridge.ts | 0 devtools/common/{src => src-old}/rpc/index.ts | 0 devtools/common/src-old/runtime/message.ts | 61 ++++ .../common/{src => src-old}/types/alias.ts | 0 .../common/{src => src-old}/types/index.ts | 0 .../common/{src => src-old}/types/state.ts | 0 devtools/common/src/actions.ts | 262 ++++++++++++++++++ devtools/common/src/events.ts | 141 ++++++++++ devtools/common/src/index.ts | 16 +- devtools/common/src/logger.ts | 14 + devtools/common/src/redux/actions.ts | 25 -- devtools/common/src/types.ts | 116 ++++++++ devtools/flipper/BUILD | 1 + devtools/flipper/src/index.tsx | 14 +- devtools/plugins/README.md | 4 + 30 files changed, 880 insertions(+), 108 deletions(-) create mode 100644 devtools/client/src/redux/listeners.ts create mode 100644 devtools/client/src/redux/state.ts rename devtools/common/{src => }/README.md (100%) rename devtools/common/{src => src-old}/constants.ts (100%) create mode 100644 devtools/common/src-old/index.ts create mode 100644 devtools/common/src-old/logger.ts create mode 100644 devtools/common/src-old/redux/actions.ts rename devtools/common/{src => src-old}/redux/index.ts (100%) rename devtools/common/{src => src-old}/redux/reducers.ts (100%) rename devtools/common/{src => src-old}/redux/selectors.ts (100%) rename devtools/common/{src => src-old}/rpc/bridge.ts (100%) rename devtools/common/{src => src-old}/rpc/index.ts (100%) create mode 100644 devtools/common/src-old/runtime/message.ts rename devtools/common/{src => src-old}/types/alias.ts (100%) rename devtools/common/{src => src-old}/types/index.ts (100%) rename devtools/common/{src => src-old}/types/state.ts (100%) create mode 100644 devtools/common/src/actions.ts create mode 100644 devtools/common/src/events.ts delete mode 100644 devtools/common/src/redux/actions.ts create mode 100644 devtools/common/src/types.ts create mode 100644 devtools/plugins/README.md diff --git a/devtools/client/README.md b/devtools/client/README.md index ae79c9c3..b104c110 100644 --- a/devtools/client/README.md +++ b/devtools/client/README.md @@ -1,3 +1,3 @@ # 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 state and consuming events and async actions from a devtools client, i.e. flipper plugin or web extension. diff --git a/devtools/client/src/index.ts b/devtools/client/src/index.ts index 52a13430..7452096b 100644 --- a/devtools/client/src/index.ts +++ b/devtools/client/src/index.ts @@ -1,3 +1,3 @@ export * from './redux'; export * from './rpc'; -export * from '@player-tools/devtools-common'; +// export { Runtime } from '@player-tools/devtools-common'; diff --git a/devtools/client/src/redux/actions.ts b/devtools/client/src/redux/actions.ts index 58a1ac64..681112c6 100644 --- a/devtools/client/src/redux/actions.ts +++ b/devtools/client/src/redux/actions.ts @@ -1,4 +1,11 @@ import { type AsyncThunk, createAsyncThunk } from '@reduxjs/toolkit'; +import { + createAsyncThunk, + AsyncThunk, + createAction, + PayloadAction, + ActionCreatorWithPayload, +} from '@reduxjs/toolkit'; import { Runtime, createLogger, @@ -16,6 +23,13 @@ export type AsyncRPCActions = { >; }; +export type EventActions = { + [key in Runtime.RuntimeEventTypes]: ActionCreatorWithPayload< + Extract, + key + >; +}; + export const buildRPCActions = ( handlers: RuntimeRPCRequestHandlers ): AsyncRPCActions => @@ -36,3 +50,57 @@ export const buildRPCActions = ( }); return acc; }, {} as AsyncRPCActions); + +// Actions +export const actions: EventActions = Runtime.RuntimeEventTypes.reduce( + (actions, event) => { + actions[event] = + createAction>( + event + ); + + return actions; + }, + {} as EventActions +); + +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'); + +export namespace Actions { + export const Events = Runtime.RuntimeEventTypes.reduce((actions, event) => { + actions[event] = + createAction>( + event + ); + + return actions; + }, {} as EventActions); + + +} + +export namespace Actions { + export namespace Console { + export const clear = createAction('console-clear'); + } +} diff --git a/devtools/client/src/redux/index.ts b/devtools/client/src/redux/index.ts index ba2d9369..033d5e39 100644 --- a/devtools/client/src/redux/index.ts +++ b/devtools/client/src/redux/index.ts @@ -1,70 +1,5 @@ -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 './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 './state'; diff --git a/devtools/client/src/redux/listeners.ts b/devtools/client/src/redux/listeners.ts new file mode 100644 index 00000000..ad7fc2c8 --- /dev/null +++ b/devtools/client/src/redux/listeners.ts @@ -0,0 +1,58 @@ +import { Message } from "@player-tools/devtools-common"; +import { Store } from "redux"; +import { Actions } from '.'; + + +export function handleMessage(store: Store, message: Message) { + switch (message.type) { + case 'runtime-init': + store.dispatch(clearStore()); + break; + case 'player-init': + store.dispatch(Actions.Events['player-init'](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; + } +} diff --git a/devtools/client/src/redux/state.ts b/devtools/client/src/redux/state.ts new file mode 100644 index 00000000..e4776c21 --- /dev/null +++ b/devtools/client/src/redux/state.ts @@ -0,0 +1,109 @@ +import { Flow, View } from '@player-ui/types'; +import { Runtime, ProfilerNode } from '@player-tools/devtools-common'; + +export type ActivePlayerState = { + /** + * state associated with player config + */ + configState?: Runtime.PlayerConfigRPC['result'] | null; + /** + * Flow related Information of the player. + */ + flowInfo?: Runtime.PlayerRuntimeInfoRPC['result'] | null; + /** + * A collection of all the events associated with the running player instance. + */ + timelineEvents: Array< + | Runtime.PlayerDataChangeEvent + | Runtime.PlayerLogEvent + | Runtime.PlayerFlowStartEvent + >; + /** + * View related information of the player. + */ + view?: View | undefined; + /** + * State assocaited with the data of the player + */ + dataState: DataState; + /** + * State associated with the console evaluations. + */ + consoleState: ConsoleState; + /** + * profiler related information of the player + */ + profilerInfo?: ProfilerNode | null; +}; + +export type PlayersState = { + /** + * This represents the currently selected player id. + */ + selectedPlayerId: string | null; + /** + * Collection of all the players active on the web page. + */ + activePlayers: Record; + /** + * Web Player version + */ + version: string; +}; + +export type StoreState = { + /** + * State related to all the players on the web page. + */ + players: PlayersState; +}; + +export type FlowInfoState = { + /** + * Current Flow Id. + */ + currentFlowID?: string; + /** + * Current Flow State + */ + currentFlowState?: string; + /** + * Current View Id. + */ + currentViewID?: string; + /** + * Current FLow + */ + currentFlow?: Flow; +}; + +export type DataState = { + /** + * The binding selected on the Data panel. + */ + selectedBinding?: Runtime.PlayerDataBindingRPC['result']; + /** + * All the bindings in the data state. + */ + allBindings?: Runtime.PlayerDataBindingRPC['result']; +}; + +export interface ConsoleState { + /** + * History of all the console evaluations + */ + history: Array<{ + /** + * Unique Id representing a Console evaluation. + */ + id: string; + /** + * Expression being evaluated. + */ + expression: string; + /** + * Result of the console evaluation. + */ + result: Runtime.PlayerExpressionRPC['result']; + }>; +} diff --git a/devtools/client/src/rpc/index.ts b/devtools/client/src/rpc/index.ts index f1140847..dbe96de3 100644 --- a/devtools/client/src/rpc/index.ts +++ b/devtools/client/src/rpc/index.ts @@ -10,6 +10,7 @@ export type RuntimeRPCRequestHandlers = { [key in Runtime.RuntimeRPCTypes]: RPCRequestHandler; }; +/** Builder for consistently handling RPC requests and responses */ export const buildRPCRequests = ( onRequestMessage: ( message: RPCRequestMessageEvent diff --git a/devtools/common/src/README.md b/devtools/common/README.md similarity index 100% rename from devtools/common/src/README.md rename to devtools/common/README.md diff --git a/devtools/common/src/constants.ts b/devtools/common/src-old/constants.ts similarity index 100% rename from devtools/common/src/constants.ts rename to devtools/common/src-old/constants.ts diff --git a/devtools/common/src-old/index.ts b/devtools/common/src-old/index.ts new file mode 100644 index 00000000..87548389 --- /dev/null +++ b/devtools/common/src-old/index.ts @@ -0,0 +1,8 @@ +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'; diff --git a/devtools/common/src-old/logger.ts b/devtools/common/src-old/logger.ts new file mode 100644 index 00000000..4f5a20e3 --- /dev/null +++ b/devtools/common/src-old/logger.ts @@ -0,0 +1,13 @@ +interface Logger { + /** Log a message */ + log: (...args: any[]) => void; +} + +/** Create a logger that tracks the script source */ +export function createLogger(source: string): Logger { + return { + log: (...args: any[]) => { + console.log(source, ...args); + }, + }; +} diff --git a/devtools/common/src-old/redux/actions.ts b/devtools/common/src-old/redux/actions.ts new file mode 100644 index 00000000..7aab0884 --- /dev/null +++ b/devtools/common/src-old/redux/actions.ts @@ -0,0 +1,4 @@ +import { createAction } from '@reduxjs/toolkit'; +import { Runtime } from '../types'; + + diff --git a/devtools/common/src/redux/index.ts b/devtools/common/src-old/redux/index.ts similarity index 100% rename from devtools/common/src/redux/index.ts rename to devtools/common/src-old/redux/index.ts diff --git a/devtools/common/src/redux/reducers.ts b/devtools/common/src-old/redux/reducers.ts similarity index 100% rename from devtools/common/src/redux/reducers.ts rename to devtools/common/src-old/redux/reducers.ts diff --git a/devtools/common/src/redux/selectors.ts b/devtools/common/src-old/redux/selectors.ts similarity index 100% rename from devtools/common/src/redux/selectors.ts rename to devtools/common/src-old/redux/selectors.ts 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/rpc/index.ts b/devtools/common/src-old/rpc/index.ts similarity index 100% rename from devtools/common/src/rpc/index.ts rename to devtools/common/src-old/rpc/index.ts diff --git a/devtools/common/src-old/runtime/message.ts b/devtools/common/src-old/runtime/message.ts new file mode 100644 index 00000000..c3381b45 --- /dev/null +++ b/devtools/common/src-old/runtime/message.ts @@ -0,0 +1,61 @@ +import { RPCFunctionCallback } from '../rpc'; +import { 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/alias.ts b/devtools/common/src-old/types/alias.ts similarity index 100% rename from devtools/common/src/types/alias.ts rename to devtools/common/src-old/types/alias.ts diff --git a/devtools/common/src/types/index.ts b/devtools/common/src-old/types/index.ts similarity index 100% rename from devtools/common/src/types/index.ts rename to devtools/common/src-old/types/index.ts diff --git a/devtools/common/src/types/state.ts b/devtools/common/src-old/types/state.ts similarity index 100% rename from devtools/common/src/types/state.ts rename to devtools/common/src-old/types/state.ts diff --git a/devtools/common/src/actions.ts b/devtools/common/src/actions.ts new file mode 100644 index 00000000..19cdb4d0 --- /dev/null +++ b/devtools/common/src/actions.ts @@ -0,0 +1,262 @@ +import { Binding, Flow, Schema, View } from '@player-ui/types'; +import { BaseRPCType, ProfilerNode } from './types'; + +// Bidirectional RPCs originating from the devtools client +export namespace Actions { + 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 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]; +} diff --git a/devtools/common/src/events.ts b/devtools/common/src/events.ts new file mode 100644 index 00000000..43d2b134 --- /dev/null +++ b/devtools/common/src/events.ts @@ -0,0 +1,141 @@ +import { Severity } from "@player-ui/logger"; +import { Binding, Flow, View } from "@player-ui/types"; +import { BaseEventMessage, BaseMessageWithPlayerID } from "."; +import { RUNTIME_SOURCE } from "./logger"; + +// Unidirectional events originating from the Player +export namespace Events { + 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 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]; +} diff --git a/devtools/common/src/index.ts b/devtools/common/src/index.ts index 87548389..0114280c 100644 --- a/devtools/common/src/index.ts +++ b/devtools/common/src/index.ts @@ -1,8 +1,12 @@ -export * from './redux'; +import { Actions as actions } from './actions'; +import { Events as events } from './events'; + 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 { Actions } from './actions'; + +export namespace Runtime { + export const Actions = actions; + export const Events = events; +} 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/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/types.ts b/devtools/common/src/types.ts new file mode 100644 index 00000000..5ef8d88e --- /dev/null +++ b/devtools/common/src/types.ts @@ -0,0 +1,116 @@ +import { Events } from './events'; +import { Actions } from './actions'; + +export interface BaseEventMessage< + T extends Events.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']; +} + +// TODO: Convert to RPC +export type Message = + | RPCRequestMessageEvent + | RPCResponseMessageEvent + | Events.RuntimeEvent; + + + +// 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/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..06d1dc0b 100644 --- a/devtools/flipper/src/index.tsx +++ b/devtools/flipper/src/index.tsx @@ -15,7 +15,7 @@ import { RuntimeRPCRequestHandlers, } 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'; +import { App } from '@player-tools/devtools-ui'; type Events = { [key in Runtime.RuntimeEventTypes]: Extract< @@ -29,14 +29,16 @@ type Events = { type Methods = { [key in Runtime.RuntimeRPCTypes]: ( payload: RPCRequestMessageEvent - ) => Promise; + ) => Promise>; }; export function plugin(client: PluginClient) { const rpcHandlers: RuntimeRPCRequestHandlers = buildRPCRequests( - (message: RPCRequestMessageEvent) => { + async (message: RPCRequestMessageEvent) => { // TODO: Do `send('rpc-request', message)` - client.send(message.rpcType, message); + const response = await client.send(message.rpcType, message); + // TODO: This doesn't currently work + rpcHandlers[message.rpcType].onMessage(response); } ); const actions = buildRPCActions(rpcHandlers); @@ -51,10 +53,6 @@ export function plugin(client: PluginClient) { }); }); - client.onMessage('rpc-response', (params) => { - rpcHandlers[params.rpcType].onMessage(params); - }); - 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. + From dfba8291e8ae41282a70e5b118ee6c1f9722197a Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Wed, 14 Dec 2022 19:21:04 -0500 Subject: [PATCH 02/10] wip 2: electric boogaloo --- devtools/client/src/redux/actions.ts | 105 +++++++++------------ devtools/client/src/redux/index.ts | 2 +- devtools/client/src/redux/listeners.ts | 36 +++---- devtools/client/src/redux/reducers.ts | 126 ++++++++++++++++++++++++- devtools/client/src/redux/state.ts | 18 ++-- devtools/client/src/rpc/index.ts | 8 +- devtools/common/src/actions.ts | 1 + devtools/common/src/events.ts | 6 ++ devtools/common/src/runtime/message.ts | 3 + devtools/common/src/types.ts | 2 - devtools/flipper/src/index.tsx | 3 + 11 files changed, 218 insertions(+), 92 deletions(-) diff --git a/devtools/client/src/redux/actions.ts b/devtools/client/src/redux/actions.ts index 681112c6..23ab1d27 100644 --- a/devtools/client/src/redux/actions.ts +++ b/devtools/client/src/redux/actions.ts @@ -1,7 +1,5 @@ import { type AsyncThunk, createAsyncThunk } from '@reduxjs/toolkit'; import { - createAsyncThunk, - AsyncThunk, createAction, PayloadAction, ActionCreatorWithPayload, @@ -10,22 +8,24 @@ import { Runtime, createLogger, BACKGROUND_SOURCE, + Actions, + Events, } 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'], + [key in Actions.RuntimeRPCTypes]: AsyncThunk< + Extract['result'], + Extract['params'], any >; }; export type EventActions = { - [key in Runtime.RuntimeEventTypes]: ActionCreatorWithPayload< - Extract, + [key in Events.RuntimeEvent["type"]]: ActionCreatorWithPayload< + Extract, key >; }; @@ -33,16 +33,16 @@ export type EventActions = { export const buildRPCActions = ( handlers: RuntimeRPCRequestHandlers ): AsyncRPCActions => - Runtime.RuntimeRPCTypes.reduce((acc, rpcType) => { + Actions.RuntimeRPCTypes.reduce((acc, rpcType) => { // TODO: Fix this // @ts-ignore acc[rpcType] = createAsyncThunk< - Extract['result'], - Extract['params'] + Extract['result'], + Extract['params'] >(rpcType, async (params) => { logger.log(`Requesting ${rpcType}`, params); const data = (await handlers[rpcType].call(params)) as Extract< - Runtime.RuntimeRPC, + Actions.RuntimeRPC, { type: typeof rpcType } >['result']; logger.log(`Response from ${rpcType}`, data); @@ -51,56 +51,45 @@ export const buildRPCActions = ( return acc; }, {} as AsyncRPCActions); -// Actions -export const actions: EventActions = Runtime.RuntimeEventTypes.reduce( - (actions, event) => { - actions[event] = - createAction>( - event - ); +export const eventActions: EventActions = Object.fromEntries(Events.RuntimeEventTypes.map(event => [event, createAction>(event)])) as EventActions - return actions; - }, - {} as EventActions -); +// 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') -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'); +// export namespace Actions { +// export const Events = Runtime.RuntimeEventTypes.reduce((actions, event) => { +// actions[event] = +// createAction>( +// event +// ); -export namespace Actions { - export const Events = Runtime.RuntimeEventTypes.reduce((actions, event) => { - actions[event] = - createAction>( - event - ); - - return actions; - }, {} as EventActions); +// return actions; +// }, {} as EventActions); -} +// } -export namespace Actions { - export namespace Console { - export const clear = createAction('console-clear'); - } -} +// export namespace Actions { +// export namespace Console { +// export const clear = createAction('console-clear'); +// } +// } diff --git a/devtools/client/src/redux/index.ts b/devtools/client/src/redux/index.ts index 033d5e39..2465601f 100644 --- a/devtools/client/src/redux/index.ts +++ b/devtools/client/src/redux/index.ts @@ -1,5 +1,5 @@ export * from './actions'; export * from './aliases'; -export * from './listeners'; +// export * from './listeners'; export * from './reducers'; export * from './state'; diff --git a/devtools/client/src/redux/listeners.ts b/devtools/client/src/redux/listeners.ts index ad7fc2c8..47bf2c8f 100644 --- a/devtools/client/src/redux/listeners.ts +++ b/devtools/client/src/redux/listeners.ts @@ -1,35 +1,39 @@ -import { Message } from "@player-tools/devtools-common"; +import { Events, Message } from "@player-tools/devtools-common"; +import { eventActions } from "./actions"; import { Store } from "redux"; -import { Actions } from '.'; - +import { GET_DATA_BINDING_DETAILS } from "./aliases"; export function handleMessage(store: Store, message: Message) { + // propagate message to default event handlers + const { type } = message; + if (type in Events.RuntimeEventTypes) { + eventActions[type as Events.RuntimeEventTypes](message as any) + } + + // other event handlers switch (message.type) { case 'runtime-init': store.dispatch(clearStore()); break; - case 'player-init': - store.dispatch(Actions.Events['player-init'](message)); - store.dispatch(selectedPlayerAction()); - break; case 'player-removed': - store.dispatch(playerRemoveAction(message.playerID)); + case 'player-init': store.dispatch(selectedPlayerAction()); break; case 'player-flow-start': - store.dispatch(playerFlowStartAction(message)); - store.dispatch(playerTimelineAction(message)); + // TODO: Can I map an action to another action? alias? + // store.dispatch(playerTimelineAction(message)); + // retrieve data model on start 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-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(); diff --git a/devtools/client/src/redux/reducers.ts b/devtools/client/src/redux/reducers.ts index e1a6b997..b51cba2a 100644 --- a/devtools/client/src/redux/reducers.ts +++ b/devtools/client/src/redux/reducers.ts @@ -1,6 +1,7 @@ import type { ActionReducerMapBuilder } from '@reduxjs/toolkit'; -import type { PlayersState } from '@player-tools/devtools-common'; -import type { AsyncRPCActions } from './actions'; +import type { PlayersState } from './state'; +import { AsyncRPCActions, eventActions } from './actions'; +import { Events } from '@player-tools/devtools-common'; /** * Callback function that adds cases for async actions for the player. @@ -10,6 +11,127 @@ import type { AsyncRPCActions } from './actions'; export const buildPlayerReducerCallback = (actions: AsyncRPCActions) => (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'], (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 = []; + }); + + + + builder.addCase( actions['player-runtime-info-request'].fulfilled, (state, action) => { diff --git a/devtools/client/src/redux/state.ts b/devtools/client/src/redux/state.ts index e4776c21..cfcbd255 100644 --- a/devtools/client/src/redux/state.ts +++ b/devtools/client/src/redux/state.ts @@ -1,22 +1,22 @@ import { Flow, View } from '@player-ui/types'; -import { Runtime, ProfilerNode } from '@player-tools/devtools-common'; +import { Runtime, Actions, Events, ProfilerNode } from '@player-tools/devtools-common'; export type ActivePlayerState = { /** * state associated with player config */ - configState?: Runtime.PlayerConfigRPC['result'] | null; + configState?: Actions.PlayerConfigRPC['result'] | null; /** * Flow related Information of the player. */ - flowInfo?: Runtime.PlayerRuntimeInfoRPC['result'] | null; + flowInfo?: Actions.PlayerRuntimeInfoRPC['result'] | null; /** * A collection of all the events associated with the running player instance. */ timelineEvents: Array< - | Runtime.PlayerDataChangeEvent - | Runtime.PlayerLogEvent - | Runtime.PlayerFlowStartEvent + | Events.PlayerDataChangeEvent + | Events.PlayerLogEvent + | Events.PlayerFlowStartEvent >; /** * View related information of the player. @@ -81,11 +81,11 @@ export type DataState = { /** * The binding selected on the Data panel. */ - selectedBinding?: Runtime.PlayerDataBindingRPC['result']; + selectedBinding?: Actions.PlayerDataBindingRPC['result']; /** * All the bindings in the data state. */ - allBindings?: Runtime.PlayerDataBindingRPC['result']; + allBindings?: Actions.PlayerDataBindingRPC['result']; }; export interface ConsoleState { @@ -104,6 +104,6 @@ export interface ConsoleState { /** * Result of the console evaluation. */ - result: Runtime.PlayerExpressionRPC['result']; + result: Actions.PlayerExpressionRPC['result']; }>; } diff --git a/devtools/client/src/rpc/index.ts b/devtools/client/src/rpc/index.ts index dbe96de3..0b861850 100644 --- a/devtools/client/src/rpc/index.ts +++ b/devtools/client/src/rpc/index.ts @@ -2,21 +2,21 @@ import { type RPCRequestMessageEvent, type RPCRequestHandler, createRPCRequest, - Runtime, + Actions, PANEL_SOURCE, } from '@player-tools/devtools-common'; export type RuntimeRPCRequestHandlers = { - [key in Runtime.RuntimeRPCTypes]: RPCRequestHandler; + [key in Actions.RuntimeRPCTypes]: RPCRequestHandler; }; /** Builder for consistently handling RPC requests and responses */ export const buildRPCRequests = ( onRequestMessage: ( - message: RPCRequestMessageEvent + message: RPCRequestMessageEvent ) => void ): RuntimeRPCRequestHandlers => - Runtime.RuntimeRPCTypes.reduce((acc, rpcType) => { + Actions.RuntimeRPCTypes.reduce((acc, rpcType) => { acc[rpcType] = createRPCRequest(rpcType, PANEL_SOURCE, onRequestMessage); return acc; }, {} as RuntimeRPCRequestHandlers); diff --git a/devtools/common/src/actions.ts b/devtools/common/src/actions.ts index 19cdb4d0..51cb67f1 100644 --- a/devtools/common/src/actions.ts +++ b/devtools/common/src/actions.ts @@ -1,6 +1,7 @@ import { Binding, Flow, Schema, View } from '@player-ui/types'; import { BaseRPCType, ProfilerNode } from './types'; +// TODO: Settle on actions vs RPC nomenclature // Bidirectional RPCs originating from the devtools client export namespace Actions { export interface PlayerConfigRPC diff --git a/devtools/common/src/events.ts b/devtools/common/src/events.ts index 43d2b134..e9620f80 100644 --- a/devtools/common/src/events.ts +++ b/devtools/common/src/events.ts @@ -123,6 +123,8 @@ export namespace Events { | PlayerLogEvent | PlayerViewUpdateEvent | PlayerFlowStartEvent + | PlayerFlowTransitionEvent + | PlayerFlowEndEvent | RuntimeInitEvent; export const RuntimeEventTypes = [ @@ -137,5 +139,9 @@ export namespace Events { 'player-flow-end', ] as const; + // export const RuntimeEventTypes2 = [ + // key in RuntimeEventTypes["type"] + // ] + export type RuntimeEventTypes = typeof RuntimeEventTypes[number]; } diff --git a/devtools/common/src/runtime/message.ts b/devtools/common/src/runtime/message.ts index d4eb8f31..a71705d3 100644 --- a/devtools/common/src/runtime/message.ts +++ b/devtools/common/src/runtime/message.ts @@ -1,6 +1,9 @@ import type { RPCFunctionCallback } from '../rpc'; import type { Runtime } from '../types'; + +// TODO: Not sure about these types staying in common + export type RuntimeEventWithoutSource = | Omit | Omit diff --git a/devtools/common/src/types.ts b/devtools/common/src/types.ts index 5ef8d88e..f6719cdf 100644 --- a/devtools/common/src/types.ts +++ b/devtools/common/src/types.ts @@ -83,8 +83,6 @@ export type Message = | RPCResponseMessageEvent | Events.RuntimeEvent; - - // TODO: export via plugins export type ProfilerNode = { /** diff --git a/devtools/flipper/src/index.tsx b/devtools/flipper/src/index.tsx index 06d1dc0b..29310212 100644 --- a/devtools/flipper/src/index.tsx +++ b/devtools/flipper/src/index.tsx @@ -33,6 +33,7 @@ type Methods = { }; export function plugin(client: PluginClient) { + // Build RPC handlers (delegation to plugin for how to handle communications to hook up to redux) const rpcHandlers: RuntimeRPCRequestHandlers = buildRPCRequests( async (message: RPCRequestMessageEvent) => { // TODO: Do `send('rpc-request', message)` @@ -41,6 +42,8 @@ export function plugin(client: PluginClient) { rpcHandlers[message.rpcType].onMessage(response); } ); + + // Build redux actions to hook up const actions = buildRPCActions(rpcHandlers); const store = createStore( buildAliases(actions), From a79b8656416feffa874af15ef2140d03e177e62f Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Thu, 15 Dec 2022 23:28:51 -0500 Subject: [PATCH 03/10] restructure event and method definitions --- devtools/client/src/index.ts | 1 - devtools/client/src/redux/actions.ts | 139 +++-- devtools/client/src/redux/aliases.ts | 42 +- devtools/client/src/redux/index.ts | 4 +- devtools/client/src/redux/listeners.ts | 60 +-- devtools/client/src/redux/middleware.ts | 63 +++ devtools/client/src/redux/reducers.ts | 392 +++++++------- .../src-old => client/src}/redux/selectors.ts | 32 +- devtools/client/src/redux/state.ts | 18 +- devtools/client/src/redux/store.ts | 49 ++ devtools/client/src/rpc/index.ts | 22 - devtools/common/src-old/constants.ts | 12 - devtools/common/src-old/index.ts | 8 - devtools/common/src-old/logger.ts | 13 - devtools/common/src-old/redux/actions.ts | 4 - devtools/common/src-old/redux/index.ts | 3 - devtools/common/src-old/redux/reducers.ts | 199 -------- devtools/common/src-old/runtime/message.ts | 61 --- devtools/common/src-old/types/alias.ts | 50 -- devtools/common/src-old/types/index.ts | 478 ------------------ devtools/common/src-old/types/state.ts | 137 ----- devtools/common/src/events.ts | 24 +- devtools/common/src/index.ts | 12 +- .../common/src/{actions.ts => methods.ts} | 70 ++- devtools/common/src/runtime/message.ts | 30 +- devtools/common/src/types.ts | 37 +- devtools/flipper/src/index.tsx | 68 +-- devtools/ui/src/components/config/Config.tsx | 6 +- .../ui/src/components/config/ConfigPanel.tsx | 13 +- .../src/components/console/ConsolePanel.tsx | 14 +- devtools/ui/src/components/data/Data.tsx | 14 +- devtools/ui/src/components/data/DataPanel.tsx | 12 +- devtools/ui/src/components/events/Events.tsx | 13 +- .../ui/src/components/events/EventsPanel.tsx | 8 +- devtools/ui/src/components/flow/FlowPanel.tsx | 8 +- devtools/ui/src/components/info/Info.tsx | 4 +- devtools/ui/src/components/info/InfoPanel.tsx | 16 +- .../ui/src/components/profiler/Profiler.tsx | 4 +- .../src/components/profiler/ProfilerPanel.tsx | 10 +- devtools/ui/src/components/sidebar.tsx | 17 +- .../ui/src/components/view/ViewInspector.tsx | 4 +- devtools/ui/src/components/view/ViewPanel.tsx | 10 +- 42 files changed, 583 insertions(+), 1598 deletions(-) create mode 100644 devtools/client/src/redux/middleware.ts rename devtools/{common/src-old => client/src}/redux/selectors.ts (66%) create mode 100644 devtools/client/src/redux/store.ts delete mode 100644 devtools/client/src/rpc/index.ts delete mode 100644 devtools/common/src-old/constants.ts delete mode 100644 devtools/common/src-old/index.ts delete mode 100644 devtools/common/src-old/logger.ts delete mode 100644 devtools/common/src-old/redux/actions.ts delete mode 100644 devtools/common/src-old/redux/index.ts delete mode 100644 devtools/common/src-old/redux/reducers.ts delete mode 100644 devtools/common/src-old/runtime/message.ts delete mode 100644 devtools/common/src-old/types/alias.ts delete mode 100644 devtools/common/src-old/types/index.ts delete mode 100644 devtools/common/src-old/types/state.ts rename devtools/common/src/{actions.ts => methods.ts} (75%) diff --git a/devtools/client/src/index.ts b/devtools/client/src/index.ts index 7452096b..d8944f87 100644 --- a/devtools/client/src/index.ts +++ b/devtools/client/src/index.ts @@ -1,3 +1,2 @@ export * from './redux'; -export * from './rpc'; // export { Runtime } from '@player-tools/devtools-common'; diff --git a/devtools/client/src/redux/actions.ts b/devtools/client/src/redux/actions.ts index 23ab1d27..548c6b45 100644 --- a/devtools/client/src/redux/actions.ts +++ b/devtools/client/src/redux/actions.ts @@ -1,95 +1,82 @@ -import { type AsyncThunk, createAsyncThunk } from '@reduxjs/toolkit'; +import { type AsyncThunk, createAsyncThunk, AnyAction } from '@reduxjs/toolkit'; import { createAction, - PayloadAction, ActionCreatorWithPayload, } from '@reduxjs/toolkit'; import { - Runtime, createLogger, BACKGROUND_SOURCE, - Actions, - Events, + // TODO: This is where being able to import the `Runtime` namespace is beneficial + Methods as RuntimeMethods, + Events as RuntimeEvents, } from '@player-tools/devtools-common'; -import type { RuntimeRPCRequestHandlers } from '../rpc'; const logger = createLogger(BACKGROUND_SOURCE); -export type AsyncRPCActions = { - [key in Actions.RuntimeRPCTypes]: AsyncThunk< - Extract['result'], - Extract['params'], - any - >; -}; +export namespace Methods { -export type EventActions = { - [key in Events.RuntimeEvent["type"]]: ActionCreatorWithPayload< - Extract, - key - >; -}; + /** Type describing an object containing async thunks for each Method defined */ + export type MethodThunks = { + [key in RuntimeMethods.Method["type"]]: AsyncThunk< + RuntimeMethods.ByType['result'], + RuntimeMethods.ByType, + any + >; + }; -export const buildRPCActions = ( - handlers: RuntimeRPCRequestHandlers -): AsyncRPCActions => - Actions.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< - Actions.RuntimeRPC, - { type: typeof rpcType } - >['result']; - logger.log(`Response from ${rpcType}`, data); - return data; - }); - return acc; - }, {} as AsyncRPCActions); + export type MethodHandler = ( + method: RuntimeMethods.ByType + ) => Promise['result']>; -export const eventActions: EventActions = Object.fromEntries(Events.RuntimeEventTypes.map(event => [event, createAction>(event)])) as EventActions + export const buildAsyncThunks = ( + onMethodRequest: MethodHandler + ): MethodThunks => Object.fromEntries( + RuntimeMethods.MethodTypes.map(method => + [method, createAsyncThunk< + RuntimeMethods.ByType['result'], + RuntimeMethods.ByType + >(method, async (method) => { + logger.log(`Requesting ${method.type}`, method.params); + const data = (await onMethodRequest(method)) as + RuntimeMethods.ByType['result']; + logger.log(`Response from ${method.type}`, data); + return data; + })] + ) + ) as MethodThunks +} -// 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') +// TODO: What the hell should we do here? merged namespace for Events or new namespace? +export namespace Events { -// export namespace Actions { -// export const Events = Runtime.RuntimeEventTypes.reduce((actions, event) => { -// actions[event] = -// createAction>( -// event -// ); + /** Redux actions associated against all possible event types */ + type EventActions = { + [key in RuntimeEvents.EventTypes]: ActionCreatorWithPayload< + RuntimeEvents.ByType, + key + >; + }; -// return actions; -// }, {} as EventActions); + export interface EventAction extends AnyAction { + payload: RuntimeEvents.ByType; + } - -// } + /** Redux actions associated against all defined event types */ + export const actions: EventActions = Object.fromEntries( + RuntimeEvents.EventTypes.map(event => + [event, createAction>(event)] + ) + ) as EventActions +} -// export namespace Actions { -// export namespace Console { -// export const clear = createAction('console-clear'); -// } -// } +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/aliases.ts b/devtools/client/src/redux/aliases.ts index 2eb1305d..a982f9eb 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 type { Methods as RuntimeMethods } from '@player-tools/devtools-common'; +import { Methods } from './actions'; 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'; + +export interface MethodAction { + payload: RuntimeMethods.ByType; +} + +// 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,15 @@ const _alias = (aliases: any) => () => (next: any) => (action: any) => { return next(action); }; -export const buildAliases = (actions: AsyncRPCActions) => +const alias = (alias: T, methods: Methods.MethodThunks) => (action: MethodAction) => methods[alias](action.payload) + +export const buildAliases = (methods: 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 2465601f..2354615b 100644 --- a/devtools/client/src/redux/index.ts +++ b/devtools/client/src/redux/index.ts @@ -1,5 +1,7 @@ export * from './actions'; export * from './aliases'; -// export * from './listeners'; +export * from './listeners'; export * from './reducers'; +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 index 47bf2c8f..1592aae3 100644 --- a/devtools/client/src/redux/listeners.ts +++ b/devtools/client/src/redux/listeners.ts @@ -1,62 +1,10 @@ import { Events, Message } from "@player-tools/devtools-common"; -import { eventActions } from "./actions"; -import { Store } from "redux"; -import { GET_DATA_BINDING_DETAILS } from "./aliases"; +import { Events as EventActions } from "./actions"; -export function handleMessage(store: Store, message: Message) { +export function handleMessage(message: Message) { // propagate message to default event handlers const { type } = message; - if (type in Events.RuntimeEventTypes) { - eventActions[type as Events.RuntimeEventTypes](message as any) - } - - // other event handlers - switch (message.type) { - case 'runtime-init': - store.dispatch(clearStore()); - break; - case 'player-removed': - case 'player-init': - store.dispatch(selectedPlayerAction()); - break; - case 'player-flow-start': - // TODO: Can I map an action to another action? alias? - // store.dispatch(playerTimelineAction(message)); - // retrieve data model on start - 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; + if (type in Events.EventTypes) { + EventActions.actions[type as Events.EventTypes](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..e29852f6 --- /dev/null +++ b/devtools/client/src/redux/middleware.ts @@ -0,0 +1,63 @@ +import { StoreState } from "@player-tools/devtools-client"; +import { createListenerMiddleware, isAnyOf } from "@reduxjs/toolkit"; +import { Actions, Events } from "./actions"; +import { GET_DATA_BINDING_DETAILS } from "./aliases"; + +export const listenerMiddleware = createListenerMiddleware(); + +listenerMiddleware.startListening({ + matcher: isAnyOf( + Events.actions['player-data-change-event'], + Events.actions['player-log-event'], + Events.actions['player-flow-start'], + // TODO: I don't actually think this _was_ included + Events.actions['player-view-update-event'] + ), + effect: (action, api) => { + api.dispatch(Actions['player-timeline-event'](action.payload)); + }, +}); + +listenerMiddleware.startListening({ + actionCreator: Events.actions['runtime-init'], + effect: (_, api) => { + api.dispatch(Actions["clear-store"]()) + } +}) + +listenerMiddleware.startListening({ + matcher: isAnyOf( + Events.actions["player-init"], + Events.actions["player-removed"], + ), + effect: (_, api) => { + api.dispatch(Actions["selected-player"]()) + } +}); + +listenerMiddleware.startListening({ + matcher: isAnyOf( + Events.actions["player-flow-start"], + Events.actions["player-data-change-event"], + ), + effect: (action, api) => { + // TODO: Just appropriately type the middleware + const { players } = api.getState() as StoreState; + 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 b51cba2a..7cb46c68 100644 --- a/devtools/client/src/redux/reducers.ts +++ b/devtools/client/src/redux/reducers.ts @@ -1,240 +1,248 @@ -import type { ActionReducerMapBuilder } from '@reduxjs/toolkit'; +import { ActionReducerMapBuilder, createReducer } from '@reduxjs/toolkit'; import type { PlayersState } from './state'; -import { AsyncRPCActions, eventActions } from './actions'; -import { Events } from '@player-tools/devtools-common'; - -/** - * Callback function that adds cases for async actions for the player. - * @param playerReducerCallback - * @returns - */ -export const buildPlayerReducerCallback = - (actions: AsyncRPCActions) => - (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]; - }); +import { Actions, Events, Methods } from './actions'; + +const initialState = { + selectedPlayerId: null, + activePlayers: {}, +} as PlayersState; + +// TODO: It'd be nice if methodThunks didn't have to be passed in - but it is kinda client dependent +export const methodsReducer = (methods: Methods.MethodThunks) => (builder: ActionReducerMapBuilder) => { + builder.addCase( + methods['player-runtime-info-request'].fulfilled, + (state, action) => { + const { activePlayers, selectedPlayerId } = state; - builder.addCase(eventActions['player'], (state, action) => { - if (action.payload) { - state.selectedPlayerId = action.payload; + if (!selectedPlayerId) { return; } - state.selectedPlayerId = Object.keys(state.activePlayers)[0] || null; - }); - - builder.addCase(playerFlowStartAction, (state, action) => { - const { - payload: { flow, playerID }, - } = action; + 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 (!state.activePlayers[playerID]) { - state.activePlayers[playerID] = { - flowInfo: { currentFlow: flow }, - timelineEvents: [], - dataState: {}, - consoleState: { history: [] }, - }; - state.selectedPlayerId = playerID; + if (!selectedPlayerId) { return; } - state.activePlayers[playerID].flowInfo = { - ...state.activePlayers[playerID].flowInfo, - currentFlow: flow, - }; - }); + activePlayers[selectedPlayerId].configState = action.payload; + } + ); - builder.addCase(playerTimelineAction, (state, action) => { - const { - payload: { playerID }, - } = action; + builder.addCase( + methods['player-view-details-request'].fulfilled, + (state, action) => { + const { activePlayers, selectedPlayerId } = state; - if (!state.activePlayers[playerID]) { - state.activePlayers[playerID] = { - timelineEvents: [action.payload], - dataState: {}, - consoleState: { history: [] }, - }; - state.selectedPlayerId = playerID; + if (!selectedPlayerId) { return; } - state.activePlayers[playerID].timelineEvents.push(action.payload); - }); + activePlayers[selectedPlayerId].view = action.payload?.lastViewUpdate; + } + ); - builder.addCase(playerViewUpdateAction, (state, action) => { + builder.addCase( + methods['player-data-binding-details'].fulfilled, + (state, action) => { const { - payload: { playerID, update }, + meta: { + arg: { params: { binding, playerID } }, + }, + payload, } = action; + const { activePlayers } = state; - if (!state.activePlayers[playerID]) { - state.activePlayers[playerID] = { - view: update, - timelineEvents: [], - dataState: {}, - consoleState: { history: [] }, - }; - state.selectedPlayerId = playerID; + if (!playerID || !activePlayers[playerID]) { return; } - state.activePlayers[playerID].view = update; - }); - - builder.addCase(clearSelectedDataDetails, (state) => { - const { activePlayers, selectedPlayerId } = state; - - if (!selectedPlayerId || !activePlayers[selectedPlayerId]) { + if (binding === '') { + activePlayers[playerID].dataState.allBindings = payload; return; } - activePlayers[selectedPlayerId].dataState.selectedBinding = undefined; - }); + activePlayers[playerID].dataState.selectedBinding = payload; + } + ); - builder.addCase(consoleClearAction, (state) => { + builder.addCase( + methods['player-execute-expression'].fulfilled, + (state, action) => { const { activePlayers, selectedPlayerId } = state; if (!selectedPlayerId) { return; } - activePlayers[selectedPlayerId].consoleState = { - history: [], - }; - }); - builder.addCase(clearStore, () => { - return initialState; - }); - builder.addCase(logsClearAction, (state) => { + 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].timelineEvents = []; - }); - + if (!selectedPlayerId) return; + activePlayers[selectedPlayerId].profilerInfo = action.payload?.data; + } + ); + builder.addCase( + methods['player-stop-profiler-request'].fulfilled, + (state, action) => { + const { activePlayers, selectedPlayerId } = state; - 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; - } - ); + if (!selectedPlayerId) return; + + activePlayers[selectedPlayerId].profilerInfo = action.payload?.data; + } + ); +}; + +export const eventsReducer = (builder: ActionReducerMapBuilder) => { + builder.addCase(Events.actions['player-init'], (state, action) => { + const { + payload: { version, playerID }, + } = action; + state.activePlayers[playerID] = { + timelineEvents: [], + dataState: {}, + consoleState: { history: [] }, + }; + state.version = version; + }); + + builder.addCase(Events.actions['player-removed'], (state, action) => { + delete state.activePlayers[action.payload.playerID]; + }); + + builder.addCase(Events.actions['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(Events.actions['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; + }); +}; + +export 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; + } - builder.addCase( - actions['player-config-request'].fulfilled, - (state, action) => { - const { activePlayers, selectedPlayerId } = state; + state.activePlayers[playerID].timelineEvents.push(action.payload); + }); - if (!selectedPlayerId) { - return; - } + builder.addCase(Actions['clear-selected-data-details'], (state) => { + const { activePlayers, selectedPlayerId } = state; - activePlayers[selectedPlayerId].configState = action.payload; - } - ); + if (!selectedPlayerId || !activePlayers[selectedPlayerId]) { + return; + } - builder.addCase( - actions['player-view-details-request'].fulfilled, - (state, action) => { - const { activePlayers, selectedPlayerId } = state; + activePlayers[selectedPlayerId].dataState.selectedBinding = undefined; + }); - if (!selectedPlayerId) { - return; - } + builder.addCase(Actions['clear-console'], (state) => { + 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; + activePlayers[selectedPlayerId].consoleState = { + history: [], + }; + }); - if (!selectedPlayerId) return; + builder.addCase(Actions['clear-logs'], (state) => { + const { activePlayers, selectedPlayerId } = state; - activePlayers[selectedPlayerId].profilerInfo = action.payload?.data; - } - ); + if (!selectedPlayerId) { + return; + } - builder.addCase( - actions['player-stop-profiler-request'].fulfilled, - (state, action) => { - const { activePlayers, selectedPlayerId } = state; + activePlayers[selectedPlayerId].timelineEvents = []; + }); - if (!selectedPlayerId) return; + builder.addCase(Actions['clear-store'], () => { + return initialState; + }); +}; - activePlayers[selectedPlayerId].profilerInfo = action.payload?.data; - } - ); - }; +export const playersReducer = (methods: Methods.MethodThunks) => + createReducer(initialState, (builder) => { + actionsReducer(builder) + eventsReducer(builder) + methodsReducer(methods)(builder) + }); diff --git a/devtools/common/src-old/redux/selectors.ts b/devtools/client/src/redux/selectors.ts similarity index 66% rename from devtools/common/src-old/redux/selectors.ts rename to devtools/client/src/redux/selectors.ts index a634f27c..281d4323 100644 --- a/devtools/common/src-old/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/client/src/redux/state.ts b/devtools/client/src/redux/state.ts index cfcbd255..f6c5820f 100644 --- a/devtools/client/src/redux/state.ts +++ b/devtools/client/src/redux/state.ts @@ -1,23 +1,19 @@ import { Flow, View } from '@player-ui/types'; -import { Runtime, Actions, Events, ProfilerNode } from '@player-tools/devtools-common'; +import { Methods, Events, ProfilerNode } from '@player-tools/devtools-common'; export type ActivePlayerState = { /** * state associated with player config */ - configState?: Actions.PlayerConfigRPC['result'] | null; + configState?: Methods.PlayerConfigMethod['result'] | null; /** * Flow related Information of the player. */ - flowInfo?: Actions.PlayerRuntimeInfoRPC['result'] | null; + flowInfo?: Methods.PlayerRuntimeInfoMethod['result'] | null; /** * A collection of all the events associated with the running player instance. */ - timelineEvents: Array< - | Events.PlayerDataChangeEvent - | Events.PlayerLogEvent - | Events.PlayerFlowStartEvent - >; + timelineEvents: Array; /** * View related information of the player. */ @@ -81,11 +77,11 @@ export type DataState = { /** * The binding selected on the Data panel. */ - selectedBinding?: Actions.PlayerDataBindingRPC['result']; + selectedBinding?: Methods.PlayerDataBindingMethod['result']; /** * All the bindings in the data state. */ - allBindings?: Actions.PlayerDataBindingRPC['result']; + allBindings?: Methods.PlayerDataBindingMethod['result']; }; export interface ConsoleState { @@ -104,6 +100,6 @@ export interface ConsoleState { /** * Result of the console evaluation. */ - result: Actions.PlayerExpressionRPC['result']; + result: Methods.PlayerExpressionMethod['result']; }>; } diff --git a/devtools/client/src/redux/store.ts b/devtools/client/src/redux/store.ts new file mode 100644 index 00000000..1674df45 --- /dev/null +++ b/devtools/client/src/redux/store.ts @@ -0,0 +1,49 @@ +import { + type AnyAction, + type Dispatch, + type Middleware, + type MiddlewareArray, + type ActionReducerMapBuilder, + type EnhancedStore, + configureStore, + createReducer, + combineReducers, + createAsyncThunk, +} from '@reduxjs/toolkit'; +import { Message } from '@player-tools/devtools-common'; +import { PlayersState, StoreState } from './state'; +import { Methods } from './actions'; +import { playersReducer } from './reducers'; +import { listenerMiddleware } from './middleware'; + +/** + * 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 additionalReducers Additional reducers to be added to the store. Optional + * @returns + */ +export const createDevtoolsStore = ( + onMethodRequest: Methods.MethodHandler, + middleware?: Middleware>, + additionalReducers?: any, +): EnhancedStore< + StoreState, + any, + Middleware>[] +> => + configureStore({ + reducer: { + // TODO: Look into slices + players: playersReducer(Methods.buildAsyncThunks(onMethodRequest)), + ...additionalReducers, + }, + middleware: (getDefaultMiddleware) => { + // TODO: Potentially hook up our own middleware for dispatching additional actions from event actions + const m = getDefaultMiddleware() + .concat(listenerMiddleware.middleware) + + if (middleware) m.prepend(middleware) + + return m + } + }); diff --git a/devtools/client/src/rpc/index.ts b/devtools/client/src/rpc/index.ts deleted file mode 100644 index 0b861850..00000000 --- a/devtools/client/src/rpc/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - type RPCRequestMessageEvent, - type RPCRequestHandler, - createRPCRequest, - Actions, - PANEL_SOURCE, -} from '@player-tools/devtools-common'; - -export type RuntimeRPCRequestHandlers = { - [key in Actions.RuntimeRPCTypes]: RPCRequestHandler; -}; - -/** Builder for consistently handling RPC requests and responses */ -export const buildRPCRequests = ( - onRequestMessage: ( - message: RPCRequestMessageEvent - ) => void -): RuntimeRPCRequestHandlers => - Actions.RuntimeRPCTypes.reduce((acc, rpcType) => { - acc[rpcType] = createRPCRequest(rpcType, PANEL_SOURCE, onRequestMessage); - return acc; - }, {} as RuntimeRPCRequestHandlers); diff --git a/devtools/common/src-old/constants.ts b/devtools/common/src-old/constants.ts deleted file mode 100644 index 0c60f6fe..00000000 --- a/devtools/common/src-old/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-old/index.ts b/devtools/common/src-old/index.ts deleted file mode 100644 index 87548389..00000000 --- a/devtools/common/src-old/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -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'; diff --git a/devtools/common/src-old/logger.ts b/devtools/common/src-old/logger.ts deleted file mode 100644 index 4f5a20e3..00000000 --- a/devtools/common/src-old/logger.ts +++ /dev/null @@ -1,13 +0,0 @@ -interface Logger { - /** Log a message */ - log: (...args: any[]) => void; -} - -/** Create a logger that tracks the script source */ -export function createLogger(source: string): Logger { - return { - log: (...args: any[]) => { - console.log(source, ...args); - }, - }; -} diff --git a/devtools/common/src-old/redux/actions.ts b/devtools/common/src-old/redux/actions.ts deleted file mode 100644 index 7aab0884..00000000 --- a/devtools/common/src-old/redux/actions.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { createAction } from '@reduxjs/toolkit'; -import { Runtime } from '../types'; - - diff --git a/devtools/common/src-old/redux/index.ts b/devtools/common/src-old/redux/index.ts deleted file mode 100644 index 1f364263..00000000 --- a/devtools/common/src-old/redux/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './actions'; -export * from './reducers'; -export * from './selectors'; diff --git a/devtools/common/src-old/redux/reducers.ts b/devtools/common/src-old/redux/reducers.ts deleted file mode 100644 index 971832bf..00000000 --- a/devtools/common/src-old/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-old/runtime/message.ts b/devtools/common/src-old/runtime/message.ts deleted file mode 100644 index c3381b45..00000000 --- a/devtools/common/src-old/runtime/message.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { RPCFunctionCallback } from '../rpc'; -import { 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-old/types/alias.ts b/devtools/common/src-old/types/alias.ts deleted file mode 100644 index f37001eb..00000000 --- a/devtools/common/src-old/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-old/types/index.ts b/devtools/common/src-old/types/index.ts deleted file mode 100644 index 2a2ce3e9..00000000 --- a/devtools/common/src-old/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-old/types/state.ts b/devtools/common/src-old/types/state.ts deleted file mode 100644 index 5e8efd47..00000000 --- a/devtools/common/src-old/types/state.ts +++ /dev/null @@ -1,137 +0,0 @@ -import type { Flow, View } from '@player-ui/types'; -import type { Runtime } from '.'; - -export type ActivePlayerState = { - /** - * state associated with player config - */ - configState?: Runtime.PlayerConfigRPC['result'] | null; - /** - * Flow related Information of the player. - */ - flowInfo?: Runtime.PlayerRuntimeInfoRPC['result'] | null; - /** - * A collection of all the events associated with the running player instance. - */ - timelineEvents: Array< - | Runtime.PlayerDataChangeEvent - | Runtime.PlayerLogEvent - | Runtime.PlayerFlowStartEvent - >; - /** - * View related information of the player. - */ - view?: View | undefined; - /** - * State assocaited with the data of the player - */ - dataState: DataState; - /** - * State associated with the console evaluations. - */ - consoleState: ConsoleState; - /** - * profiler related information of the player - */ - profilerInfo?: ProfilerNode | null; -}; - -export type PlayersState = { - /** - * This represents the currently selected player id. - */ - selectedPlayerId: string | null; - /** - * Collection of all the players active on the web page. - */ - activePlayers: Record; - /** - * Web Player version - */ - version: string; -}; - -export type StoreState = { - /** - * State related to all the players on the web page. - */ - players: PlayersState; -}; - -export type FlowInfoState = { - /** - * Current Flow Id. - */ - currentFlowID?: string; - /** - * Current Flow State - */ - currentFlowState?: string; - /** - * Current View Id. - */ - currentViewID?: string; - /** - * Current FLow - */ - currentFlow?: Flow; -}; - -export type DataState = { - /** - * The binding selected on the Data panel. - */ - selectedBinding?: Runtime.PlayerDataBindingRPC['result']; - /** - * All the bindings in the data state. - */ - allBindings?: Runtime.PlayerDataBindingRPC['result']; -}; - -export interface ConsoleState { - /** - * History of all the console evaluations - */ - history: Array<{ - /** - * Unique Id representing a Console evaluation. - */ - id: string; - /** - * Expression being evaluated. - */ - expression: string; - /** - * Result of the console evaluation. - */ - result: Runtime.PlayerExpressionRPC['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/common/src/events.ts b/devtools/common/src/events.ts index e9620f80..ddf1c3b1 100644 --- a/devtools/common/src/events.ts +++ b/devtools/common/src/events.ts @@ -1,11 +1,12 @@ import { Severity } from "@player-ui/logger"; import { Binding, Flow, View } from "@player-ui/types"; -import { BaseEventMessage, BaseMessageWithPlayerID } from "."; +import { BaseEventMessage, BaseMessageWithPlayerID, DiscriminateByType } from "."; import { RUNTIME_SOURCE } from "./logger"; +// TODO: Maybe reverse the pluralness // Unidirectional events originating from the Player export namespace Events { - interface PlayerTimelineEvent + export interface PlayerTimelineEvent extends BaseMessageWithPlayerID { /** * The time in milliseconds when the event was received. @@ -116,18 +117,21 @@ export namespace Events { id: string; } - export type RuntimeEvent = - | PlayerInitEvent - | PlayerRemovedEvent + export type TimelineEvents = | PlayerDataChangeEvent | PlayerLogEvent - | PlayerViewUpdateEvent | PlayerFlowStartEvent + | PlayerViewUpdateEvent; + + export type Event = + | TimelineEvents + | PlayerInitEvent + | PlayerRemovedEvent | PlayerFlowTransitionEvent | PlayerFlowEndEvent | RuntimeInitEvent; - export const RuntimeEventTypes = [ + export const EventTypes = [ 'runtime-init', 'player-init', 'player-removed', @@ -139,9 +143,7 @@ export namespace Events { 'player-flow-end', ] as const; - // export const RuntimeEventTypes2 = [ - // key in RuntimeEventTypes["type"] - // ] + export type EventTypes = typeof EventTypes[number]; - export type RuntimeEventTypes = typeof RuntimeEventTypes[number]; + export type ByType = DiscriminateByType; } diff --git a/devtools/common/src/index.ts b/devtools/common/src/index.ts index 0114280c..1e28be58 100644 --- a/devtools/common/src/index.ts +++ b/devtools/common/src/index.ts @@ -1,12 +1,16 @@ -import { Actions as actions } from './actions'; import { Events as events } from './events'; +import { Methods as methods } from './methods'; export * from './types'; export * from './logger'; + export { Events } from './events'; -export { Actions } from './actions'; +export { Methods } from './methods'; +// TODO: I'm really not sure about the Runtime namespace export namespace Runtime { - export const Actions = actions; - export const Events = events; + // export const Methods = methods; + // export const Events = events; + // export namespace Methods {} + // export namespace Events {} } diff --git a/devtools/common/src/actions.ts b/devtools/common/src/methods.ts similarity index 75% rename from devtools/common/src/actions.ts rename to devtools/common/src/methods.ts index 51cb67f1..e7d0c821 100644 --- a/devtools/common/src/actions.ts +++ b/devtools/common/src/methods.ts @@ -1,11 +1,29 @@ import { Binding, Flow, Schema, View } from '@player-ui/types'; -import { BaseRPCType, ProfilerNode } from './types'; +import { DiscriminateByType, ProfilerNode } from './types'; -// TODO: Settle on actions vs RPC nomenclature -// Bidirectional RPCs originating from the devtools client -export namespace Actions { - export interface PlayerConfigRPC - extends BaseRPCType<'player-config-request'> { +// Bidirectional methods originating from the devtools client +export namespace Methods { + + export /** TODO: */ interface BaseMethod { + /** + * 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 PlayerConfigMethod extends BaseMethod<'player-config-request'> { /** * Parameters associated with the Runtime Info RPC. */ @@ -34,8 +52,7 @@ export namespace Actions { }; } - export interface PlayerDataBindingRPC - extends BaseRPCType<'player-data-binding-details'> { + export interface PlayerDataBindingMethod extends BaseMethod<'player-data-binding-details'> { /** * Parameters associated with the Data Binding RPC. */ @@ -90,8 +107,7 @@ export namespace Actions { }; } - export interface PlayerViewDetailsRPC - extends BaseRPCType<'player-view-details-request'> { + export interface PlayerViewDetailsMethod extends BaseMethod<'player-view-details-request'> { /** * Parameters associated with the View Details RPC. */ @@ -112,8 +128,7 @@ export namespace Actions { }; } - export interface PlayerRuntimeInfoRPC - extends BaseRPCType<'player-runtime-info-request'> { + export interface PlayerRuntimeInfoMethod extends BaseMethod<'player-runtime-info-request'> { /** * Parameters associated with the Runtime Info RPC. */ @@ -146,8 +161,7 @@ export namespace Actions { }; } - export interface PlayerExpressionRPC - extends BaseRPCType<'player-execute-expression'> { + export interface PlayerExpressionMethod extends BaseMethod<'player-execute-expression'> { /** * Parameters associated with the Expression RPC. */ @@ -195,8 +209,7 @@ export namespace Actions { }; } - export interface PlayerStartProfilerRPC - extends BaseRPCType<'player-start-profiler-request'> { + export interface PlayerStartProfilerMethod extends BaseMethod<'player-start-profiler-request'> { /** * Parameters associated with the Profiler Details RPC. */ @@ -217,8 +230,7 @@ export namespace Actions { }; } - export interface PlayerStopProfilerRPC - extends BaseRPCType<'player-stop-profiler-request'> { + export interface PlayerStopProfilerMethod extends BaseMethod<'player-stop-profiler-request'> { /** * Parameters associated with the Profiler Details RPC. */ @@ -239,17 +251,17 @@ export namespace Actions { }; } - export type RuntimeRPC = - | PlayerConfigRPC - | PlayerDataBindingRPC - | PlayerViewDetailsRPC - | PlayerRuntimeInfoRPC - | PlayerExpressionRPC - | PlayerStartProfilerRPC - | PlayerStopProfilerRPC; + export type Method = + | PlayerConfigMethod + | PlayerDataBindingMethod + | PlayerViewDetailsMethod + | PlayerRuntimeInfoMethod + | PlayerExpressionMethod + | PlayerStartProfilerMethod + | PlayerStopProfilerMethod; // TODO: Generate this from `RuntimeRPC['type']` if I ever can - export const RuntimeRPCTypes = [ + export const MethodTypes = [ 'player-config-request', 'player-data-binding-details', 'player-view-details-request', @@ -259,5 +271,7 @@ export namespace Actions { 'player-stop-profiler-request', ] as const; - export type RuntimeRPCTypes = typeof RuntimeRPCTypes[number]; + export type MethodTypes = typeof MethodTypes[number]; + + export type ByType = DiscriminateByType; } diff --git a/devtools/common/src/runtime/message.ts b/devtools/common/src/runtime/message.ts index a71705d3..31d1bb97 100644 --- a/devtools/common/src/runtime/message.ts +++ b/devtools/common/src/runtime/message.ts @@ -1,35 +1,35 @@ import type { RPCFunctionCallback } from '../rpc'; -import type { Runtime } from '../types'; +import type { Events, Methods } from '..'; // TODO: Not sure about these types staying in common export type RuntimeEventWithoutSource = - | Omit - | Omit - | Omit - | Omit - | Omit - | Omit - | Omit; + | Omit + | Omit + | Omit + | Omit + | Omit + | Omit + | Omit; export type RuntimeEventPublisher = ( message: RuntimeEventWithoutSource ) => void; -export type ConfigRequestHandler = RPCFunctionCallback; +export type ConfigRequestHandler = RPCFunctionCallback; export type DataBindingRequestHandler = - RPCFunctionCallback; + RPCFunctionCallback; export type RuntimeInfoRequestHandler = - RPCFunctionCallback; + RPCFunctionCallback; export type ViewInfoRequestHandler = - RPCFunctionCallback; + RPCFunctionCallback; export type ExpressionEvalHandler = - RPCFunctionCallback; + RPCFunctionCallback; export type StartProfilerRequestHandler = - RPCFunctionCallback; + RPCFunctionCallback; export type StopProfilerRequestHandler = - RPCFunctionCallback; + RPCFunctionCallback; export interface PlayerRuntimeRPCCallbacks { /** diff --git a/devtools/common/src/types.ts b/devtools/common/src/types.ts index f6719cdf..697da6b8 100644 --- a/devtools/common/src/types.ts +++ b/devtools/common/src/types.ts @@ -1,8 +1,8 @@ import { Events } from './events'; -import { Actions } from './actions'; +import { Methods } from './methods'; export interface BaseEventMessage< - T extends Events.RuntimeEventTypes | 'rpc-request' | 'rpc-response' + T extends Events.EventTypes | 'rpc-request' | 'rpc-response' > { /** * Source of the Message @@ -14,26 +14,7 @@ export interface BaseEventMessage< 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 +export interface BaseMessageWithPlayerID extends BaseEventMessage { /** * Unique Player Id associated with the message. @@ -41,7 +22,7 @@ export interface BaseMessageWithPlayerID playerID: string; } -export interface RPCRequestMessageEvent> +export interface MethodRequestMessageEvent> extends BaseEventMessage<'rpc-request'> { /** * Unique Player Id associated with the RPC Request message. @@ -57,7 +38,7 @@ export interface RPCRequestMessageEvent> params: T['params']; } -export interface RPCResponseMessageEvent> +export interface MethodResponseMessageEvent> extends BaseEventMessage<'rpc-response'> { /** * Unique Id associated with the RPC Response message. @@ -77,11 +58,10 @@ export interface RPCResponseMessageEvent> params: T['params']; } -// TODO: Convert to RPC export type Message = - | RPCRequestMessageEvent - | RPCResponseMessageEvent - | Events.RuntimeEvent; + | MethodRequestMessageEvent + | MethodResponseMessageEvent + | Events.Event; // TODO: export via plugins export type ProfilerNode = { @@ -112,3 +92,4 @@ export type ProfilerNode = { children: ProfilerNode[]; }; +export type DiscriminateByType = Extract diff --git a/devtools/flipper/src/index.tsx b/devtools/flipper/src/index.tsx index 29310212..5f7c9a4e 100644 --- a/devtools/flipper/src/index.tsx +++ b/devtools/flipper/src/index.tsx @@ -1,62 +1,50 @@ import React from 'react'; import { PluginClient, usePlugin } from 'flipper-plugin'; import { - createStore, Runtime, - RPCRequestMessageEvent, - RPCResponseMessageEvent, + Events, + Methods, } from '@player-tools/devtools-common'; import { + createDevtoolsStore, handleMessage, - buildAliases, - buildPlayerReducerCallback, - buildRPCActions, - buildRPCRequests, - RuntimeRPCRequestHandlers, } 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'; type Events = { - [key in Runtime.RuntimeEventTypes]: Extract< - Runtime.RuntimeEvent, - { type: key } - >; -} & { - 'rpc-response': RPCResponseMessageEvent; -}; + [type in Events.Event['type']]: Events.ByType; +}; +// & { +// 'rpc-response': RPCResponseMessageEvent; +// }; 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) { - // Build RPC handlers (delegation to plugin for how to handle communications to hook up to redux) - const rpcHandlers: RuntimeRPCRequestHandlers = buildRPCRequests( - async (message: RPCRequestMessageEvent) => { - // TODO: Do `send('rpc-request', message)` - const response = await client.send(message.rpcType, message); - // TODO: This doesn't currently work - rpcHandlers[message.rpcType].onMessage(response); - } - ); - - // Build redux actions to hook up - const actions = buildRPCActions(rpcHandlers); - const store = createStore( - buildAliases(actions), - buildPlayerReducerCallback(actions) - ); - - Runtime.RuntimeEventTypes.forEach((event) => { - client.onMessage(event, (message) => { - handleMessage(store, message); - }); + // Listen for events + Events.EventTypes.forEach((event) => { + client.onMessage(event, handleMessage); }); - return { store }; + // 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']; + } + // TODO: What do we do when things aren't supported? + }; + + return { store: createDevtoolsStore(methodHandler) }; } export const Component = () => { 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(() => { From 63948c914a1769ac943072838bdabea71f9ce086 Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Fri, 16 Dec 2022 14:37:05 -0500 Subject: [PATCH 04/10] re-introduce web-ext --- devtools/client/src/index.ts | 1 - devtools/web-ext/BUILD | 77 +++ devtools/web-ext/README.md | 7 + devtools/web-ext/postcss.config.js | 28 + devtools/web-ext/src/background/background.ts | 63 +++ devtools/web-ext/src/constants.ts | 1 + devtools/web-ext/src/content/content.ts | 39 ++ devtools/web-ext/src/manifest.json | 37 ++ devtools/web-ext/src/media/player-logo.png | Bin 0 -> 38521 bytes devtools/web-ext/src/options/options.ts | 0 devtools/web-ext/src/panel/global.css | 3 + devtools/web-ext/src/panel/panel.tsx | 16 + devtools/web-ext/src/panel/startup.ts | 7 + devtools/web-ext/src/popup/popup.ts | 0 devtools/web-ext/src/redux/proxy-store.ts | 3 + devtools/web-ext/src/runtime/runtime.tsx | 5 + devtools/web-ext/tsconfig.json | 15 + devtools/web-ext/webpack.config.js | 138 +++++ package.json | 19 +- yarn.lock | 522 +++++++++++++++++- 20 files changed, 946 insertions(+), 35 deletions(-) create mode 100644 devtools/web-ext/BUILD create mode 100644 devtools/web-ext/README.md create mode 100644 devtools/web-ext/postcss.config.js create mode 100644 devtools/web-ext/src/background/background.ts create mode 100644 devtools/web-ext/src/constants.ts create mode 100644 devtools/web-ext/src/content/content.ts create mode 100644 devtools/web-ext/src/manifest.json create mode 100644 devtools/web-ext/src/media/player-logo.png create mode 100644 devtools/web-ext/src/options/options.ts create mode 100644 devtools/web-ext/src/panel/global.css create mode 100644 devtools/web-ext/src/panel/panel.tsx create mode 100644 devtools/web-ext/src/panel/startup.ts create mode 100644 devtools/web-ext/src/popup/popup.ts create mode 100644 devtools/web-ext/src/redux/proxy-store.ts create mode 100644 devtools/web-ext/src/runtime/runtime.tsx create mode 100644 devtools/web-ext/tsconfig.json create mode 100644 devtools/web-ext/webpack.config.js diff --git a/devtools/client/src/index.ts b/devtools/client/src/index.ts index d8944f87..9686012a 100644 --- a/devtools/client/src/index.ts +++ b/devtools/client/src/index.ts @@ -1,2 +1 @@ export * from './redux'; -// export { Runtime } from '@player-tools/devtools-common'; 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..b5f658d2 --- /dev/null +++ b/devtools/web-ext/src/background/background.ts @@ -0,0 +1,63 @@ +import { wrapStore } from 'webext-redux'; +import { v4 as uuid } from 'uuid'; +import { + createLogger, + BACKGROUND_SOURCE, + type Methods, +} from '@player-tools/devtools-common'; +import { createDevtoolsStore } from '@player-tools/devtools-client'; +import { browser, Tabs } from 'webextension-polyfill-ts'; + +const logger = createLogger(BACKGROUND_SOURCE); +const requestsInFlight = new Map void>(); + +// TODO: Not sure if this belongs in background +browser.runtime.onMessage.addListener((message) => { + logger.log('i gots a message: ' + JSON.stringify(message)); + + const handler = requestsInFlight.get(message.id); + requestsInFlight.delete(message.id); + handler?.(message.result); +}); + +async function sendMessage(message: Methods.Method & { id: string }) { + const tabs: Tabs.Tab[] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + const tabId = tabs[0]?.id; + if (!tabId) { + return; + } + + // TODO: Can we just use the return value here? + browser.tabs.sendMessage(tabId, message); +} + + +// Delegate to plugin for how to handle communications +const methodHandler = async ( + method: Methods.ByType +): Promise['result']> => { + return new Promise((resolve) => { + const id = uuid(); + + const message = { + ...method, + id, + // TODO: Why can't I just send method w/ a uuid? + // params: method.params, + // type: method.type, + } + + // const result = {} as Methods.ByType['result'] + // resolve(result) + + requestsInFlight.set(id, resolve as any); + sendMessage(message) + }) +}; + +// TODO: publish message +wrapStore(createDevtoolsStore(methodHandler)); +logger.log('Wrapped Redux Store'); 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..3fb6dc38 --- /dev/null +++ b/devtools/web-ext/src/content/content.ts @@ -0,0 +1,39 @@ +import { browser } from 'webextension-polyfill-ts'; +import { + Message, + RUNTIME_SOURCE, + CONTENT_SOURCE, + createLogger, +} from '@player-tools/devtools-common'; +import { handleMessage } from '@player-tools/devtools-client'; +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; +} + +// TODO: Barf +document.documentElement.append(getScript()); + +browser.runtime.onMessage.addListener((message) => { + window.postMessage(message, '*'); +}); + +window.addEventListener('message', (event: MessageEvent) => { + if (event.source !== window) { + return; + } + + if (event.data.source === RUNTIME_SOURCE) { + logger.log('Sending Message to Background', event.data); + // TODO: I hate that we're diverging here, like just send to a single place and observe from there + browser.runtime.sendMessage(event.data); + handleMessage(event.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 0000000000000000000000000000000000000000..726b21bb86ad365c0d8a27fceeb2be73d0ac6303 GIT binary patch literal 38521 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 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..2ad70ca7 --- /dev/null +++ b/devtools/web-ext/src/runtime/runtime.tsx @@ -0,0 +1,5 @@ +import { RUNTIME_SOURCE } from "@player-tools/devtools-common"; + +window.postMessage({type: 'runtime-init', source: RUNTIME_SOURCE}, '*') + +console.warn("If something isn't working, maybe the runtime isn't set up? 2"); 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..4c209192 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", @@ -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..ae3dccb8 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" @@ -3630,6 +3643,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" @@ -3883,7 +3901,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 +3965,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 +4706,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 +5440,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 +5469,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 +5477,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 +5715,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 +5999,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 +6060,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 +6236,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 +6281,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 +6711,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 +6846,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" @@ -6793,6 +6895,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 +6937,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 +6951,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== @@ -7096,6 +7205,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 +8035,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 +8789,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 +8819,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 +8941,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 +9103,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 +9193,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 +9379,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 +9409,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 +9647,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 +11000,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 +11069,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 +11084,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 +11124,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 +11184,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 +11678,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== @@ -12113,6 +12379,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" @@ -12443,7 +12719,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== @@ -12825,6 +13101,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 +13177,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 +13249,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 +13351,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 +13378,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 +13450,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 +13589,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" @@ -13860,6 +14201,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 +14233,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 +14630,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 +14680,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" @@ -14606,7 +14970,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 +15214,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 +15232,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 +15392,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 +15591,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== @@ -15293,7 +15682,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 +15812,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 +16185,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 +16252,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 +16299,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 +16553,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 +16610,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 +16850,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 +16911,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" From 44fe5806ce052c9236ba88aa4d53078d623d851d Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Fri, 16 Dec 2022 15:03:01 -0500 Subject: [PATCH 05/10] upgrade rules_pkg@0.7.0 --- WORKSPACE | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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", From 181916cd4ae0165660e9304994e385bdf1c7054e Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Fri, 16 Dec 2022 18:31:25 -0500 Subject: [PATCH 06/10] upgrade to latest player --- devtools/client/src/redux/store.ts | 8 +-- devtools/common/BUILD | 2 +- devtools/common/src/events.ts | 4 +- package.json | 4 +- yarn.lock | 87 ++++++++++++++++++++++++++---- 5 files changed, 83 insertions(+), 22 deletions(-) diff --git a/devtools/client/src/redux/store.ts b/devtools/client/src/redux/store.ts index 1674df45..ddca0dcb 100644 --- a/devtools/client/src/redux/store.ts +++ b/devtools/client/src/redux/store.ts @@ -2,16 +2,10 @@ import { type AnyAction, type Dispatch, type Middleware, - type MiddlewareArray, - type ActionReducerMapBuilder, type EnhancedStore, configureStore, - createReducer, - combineReducers, - createAsyncThunk, } from '@reduxjs/toolkit'; -import { Message } from '@player-tools/devtools-common'; -import { PlayersState, StoreState } from './state'; +import { StoreState } from './state'; import { Methods } from './actions'; import { playersReducer } from './reducers'; import { listenerMiddleware } from './middleware'; 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/src/events.ts b/devtools/common/src/events.ts index ddf1c3b1..86ff9580 100644 --- a/devtools/common/src/events.ts +++ b/devtools/common/src/events.ts @@ -1,5 +1,5 @@ -import { Severity } from "@player-ui/logger"; -import { Binding, Flow, View } from "@player-ui/types"; +import type { Severity } from "@player-ui/player"; +import type { Binding, Flow, View } from "@player-ui/types"; import { BaseEventMessage, BaseMessageWithPlayerID, DiscriminateByType } from "."; import { RUNTIME_SOURCE } from "./logger"; diff --git a/package.json b/package.json index 4c209192..fae20547 100644 --- a/package.json +++ b/package.json @@ -39,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", diff --git a/yarn.lock b/yarn.lock index ae3dccb8..5a0450d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3217,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" @@ -3596,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" @@ -3743,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" @@ -3781,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" @@ -6876,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" @@ -7011,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" @@ -12016,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" @@ -12555,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" @@ -12838,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" @@ -13702,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== @@ -14923,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" @@ -15667,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== From e09ad9f6c7250fbbcfe873db26925e96ce21717a Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Tue, 20 Dec 2022 15:22:33 -0500 Subject: [PATCH 07/10] wip 3: is functional - i promise --- devtools/client/src/redux/aliases.ts | 11 +++- devtools/client/src/redux/listeners.ts | 12 ++-- devtools/client/src/redux/store.ts | 42 ++++++++----- devtools/common/src/events.ts | 4 +- devtools/common/src/methods.ts | 5 +- devtools/common/src/runtime/message.ts | 63 ------------------- devtools/common/src/types.ts | 13 +++- devtools/flipper/src/index.tsx | 21 +++---- devtools/web-ext/src/background/background.ts | 60 +++++------------- devtools/web-ext/src/content/content.ts | 50 ++++++++++----- devtools/web-ext/src/runtime/runtime.tsx | 2 - 11 files changed, 118 insertions(+), 165 deletions(-) delete mode 100644 devtools/common/src/runtime/message.ts diff --git a/devtools/client/src/redux/aliases.ts b/devtools/client/src/redux/aliases.ts index a982f9eb..f8b61a1a 100644 --- a/devtools/client/src/redux/aliases.ts +++ b/devtools/client/src/redux/aliases.ts @@ -1,4 +1,4 @@ -import type { Methods as RuntimeMethods } from '@player-tools/devtools-common'; +import { Methods as RuntimeMethods, RUNTIME_SOURCE } from '@player-tools/devtools-common'; import { Methods } from './actions'; export const GET_INFO_DETAILS = 'GET_INFO_DETAILS'; @@ -11,7 +11,7 @@ export const STOP_PROFILER = 'STOP_PROFILER'; export interface MethodAction { - payload: RuntimeMethods.ByType; + payload: RuntimeMethods.ByType['params']; } // Copied from webext redux library not allowed in flipper @@ -25,7 +25,12 @@ const _alias = (aliases: any) => () => (next: any) => (action: any) => { return next(action); }; -const alias = (alias: T, methods: Methods.MethodThunks) => (action: MethodAction) => methods[alias](action.payload) +// TODO: Make sure typings are correct here +const alias = (type: T, methods: Methods.MethodThunks) => (action: MethodAction) => methods[type]({ + type, + params: action.payload, + source: RUNTIME_SOURCE, +} as RuntimeMethods.ByType) export const buildAliases = (methods: Methods.MethodThunks) => _alias({ diff --git a/devtools/client/src/redux/listeners.ts b/devtools/client/src/redux/listeners.ts index 1592aae3..e5295250 100644 --- a/devtools/client/src/redux/listeners.ts +++ b/devtools/client/src/redux/listeners.ts @@ -1,10 +1,8 @@ -import { Events, Message } from "@player-tools/devtools-common"; +import { Events } from "@player-tools/devtools-common"; +import { Dispatch } from "redux"; import { Events as EventActions } from "./actions"; -export function handleMessage(message: Message) { - // propagate message to default event handlers - const { type } = message; - if (type in Events.EventTypes) { - EventActions.actions[type as Events.EventTypes](message as any) - } +/** 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.actions[message.type](message as any)) } diff --git a/devtools/client/src/redux/store.ts b/devtools/client/src/redux/store.ts index ddca0dcb..c5af8dea 100644 --- a/devtools/client/src/redux/store.ts +++ b/devtools/client/src/redux/store.ts @@ -4,22 +4,18 @@ import { type Middleware, type EnhancedStore, configureStore, + ReducersMapObject, } from '@reduxjs/toolkit'; import { StoreState } from './state'; import { Methods } from './actions'; import { playersReducer } from './reducers'; import { listenerMiddleware } from './middleware'; +import { buildAliases } from './aliases'; -/** - * 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 additionalReducers Additional reducers to be added to the store. Optional - * @returns - */ -export const createDevtoolsStore = ( - onMethodRequest: Methods.MethodHandler, +const createStore = ( + methodThunks: Methods.MethodThunks, middleware?: Middleware>, - additionalReducers?: any, + reducers?: ReducersMapObject ): EnhancedStore< StoreState, any, @@ -28,16 +24,34 @@ export const createDevtoolsStore = ( configureStore({ reducer: { // TODO: Look into slices - players: playersReducer(Methods.buildAsyncThunks(onMethodRequest)), - ...additionalReducers, + players: playersReducer(methodThunks), + ...reducers, }, middleware: (getDefaultMiddleware) => { // TODO: Potentially hook up our own middleware for dispatching additional actions from event actions const m = getDefaultMiddleware() + .prepend(buildAliases(methodThunks)) .concat(listenerMiddleware.middleware) - if (middleware) m.prepend(middleware) + if (middleware) m.prepend(middleware); - return m - } + 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? +export const createDevtoolsStore = ( + onMethodRequest: Methods.MethodHandler, + middleware?: Middleware>, + additionalReducers?: any +): EnhancedStore< + StoreState, + any, + Middleware>[] +> => createStore(Methods.buildAsyncThunks(onMethodRequest), middleware, additionalReducers) diff --git a/devtools/common/src/events.ts b/devtools/common/src/events.ts index 86ff9580..42a4ca83 100644 --- a/devtools/common/src/events.ts +++ b/devtools/common/src/events.ts @@ -1,6 +1,6 @@ import type { Severity } from "@player-ui/player"; import type { Binding, Flow, View } from "@player-ui/types"; -import { BaseEventMessage, BaseMessageWithPlayerID, DiscriminateByType } from "."; +import { BaseEventMessage, BaseMessageWithPlayerID, DiscriminateByType, isKnownType } from "./types"; import { RUNTIME_SOURCE } from "./logger"; // TODO: Maybe reverse the pluralness @@ -146,4 +146,6 @@ export namespace Events { export type EventTypes = typeof EventTypes[number]; export type ByType = DiscriminateByType; + + export const isEvent = isKnownType(EventTypes); } diff --git a/devtools/common/src/methods.ts b/devtools/common/src/methods.ts index e7d0c821..06c73338 100644 --- a/devtools/common/src/methods.ts +++ b/devtools/common/src/methods.ts @@ -1,9 +1,8 @@ import { Binding, Flow, Schema, View } from '@player-ui/types'; -import { DiscriminateByType, ProfilerNode } from './types'; +import { DiscriminateByType, isKnownType, ProfilerNode } from './types'; // Bidirectional methods originating from the devtools client export namespace Methods { - export /** TODO: */ interface BaseMethod { /** * Source of the RPC type @@ -274,4 +273,6 @@ export namespace Methods { export type MethodTypes = typeof MethodTypes[number]; export type ByType = DiscriminateByType; + + export const isMethod = isKnownType(MethodTypes); } diff --git a/devtools/common/src/runtime/message.ts b/devtools/common/src/runtime/message.ts deleted file mode 100644 index 31d1bb97..00000000 --- a/devtools/common/src/runtime/message.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { RPCFunctionCallback } from '../rpc'; -import type { Events, Methods } from '..'; - - -// TODO: Not sure about these types staying in common - -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 index 697da6b8..8e12b102 100644 --- a/devtools/common/src/types.ts +++ b/devtools/common/src/types.ts @@ -58,6 +58,7 @@ export interface MethodResponseMessageEvent> params: T['params']; } +// TODO: This should include methods export type Message = | MethodRequestMessageEvent | MethodResponseMessageEvent @@ -92,4 +93,14 @@ export type ProfilerNode = { children: ProfilerNode[]; }; -export type DiscriminateByType = Extract +export type DiscriminateByType = Extract< + Types, + { type: T } +>; + +/** Higher order utility for determining what types things are */ +export const isKnownType = (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/src/index.tsx b/devtools/flipper/src/index.tsx index 5f7c9a4e..d7a24336 100644 --- a/devtools/flipper/src/index.tsx +++ b/devtools/flipper/src/index.tsx @@ -7,17 +7,14 @@ import { } from '@player-tools/devtools-common'; import { createDevtoolsStore, - handleMessage, + 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'; type Events = { [type in Events.Event['type']]: Events.ByType; -}; -// & { -// 'rpc-response': RPCResponseMessageEvent; -// }; +}; type Methods = { [type in Methods.Method["type"]]: ( @@ -26,11 +23,6 @@ type Methods = { }; export function plugin(client: PluginClient) { - // Listen for events - Events.EventTypes.forEach((event) => { - client.onMessage(event, handleMessage); - }); - // Delegate to plugin for how to handle communications const methodHandler = async ( method: Methods.ByType @@ -44,7 +36,14 @@ export function plugin(client: PluginClient) { // TODO: What do we do when things aren't supported? }; - return { store: createDevtoolsStore(methodHandler) }; + const store = createDevtoolsStore(methodHandler) + + // Listen for events + Events.EventTypes.forEach((event) => { + client.onMessage(event, dispatchEvents(store.dispatch)); + }); + + return { store }; } export const Component = () => { diff --git a/devtools/web-ext/src/background/background.ts b/devtools/web-ext/src/background/background.ts index b5f658d2..29173e23 100644 --- a/devtools/web-ext/src/background/background.ts +++ b/devtools/web-ext/src/background/background.ts @@ -1,63 +1,31 @@ import { wrapStore } from 'webext-redux'; -import { v4 as uuid } from 'uuid'; import { createLogger, BACKGROUND_SOURCE, type Methods, } from '@player-tools/devtools-common'; -import { createDevtoolsStore } from '@player-tools/devtools-client'; -import { browser, Tabs } from 'webextension-polyfill-ts'; +import { createDevtoolsStore, dispatchEvents } from '@player-tools/devtools-client'; +import { browser } from 'webextension-polyfill-ts'; const logger = createLogger(BACKGROUND_SOURCE); -const requestsInFlight = new Map void>(); -// TODO: Not sure if this belongs in background -browser.runtime.onMessage.addListener((message) => { - logger.log('i gots a message: ' + JSON.stringify(message)); - - const handler = requestsInFlight.get(message.id); - requestsInFlight.delete(message.id); - handler?.(message.result); -}); - -async function sendMessage(message: Methods.Method & { id: string }) { - const tabs: Tabs.Tab[] = await browser.tabs.query({ +async function sendMessage( + method: Methods.ByType, +): Promise['result']> { + const [{ id: tabId }] = await browser.tabs.query({ active: true, currentWindow: true, }); - const tabId = tabs[0]?.id; - if (!tabId) { - return; - } - - // TODO: Can we just use the return value here? - browser.tabs.sendMessage(tabId, message); -} + if (!tabId) return Promise.reject("could not find active tab"); -// Delegate to plugin for how to handle communications -const methodHandler = async ( - method: Methods.ByType -): Promise['result']> => { - return new Promise((resolve) => { - const id = uuid(); - - const message = { - ...method, - id, - // TODO: Why can't I just send method w/ a uuid? - // params: method.params, - // type: method.type, - } - - // const result = {} as Methods.ByType['result'] - // resolve(result) + return await browser.tabs.sendMessage(tabId, method); +} - requestsInFlight.set(id, resolve as any); - sendMessage(message) - }) -}; +const store = createDevtoolsStore(sendMessage); -// TODO: publish message -wrapStore(createDevtoolsStore(methodHandler)); +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/content/content.ts b/devtools/web-ext/src/content/content.ts index 3fb6dc38..b5cc3a97 100644 --- a/devtools/web-ext/src/content/content.ts +++ b/devtools/web-ext/src/content/content.ts @@ -1,11 +1,11 @@ import { browser } from 'webextension-polyfill-ts'; +import { v4 as uuid } from 'uuid'; import { - Message, - RUNTIME_SOURCE, CONTENT_SOURCE, createLogger, + Methods, + Events, } from '@player-tools/devtools-common'; -import { handleMessage } from '@player-tools/devtools-client'; import { INJECTED_SCRIPT_ID } from '../constants'; const logger = createLogger(CONTENT_SOURCE); @@ -18,22 +18,42 @@ function getScript() { return injectedScript; } -// TODO: Barf document.documentElement.append(getScript()); -browser.runtime.onMessage.addListener((message) => { - window.postMessage(message, '*'); -}); +/** Method type with unique identifier for tracking incoming method responses */ +type MethodWithId = Methods.ByType & { id: string }; + +/** Tracker for method requests sent to the frontend */ +// TODO: Clear on store reset? +const requestsInFlight = new Map void>(); + +/** Send method requests from background to Player */ +browser.runtime.onMessage.addListener((request) => { + // Only process request if known and we don't have a result yet + if (Methods.isMethod(request) && !request.result) { + const id = uuid(); + window.postMessage({ + ...request, + id, + }, '*'); -window.addEventListener('message', (event: MessageEvent) => { - if (event.source !== window) { - return; + return new Promise((resolve) => { + requestsInFlight.set(id, resolve) + }) } - if (event.data.source === RUNTIME_SOURCE) { - logger.log('Sending Message to Background', event.data); - // TODO: I hate that we're diverging here, like just send to a single place and observe from there - browser.runtime.sendMessage(event.data); - handleMessage(event.data); + // TODO: Fail silently? + return Promise.reject(`Unknown request: ${JSON.stringify(request)}`) +}); + +/** 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)) { + browser.runtime.sendMessage(data); } }); diff --git a/devtools/web-ext/src/runtime/runtime.tsx b/devtools/web-ext/src/runtime/runtime.tsx index 2ad70ca7..a3bee868 100644 --- a/devtools/web-ext/src/runtime/runtime.tsx +++ b/devtools/web-ext/src/runtime/runtime.tsx @@ -1,5 +1,3 @@ import { RUNTIME_SOURCE } from "@player-tools/devtools-common"; window.postMessage({type: 'runtime-init', source: RUNTIME_SOURCE}, '*') - -console.warn("If something isn't working, maybe the runtime isn't set up? 2"); From f18782e5df88af1e101f6748460a10c1f6ab3326 Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Tue, 20 Dec 2022 19:20:04 -0500 Subject: [PATCH 08/10] wip 4: cleanup --- devtools/client/README.md | 4 +- devtools/client/src/redux/actions.ts | 1 + devtools/client/src/redux/aliases.ts | 2 +- devtools/client/src/redux/index.ts | 1 + devtools/client/src/redux/middleware.ts | 14 +- devtools/client/src/redux/store.ts | 5 +- devtools/common/README.md | 2 +- devtools/common/src-old/rpc/index.ts | 144 ------------------ devtools/common/src/events.ts | 37 +++-- devtools/common/src/methods.ts | 50 +++--- devtools/common/src/types.ts | 78 ---------- devtools/common/src/utils.ts | 18 +++ devtools/web-ext/src/background/background.ts | 11 +- devtools/web-ext/src/content/content.ts | 14 +- 14 files changed, 97 insertions(+), 284 deletions(-) delete mode 100644 devtools/common/src-old/rpc/index.ts create mode 100644 devtools/common/src/utils.ts diff --git a/devtools/client/README.md b/devtools/client/README.md index b104c110..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 responsible for managing state and consuming events and async actions from a devtools client, i.e. flipper plugin or web extension. +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/redux/actions.ts b/devtools/client/src/redux/actions.ts index 548c6b45..fd1f9e06 100644 --- a/devtools/client/src/redux/actions.ts +++ b/devtools/client/src/redux/actions.ts @@ -69,6 +69,7 @@ export namespace Events { ) as EventActions } +/** 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'), diff --git a/devtools/client/src/redux/aliases.ts b/devtools/client/src/redux/aliases.ts index f8b61a1a..19c5097a 100644 --- a/devtools/client/src/redux/aliases.ts +++ b/devtools/client/src/redux/aliases.ts @@ -25,7 +25,7 @@ const _alias = (aliases: any) => () => (next: any) => (action: any) => { return next(action); }; -// TODO: Make sure typings are correct here +/** Helper for building corresponding method action via supplied thunks */ const alias = (type: T, methods: Methods.MethodThunks) => (action: MethodAction) => methods[type]({ type, params: action.payload, diff --git a/devtools/client/src/redux/index.ts b/devtools/client/src/redux/index.ts index 2354615b..d8d1ed24 100644 --- a/devtools/client/src/redux/index.ts +++ b/devtools/client/src/redux/index.ts @@ -1,6 +1,7 @@ export * from './actions'; export * from './aliases'; export * from './listeners'; +export * from './middleware'; export * from './reducers'; export * from './selectors'; export * from './state'; diff --git a/devtools/client/src/redux/middleware.ts b/devtools/client/src/redux/middleware.ts index e29852f6..62c2d648 100644 --- a/devtools/client/src/redux/middleware.ts +++ b/devtools/client/src/redux/middleware.ts @@ -1,17 +1,19 @@ -import { StoreState } from "@player-tools/devtools-client"; import { createListenerMiddleware, isAnyOf } from "@reduxjs/toolkit"; import { Actions, Events } from "./actions"; import { GET_DATA_BINDING_DETAILS } from "./aliases"; +import { type StoreState } from './state'; -export const listenerMiddleware = createListenerMiddleware(); +/** + * 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( Events.actions['player-data-change-event'], Events.actions['player-log-event'], Events.actions['player-flow-start'], - // TODO: I don't actually think this _was_ included - Events.actions['player-view-update-event'] ), effect: (action, api) => { api.dispatch(Actions['player-timeline-event'](action.payload)); @@ -21,6 +23,7 @@ listenerMiddleware.startListening({ listenerMiddleware.startListening({ actionCreator: Events.actions['runtime-init'], effect: (_, api) => { + console.log("um") api.dispatch(Actions["clear-store"]()) } }) @@ -41,8 +44,7 @@ listenerMiddleware.startListening({ Events.actions["player-data-change-event"], ), effect: (action, api) => { - // TODO: Just appropriately type the middleware - const { players } = api.getState() as StoreState; + const { players } = api.getState(); const { playerID } = action.payload; if ( diff --git a/devtools/client/src/redux/store.ts b/devtools/client/src/redux/store.ts index c5af8dea..caed4016 100644 --- a/devtools/client/src/redux/store.ts +++ b/devtools/client/src/redux/store.ts @@ -28,12 +28,11 @@ const createStore = ( ...reducers, }, middleware: (getDefaultMiddleware) => { - // TODO: Potentially hook up our own middleware for dispatching additional actions from event actions const m = getDefaultMiddleware() .prepend(buildAliases(methodThunks)) .concat(listenerMiddleware.middleware) - if (middleware) m.prepend(middleware); + if (middleware) m.concat(middleware) return m; }, @@ -45,7 +44,7 @@ const createStore = ( * @param reducers Additional reducers to be added to the store. Optional * @returns */ -// TODO: Turn into store enhancer? +// TODO: Turn into store enhancer? Maybe remove middleware and additionalReducers? export const createDevtoolsStore = ( onMethodRequest: Methods.MethodHandler, middleware?: Middleware>, diff --git a/devtools/common/README.md b/devtools/common/README.md index 85a5f951..960192a7 100644 --- a/devtools/common/README.md +++ b/devtools/common/README.md @@ -1,3 +1,3 @@ # devtools-common -Common definition of events and RPC actions +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-old/rpc/index.ts b/devtools/common/src-old/rpc/index.ts deleted file mode 100644 index ac69706c..00000000 --- a/devtools/common/src-old/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/events.ts b/devtools/common/src/events.ts index 42a4ca83..1bb7efaa 100644 --- a/devtools/common/src/events.ts +++ b/devtools/common/src/events.ts @@ -1,28 +1,38 @@ import type { Severity } from "@player-ui/player"; import type { Binding, Flow, View } from "@player-ui/types"; -import { BaseEventMessage, BaseMessageWithPlayerID, DiscriminateByType, isKnownType } from "./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 { - export interface PlayerTimelineEvent - extends BaseMessageWithPlayerID { + 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 BaseEventMessage<'runtime-init'> { + export interface RuntimeInitEvent extends BaseEvent<'runtime-init'> { /** * Source of the event. */ source: typeof RUNTIME_SOURCE; } - export interface PlayerInitEvent - extends BaseMessageWithPlayerID<'player-init'> { + export interface PlayerInitEvent extends BaseEventWithPlayerID<'player-init'> { /** * Source of the event. */ @@ -33,16 +43,14 @@ export namespace Events { version: string; } - export interface PlayerRemovedEvent - extends BaseMessageWithPlayerID<'player-removed'> { + export interface PlayerRemovedEvent extends BaseEventWithPlayerID<'player-removed'> { /** * Source of the event. */ source: string; } - export interface PlayerLogEvent - extends PlayerTimelineEvent<'player-log-event'> { + export interface PlayerLogEvent extends PlayerTimelineEvent<'player-log-event'> { /** * Source of the event. */ @@ -57,8 +65,7 @@ export namespace Events { message: Array; } - export interface PlayerDataChangeEvent - extends PlayerTimelineEvent<'player-data-change-event'> { + export interface PlayerDataChangeEvent extends PlayerTimelineEvent<'player-data-change-event'> { /** * Source of the event. */ @@ -77,8 +84,7 @@ export namespace Events { newValue: any; } - export interface PlayerViewUpdateEvent - extends PlayerTimelineEvent<'player-view-update-event'> { + export interface PlayerViewUpdateEvent extends PlayerTimelineEvent<'player-view-update-event'> { /** * Source of the event. */ @@ -89,8 +95,7 @@ export namespace Events { update: View; } - export interface PlayerFlowTransitionEvent - extends PlayerTimelineEvent<'player-flow-transition-event'> { + export interface PlayerFlowTransitionEvent extends PlayerTimelineEvent<'player-flow-transition-event'> { /** * The state from which the transition has taken place. */ diff --git a/devtools/common/src/methods.ts b/devtools/common/src/methods.ts index 06c73338..741deb68 100644 --- a/devtools/common/src/methods.ts +++ b/devtools/common/src/methods.ts @@ -1,30 +1,28 @@ import { Binding, Flow, Schema, View } from '@player-ui/types'; -import { DiscriminateByType, isKnownType, ProfilerNode } from './types'; +import { ProfilerNode } from './types'; +import { DiscriminateByType, DiscriminatedType, isKnownType } from './utils'; // Bidirectional methods originating from the devtools client export namespace Methods { - export /** TODO: */ interface BaseMethod { + 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 RPC type + * Source of the method type */ source: string; /** - * Unique type associated with the RPC type. - */ - type: T; - /** - * Parameters associated with the RPC type. + * Parameters associated with the method request. */ params?: unknown; /** - * Result of the RPC call. + * Result of the method response. */ result?: unknown; } export interface PlayerConfigMethod extends BaseMethod<'player-config-request'> { /** - * Parameters associated with the Runtime Info RPC. + * Parameters associated with the Runtime Info method request. */ params: { /** @@ -33,7 +31,7 @@ export namespace Methods { playerID: string; }; /** - * Result of the RPC call. + * Result of the method response. */ result?: { /** @@ -53,7 +51,7 @@ export namespace Methods { export interface PlayerDataBindingMethod extends BaseMethod<'player-data-binding-details'> { /** - * Parameters associated with the Data Binding RPC. + * Parameters associated with the Data Binding method request. */ params: { /** @@ -61,16 +59,16 @@ export namespace Methods { */ playerID: string; /** - * Binding associated with the Data binding RPC. + * Binding associated with the Data binding method request. */ binding: Binding; }; /** - * Result of the RPC call. + * Result of the method response. */ result?: { /** - * Binding associated with the Data binding RPC result. + * Binding associated with the Data binding method response. */ binding: Binding; /** @@ -108,7 +106,7 @@ export namespace Methods { export interface PlayerViewDetailsMethod extends BaseMethod<'player-view-details-request'> { /** - * Parameters associated with the View Details RPC. + * Parameters associated with the View Details method request. */ params: { /** @@ -117,7 +115,7 @@ export namespace Methods { playerID: string; }; /** - * Result of the RPC call. + * Result of the method response. */ result?: { /** @@ -129,7 +127,7 @@ export namespace Methods { export interface PlayerRuntimeInfoMethod extends BaseMethod<'player-runtime-info-request'> { /** - * Parameters associated with the Runtime Info RPC. + * Parameters associated with the Runtime Info method request. */ params: { /** @@ -138,7 +136,7 @@ export namespace Methods { playerID: string; }; /** - * Result of the RPC call. + * Result of the method response. */ result?: { /** @@ -162,7 +160,7 @@ export namespace Methods { export interface PlayerExpressionMethod extends BaseMethod<'player-execute-expression'> { /** - * Parameters associated with the Expression RPC. + * Parameters associated with the Expression method request. */ params: { /** @@ -175,7 +173,7 @@ export namespace Methods { expression: string; }; /** - * Result of the RPC call. + * Result of the method response. */ result?: | { @@ -210,7 +208,7 @@ export namespace Methods { export interface PlayerStartProfilerMethod extends BaseMethod<'player-start-profiler-request'> { /** - * Parameters associated with the Profiler Details RPC. + * Parameters associated with the Profiler Details method request. */ params: { /** @@ -219,7 +217,7 @@ export namespace Methods { playerID: string; }; /** - * Result of the RPC call. + * Result of the method response. */ result?: { /** @@ -231,7 +229,7 @@ export namespace Methods { export interface PlayerStopProfilerMethod extends BaseMethod<'player-stop-profiler-request'> { /** - * Parameters associated with the Profiler Details RPC. + * Parameters associated with the Profiler Details method request. */ params: { /** @@ -240,7 +238,7 @@ export namespace Methods { playerID: string; }; /** - * Result of the RPC call. + * Result of the method response. */ result?: { /** @@ -259,7 +257,7 @@ export namespace Methods { | PlayerStartProfilerMethod | PlayerStopProfilerMethod; - // TODO: Generate this from `RuntimeRPC['type']` if I ever can + // TODO: Generate this from `Method['type']` if I ever can export const MethodTypes = [ 'player-config-request', 'player-data-binding-details', diff --git a/devtools/common/src/types.ts b/devtools/common/src/types.ts index 8e12b102..707bc5a1 100644 --- a/devtools/common/src/types.ts +++ b/devtools/common/src/types.ts @@ -1,69 +1,3 @@ -import { Events } from './events'; -import { Methods } from './methods'; - -export interface BaseEventMessage< - T extends Events.EventTypes | 'rpc-request' | 'rpc-response' -> { - /** - * Source of the Message - */ - source: string; - /** - * Unique type associated with the message. - */ - type: T; -} - -export interface BaseMessageWithPlayerID - extends BaseEventMessage { - /** - * Unique Player Id associated with the message. - */ - playerID: string; -} - -export interface MethodRequestMessageEvent> - 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 MethodResponseMessageEvent> - 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']; -} - -// TODO: This should include methods -export type Message = - | MethodRequestMessageEvent - | MethodResponseMessageEvent - | Events.Event; - // TODO: export via plugins export type ProfilerNode = { /** @@ -92,15 +26,3 @@ export type ProfilerNode = { */ children: ProfilerNode[]; }; - -export type DiscriminateByType = Extract< - Types, - { type: T } ->; - -/** Higher order utility for determining what types things are */ -export const isKnownType = (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/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/web-ext/src/background/background.ts b/devtools/web-ext/src/background/background.ts index 29173e23..060346d1 100644 --- a/devtools/web-ext/src/background/background.ts +++ b/devtools/web-ext/src/background/background.ts @@ -4,7 +4,7 @@ import { BACKGROUND_SOURCE, type Methods, } from '@player-tools/devtools-common'; -import { createDevtoolsStore, dispatchEvents } from '@player-tools/devtools-client'; +import { createDevtoolsStore, dispatchEvents, Actions, listenerMiddleware } from '@player-tools/devtools-client'; import { browser } from 'webextension-polyfill-ts'; const logger = createLogger(BACKGROUND_SOURCE); @@ -22,6 +22,15 @@ async function sendMessage( return await browser.tabs.sendMessage(tabId, method); } +// 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); diff --git a/devtools/web-ext/src/content/content.ts b/devtools/web-ext/src/content/content.ts index b5cc3a97..a519de60 100644 --- a/devtools/web-ext/src/content/content.ts +++ b/devtools/web-ext/src/content/content.ts @@ -24,12 +24,11 @@ document.documentElement.append(getScript()); type MethodWithId = Methods.ByType & { id: string }; /** Tracker for method requests sent to the frontend */ -// TODO: Clear on store reset? const requestsInFlight = new Map void>(); -/** Send method requests from background to Player */ +/** Handle messages sent from the background to the content script */ browser.runtime.onMessage.addListener((request) => { - // Only process request if known and we don't have a result yet + // Forward method requests to Player context if (Methods.isMethod(request) && !request.result) { const id = uuid(); window.postMessage({ @@ -40,10 +39,10 @@ browser.runtime.onMessage.addListener((request) => { 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() } - - // TODO: Fail silently? - return Promise.reject(`Unknown request: ${JSON.stringify(request)}`) }); /** Send window events from Player to background */ @@ -52,8 +51,9 @@ window.addEventListener('message', ({ data, source }: MessageEvent Date: Tue, 20 Dec 2022 19:57:12 -0500 Subject: [PATCH 09/10] wip 5: remove action namespaces --- devtools/client/src/redux/actions.ts | 83 ------------------- devtools/client/src/redux/actions/events.ts | 16 ++++ devtools/client/src/redux/actions/index.ts | 20 +++++ devtools/client/src/redux/actions/methods.ts | 43 ++++++++++ devtools/client/src/redux/aliases.ts | 14 ++-- devtools/client/src/redux/listeners.ts | 4 +- devtools/client/src/redux/middleware.ts | 19 ++--- devtools/client/src/redux/reducers.ts | 21 +++-- devtools/client/src/redux/store.ts | 21 +++-- devtools/common/src/index.ts | 11 --- devtools/web-ext/src/background/background.ts | 8 +- 11 files changed, 125 insertions(+), 135 deletions(-) delete mode 100644 devtools/client/src/redux/actions.ts create mode 100644 devtools/client/src/redux/actions/events.ts create mode 100644 devtools/client/src/redux/actions/index.ts create mode 100644 devtools/client/src/redux/actions/methods.ts diff --git a/devtools/client/src/redux/actions.ts b/devtools/client/src/redux/actions.ts deleted file mode 100644 index fd1f9e06..00000000 --- a/devtools/client/src/redux/actions.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { type AsyncThunk, createAsyncThunk, AnyAction } from '@reduxjs/toolkit'; -import { - createAction, - ActionCreatorWithPayload, -} from '@reduxjs/toolkit'; -import { - createLogger, - BACKGROUND_SOURCE, - // TODO: This is where being able to import the `Runtime` namespace is beneficial - Methods as RuntimeMethods, - Events as RuntimeEvents, -} from '@player-tools/devtools-common'; - -const logger = createLogger(BACKGROUND_SOURCE); - -export namespace Methods { - - /** Type describing an object containing async thunks for each Method defined */ - export type MethodThunks = { - [key in RuntimeMethods.Method["type"]]: AsyncThunk< - RuntimeMethods.ByType['result'], - RuntimeMethods.ByType, - any - >; - }; - - export type MethodHandler = ( - method: RuntimeMethods.ByType - ) => Promise['result']>; - - export const buildAsyncThunks = ( - onMethodRequest: MethodHandler - ): MethodThunks => Object.fromEntries( - RuntimeMethods.MethodTypes.map(method => - [method, createAsyncThunk< - RuntimeMethods.ByType['result'], - RuntimeMethods.ByType - >(method, async (method) => { - logger.log(`Requesting ${method.type}`, method.params); - const data = (await onMethodRequest(method)) as - RuntimeMethods.ByType['result']; - logger.log(`Response from ${method.type}`, data); - return data; - })] - ) - ) as MethodThunks -} - -// TODO: What the hell should we do here? merged namespace for Events or new namespace? -export namespace Events { - - /** Redux actions associated against all possible event types */ - type EventActions = { - [key in RuntimeEvents.EventTypes]: ActionCreatorWithPayload< - RuntimeEvents.ByType, - key - >; - }; - - export interface EventAction extends AnyAction { - payload: RuntimeEvents.ByType; - } - - /** Redux actions associated against all defined event types */ - export const actions: EventActions = Object.fromEntries( - RuntimeEvents.EventTypes.map(event => - [event, createAction>(event)] - ) - ) as EventActions -} - -/** 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/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 19c5097a..b8260cb6 100644 --- a/devtools/client/src/redux/aliases.ts +++ b/devtools/client/src/redux/aliases.ts @@ -1,5 +1,5 @@ -import { Methods as RuntimeMethods, RUNTIME_SOURCE } from '@player-tools/devtools-common'; -import { Methods } 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'; @@ -10,8 +10,8 @@ export const START_PROFILER = 'START_PROFILER'; export const STOP_PROFILER = 'STOP_PROFILER'; -export interface MethodAction { - payload: RuntimeMethods.ByType['params']; +interface MethodAction { + payload: Methods.ByType['params']; } // Copied from webext redux library not allowed in flipper @@ -26,13 +26,13 @@ const _alias = (aliases: any) => () => (next: any) => (action: any) => { }; /** Helper for building corresponding method action via supplied thunks */ -const alias = (type: T, methods: Methods.MethodThunks) => (action: MethodAction) => methods[type]({ +const alias = (type: T, methods: MethodThunks) => (action: MethodAction) => methods[type]({ type, params: action.payload, source: RUNTIME_SOURCE, -} as RuntimeMethods.ByType) +} as Methods.ByType) -export const buildAliases = (methods: Methods.MethodThunks) => +export const buildAliases = (methods: MethodThunks) => _alias({ GET_INFO_DETAILS: alias('player-runtime-info-request', methods), GET_CONFIG_DETAILS: alias('player-config-request', methods), diff --git a/devtools/client/src/redux/listeners.ts b/devtools/client/src/redux/listeners.ts index e5295250..3d757f02 100644 --- a/devtools/client/src/redux/listeners.ts +++ b/devtools/client/src/redux/listeners.ts @@ -1,8 +1,8 @@ import { Events } from "@player-tools/devtools-common"; import { Dispatch } from "redux"; -import { Events as EventActions } from "./actions"; +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.actions[message.type](message as 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 index 62c2d648..632b4049 100644 --- a/devtools/client/src/redux/middleware.ts +++ b/devtools/client/src/redux/middleware.ts @@ -1,5 +1,5 @@ import { createListenerMiddleware, isAnyOf } from "@reduxjs/toolkit"; -import { Actions, Events } from "./actions"; +import { Actions, EventActions } from "./actions"; import { GET_DATA_BINDING_DETAILS } from "./aliases"; import { type StoreState } from './state'; @@ -11,9 +11,9 @@ export const listenerMiddleware = createListenerMiddleware(); listenerMiddleware.startListening({ matcher: isAnyOf( - Events.actions['player-data-change-event'], - Events.actions['player-log-event'], - Events.actions['player-flow-start'], + EventActions['player-data-change-event'], + EventActions['player-log-event'], + EventActions['player-flow-start'], ), effect: (action, api) => { api.dispatch(Actions['player-timeline-event'](action.payload)); @@ -21,17 +21,16 @@ listenerMiddleware.startListening({ }); listenerMiddleware.startListening({ - actionCreator: Events.actions['runtime-init'], + actionCreator: EventActions['runtime-init'], effect: (_, api) => { - console.log("um") api.dispatch(Actions["clear-store"]()) } }) listenerMiddleware.startListening({ matcher: isAnyOf( - Events.actions["player-init"], - Events.actions["player-removed"], + EventActions["player-init"], + EventActions["player-removed"], ), effect: (_, api) => { api.dispatch(Actions["selected-player"]()) @@ -40,8 +39,8 @@ listenerMiddleware.startListening({ listenerMiddleware.startListening({ matcher: isAnyOf( - Events.actions["player-flow-start"], - Events.actions["player-data-change-event"], + EventActions["player-flow-start"], + EventActions["player-data-change-event"], ), effect: (action, api) => { const { players } = api.getState(); diff --git a/devtools/client/src/redux/reducers.ts b/devtools/client/src/redux/reducers.ts index 7cb46c68..91d2279a 100644 --- a/devtools/client/src/redux/reducers.ts +++ b/devtools/client/src/redux/reducers.ts @@ -1,14 +1,13 @@ -import { ActionReducerMapBuilder, createReducer } from '@reduxjs/toolkit'; +import { type ActionReducerMapBuilder, createReducer } from '@reduxjs/toolkit'; import type { PlayersState } from './state'; -import { Actions, Events, Methods } from './actions'; +import { Actions, EventActions, type MethodThunks } from './actions'; const initialState = { selectedPlayerId: null, activePlayers: {}, } as PlayersState; -// TODO: It'd be nice if methodThunks didn't have to be passed in - but it is kinda client dependent -export const methodsReducer = (methods: Methods.MethodThunks) => (builder: ActionReducerMapBuilder) => { +const methodsReducer = (methods: MethodThunks) => (builder: ActionReducerMapBuilder) => { builder.addCase( methods['player-runtime-info-request'].fulfilled, (state, action) => { @@ -116,8 +115,8 @@ export const methodsReducer = (methods: Methods.MethodThunks) => (builder: Actio ); }; -export const eventsReducer = (builder: ActionReducerMapBuilder) => { - builder.addCase(Events.actions['player-init'], (state, action) => { +const eventsReducer = (builder: ActionReducerMapBuilder) => { + builder.addCase(EventActions['player-init'], (state, action) => { const { payload: { version, playerID }, } = action; @@ -129,11 +128,11 @@ export const eventsReducer = (builder: ActionReducerMapBuilder) => state.version = version; }); - builder.addCase(Events.actions['player-removed'], (state, action) => { + builder.addCase(EventActions['player-removed'], (state, action) => { delete state.activePlayers[action.payload.playerID]; }); - builder.addCase(Events.actions['player-flow-start'], (state, action) => { + builder.addCase(EventActions['player-flow-start'], (state, action) => { const { payload: { flow, playerID }, } = action; @@ -155,7 +154,7 @@ export const eventsReducer = (builder: ActionReducerMapBuilder) => }; }); - builder.addCase(Events.actions['player-view-update-event'], (state, action) => { + builder.addCase(EventActions['player-view-update-event'], (state, action) => { const { payload: { playerID, update }, } = action; @@ -175,7 +174,7 @@ export const eventsReducer = (builder: ActionReducerMapBuilder) => }); }; -export const actionsReducer = (builder: ActionReducerMapBuilder) => { +const actionsReducer = (builder: ActionReducerMapBuilder) => { builder.addCase(Actions['selected-player'], (state, action) => { if (action.payload) { state.selectedPlayerId = action.payload; @@ -240,7 +239,7 @@ export const actionsReducer = (builder: ActionReducerMapBuilder) = }); }; -export const playersReducer = (methods: Methods.MethodThunks) => +export const playersReducer = (methods: MethodThunks) => createReducer(initialState, (builder) => { actionsReducer(builder) eventsReducer(builder) diff --git a/devtools/client/src/redux/store.ts b/devtools/client/src/redux/store.ts index caed4016..9dfc2fcb 100644 --- a/devtools/client/src/redux/store.ts +++ b/devtools/client/src/redux/store.ts @@ -7,13 +7,17 @@ import { ReducersMapObject, } from '@reduxjs/toolkit'; import { StoreState } from './state'; -import { Methods } from './actions'; +import { + type MethodThunks, + type MethodHandler, + buildMethodThunks, +} from './actions'; import { playersReducer } from './reducers'; import { listenerMiddleware } from './middleware'; import { buildAliases } from './aliases'; const createStore = ( - methodThunks: Methods.MethodThunks, + methodThunks: MethodThunks, middleware?: Middleware>, reducers?: ReducersMapObject ): EnhancedStore< @@ -30,9 +34,9 @@ const createStore = ( middleware: (getDefaultMiddleware) => { const m = getDefaultMiddleware() .prepend(buildAliases(methodThunks)) - .concat(listenerMiddleware.middleware) + .concat(listenerMiddleware.middleware); - if (middleware) m.concat(middleware) + if (middleware) m.concat(middleware); return m; }, @@ -46,11 +50,16 @@ const createStore = ( */ // TODO: Turn into store enhancer? Maybe remove middleware and additionalReducers? export const createDevtoolsStore = ( - onMethodRequest: Methods.MethodHandler, + onMethodRequest: MethodHandler, middleware?: Middleware>, additionalReducers?: any ): EnhancedStore< StoreState, any, Middleware>[] -> => createStore(Methods.buildAsyncThunks(onMethodRequest), middleware, additionalReducers) +> => + createStore( + buildMethodThunks(onMethodRequest), + middleware, + additionalReducers + ); diff --git a/devtools/common/src/index.ts b/devtools/common/src/index.ts index 1e28be58..b8bd39bd 100644 --- a/devtools/common/src/index.ts +++ b/devtools/common/src/index.ts @@ -1,16 +1,5 @@ -import { Events as events } from './events'; -import { Methods as methods } from './methods'; - export * from './types'; export * from './logger'; export { Events } from './events'; export { Methods } from './methods'; - -// TODO: I'm really not sure about the Runtime namespace -export namespace Runtime { - // export const Methods = methods; - // export const Events = events; - // export namespace Methods {} - // export namespace Events {} -} diff --git a/devtools/web-ext/src/background/background.ts b/devtools/web-ext/src/background/background.ts index 060346d1..b20602d3 100644 --- a/devtools/web-ext/src/background/background.ts +++ b/devtools/web-ext/src/background/background.ts @@ -9,17 +9,15 @@ import { browser } from 'webextension-polyfill-ts'; const logger = createLogger(BACKGROUND_SOURCE); -async function sendMessage( - method: Methods.ByType, -): Promise['result']> { +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"); + if (!tabId) return Promise.reject('could not find active tab'); - return await browser.tabs.sendMessage(tabId, method); + return await browser.tabs.sendMessage(tabId, message); } // Configure middleware to forward 'clear-store' actions From c2a996d9c9a09f91ea988921aa5e70eb73a733f2 Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Wed, 21 Dec 2022 17:44:29 -0500 Subject: [PATCH 10/10] use prod bundle of devtools ui --- devtools/flipper/src/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/flipper/src/index.tsx b/devtools/flipper/src/index.tsx index d7a24336..4471da4c 100644 --- a/devtools/flipper/src/index.tsx +++ b/devtools/flipper/src/index.tsx @@ -10,7 +10,7 @@ import { 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'; +import { App } from '@player-tools/devtools-ui/dist/devtools-ui.prod'; type Events = { [type in Events.Event['type']]: Events.ByType;