@@ -6,8 +6,13 @@ import {
66} from "playwright-core" ;
77
88import { type LaunchOptions , PlaywrightBrowser } from "./browser" ;
9+ import { PlaywrightBrowserContext } from "./browser-context" ;
910import { type PlaywrightError , wrapError } from "./errors" ;
1011
12+ type LaunchPersistentContextOptions = Parameters <
13+ BrowserType [ "launchPersistentContext" ]
14+ > [ 1 ] ;
15+
1116/**
1217 * @category model
1318 * @since 0.1.0
@@ -72,6 +77,105 @@ export interface PlaywrightService {
7277 PlaywrightError ,
7378 Scope . Scope
7479 > ;
80+ /**
81+ * Launches a persistent browser context.
82+ *
83+ * Unlike {@link launchPersistentContextScoped}, this method does **not** close the
84+ * context automatically when scope is closed. You are responsible for closing it.
85+ *
86+ * This launches a browser with a persistent profile under `userDataDir` and returns
87+ * the single persistent context for that browser.
88+ *
89+ * Closing this context also closes the underlying browser process.
90+ *
91+ * ```ts
92+ * import { Effect } from "effect";
93+ * import { Playwright } from "effect-playwright";
94+ * import { chromium } from "playwright-core";
95+ *
96+ * const program = Effect.gen(function* () {
97+ * const playwright = yield* Playwright;
98+ * const context = yield* playwright.launchPersistentContext(
99+ * chromium,
100+ * "./.playwright-profile",
101+ * );
102+ *
103+ * const page = yield* context.newPage;
104+ * yield* page.goto("https://example.com");
105+ *
106+ * // Closes the persistent context and browser process.
107+ * yield* context.close;
108+ * });
109+ *
110+ * await Effect.runPromise(program);
111+ * ```
112+ *
113+ * If you call this non-scoped variant inside a scope, add a finalizer for cleanup:
114+ *
115+ * ```ts
116+ * const program = Effect.gen(function* () {
117+ * const playwright = yield* Playwright;
118+ * const context = yield* playwright.launchPersistentContext(
119+ * chromium,
120+ * "./.playwright-profile",
121+ * );
122+ *
123+ * yield* Effect.addFinalizer(() => context.close.pipe(Effect.ignore));
124+ * });
125+ *
126+ * await Effect.runPromise(program.pipe(Effect.scoped));
127+ * ```
128+ *
129+ * @param browserType - The browser type to launch (e.g. chromium, firefox, webkit).
130+ * @param userDataDir - Directory used for persistent browser profile data. Pass `""` for a temporary profile directory.
131+ * @param options - Optional persistent context launch options.
132+ * @since 0.2.4
133+ */
134+ launchPersistentContext : (
135+ browserType : BrowserType ,
136+ userDataDir : string ,
137+ options ?: LaunchPersistentContextOptions ,
138+ ) => Effect . Effect < typeof PlaywrightBrowserContext . Service , PlaywrightError > ;
139+ /**
140+ * Launches a persistent browser context managed by a Scope.
141+ *
142+ * This automatically closes the persistent context (and therefore the browser process)
143+ * when the scope is closed.
144+ *
145+ * ```ts
146+ * import { Effect } from "effect";
147+ * import { Playwright } from "effect-playwright";
148+ * import { chromium } from "playwright-core";
149+ *
150+ * const program = Effect.gen(function* () {
151+ * const playwright = yield* Playwright;
152+ * const context = yield* playwright.launchPersistentContextScoped(
153+ * chromium,
154+ * "./.playwright-profile",
155+ * );
156+ *
157+ * const page = yield* context.newPage;
158+ * yield* page.goto("https://example.com");
159+ * // Context/browser cleanup is automatic when scope closes.
160+ * }).pipe(Effect.scoped);
161+ *
162+ * await Effect.runPromise(program);
163+ * ```
164+ *
165+ * @param browserType - The browser type to launch (e.g. chromium, firefox, webkit).
166+ * @param userDataDir - Directory used for persistent browser profile data. Pass `""` for a temporary profile directory.
167+ * @param options - Optional persistent context launch options.
168+ * @since 0.2.4
169+ */
170+ launchPersistentContextScoped : (
171+ browserType : BrowserType ,
172+ userDataDir : string ,
173+ options ?: LaunchPersistentContextOptions ,
174+ ) => Effect . Effect <
175+ typeof PlaywrightBrowserContext . Service ,
176+ PlaywrightError ,
177+ Scope . Scope
178+ > ;
75179 /**
76180 * Connects to a browser instance via Chrome DevTools Protocol (CDP).
77181 *
@@ -163,6 +267,24 @@ const connectCDP: (
163267 return PlaywrightBrowser . make ( browser ) ;
164268 } ) ;
165269
270+ const launchPersistentContext : (
271+ browserType : BrowserType ,
272+ userDataDir : string ,
273+ options ?: LaunchPersistentContextOptions ,
274+ ) => Effect . Effect < typeof PlaywrightBrowserContext . Service , PlaywrightError > =
275+ Effect . fn ( function * (
276+ browserType : BrowserType ,
277+ userDataDir : string ,
278+ options ?: LaunchPersistentContextOptions ,
279+ ) {
280+ const rawContext = yield * Effect . tryPromise ( {
281+ try : ( ) => browserType . launchPersistentContext ( userDataDir , options ) ,
282+ catch : wrapError ,
283+ } ) ;
284+
285+ return PlaywrightBrowserContext . make ( rawContext ) ;
286+ } ) ;
287+
166288/**
167289 * @category tag
168290 * @since 0.1.0
@@ -179,6 +301,12 @@ export class Playwright extends Context.Tag(
179301 Effect . acquireRelease ( launch ( browserType , options ) , ( browser ) =>
180302 browser . close . pipe ( Effect . ignore ) ,
181303 ) ,
304+ launchPersistentContext,
305+ launchPersistentContextScoped : ( browserType , userDataDir , options ) =>
306+ Effect . acquireRelease (
307+ launchPersistentContext ( browserType , userDataDir , options ) ,
308+ ( context ) => context . close . pipe ( Effect . ignore ) ,
309+ ) ,
182310 connectCDP,
183311 connectCDPScoped : ( cdpUrl , options ) =>
184312 Effect . acquireRelease ( connectCDP ( cdpUrl , options ) , ( browser ) =>
0 commit comments