or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

browser-launchers.mdcustom-integration.mdindex.mdplugin-development.mdplugin-management.mdpuppeteer-compatibility.md

plugin-development.mddocs/

0

# Plugin Development

1

2

Interfaces and types for creating compatible plugins that work with both Playwright Extra and puppeteer-extra. The plugin system provides comprehensive lifecycle hooks and compatibility features for extending browser automation capabilities.

3

4

## Capabilities

5

6

### Plugin Interface

7

8

The core interface that all plugins must implement to be compatible with Playwright Extra.

9

10

```typescript { .api }

11

/**

12

* PuppeteerExtraPlugin interface, strongly typed for internal use

13

*/

14

interface PuppeteerExtraPlugin extends Partial<PluginLifecycleMethods> {

15

/** Plugin identification flag - must be true */

16

_isPuppeteerExtraPlugin: boolean;

17

/** Unique plugin name */

18

name: string;

19

/** Disable the puppeteer compatibility shim for this plugin */

20

noPuppeteerShim?: boolean;

21

/** Plugin requirements and capabilities */

22

requirements?: PluginRequirements;

23

/** Plugin dependencies that should be auto-loaded */

24

dependencies?: PluginDependencies;

25

/** Data that can be shared with other plugins */

26

data?: PluginData[];

27

/** Access data from other plugins (if dataFromPlugins requirement is set) */

28

getDataFromPlugins?(name?: string): void;

29

/** Method for registering child class members (PuppeteerExtraPlugin compatibility) */

30

_registerChildClassMembers?(prototype: any): void;

31

/** List of child class members (PuppeteerExtraPlugin compatibility) */

32

_childClassMembers?: string[];

33

/** Sub-plugins that should be registered automatically */

34

plugins?: CompatiblePlugin[];

35

}

36

```

37

38

### Plugin Lifecycle Methods

39

40

Abstract class defining all available lifecycle hooks that plugins can implement.

41

42

```typescript { .api }

43

/**

44

* Strongly typed plugin lifecycle events for internal use

45

*/

46

abstract class PluginLifecycleMethods {

47

/** Called when plugin is registered with the framework */

48

async onPluginRegistered(env?: PluginEnv): Promise<void> {}

49

50

/** Called before browser launch, can modify launch options */

51

async beforeLaunch(options: LaunchOptions): Promise<LaunchOptions | void> {}

52

53

/** Called after browser launch, receives browser or context instance */

54

async afterLaunch(browserOrContext?: Browser | BrowserContext): Promise<void> {}

55

56

/** Called before connecting to existing browser, can modify connect options */

57

async beforeConnect(options: ConnectOptions): Promise<ConnectOptions | void> {}

58

59

/** Called after connecting to existing browser */

60

async afterConnect(browser: Browser): Promise<void> {}

61

62

/** Called when browser instance is available */

63

async onBrowser(browser: Browser): Promise<void> {}

64

65

/** Called when a new page is created */

66

async onPageCreated(page: Page): Promise<void> {}

67

68

/** Called when a page is closed */

69

async onPageClose(page: Page): Promise<void> {}

70

71

/** Called when browser is disconnected */

72

async onDisconnected(browser?: Browser): Promise<void> {}

73

74

/** Called before creating browser context, can modify context options */

75

async beforeContext(

76

options?: BrowserContextOptions,

77

browser?: Browser

78

): Promise<BrowserContextOptions | void> {}

79

80

/** Called when a new browser context is created */

81

async onContextCreated(

82

context?: BrowserContext,

83

options?: BrowserContextOptions

84

): Promise<void> {}

85

}

86

```

87

88

### Basic Plugin Example

89

90

Simple plugin implementation demonstrating the core structure:

91

92

```typescript

93

import { PuppeteerExtraPlugin } from 'puppeteer-extra-plugin';

94

95

class MyPlugin extends PuppeteerExtraPlugin {

96

constructor(opts = {}) {

97

super(opts);

98

}

99

100

get name() {

101

return 'my-plugin';

102

}

103

104

async onPluginRegistered() {

105

console.log('Plugin registered with Playwright Extra');

106

}

107

108

async beforeLaunch(options) {

109

console.log('Before launch:', options);

110

// Modify launch options

111

options.args = options.args || [];

112

options.args.push('--custom-flag');

113

return options;

114

}

115

116

async onPageCreated(page) {

117

console.log('New page created');

118

// Add custom functionality to pages

119

await page.addInitScript(() => {

120

console.log('Custom script injected');

121

});

122

}

123

}

124

125

module.exports = (pluginConfig) => new MyPlugin(pluginConfig);

126

```

127

128

### Plugin Requirements

129

130

Plugins can declare requirements that affect their behavior and execution order:

131

132

```typescript { .api }

133

type PluginRequirements = Set<

134

| 'launch' // Plugin requires browser launch

135

| 'headful' // Plugin requires headful (non-headless) mode

136

| 'dataFromPlugins' // Plugin needs access to data from other plugins

137

| 'runLast' // Plugin should run after all other plugins

138

>;

139

```

140

141

**Usage Examples:**

142

143

