Skip to content

Commit fcaa3cf

Browse files
committed
feat: first stab at documenting plugins + simplified plugin architecture
1 parent b83c4b9 commit fcaa3cf

File tree

85 files changed

+758
-637
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+758
-637
lines changed

client/lib/Agent.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import CSSStyleDeclaration from 'awaited-dom/impl/official-klasses/CSSStyleDecla
2727
import IAgentMeta from '@secret-agent/interfaces/IAgentMeta';
2828
import IScreenshotOptions from '@secret-agent/interfaces/IScreenshotOptions';
2929
import { INodeVisibility } from '@secret-agent/interfaces/INodeVisibility';
30-
import { IClientExtender } from '@secret-agent/interfaces/IPluginClientExtender';
30+
import { IClientPlugin } from '@secret-agent/interfaces/IClientPlugin';
3131
import IAgent from '@secret-agent/interfaces/IAgent';
3232
import WebsocketResource from './WebsocketResource';
3333
import IWaitForResourceFilter from '../interfaces/IWaitForResourceFilter';
@@ -52,7 +52,7 @@ import FrameEnvironment, {
5252
getCoreFrameEnvironment,
5353
getCoreFrameEnvironmentForPosition,
5454
} from './FrameEnvironment';
55-
import getClientExtenderPluginClass from './getClientExtenderPluginClass';
55+
import getClientPluginClass from './getClientPluginClass';
5656
import FrozenTab from './FrozenTab';
5757
import FileChooser from './FileChooser';
5858
import Output, { createObservableOutput } from './Output';
@@ -70,7 +70,7 @@ export interface IState {
7070
connection: SessionConnection;
7171
isClosing: boolean;
7272
options: ISessionCreateOptions & Pick<IAgentCreateOptions, 'connectionToCore' | 'showReplay'>;
73-
plugins: IClientExtender[];
73+
clientPlugins: IClientPlugin[];
7474
}
7575

7676
const propertyKeys: (keyof Agent)[] = [
@@ -122,7 +122,7 @@ export default class Agent extends AwaitedEventTarget<{ close: void }> implement
122122
scriptInstanceMeta: scriptInstance.meta,
123123
dependencyMap: {},
124124
},
125-
plugins: [],
125+
clientPlugins: [],
126126
});
127127
}
128128

@@ -317,11 +317,11 @@ export default class Agent extends AwaitedEventTarget<{ close: void }> implement
317317
// PLUGINS
318318

319319
public use(PluginObject): Agent {
320-
const ClientExtender = getClientExtenderPluginClass(PluginObject);
321-
const { plugins, options } = getState(this);
322-
const plugin = new ClientExtender();
323-
plugins.push(plugin);
324-
options.dependencyMap[ClientExtender.id] = ClientExtender.coreDependencyIds || [];
320+
const ClientPlugin = getClientPluginClass(PluginObject);
321+
const { clientPlugins, options } = getState(this);
322+
const clientPlugin = new ClientPlugin();
323+
clientPlugins.push(clientPlugin);
324+
options.dependencyMap[ClientPlugin.id] = ClientPlugin.coreDependencyIds || [];
325325
return this;
326326
}
327327

@@ -507,7 +507,7 @@ class SessionConnection {
507507
}
508508
this.hasConnected = true;
509509

510-
const { plugins } = getState(this.agent);
510+
const { clientPlugins } = getState(this.agent);
511511
const { showReplay, connectionToCore, ...options } = getState(this.agent)
512512
.options as IAgentCreateOptions;
513513

