@marktoflow/integrations 2.0.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.md +1341 -0
  2. package/dist/adapters/claude-agent-hooks.d.ts.map +1 -0
  3. package/dist/adapters/claude-agent-hooks.js +339 -0
  4. package/dist/adapters/claude-agent-hooks.js.map +1 -0
  5. package/dist/adapters/claude-agent-types.d.ts +426 -0
  6. package/dist/adapters/claude-agent-types.d.ts.map +1 -0
  7. package/dist/adapters/claude-agent-types.js +62 -0
  8. package/dist/adapters/claude-agent-types.js.map +1 -0
  9. package/dist/adapters/claude-agent-workflow.d.ts +393 -0
  10. package/dist/adapters/claude-agent-workflow.d.ts.map +1 -0
  11. package/dist/adapters/claude-agent-workflow.js +445 -0
  12. package/dist/adapters/claude-agent-workflow.js.map +1 -0
  13. package/dist/adapters/claude-agent.d.ts +189 -0
  14. package/dist/adapters/claude-agent.d.ts.map +1 -0
  15. package/dist/adapters/claude-agent.js +504 -0
  16. package/dist/adapters/claude-agent.js.map +1 -0
  17. package/dist/adapters/claude-code.d.ts +34 -0
  18. package/dist/adapters/claude-code.d.ts.map +1 -0
  19. package/dist/adapters/claude-code.js +89 -0
  20. package/dist/adapters/claude-code.js.map +1 -0
  21. package/dist/adapters/codex-types.d.ts +463 -0
  22. package/dist/adapters/codex-types.d.ts.map +1 -0
  23. package/dist/adapters/codex-types.js +53 -0
  24. package/dist/adapters/codex-types.js.map +1 -0
  25. package/dist/adapters/codex-workflow.d.ts +414 -0
  26. package/dist/adapters/codex-workflow.d.ts.map +1 -0
  27. package/dist/adapters/codex-workflow.js +470 -0
  28. package/dist/adapters/codex-workflow.js.map +1 -0
  29. package/dist/adapters/codex.d.ts +154 -0
  30. package/dist/adapters/codex.d.ts.map +1 -0
  31. package/dist/adapters/codex.js +416 -0
  32. package/dist/adapters/codex.js.map +1 -0
  33. package/dist/adapters/github-copilot-types.d.ts +1012 -0
  34. package/dist/adapters/github-copilot-types.d.ts.map +1 -0
  35. package/dist/adapters/github-copilot-types.js +80 -0
  36. package/dist/adapters/github-copilot-types.js.map +1 -0
  37. package/dist/adapters/github-copilot-workflow.d.ts +462 -0
  38. package/dist/adapters/github-copilot-workflow.d.ts.map +1 -0
  39. package/dist/adapters/github-copilot-workflow.js +473 -0
  40. package/dist/adapters/github-copilot-workflow.js.map +1 -0
  41. package/dist/adapters/github-copilot.d.ts +280 -0
  42. package/dist/adapters/github-copilot.d.ts.map +1 -0
  43. package/dist/adapters/github-copilot.js +468 -0
  44. package/dist/adapters/github-copilot.js.map +1 -0
  45. package/dist/adapters/ollama.d.ts.map +1 -0
  46. package/dist/adapters/ollama.js +9 -0
  47. package/dist/adapters/ollama.js.map +1 -0
  48. package/dist/adapters/opencode.d.ts +41 -0
  49. package/dist/adapters/opencode.d.ts.map +1 -0
  50. package/dist/adapters/opencode.js +148 -0
  51. package/dist/adapters/opencode.js.map +1 -0
  52. package/dist/index.d.ts +52 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +161 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/services/ai-browser.d.ts +112 -0
  57. package/dist/services/ai-browser.d.ts.map +1 -0
  58. package/dist/services/ai-browser.js +363 -0
  59. package/dist/services/ai-browser.js.map +1 -0
  60. package/dist/services/airtable.d.ts.map +1 -0
  61. package/dist/services/airtable.js +295 -0
  62. package/dist/services/airtable.js.map +1 -0
  63. package/dist/services/asana.d.ts +159 -0
  64. package/dist/services/asana.d.ts.map +1 -0
  65. package/dist/services/asana.js +196 -0
  66. package/dist/services/asana.js.map +1 -0
  67. package/dist/services/aws-s3.d.ts +89 -0
  68. package/dist/services/aws-s3.d.ts.map +1 -0
  69. package/dist/services/aws-s3.js +154 -0
  70. package/dist/services/aws-s3.js.map +1 -0
  71. package/dist/services/confluence.d.ts.map +1 -0
  72. package/dist/services/confluence.js +356 -0
  73. package/dist/services/confluence.js.map +1 -0
  74. package/dist/services/discord.d.ts.map +1 -0
  75. package/dist/services/discord.js +279 -0
  76. package/dist/services/discord.js.map +1 -0
  77. package/dist/services/dropbox.d.ts +134 -0
  78. package/dist/services/dropbox.d.ts.map +1 -0
  79. package/dist/services/dropbox.js +190 -0
  80. package/dist/services/dropbox.js.map +1 -0
  81. package/dist/services/github.d.ts.map +1 -0
  82. package/dist/services/github.js +13 -0
  83. package/dist/services/github.js.map +1 -0
  84. package/dist/services/gmail-trigger.d.ts.map +1 -0
  85. package/dist/services/gmail-trigger.js +172 -0
  86. package/dist/services/gmail-trigger.js.map +1 -0
  87. package/dist/services/gmail.d.ts.map +1 -0
  88. package/dist/services/gmail.js +372 -0
  89. package/dist/services/gmail.js.map +1 -0
  90. package/dist/services/google-calendar.d.ts.map +1 -0
  91. package/dist/services/google-calendar.js +357 -0
  92. package/dist/services/google-calendar.js.map +1 -0
  93. package/dist/services/google-docs.d.ts.map +1 -0
  94. package/dist/services/google-docs.js +332 -0
  95. package/dist/services/google-docs.js.map +1 -0
  96. package/dist/services/google-drive.d.ts.map +1 -0
  97. package/dist/services/google-drive.js +327 -0
  98. package/dist/services/google-drive.js.map +1 -0
  99. package/dist/services/google-sheets.d.ts.map +1 -0
  100. package/dist/services/google-sheets.js +391 -0
  101. package/dist/services/google-sheets.js.map +1 -0
  102. package/dist/services/http.d.ts.map +1 -0
  103. package/dist/services/http.js +284 -0
  104. package/dist/services/http.js.map +1 -0
  105. package/dist/services/jira.d.ts.map +1 -0
  106. package/dist/services/jira.js +35 -0
  107. package/dist/services/jira.js.map +1 -0
  108. package/dist/services/linear.d.ts.map +1 -0
  109. package/dist/services/linear.js +326 -0
  110. package/dist/services/linear.js.map +1 -0
  111. package/dist/services/mailchimp.d.ts +169 -0
  112. package/dist/services/mailchimp.d.ts.map +1 -0
  113. package/dist/services/mailchimp.js +180 -0
  114. package/dist/services/mailchimp.js.map +1 -0
  115. package/dist/services/mysql.d.ts.map +1 -0
  116. package/dist/services/mysql.js +250 -0
  117. package/dist/services/mysql.js.map +1 -0
  118. package/dist/services/notion.d.ts.map +1 -0
  119. package/dist/services/notion.js +358 -0
  120. package/dist/services/notion.js.map +1 -0
  121. package/dist/services/outlook-trigger.d.ts.map +1 -0
  122. package/dist/services/outlook-trigger.js +204 -0
  123. package/dist/services/outlook-trigger.js.map +1 -0
  124. package/dist/services/outlook.d.ts.map +1 -0
  125. package/dist/services/outlook.js +486 -0
  126. package/dist/services/outlook.js.map +1 -0
  127. package/dist/services/playwright.d.ts +678 -0
  128. package/dist/services/playwright.d.ts.map +1 -0
  129. package/dist/services/playwright.js +1141 -0
  130. package/dist/services/playwright.js.map +1 -0
  131. package/dist/services/postgres.d.ts.map +1 -0
  132. package/dist/services/postgres.js +230 -0
  133. package/dist/services/postgres.js.map +1 -0
  134. package/dist/services/sendgrid.d.ts +43 -0
  135. package/dist/services/sendgrid.d.ts.map +1 -0
  136. package/dist/services/sendgrid.js +87 -0
  137. package/dist/services/sendgrid.js.map +1 -0
  138. package/dist/services/shopify.d.ts +160 -0
  139. package/dist/services/shopify.d.ts.map +1 -0
  140. package/dist/services/shopify.js +166 -0
  141. package/dist/services/shopify.js.map +1 -0
  142. package/dist/services/slack-socket.d.ts.map +1 -0
  143. package/dist/services/slack-socket.js +48 -0
  144. package/dist/services/slack-socket.js.map +1 -0
  145. package/dist/services/slack.d.ts.map +1 -0
  146. package/dist/services/slack.js +11 -0
  147. package/dist/services/slack.js.map +1 -0
  148. package/dist/services/stripe.d.ts +275 -0
  149. package/dist/services/stripe.d.ts.map +1 -0
  150. package/dist/services/stripe.js +229 -0
  151. package/dist/services/stripe.js.map +1 -0
  152. package/dist/services/supabase.d.ts.map +1 -0
  153. package/dist/services/supabase.js +328 -0
  154. package/dist/services/supabase.js.map +1 -0
  155. package/dist/services/teams.d.ts +224 -0
  156. package/dist/services/teams.d.ts.map +1 -0
  157. package/dist/services/teams.js +229 -0
  158. package/dist/services/teams.js.map +1 -0
  159. package/dist/services/telegram.d.ts.map +1 -0
  160. package/dist/services/telegram.js +247 -0
  161. package/dist/services/telegram.js.map +1 -0
  162. package/dist/services/trello.d.ts +160 -0
  163. package/dist/services/trello.d.ts.map +1 -0
  164. package/dist/services/trello.js +194 -0
  165. package/dist/services/trello.js.map +1 -0
  166. package/dist/services/twilio.d.ts +126 -0
  167. package/dist/services/twilio.d.ts.map +1 -0
  168. package/dist/services/twilio.js +153 -0
  169. package/dist/services/twilio.js.map +1 -0
  170. package/dist/services/whatsapp.d.ts.map +1 -0
  171. package/dist/services/whatsapp.js +253 -0
  172. package/dist/services/whatsapp.js.map +1 -0
  173. package/dist/services/zendesk.d.ts +134 -0
  174. package/dist/services/zendesk.d.ts.map +1 -0
  175. package/dist/services/zendesk.js +148 -0
  176. package/dist/services/zendesk.js.map +1 -0
  177. package/dist/tools/script.d.ts +21 -0
  178. package/dist/tools/script.d.ts.map +1 -0
  179. package/dist/tools/script.js +136 -0
  180. package/dist/tools/script.js.map +1 -0
  181. package/package.json +98 -0