```typescript

144

class DataCollectorPlugin extends PuppeteerExtraPlugin {

145

constructor(opts = {}) {

146

super(opts);

147

this.requirements = new Set(['dataFromPlugins', 'runLast']);

148

}

149

150

get name() {

151

return 'data-collector';

152

}

153

154

async onPluginRegistered() {

155

// This plugin will have access to getDataFromPlugins method

156

const allData = this.getDataFromPlugins();

157

console.log('Data from other plugins:', allData);

158

}

159

}

160

```

161

162

### Plugin Dependencies

163

164

Plugins can declare dependencies that will be automatically loaded:

165

166

```typescript { .api }

167

type PluginDependencies = Set<string> | Map<string, any> | string[];

168

```

169

170

**Usage Examples:**

171

172

```typescript

173

class CompositePlugin extends PuppeteerExtraPlugin {

174

constructor(opts = {}) {

175

super(opts);

176

// Declare dependencies as Set

177

this.dependencies = new Set([

178

'stealth/evasions/webgl.vendor',

179

'stealth/evasions/user-agent-override'

180

]);

181

}

182

183

get name() {

184

return 'composite-plugin';

185

}

186

}

187

188

class ConfigurablePlugin extends PuppeteerExtraPlugin {

189

constructor(opts = {}) {

190

super(opts);

191

// Declare dependencies with options as Map

192

this.dependencies = new Map([

193

['stealth/evasions/webgl.vendor', {

194

vendor: 'Custom',

195

renderer: 'Custom'

196

}],

197

['stealth/evasions/user-agent-override', {

198

userAgent: 'Custom User Agent'

199

}]

200

]);

201

}

202

203

get name() {

204

return 'configurable-plugin';

205

}

206

}

207

```

208

209

### Plugin Data Sharing

210

211

Plugins can expose data that other plugins can access:

212

213

```typescript { .api }

214

interface PluginData {

215

name: string | { [key: string]: any };

216

value: { [key: string]: any };

217

}

218

```

219

220

**Usage Examples:**

221

222

```typescript

223

class DataProviderPlugin extends PuppeteerExtraPlugin {

224

constructor(opts = {}) {

225

super(opts);

226

this.data = [

227

{

228

name: 'config',

229

value: { apiKey: 'secret-key', endpoint: 'https://api.example.com' }

230

},

231

{

232

name: 'cache',

233

value: { results: [], timestamps: [] }

234

}

235

];

236

}

237

238

get name() {

239

return 'data-provider';

240

}

241

}

242

243

class DataConsumerPlugin extends PuppeteerExtraPlugin {

244

constructor(opts = {}) {

245

super(opts);

246

this.requirements = new Set(['dataFromPlugins']);

247

}

248

249

get name() {

250

return 'data-consumer';

251

}

252

253

async onPluginRegistered() {

254

// Access data from other plugins

255

const configData = this.getDataFromPlugins('config');

256

const cacheData = this.getDataFromPlugins('cache');

257

258

console.log('Config:', configData);

259

console.log('Cache:', cacheData);

260

}

261

}

262

```

263

264

### Advanced Plugin Pattern

265

266

Complex plugin with multiple lifecycle hooks and error handling:

267

268

```typescript

269

class AdvancedPlugin extends PuppeteerExtraPlugin {

270

constructor(opts = {}) {

271

super(opts);

272

this.options = { timeout: 30000, retry: 3, ...opts };

273

}

274

275

get name() {

276

return 'advanced-plugin';

277

}

278

279

async beforeLaunch(options) {

280

try {

281

// Pre-launch setup

282

console.log('Configuring browser launch');

283

options.args = options.args || [];

284

options.args.push(`--timeout=${this.options.timeout}`);

285

return options;

286

} catch (error) {

287

console.error('Error in beforeLaunch:', error);

288

throw error;

289

}

290

}

291

292

async onBrowser(browser) {

293

// Set up browser-level monitoring

294

browser.on('disconnected', () => {

295

console.log('Browser disconnected');

296

});

297

}

298

299

async beforeContext(options, browser) {

300

// Configure context options

301

options = options || {};

302

options.userAgent = 'Custom User Agent';

303

return options;

304

}

305

306

async onPageCreated(page) {

307

// Set up page-level functionality

308

await page.addInitScript(() => {

309

// Inject custom functionality

310

window.customAPI = {

311

version: '1.0.0',

312

initialized: true

313

};

314

});

315

316

// Add error handling

317

page.on('pageerror', error => {

318

console.error('Page error:', error);

319

});

320

}

321

}

322

323

module.exports = (pluginConfig) => new AdvancedPlugin(pluginConfig);

324

```

325

326

## Core Types

327

328

```typescript { .api }

329

type PluginMethodName = keyof PluginLifecycleMethods;

330

331

interface PluginEnv {

332

framework: 'playwright';

333

}

334

335

type CompatiblePlugin =

336

| CompatiblePuppeteerPlugin

337

| CompatiblePlaywrightPlugin

338

| CompatibleExtraPlugin;

339

340

interface CompatiblePuppeteerPlugin {

341

_isPuppeteerExtraPlugin: boolean;

342

name?: string;

343

}

344

345

interface CompatiblePlaywrightPlugin {

346

_isPlaywrightExtraPlugin: boolean;

347

name?: string;

348

}

349

350

interface CompatibleExtraPlugin {

351

_isExtraPlugin: boolean;

352

name?: string;

353

}

354

```