@@ -536,16 +536,16 @@ class SessionConnection {
536536
this._activeTab = createTab(this.agent, coreTab);
537537
this._tabs = [this._activeTab];
538538

539-
for (const plugin of plugins) {
540-
await plugin.onAgent(this.agent, this.sendToActiveTab.bind(this));
539+
for (const clientPlugin of clientPlugins) {
540+
await clientPlugin.onAgent(this.agent, this.sendToActiveTab.bind(this));
541541
}
542542

543543
return await coreSession;
544544
}
545545

546-
private async sendToActiveTab(sendToPluginId: string, ...args: any[]): Promise<any> {
546+
private async sendToActiveTab(toPluginId: string, ...args: any[]): Promise<any> {
547547
const coreSession = (await this._coreSession) as CoreSession;
548548
const coreTab = coreSession.tabsById.get(await this._activeTab.tabId);
549-
return coreTab.commandQueue.run('Tab.runPluginCommand', sendToPluginId, args);
549+
return coreTab.commandQueue.run('Tab.runPluginCommand', toPluginId, args);
550550
}
551551
}

client/lib/Tab.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ export default class Tab extends AwaitedEventTarget<IEventType> implements ITab
8989
return (await coreTab).commandQueue.run('Tab.runPluginCommand', pluginId, args);
9090
}
9191

92-
for (const plugin of agentState.getState(agent).plugins) {
93-
plugin.onTab(agent, this, sendToTab);
92+
for (const clientPlugin of agentState.getState(agent).clientPlugins) {
93+
clientPlugin.onTab(agent, this, sendToTab);
9494
}
9595
}
9696

client/lib/getClientExtenderPluginClass.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

client/lib/getClientPluginClass.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { IClientPluginClass } from '@secret-agent/interfaces/IClientPlugin';
2+
import ClientPlugin from '@secret-agent/plugin-utils/lib/ClientPlugin';
3+
import { PluginTypes } from '@secret-agent/interfaces/IPluginTypes';
4+
5+
export default function getClientPluginClass(PluginObject: any): IClientPluginClass {
6+
if (isClientPluginClass(PluginObject)) {
7+
return PluginObject;
8+
}
9+
if (isClientPluginClass(PluginObject.ClientPlugin)) {
10+
return PluginObject.ClientPlugin;
11+
}
12+
throw new Error(`Plugin (${PluginObject.name}) is not a valid ClientPlugin`);
13+
}
14+
15+
function isClientPluginClass(PluginObject: any): boolean {
16+
if (!PluginObject) return false;
17+
if (PluginObject.prototype instanceof ClientPlugin) {
18+
return true;
19+
}
20+
return PluginObject.type === PluginTypes.ClientPlugin;
21+
}