@@ -0,0 +1,1141 @@
1
+ /**
2
+ * Playwright Integration
3
+ *
4
+ * Browser automation for web scraping, testing, and automation tasks.
5
+ * Supports Chromium, Firefox, and WebKit browsers.
6
+ */
7
+ // ============================================================================
8
+ // Playwright Client
9
+ // ============================================================================
10
+ /**
11
+ * Playwright browser automation client with session persistence and AI support
12
+ */
13
+ export class PlaywrightClient {
14
+ playwright = null;
15
+ browser = null;
16
+ context = null;
17
+ page = null;
18
+ config;
19
+ stagehand = null;
20
+ aiBrowser = null;
21
+ sessionPath = null;
22
+ constructor(config = {}) {
23
+ this.config = {
24
+ browserType: 'chromium',
25
+ headless: true,
26
+ timeout: 30000,
27
+ viewport: { width: 1280, height: 720 },
28
+ sessionsDir: './sessions',
29
+ autoSaveSession: false,
30
+ ...config,
31
+ };
32
+ }
33
+ /**
34
+ * Get the session file path for a given session ID
35
+ */
36
+ getSessionPath(sessionId) {
37
+ const fs = require('fs');
38
+ const path = require('path');
39
+ const sessionsDir = this.config.sessionsDir || './sessions';
40
+ // Ensure sessions directory exists
41
+ if (!fs.existsSync(sessionsDir)) {
42
+ fs.mkdirSync(sessionsDir, { recursive: true });
43
+ }
44
+ return path.join(sessionsDir, `${sessionId}.json`);
45
+ }
46
+ /**
47
+ * Initialize and launch the browser
48
+ */
49
+ async launch() {
50
+ if (this.browser)
51
+ return;
52
+ // Dynamically import playwright
53
+ this.playwright = await import('playwright');
54
+ const browserType = this.config.browserType || 'chromium';
55
+ const launchOptions = {
56
+ headless: this.config.headless,
57
+ slowMo: this.config.slowMo,
58
+ timeout: this.config.timeout,
59
+ };
60
+ if (this.config.proxy) {
61
+ launchOptions.proxy = this.config.proxy;
62
+ }
63
+ // Connect to existing browser or launch new one
64
+ if (this.config.wsEndpoint) {
65
+ this.browser = await this.playwright[browserType].connect(this.config.wsEndpoint);
66
+ }
67
+ else {
68
+ this.browser = await this.playwright[browserType].launch(launchOptions);
69
+ }
70
+ // Create browser context with options
71
+ const contextOptions = {
72
+ viewport: this.config.viewport,
73
+ userAgent: this.config.userAgent,
74
+ locale: this.config.locale,
75
+ timezoneId: this.config.timezoneId,
76
+ geolocation: this.config.geolocation,
77
+ permissions: this.config.permissions,
78
+ ignoreHTTPSErrors: this.config.ignoreHTTPSErrors,
79
+ extraHTTPHeaders: this.config.extraHTTPHeaders,
80
+ recordVideo: this.config.recordVideo,
81
+ };
82
+ // Load storage state from session or file
83
+ if (this.config.sessionId) {
84
+ const sessionPath = this.getSessionPath(this.config.sessionId);
85
+ const fs = require('fs');
86
+ if (fs.existsSync(sessionPath)) {
87
+ contextOptions.storageState = sessionPath;
88
+ this.sessionPath = sessionPath;
89
+ }
90
+ }
91
+ else if (this.config.storageState) {
92
+ contextOptions.storageState = this.config.storageState;
93
+ this.sessionPath = this.config.storageState;
94
+ }
95
+ // Handle device emulation
96
+ if (this.config.deviceName && this.playwright.devices[this.config.deviceName]) {
97
+ Object.assign(contextOptions, this.playwright.devices[this.config.deviceName]);
98
+ }
99
+ this.context = await this.browser.newContext(contextOptions);
100
+ this.page = await this.context.newPage();
101
+ }
102
+ /**
103
+ * Ensure browser is launched
104
+ */
105
+ async ensureLaunched() {
106
+ if (!this.page) {
107
+ await this.launch();
108
+ }
109
+ return this.page;
110
+ }
111
+ /**
112
+ * Navigate to a URL
113
+ */
114
+ async navigate(options) {
115
+ const page = await this.ensureLaunched();
116
+ await page.goto(options.url, {
117
+ waitUntil: options.waitUntil || 'load',
118
+ timeout: options.timeout,
119
+ referer: options.referer,
120
+ });
121
+ return {
122
+ url: page.url(),
123
+ title: await page.title(),
124
+ };
125
+ }
126
+ /**
127
+ * Click an element
128
+ */
129
+ async click(options) {
130
+ const page = await this.ensureLaunched();
131
+ await page.click(options.selector, {
132
+ button: options.button,
133
+ clickCount: options.clickCount,
134
+ delay: options.delay,
135
+ modifiers: options.modifiers,
136
+ position: options.position,
137
+ force: options.force,
138
+ timeout: options.timeout,
139
+ });
140
+ }
141
+ /**
142
+ * Double click an element
143
+ */
144
+ async dblclick(options) {
145
+ const page = await this.ensureLaunched();
146
+ await page.dblclick(options.selector, {
147
+ button: options.button,
148
+ delay: options.delay,
149
+ modifiers: options.modifiers,
150
+ position: options.position,
151
+ force: options.force,
152
+ timeout: options.timeout,
153
+ });
154
+ }
155
+ /**
156
+ * Type text into an element
157
+ */
158
+ async type(options) {
159
+ const page = await this.ensureLaunched();
160
+ if (options.clear) {
161
+ await page.fill(options.selector, '');
162
+ }
163
+ await page.type(options.selector, options.text, {
164
+ delay: options.delay,
165
+ timeout: options.timeout,
166
+ });
167
+ }
168
+ /**
169
+ * Fill an input element with a value
170
+ */
171
+ async fill(options) {
172
+ const page = await this.ensureLaunched();
173
+ await page.fill(options.selector, options.value, {
174
+ force: options.force,
175
+ timeout: options.timeout,
176
+ });
177
+ }
178
+ /**
179
+ * Select options from a dropdown
180
+ */
181
+ async select(options) {
182
+ const page = await this.ensureLaunched();
183
+ const values = Array.isArray(options.values) ? options.values : [options.values];
184
+ return page.selectOption(options.selector, values, {
185
+ timeout: options.timeout,
186
+ });
187
+ }
188
+ /**
189
+ * Check a checkbox
190
+ */
191
+ async check(selector, options) {
192
+ const page = await this.ensureLaunched();
193
+ await page.check(selector, options);
194
+ }
195
+ /**
196
+ * Uncheck a checkbox
197
+ */
198
+ async uncheck(selector, options) {
199
+ const page = await this.ensureLaunched();
200
+ await page.uncheck(selector, options);
201
+ }
202
+ /**
203
+ * Hover over an element
204
+ */
205
+ async hover(selector, options) {
206
+ const page = await this.ensureLaunched();
207
+ await page.hover(selector, options);
208
+ }
209
+ /**
210
+ * Focus an element
211
+ */
212
+ async focus(selector, options) {
213
+ const page = await this.ensureLaunched();
214
+ await page.focus(selector, options);
215
+ }
216
+ /**
217
+ * Press a key or key combination
218
+ */
219
+ async press(selector, key, options) {
220
+ const page = await this.ensureLaunched();
221
+ await page.press(selector, key, options);
222
+ }
223
+ /**
224
+ * Press keyboard keys without focusing an element
225
+ */
226
+ async keyboard(key, options) {
227
+ const page = await this.ensureLaunched();
228
+ await page.keyboard.press(key, options);
229
+ }
230
+ /**
231
+ * Take a screenshot
232
+ */
233
+ async screenshot(options = {}) {
234
+ const page = await this.ensureLaunched();
235
+ let element = null;
236
+ if (options.selector) {
237
+ element = await page.$(options.selector);
238
+ if (!element) {
239
+ throw new Error(`Element not found: ${options.selector}`);
240
+ }
241
+ }
242
+ const screenshotOptions = {
243
+ path: options.path,
244
+ type: options.type || 'png',
245
+ quality: options.type === 'jpeg' ? options.quality : undefined,
246
+ fullPage: options.fullPage,
247
+ clip: options.clip,
248
+ omitBackground: options.omitBackground,
249
+ };
250
+ const buffer = element
251
+ ? await element.screenshot(screenshotOptions)
252
+ : await page.screenshot(screenshotOptions);
253
+ return {
254
+ data: buffer.toString('base64'),
255
+ path: options.path,
256
+ type: options.type || 'png',
257
+ };
258
+ }
259
+ /**
260
+ * Generate a PDF of the page (Chromium only)
261
+ */
262
+ async pdf(options = {}) {
263
+ const page = await this.ensureLaunched();
264
+ const buffer = await page.pdf({
265
+ path: options.path,
266
+ format: options.format,
267
+ scale: options.scale,
268
+ displayHeaderFooter: options.displayHeaderFooter,
269
+ headerTemplate: options.headerTemplate,
270
+ footerTemplate: options.footerTemplate,
271
+ printBackground: options.printBackground,
272
+ landscape: options.landscape,
273
+ pageRanges: options.pageRanges,
274
+ width: options.width,
275
+ height: options.height,
276
+ margin: options.margin,
277
+ });
278
+ return {
279
+ data: buffer.toString('base64'),
280
+ path: options.path,
281
+ };
282
+ }
283
+ /**
284
+ * Evaluate JavaScript in the page context
285
+ */
286
+ async evaluate(options) {
287
+ const page = await this.ensureLaunched();
288
+ // For simple expressions, evaluate directly
289
+ // For function expressions, wrap and call
290
+ if (options.expression.trim().startsWith('(') || options.expression.trim().startsWith('function')) {
291
+ // It's a function expression, evaluate it directly
292
+ return page.evaluate(options.expression, options.args);
293
+ }
294
+ else {
295
+ // Wrap expression in a function
296
+ const wrappedExpression = `() => (${options.expression})`;
297
+ return page.evaluate(wrappedExpression);
298
+ }
299
+ }
300
+ /**
301
+ * Wait for various conditions
302
+ */
303
+ async wait(options) {
304
+ const page = await this.ensureLaunched();
305
+ if (options.selector) {
306
+ await page.waitForSelector(options.selector, {
307
+ state: options.state,
308
+ timeout: options.timeout,
309
+ });
310
+ }
311
+ else if (options.url) {
312
+ await page.waitForURL(options.url, { timeout: options.timeout });
313
+ }
314
+ else if (options.function) {
315
+ // Wrap the function expression for evaluation
316
+ const wrappedFn = `() => (${options.function})`;
317
+ await page.waitForFunction(wrappedFn, { timeout: options.timeout });
318
+ }
319
+ else if (options.loadState) {
320
+ await page.waitForLoadState(options.loadState, { timeout: options.timeout });
321
+ }
322
+ else if (options.networkIdle) {
323
+ await page.waitForLoadState('networkidle', { timeout: options.timeout });
324
+ }
325
+ else if (options.timeout) {
326
+ await page.waitForTimeout(options.timeout);
327
+ }
328
+ }
329
+ /**
330
+ * Extract data from the page
331
+ */
332
+ async extract(options) {
333
+ const page = await this.ensureLaunched();
334
+ const elements = await page.$$(options.selector);
335
+ if (elements.length === 0) {
336
+ return { data: options.all ? [] : null, count: 0 };
337
+ }
338
+ const extractElement = async (el) => {
339
+ const result = {};
340
+ if (options.text) {
341
+ result.text = await el.textContent();
342
+ }
343
+ if (options.html) {
344
+ result.html = await el.innerHTML();
345
+ }
346
+ if (options.attributes) {
347
+ for (const attr of options.attributes) {
348
+ result[attr] = await el.getAttribute(attr);
349
+ }
350
+ }
351
+ if (options.properties) {
352
+ for (const prop of options.properties) {
353
+ result[prop] = await el.evaluate((e, p) => e[p], prop);
354
+ }
355
+ }
356
+ // If only one type of data requested, return it directly
357
+ const keys = Object.keys(result);
358
+ if (keys.length === 1) {
359
+ return result[keys[0]];
360
+ }
361
+ return result;
362
+ };
363
+ if (options.all) {
364
+ const data = await Promise.all(elements.map(extractElement));
365
+ return { data, count: elements.length };
366
+ }
367
+ else {
368
+ const data = await extractElement(elements[0]);
369
+ return { data, count: 1 };
370
+ }
371
+ }
372
+ /**
373
+ * Fill a form with multiple fields
374
+ */
375
+ async fillForm(options) {
376
+ const page = await this.ensureLaunched();
377
+ const formSelector = options.formSelector || 'form';
378
+ for (const [name, value] of Object.entries(options.fields)) {
379
+ const selector = `${formSelector} [name="${name}"]`;
380
+ if (typeof value === 'boolean') {
381
+ if (value) {
382
+ await page.check(selector);
383
+ }
384
+ else {
385
+ await page.uncheck(selector);
386
+ }
387
+ }
388
+ else if (Array.isArray(value)) {
389
+ await page.selectOption(selector, value);
390
+ }
391
+ else {
392
+ await page.fill(selector, value);
393
+ }
394
+ }
395
+ if (options.submit) {
396
+ await page.click(`${formSelector} [type="submit"]`);
397
+ }
398
+ }
399
+ /**
400
+ * Get or set cookies
401
+ */
402
+ async cookies(options = {}) {
403
+ const context = this.context;
404
+ if (!context) {
405
+ await this.ensureLaunched();
406
+ }
407
+ if (options.cookies) {
408
+ await this.context.addCookies(options.cookies);
409
+ }
410
+ return this.context.cookies(options.urls);
411
+ }
412
+ /**
413
+ * Clear cookies
414
+ */
415
+ async clearCookies() {
416
+ if (this.context) {
417
+ await this.context.clearCookies();
418
+ }
419
+ }
420
+ /**
421
+ * Manage local and session storage
422
+ */
423
+ async storage(options) {
424
+ const page = await this.ensureLaunched();
425
+ const result = {};
426
+ // Set local storage
427
+ if (options.localStorage) {
428
+ await page.evaluate((items) => {
429
+ for (const [key, value] of Object.entries(items)) {
430
+ localStorage.setItem(key, value);
431
+ }
432
+ }, options.localStorage);
433
+ }
434
+ // Set session storage
435
+ if (options.sessionStorage) {
436
+ await page.evaluate((items) => {
437
+ for (const [key, value] of Object.entries(items)) {
438
+ sessionStorage.setItem(key, value);
439
+ }
440
+ }, options.sessionStorage);
441
+ }
442
+ // Get storage
443
+ if (options.getStorage === 'local' || options.getStorage === 'both') {
444
+ result.localStorage = await page.evaluate(() => {
445
+ const items = {};
446
+ for (let i = 0; i < localStorage.length; i++) {
447
+ const key = localStorage.key(i);
448
+ if (key)
449
+ items[key] = localStorage.getItem(key) || '';
450
+ }
451
+ return items;
452
+ });
453
+ }
454
+ if (options.getStorage === 'session' || options.getStorage === 'both') {
455
+ result.sessionStorage = await page.evaluate(() => {
456
+ const items = {};
457
+ for (let i = 0; i < sessionStorage.length; i++) {
458
+ const key = sessionStorage.key(i);
459
+ if (key)
460
+ items[key] = sessionStorage.getItem(key) || '';
461
+ }
462
+ return items;
463
+ });
464
+ }
465
+ return result;
466
+ }
467
+ /**
468
+ * Block network requests matching patterns
469
+ */
470
+ async blockRequests(patterns) {
471
+ const page = await this.ensureLaunched();
472
+ await page.route((url) => patterns.some((p) => url.href.includes(p)), (route) => route.abort());
473
+ }
474
+ /**
475
+ * Intercept and modify network requests
476
+ */
477
+ async interceptRequests(handler) {
478
+ const page = await this.ensureLaunched();
479
+ await page.route('**/*', handler);
480
+ }
481
+ /**
482
+ * Get page content
483
+ */
484
+ async content() {
485
+ const page = await this.ensureLaunched();
486
+ return page.content();
487
+ }
488
+ /**
489
+ * Get page info
490
+ */
491
+ async pageInfo(includeContent = false) {
492
+ const page = await this.ensureLaunched();
493
+ return {
494
+ url: page.url(),
495
+ title: await page.title(),
496
+ content: includeContent ? await page.content() : undefined,
497
+ };
498
+ }
499
+ /**
500
+ * Go back in browser history
501
+ */
502
+ async goBack(options) {
503
+ const page = await this.ensureLaunched();
504
+ await page.goBack(options);
505
+ }
506
+ /**
507
+ * Go forward in browser history
508
+ */
509
+ async goForward(options) {
510
+ const page = await this.ensureLaunched();
511
+ await page.goForward(options);
512
+ }
513
+ /**
514
+ * Reload the page
515
+ */
516
+ async reload(options) {
517
+ const page = await this.ensureLaunched();
518
+ await page.reload(options);
519
+ }
520
+ /**
521
+ * Open a new page/tab
522
+ */
523
+ async newPage() {
524
+ if (!this.context) {
525
+ await this.ensureLaunched();
526
+ }
527
+ this.page = await this.context.newPage();
528
+ }
529
+ /**
530
+ * Get all open pages
531
+ */
532
+ async getPages() {
533
+ if (!this.context) {
534
+ return [];
535
+ }
536
+ const pages = this.context.pages();
537
+ return Promise.all(pages.map(async (p) => ({
538
+ url: p.url(),
539
+ title: await p.title(),
540
+ })));
541
+ }
542
+ /**
543
+ * Switch to a page by index or URL
544
+ */
545
+ async switchToPage(indexOrUrl) {
546
+ if (!this.context) {
547
+ throw new Error('No browser context');
548
+ }
549
+ const pages = this.context.pages();
550
+ let targetPage;
551
+ if (typeof indexOrUrl === 'number') {
552
+ targetPage = pages[indexOrUrl];
553
+ }
554
+ else {
555
+ targetPage = pages.find((p) => p.url().includes(indexOrUrl));
556
+ }
557
+ if (!targetPage) {
558
+ throw new Error(`Page not found: ${indexOrUrl}`);
559
+ }
560
+ this.page = targetPage;
561
+ return {
562
+ url: this.page.url(),
563
+ title: await this.page.title(),
564
+ };
565
+ }
566
+ /**
567
+ * Close the current page
568
+ */
569
+ async closePage() {
570
+ if (this.page) {
571
+ await this.page.close();
572
+ const pages = this.context?.pages() || [];
573
+ this.page = pages.length > 0 ? pages[pages.length - 1] : null;
574
+ }
575
+ }
576
+ /**
577
+ * Upload a file to an input element
578
+ */
579
+ async uploadFile(selector, files) {
580
+ const page = await this.ensureLaunched();
581
+ await page.setInputFiles(selector, files);
582
+ }
583
+ /**
584
+ * Download a file
585
+ */
586
+ async download(options) {
587
+ const page = await this.ensureLaunched();
588
+ const downloadPromise = page.waitForEvent('download');
589
+ if (options.selector) {
590
+ await page.click(options.selector);
591
+ }
592
+ else if (options.url) {
593
+ await page.goto(options.url);
594
+ }
595
+ const download = await downloadPromise;
596
+ const path = options.path || (await download.path());
597
+ if (options.path) {
598
+ await download.saveAs(options.path);
599
+ }
600
+ return {
601
+ path: path || '',
602
+ suggestedFilename: download.suggestedFilename(),
603
+ };
604
+ }
605
+ /**
606
+ * Handle dialog (alert, confirm, prompt)
607
+ */
608
+ async handleDialog(action, promptText) {
609
+ const page = await this.ensureLaunched();
610
+ page.once('dialog', async (dialog) => {
611
+ if (action === 'accept') {
612
+ await dialog.accept(promptText);
613
+ }
614
+ else {
615
+ await dialog.dismiss();
616
+ }
617
+ });
618
+ }
619
+ /**
620
+ * Emulate media type or color scheme
621
+ */
622
+ async emulateMedia(options) {
623
+ const page = await this.ensureLaunched();
624
+ await page.emulateMedia(options);
625
+ }
626
+ /**
627
+ * Close the browser
628
+ */
629
+ async close() {
630
+ // Auto-save session if configured
631
+ if (this.config.autoSaveSession && this.config.sessionId && this.context) {
632
+ await this.saveSession().catch(() => { });
633
+ }
634
+ if (this.page) {
635
+ await this.page.close().catch(() => { });
636
+ this.page = null;
637
+ }
638
+ if (this.context) {
639
+ await this.context.close().catch(() => { });
640
+ this.context = null;
641
+ }
642
+ if (this.browser) {
643
+ await this.browser.close().catch(() => { });
644
+ this.browser = null;
645
+ }
646
+ this.stagehand = null;
647
+ }
648
+ /**
649
+ * Get the underlying Playwright page for advanced usage
650
+ */
651
+ getPage() {
652
+ return this.page;
653
+ }
654
+ /**
655
+ * Get the underlying Playwright browser for advanced usage
656
+ */
657
+ getBrowser() {
658
+ return this.browser;
659
+ }
660
+ /**
661
+ * Get the underlying Playwright context for advanced usage
662
+ */
663
+ getContext() {
664
+ return this.context;
665
+ }
666
+ // ==========================================================================
667
+ // Session Management
668
+ // ==========================================================================
669
+ /**
670
+ * Save the current session state (cookies, localStorage, sessionStorage)
671
+ */
672
+ async saveSession(sessionIdOrPath) {
673
+ if (!this.context) {
674
+ throw new Error('No browser context to save session from');
675
+ }
676
+ let savePath;
677
+ if (sessionIdOrPath) {
678
+ // Check if it's a path or session ID
679
+ if (sessionIdOrPath.includes('/') || sessionIdOrPath.includes('\\') || sessionIdOrPath.endsWith('.json')) {
680
+ savePath = sessionIdOrPath;
681
+ }
682
+ else {
683
+ savePath = this.getSessionPath(sessionIdOrPath);
684
+ }
685
+ }
686
+ else if (this.config.sessionId) {
687
+ savePath = this.getSessionPath(this.config.sessionId);
688
+ }
689
+ else if (this.sessionPath) {
690
+ savePath = this.sessionPath;
691
+ }
692
+ else {
693
+ throw new Error('No session ID or path specified');
694
+ }
695
+ // Save the storage state
696
+ await this.context.storageState({ path: savePath });
697
+ this.sessionPath = savePath;
698
+ // Get domains from cookies
699
+ const cookies = await this.context.cookies();
700
+ const domains = [...new Set(cookies.map(c => c.domain))];
701
+ // Create session info
702
+ const sessionId = savePath.split('/').pop()?.replace('.json', '') || 'unknown';
703
+ const now = new Date().toISOString();
704
+ const sessionInfo = {
705
+ id: sessionId,
706
+ path: savePath,
707
+ createdAt: now,
708
+ lastUsedAt: now,
709
+ domains,
710
+ };
711
+ return sessionInfo;
712
+ }
713
+ /**
714
+ * Load a session state from file
715
+ */
716
+ async loadSession(sessionIdOrPath) {
717
+ const fs = require('fs');
718
+ let loadPath;
719
+ // Check if it's a path or session ID
720
+ if (sessionIdOrPath.includes('/') || sessionIdOrPath.includes('\\') || sessionIdOrPath.endsWith('.json')) {
721
+ loadPath = sessionIdOrPath;
722
+ }
723
+ else {
724
+ loadPath = this.getSessionPath(sessionIdOrPath);
725
+ }
726
+ if (!fs.existsSync(loadPath)) {
727
+ throw new Error(`Session file not found: ${loadPath}`);
728
+ }
729
+ // If browser is already running, we need to create a new context
730
+ if (this.browser) {
731
+ // Close existing context
732
+ if (this.context) {
733
+ await this.context.close();
734
+ }
735
+ // Create new context with session state
736
+ const contextOptions = {
737
+ viewport: this.config.viewport,
738
+ userAgent: this.config.userAgent,
739
+ locale: this.config.locale,
740
+ timezoneId: this.config.timezoneId,
741
+ storageState: loadPath,
742
+ };
743
+ this.context = await this.browser.newContext(contextOptions);
744
+ this.page = await this.context.newPage();
745
+ }
746
+ else {
747
+ // Store path for when browser is launched
748
+ this.config.storageState = loadPath;
749
+ }
750
+ this.sessionPath = loadPath;
751
+ // Read session to get info
752
+ const sessionData = JSON.parse(fs.readFileSync(loadPath, 'utf-8'));
753
+ const domains = [...new Set((sessionData.cookies || []).map((c) => c.domain))];
754
+ const sessionId = loadPath.split('/').pop()?.replace('.json', '') || 'unknown';
755
+ return {
756
+ id: sessionId,
757
+ path: loadPath,
758
+ createdAt: new Date().toISOString(),
759
+ lastUsedAt: new Date().toISOString(),
760
+ domains: domains,
761
+ };
762
+ }
763
+ /**
764
+ * List all saved sessions
765
+ */
766
+ async listSessions() {
767
+ const fs = require('fs');
768
+ const path = require('path');
769
+ const sessionsDir = this.config.sessionsDir || './sessions';
770
+ if (!fs.existsSync(sessionsDir)) {
771
+ return [];
772
+ }
773
+ const files = fs.readdirSync(sessionsDir).filter((f) => f.endsWith('.json'));
774
+ const sessions = [];
775
+ for (const file of files) {
776
+ const filePath = path.join(sessionsDir, file);
777
+ try {
778
+ const stat = fs.statSync(filePath);
779
+ const sessionData = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
780
+ const domains = [...new Set((sessionData.cookies || []).map((c) => c.domain))];
781
+ sessions.push({
782
+ id: file.replace('.json', ''),
783
+ path: filePath,
784
+ createdAt: stat.birthtime.toISOString(),
785
+ lastUsedAt: stat.mtime.toISOString(),
786
+ domains: domains,
787
+ });
788
+ }
789
+ catch {
790
+ // Skip invalid files
791
+ }
792
+ }
793
+ return sessions;
794
+ }
795
+ /**
796
+ * Delete a saved session
797
+ */
798
+ async deleteSession(sessionId) {
799
+ const fs = require('fs');
800
+ const sessionPath = this.getSessionPath(sessionId);
801
+ if (fs.existsSync(sessionPath)) {
802
+ fs.unlinkSync(sessionPath);
803
+ return true;
804
+ }
805
+ return false;
806
+ }
807
+ /**
808
+ * Check if a session exists
809
+ */
810
+ hasSession(sessionId) {
811
+ const fs = require('fs');
812
+ return fs.existsSync(this.getSessionPath(sessionId));
813
+ }
814
+ // ==========================================================================
815
+ // AI-Powered Automation
816
+ // ==========================================================================
817
+ /**
818
+ * Initialize custom AI browser automation (Copilot/Claude Code)
819
+ */
820
+ async initAIBrowser() {
821
+ if (this.aiBrowser) {
822
+ return this.aiBrowser;
823
+ }
824
+ if (!this.config.enableAI) {
825
+ throw new Error('AI automation is not enabled. Set enableAI: true in config.');
826
+ }
827
+ if (!this.config.aiBackend || this.config.aiBackend === 'stagehand') {
828
+ throw new Error('Custom AI backend not configured. Set aiBackend to "copilot" or "claude-code"');
829
+ }
830
+ if (!this.config.aiClient) {
831
+ throw new Error(`AI client not provided. Initialize ${this.config.aiBackend} client first.`);
832
+ }
833
+ try {
834
+ const { AIBrowserClient } = await import('./ai-browser.js');
835
+ this.aiBrowser = new AIBrowserClient({
836
+ backend: this.config.aiBackend,
837
+ aiClient: this.config.aiClient,
838
+ playwrightClient: this,
839
+ debug: this.config.aiDebug,
840
+ });
841
+ return this.aiBrowser;
842
+ }
843
+ catch (error) {
844
+ throw new Error(`Failed to initialize AI browser automation: ${error instanceof Error ? error.message : String(error)}`);
845
+ }
846
+ }
847
+ /**
848
+ * Initialize Stagehand for AI-powered automation (legacy support)
849
+ */
850
+ async initStagehand() {
851
+ if (this.stagehand) {
852
+ return this.stagehand;
853
+ }
854
+ if (!this.config.enableAI) {
855
+ throw new Error('AI automation is not enabled. Set enableAI: true in config.');
856
+ }
857
+ try {
858
+ // Dynamically import Stagehand (optional dependency)
859
+ // Use string variable to bypass TypeScript module resolution
860
+ const stagehandPackage = '@browserbasehq/stagehand';
861
+ let stagehandModule = null;
862
+ try {
863
+ stagehandModule = await import(stagehandPackage);
864
+ }
865
+ catch {
866
+ throw new Error('Stagehand is not installed. Install it with: npm install @browserbasehq/stagehand');
867
+ }
868
+ const { Stagehand } = stagehandModule;
869
+ // Ensure browser is launched
870
+ await this.ensureLaunched();
871
+ // Configure Stagehand
872
+ const stagehandConfig = {
873
+ env: 'LOCAL',
874
+ enableCaching: true,
875
+ debugDom: this.config.aiDebug,
876
+ };
877
+ // Set model configuration
878
+ if (this.config.aiProvider === 'anthropic') {
879
+ stagehandConfig.modelName = this.config.aiModel || 'claude-3-5-sonnet-latest';
880
+ stagehandConfig.modelClientOptions = {
881
+ apiKey: this.config.aiApiKey || process.env.ANTHROPIC_API_KEY,
882
+ };
883
+ }
884
+ else {
885
+ // Default to OpenAI
886
+ stagehandConfig.modelName = this.config.aiModel || 'gpt-4o';
887
+ stagehandConfig.modelClientOptions = {
888
+ apiKey: this.config.aiApiKey || process.env.OPENAI_API_KEY,
889
+ };
890
+ }
891
+ this.stagehand = new Stagehand(stagehandConfig);
892
+ await this.stagehand.init({ page: this.page });
893
+ return this.stagehand;
894
+ }
895
+ catch (error) {
896
+ throw new Error(`Failed to initialize Stagehand AI. Make sure @browserbasehq/stagehand is installed: npm install @browserbasehq/stagehand\n` +
897
+ `Original error: ${error}`);
898
+ }
899
+ }
900
+ /**
901
+ * Perform an action using natural language (AI-powered)
902
+ *
903
+ * @example
904
+ * await browser.act({ instruction: 'Click the login button' })
905
+ * await browser.act({ instruction: 'Fill in the email field with test@example.com' })
906
+ */
907
+ async act(options) {
908
+ // Use custom AI backend if configured
909
+ if (this.config.aiBackend && this.config.aiBackend !== 'stagehand') {
910
+ const aiBrowser = await this.initAIBrowser();
911
+ try {
912
+ const result = await aiBrowser.act({ action: options.instruction });
913
+ return {
914
+ success: result.success,
915
+ action: result.action,
916
+ element: result.message,
917
+ };
918
+ }
919
+ catch (error) {
920
+ if (options.optional) {
921
+ return {
922
+ success: false,
923
+ error: String(error),
924
+ };
925
+ }
926
+ throw error;
927
+ }
928
+ }
929
+ // Fall back to Stagehand
930
+ const stagehand = await this.initStagehand();
931
+ try {
932
+ const result = await stagehand.act({
933
+ action: options.instruction,
934
+ });
935
+ return {
936
+ success: result.success !== false,
937
+ action: result.action || options.instruction,
938
+ element: result.message,
939
+ };
940
+ }
941
+ catch (error) {
942
+ if (options.optional) {
943
+ return {
944
+ success: false,
945
+ error: String(error),
946
+ };
947
+ }
948
+ throw error;
949
+ }
950
+ }
951
+ /**
952
+ * Observe available actions on the page (AI-powered)
953
+ *
954
+ * @example
955
+ * const result = await browser.observe({ instruction: 'Find all buttons' })
956
+ */
957
+ async observe(options = {}) {
958
+ // Use custom AI backend if configured
959
+ if (this.config.aiBackend && this.config.aiBackend !== 'stagehand') {
960
+ const aiBrowser = await this.initAIBrowser();
961
+ const elements = await aiBrowser.observe({ instruction: options.instruction });
962
+ return {
963
+ elements: elements.map(el => ({
964
+ description: el.description || '',
965
+ selector: el.selector || '',
966
+ tagName: el.tagName || 'unknown',
967
+ text: el.text,
968
+ actions: el.actions || ['click'],
969
+ })),
970
+ };
971
+ }
972
+ // Fall back to Stagehand
973
+ const stagehand = await this.initStagehand();
974
+ const result = await stagehand.observe({
975
+ instruction: options.instruction,
976
+ });
977
+ return {
978
+ elements: (result || []).map(el => ({
979
+ description: el.description || '',
980
+ selector: el.selector || '',
981
+ tagName: el.tagName || 'unknown',
982
+ actions: el.actions || ['click'],
983
+ })),
984
+ };
985
+ }
986
+ /**
987
+ * Extract structured data from the page using AI
988
+ *
989
+ * @example
990
+ * const data = await browser.aiExtract({
991
+ * instruction: 'Extract the product name and price',
992
+ * schema: { name: 'string', price: 'number' }
993
+ * })
994
+ */
995
+ async aiExtract(options) {
996
+ // Use custom AI backend if configured
997
+ if (this.config.aiBackend && this.config.aiBackend !== 'stagehand') {
998
+ const aiBrowser = await this.initAIBrowser();
999
+ const result = await aiBrowser.aiExtract({
1000
+ instruction: options.instruction,
1001
+ schema: options.schema,
1002
+ });
1003
+ return result.data;
1004
+ }
1005
+ // Fall back to Stagehand
1006
+ const stagehand = await this.initStagehand();
1007
+ // Convert simple schema description to format Stagehand expects
1008
+ let schema = undefined;
1009
+ if (options.schema) {
1010
+ // For now, pass the schema description as-is
1011
+ // Users can use Zod schemas if they import @browserbasehq/stagehand directly
1012
+ schema = options.schema;
1013
+ }
1014
+ const result = await stagehand.extract({
1015
+ instruction: options.instruction,
1016
+ schema,
1017
+ });
1018
+ return result;
1019
+ }
1020
+ /**
1021
+ * Check if AI automation is available
1022
+ */
1023
+ isAIEnabled() {
1024
+ return this.config.enableAI === true;
1025
+ }
1026
+ }
1027
+ // ============================================================================
1028
+ // SDK Initializer
1029
+ // ============================================================================
1030
+ export const PlaywrightInitializer = {
1031
+ async initialize(_module, config) {
1032
+ const options = config.options || {};
1033
+ // Helper to get option with fallback key
1034
+ const getOption = (key1, key2) => {
1035
+ const value = options[key1] ?? (key2 ? options[key2] : undefined);
1036
+ return value;
1037
+ };
1038
+ const playwrightConfig = {
1039
+ browserType: getOption('browser_type', 'browserType'),
1040
+ headless: getOption('headless'),
1041
+ slowMo: getOption('slow_mo', 'slowMo'),
1042
+ timeout: getOption('timeout'),
1043
+ viewport: getOption('viewport'),
1044
+ userAgent: getOption('user_agent', 'userAgent'),
1045
+ locale: getOption('locale'),
1046
+ timezoneId: getOption('timezone_id', 'timezoneId'),
1047
+ geolocation: getOption('geolocation'),
1048
+ permissions: getOption('permissions'),
1049
+ ignoreHTTPSErrors: getOption('ignore_https_errors', 'ignoreHTTPSErrors'),
1050
+ deviceName: getOption('device_name', 'deviceName'),
1051
+ proxy: getOption('proxy'),
1052
+ extraHTTPHeaders: getOption('extra_http_headers', 'extraHTTPHeaders'),
1053
+ recordVideo: getOption('record_video', 'recordVideo'),
1054
+ wsEndpoint: getOption('ws_endpoint', 'wsEndpoint'),
1055
+ // Session persistence
1056
+ storageState: getOption('storage_state', 'storageState'),
1057
+ sessionId: getOption('session_id', 'sessionId'),
1058
+ sessionsDir: getOption('sessions_dir', 'sessionsDir'),
1059
+ autoSaveSession: getOption('auto_save_session', 'autoSaveSession'),
1060
+ // AI automation
1061
+ enableAI: getOption('enable_ai', 'enableAI'),
1062
+ aiBackend: getOption('ai_backend', 'aiBackend'),
1063
+ aiClient: getOption('ai_client', 'aiClient'),
1064
+ aiProvider: getOption('ai_provider', 'aiProvider'),
1065
+ aiModel: getOption('ai_model', 'aiModel'),
1066
+ aiApiKey: getOption('ai_api_key', 'aiApiKey'),
1067
+ aiDebug: getOption('ai_debug', 'aiDebug'),
1068
+ };
1069
+ const client = new PlaywrightClient(playwrightConfig);
1070
+ return client;
1071
+ },
1072
+ };
1073
+ // ============================================================================
1074
+ // Convenience Functions
1075
+ // ============================================================================
1076
+ /**
1077
+ * Create a Playwright client with default options
1078
+ */
1079
+ export function createPlaywrightClient(config) {
1080
+ return new PlaywrightClient(config);
1081
+ }
1082
+ /**
1083
+ * Quick web scraping helper
1084
+ */
1085
+ export async function scrape(url, selectors, options) {
1086
+ const client = new PlaywrightClient(options);
1087
+ try {
1088
+ await client.navigate({ url });
1089
+ const result = {};
1090
+ for (const [key, selector] of Object.entries(selectors)) {
1091
+ const extracted = await client.extract({ selector, text: true, all: true });
1092
+ result[key] = extracted.data;
1093
+ }
1094
+ return result;
1095
+ }
1096
+ finally {
1097
+ await client.close();
1098
+ }
1099
+ }
1100
+ /**
1101
+ * Quick screenshot helper
1102
+ */
1103
+ export async function screenshotUrl(url, options) {
1104
+ const { path, type, quality, fullPage, selector, clip, omitBackground, ...config } = options || {};
1105
+ const client = new PlaywrightClient(config);
1106
+ try {
1107
+ await client.navigate({ url });
1108
+ return client.screenshot({ path, type, quality, fullPage, selector, clip, omitBackground });
1109
+ }
1110
+ finally {
1111
+ await client.close();
1112
+ }
1113
+ }
1114
+ /**
1115
+ * Quick PDF generation helper
1116
+ */
1117
+ export async function pdfUrl(url, options) {
1118
+ const { path, format, scale, displayHeaderFooter, headerTemplate, footerTemplate, printBackground, landscape, pageRanges, width, height, margin, ...config } = options || {};
1119
+ const client = new PlaywrightClient({ ...config, browserType: 'chromium' }); // PDF only works in Chromium
1120
+ try {
1121
+ await client.navigate({ url });
1122
+ return client.pdf({
1123
+ path,
1124
+ format,
1125
+ scale,
1126
+ displayHeaderFooter,
1127
+ headerTemplate,
1128
+ footerTemplate,
1129
+ printBackground,
1130
+ landscape,
1131
+ pageRanges,
1132
+ width,
1133
+ height,
1134
+ margin,
1135
+ });
1136
+ }
1137
+ finally {
1138
+ await client.close();
1139
+ }
1140
+ }
1141
+ //# sourceMappingURL=playwright.js.map