core/index.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import ICoreConfigureOptions from '@secret-agent/interfaces/ICoreConfigureOption
22
import { LocationTrigger } from '@secret-agent/interfaces/Location';
33
import Log, { hasBeenLoggedSymbol } from '@secret-agent/commons/Logger';
44
import Resolvable from '@secret-agent/commons/Resolvable';
5-
import { IHumanEmulatorClass } from '@secret-agent/interfaces/IPluginHumanEmulator';
6-
import { IBrowserEmulatorClass } from '@secret-agent/interfaces/IPluginBrowserEmulator';
7-
import { ICoreExtenderClass } from '@secret-agent/interfaces/IPluginCoreExtender';
5+
import {
6+
IHumanEmulatorClass,
7+
IBrowserEmulatorClass,
8+
ICorePluginClass,
9+
} from '@secret-agent/interfaces/ICorePlugin';
810
import { PluginTypes } from '@secret-agent/interfaces/IPluginTypes';
911
import DefaultBrowserEmulator from '@secret-agent/default-browser-emulator';
1012
import DefaultHumanEmulator from '@secret-agent/default-human-emulator';
@@ -24,17 +26,17 @@ export default class Core {
2426
public static server = new CoreServer();
2527
public static readonly connections: ConnectionToClient[] = [];
2628
public static pluginMap: {
27-
humanEmulatorsById: { [id: string]: IHumanEmulatorClass }
28-
browserEmulatorsById: { [id: string]: IBrowserEmulatorClass }
29-
coreExtenders: ICoreExtenderClass[];
29+
humanEmulatorsById: { [id: string]: IHumanEmulatorClass };
30+
browserEmulatorsById: { [id: string]: IBrowserEmulatorClass };
31+
corePlugins: ICorePluginClass[];
3032
} = {
3133
humanEmulatorsById: {
3234
[DefaultHumanEmulator.id]: DefaultHumanEmulator,
3335
},
3436
browserEmulatorsById: {
3537
[DefaultBrowserEmulator.id]: DefaultBrowserEmulator,
3638
},
37-
coreExtenders: [],
39+
corePlugins: [],
3840
};
3941

4042
public static onShutdown: () => void;
@@ -55,13 +57,13 @@ export default class Core {
5557
return connection;
5658
}
5759

58-
public static use(Plugin: IHumanEmulatorClass | IBrowserEmulatorClass | ICoreExtenderClass) {
59-
if (Plugin.pluginType === PluginTypes.HumanEmulator) {
60-
this.pluginMap.humanEmulatorsById[Plugin.id] = Plugin;
61-
} else if (Plugin.pluginType === PluginTypes.BrowserEmulator) {
62-
this.pluginMap.browserEmulatorsById[Plugin.id] = Plugin;
63-
} else if (Plugin.pluginType === PluginTypes.CoreExtender) {
64-
this.pluginMap.coreExtenders.push(Plugin);
60+
public static use(CorePlugin: ICorePluginClass) {
61+
if (CorePlugin.type === PluginTypes.HumanEmulator) {
62+
this.pluginMap.humanEmulatorsById[CorePlugin.id] = CorePlugin as IHumanEmulatorClass;
63+
} else if (CorePlugin.type === PluginTypes.BrowserEmulator) {
64+
this.pluginMap.browserEmulatorsById[CorePlugin.id] = CorePlugin as IBrowserEmulatorClass;
65+
} else if (CorePlugin.type === PluginTypes.CorePlugin) {
66+
this.pluginMap.corePlugins.push(CorePlugin);
6567
} else {
6668
throw new Error('Unknown plugin type');
6769
}
Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { URL } from 'url';
22
import { IBoundLog } from '@secret-agent/interfaces/ILog';
3-
import { ICoreExtender, IOnCommandMeta } from '@secret-agent/interfaces/IPluginCoreExtender';
43
import { IPuppetPage } from '@secret-agent/interfaces/IPuppetPage';
54
import { IPuppetWorker } from '@secret-agent/interfaces/IPuppetWorker';
65
import IHttpResourceLoadDetails from '@secret-agent/interfaces/IHttpResourceLoadDetails';
@@ -10,15 +9,16 @@ import ITlsSettings from '@secret-agent/interfaces/ITlsSettings';
109
import { IInteractionGroups, IInteractionStep } from '@secret-agent/interfaces/IInteractions';
1110
import IInteractionsHelper from '@secret-agent/interfaces/IInteractionsHelper';
1211
import IPoint from '@secret-agent/interfaces/IPoint';
13-
import {
12+
import ICorePlugin, {
13+
IHumanEmulator,
1414
IBrowserEmulator,
1515
IBrowserEmulatorConfig,
1616
ISelectBrowserMeta,
17-
} from '@secret-agent/interfaces/IPluginBrowserEmulator';
18-
import { IHumanEmulator } from '@secret-agent/interfaces/IPluginHumanEmulator';
19-
import IPlugins from '@secret-agent/interfaces/IPlugins';
20-
import IPlugin, { IPluginClass } from '@secret-agent/interfaces/IPlugin';
21-
import IPluginCreateOptions from '@secret-agent/interfaces/IPluginCreateOptions';
17+
ICorePluginClass,
18+
IOnClientCommandMeta,
19+
} from '@secret-agent/interfaces/ICorePlugin';
20+
import ICorePlugins from '@secret-agent/interfaces/ICorePlugins';
21+
import ICorePluginCreateOptions from '@secret-agent/interfaces/ICorePluginCreateOptions';
2222
import IBrowserEngine from '@secret-agent/interfaces/IBrowserEngine';
2323
import { PluginTypes } from '@secret-agent/interfaces/IPluginTypes';
2424
import Core from '../index';
@@ -34,16 +34,16 @@ interface IOptionsCreate {
3434
dependencyMap?: { [clientPluginId: string]: string[] };
3535
}
3636

37-
export default class Plugins implements IPlugins {
38-
public static pluginClassesById: { [id: string]: IPluginClass } = {};
37+
export default class CorePlugins implements ICorePlugins {
38+
public static corePluginClassesById: { [id: string]: ICorePluginClass } = {};
3939

4040
public readonly browserEngine: IBrowserEngine;
4141
public readonly browserEmulator: IBrowserEmulator;
4242
public readonly humanEmulator: IHumanEmulator;
4343

44-
private readonly instances: ICoreExtender[] = [];
45-
private readonly instanceById: { [id: string]: ICoreExtender } = {};
46-
private readonly createOptions: IPluginCreateOptions;
44+
private readonly instances: ICorePlugin[] = [];
45+
private readonly instanceById: { [id: string]: ICorePlugin } = {};
46+
private readonly createOptions: ICorePluginCreateOptions;
4747
private readonly logger: IBoundLog;
4848

4949
constructor(options: IOptionsCreate, logger: IBoundLog) {
@@ -66,34 +66,34 @@ export default class Plugins implements IPlugins {
6666

6767
const { browserEngine, userAgentOption } =
6868
options.selectBrowserMeta || BrowserEmulator.selectBrowserMeta(userAgentSelector);
69-
this.createOptions = { browserEngine, logger, plugins: this };
69+
this.createOptions = { browserEngine, userAgentOption, logger, corePlugins: this };
7070
this.browserEngine = browserEngine;
7171
this.logger = logger;
7272

73-
this.browserEmulator = new BrowserEmulator(this.createOptions, userAgentOption);
73+
this.browserEmulator = new BrowserEmulator(this.createOptions);
7474
this.addPluginInstance(this.browserEmulator);
7575

7676
this.humanEmulator = new HumanEmulator(this.createOptions);
7777
this.addPluginInstance(this.humanEmulator);
7878

79-
Core.pluginMap.coreExtenders.forEach(x => this.use(x));
79+
Core.pluginMap.corePlugins.forEach(x => this.use(x));
8080

8181
if (dependencyMap && Core.allowDynamicPluginDependencies) {
8282
Object.entries(dependencyMap).forEach(([clientPluginId, dependentPluginIds]) => {
83-
dependentPluginIds.forEach(pluginId => {
84-
if (this.instanceById[pluginId]) return;
85-
this.logger.info(`Dynamically using ${pluginId} required by ${clientPluginId}`);
86-
const Plugin = this.require(pluginId);
87-
if (!Plugin) {
88-
this.logger.warn(`Could not find ${pluginId} required by ${clientPluginId}`);
83+
dependentPluginIds.forEach(corePluginId => {
84+
if (this.instanceById[corePluginId]) return;
85+
this.logger.info(`Dynamically using ${corePluginId} required by ${clientPluginId}`);
86+
const PluginObject = this.require(corePluginId);
87+
if (!PluginObject) {
88+
this.logger.warn(`Could not find ${corePluginId} required by ${clientPluginId}`);
8989
return;
9090
}
91-
const CoreExtender = (Plugin as any).CoreExtender || Plugin;
92-
if (CoreExtender.pluginType !== PluginTypes.CoreExtender) {
93-
this.logger.warn(`Could not use ${pluginId} because it's not a CoreExtender`);
91+
const CorePlugin = (PluginObject as any).CorePlugin || PluginObject;
92+
if (CorePlugin.type !== PluginTypes.CorePlugin) {
93+
this.logger.warn(`Could not use ${corePluginId} because it's not a CorePlugin`);
9494
return;
9595
}
96-
this.use(CoreExtender);
96+
this.use(CorePlugin);
9797
});
9898
});
9999
}
@@ -176,40 +176,40 @@ export default class Plugins implements IPlugins {
176176
// PLUGIN COMMANDS
177177

178178
public async onPluginCommand(
179-
sendToPluginId: string,
180-
commandMeta: IOnCommandMeta,
179+
toPluginId: string,
180+
commandMeta: IOnClientCommandMeta,
181181
args: any[],
182182
): Promise<any> {
183-
const plugin = this.instanceById[sendToPluginId];
184-
if (plugin && plugin.onCommand) {
185-
return await plugin.onCommand(commandMeta, ...args);
183+
const plugin = this.instanceById[toPluginId];
184+
if (plugin && plugin.onClientCommand) {
185+
return await plugin.onClientCommand(commandMeta, ...args);
186186
}
187-
this.logger.warn(`Plugin (${sendToPluginId}) could not be found for command`);
187+
this.logger.warn(`Plugin (${toPluginId}) could not be found for command`);
188188
}
189189

190190
// ADDING PLUGINS TO THE STACK
191191

192-
public use(Plugin: IPluginClass) {
193-
if (this.instanceById[Plugin.id]) return;
194-
this.addPluginInstance(new Plugin(this.createOptions));
192+
public use(CorePlugin: ICorePluginClass) {
193+
if (this.instanceById[CorePlugin.id]) return;
194+
this.addPluginInstance(new CorePlugin(this.createOptions));
195195
}
196196

197-
private addPluginInstance(plugin: IPlugin) {
198-
this.instances.push(plugin);
199-
this.instanceById[plugin.id] = plugin;
197+
private addPluginInstance(corePlugin: ICorePlugin) {
198+
this.instances.push(corePlugin);
199+
this.instanceById[corePlugin.id] = corePlugin;
200200
}
201201

202-
private require(pluginId: string): IPluginClass {
203-
if (!Plugins.pluginClassesById[pluginId]) {
202+
private require(corePluginId: string): ICorePluginClass {
203+
if (!CorePlugins.corePluginClassesById[corePluginId]) {
204204
try {
205205
// eslint-disable-next-line global-require,import/no-dynamic-require
206-
const Plugin = require(pluginId);
207-
if (!Plugin) return;
208-
Plugins.pluginClassesById[pluginId] = Plugin.default || Plugin;
206+
const CorePlugin = require(corePluginId);
207+
if (!CorePlugin) return;
208+
CorePlugins.corePluginClassesById[corePluginId] = CorePlugin.default || CorePlugin;
209209
} catch (error) {
210210
return;
211211
}
212212
}
213-
return Plugins.pluginClassesById[pluginId];
213+
return CorePlugins.corePluginClassesById[corePluginId];
214214
}
215215
}

core/lib/Interactor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import IPoint from '@secret-agent/interfaces/IPoint';
2323
import IMouseUpResult from '@secret-agent/interfaces/IMouseUpResult';
2424
import IResolvablePromise from '@secret-agent/interfaces/IResolvablePromise';
2525
import { IPuppetKeyboard, IPuppetMouse } from '@secret-agent/interfaces/IPuppetInput';
26-
import IPlugins from '@secret-agent/interfaces/IPlugins';
26+
import ICorePlugins from '@secret-agent/interfaces/ICorePlugins';
2727
import IViewport from '@secret-agent/interfaces/IViewport';
2828
import IElementRect from '@secret-agent/interfaces/IElementRect';
2929
import { INodeVisibility } from '@secret-agent/interfaces/INodeVisibility';
@@ -83,7 +83,7 @@ export default class Interactor implements IInteractionsHelper {
8383
return this.tab.puppetPage.keyboard;
8484
}
8585

86-
private get plugins(): IPlugins {
86+
private get plugins(): ICorePlugins {
8787
return this.tab.session.plugins;
8888
}
8989

0 commit comments

Comments
 (0)