Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Use `pnpm` for all package management tasks.
### General Architecture

- **Effect-First:** All asynchronous operations must be wrapped in `Effect`.
- **Services:** Each service module exports a same-named interface and `Context.GenericTag` value (for example, `Browser`). Public functionality is grouped under the `Playwright` namespace, where `Playwright.Browser` and similar names work in both type and value positions.
- **Services:** Each service module exports a same-named interface and `Context.GenericTag` value (for example, `Browser`). Core wrapper functionality is grouped under the `Playwright` namespace, where `Playwright.Browser` and similar names work in both type and value positions. Scoped browser provisioning is grouped under `PlaywrightSpawner`.
- **Constructors:** Wrap native Playwright objects with named functions such as `makeBrowser` and `makePage`. Do not add `*Service` aliases or static `Tag.make` constructors.
- **Resource Management:** Rely on Effect's `Scope` for automatic resource cleanup (browsers, contexts).

Expand All @@ -29,7 +29,7 @@ Use `pnpm` for all package management tasks.
- **Effect:** Import widely used modules from `effect` (e.g., `Effect`, `Context`, `Stream`).
- **Playwright:** Import types from `playwright-core`.
- **Internal:** Use relative imports (e.g., `./common`, `./errors`).
- **Public API:** Re-export canonical service names and named constructors directly from `src/playwright-api.ts`; do not add import-and-alias mappings. Consumers import the namespace through `Playwright` from `effect-playwright` and the experimental browser spawner through `PlaywrightSpawner` from `effect-playwright/experimental`. Do not reintroduce top-level wrapper exports, `*Service` aliases, or the old `Environment` name.
- **Public API:** Re-export canonical service names and named constructors directly from `src/playwright-api.ts`; do not add import-and-alias mappings. Consumers import the namespaces `Playwright` and `PlaywrightSpawner` from `effect-playwright`. Do not reintroduce top-level wrapper exports, `*Service` aliases, or the old `Environment` name.

### Error Handling

Expand All @@ -52,8 +52,7 @@ Use `pnpm` for all package management tasks.
```typescript
import { assert, layer } from "@effect/vitest";
import { Effect } from "effect";
import { Playwright, chromium } from "effect-playwright";
import { PlaywrightSpawner } from "effect-playwright/experimental";
import { Playwright, PlaywrightSpawner, chromium } from "effect-playwright";

// Use the PlaywrightSpawner layer
layer(PlaywrightSpawner.layer(chromium))("Suite Name", (it) => {
Expand All @@ -73,5 +72,4 @@ Use `pnpm` for all package management tasks.

## 4. Experimental Features

- Features in `src/experimental/` may have different stability guarantees but should follow the same coding standards. The browser-spawning service lives in `src/experimental/playwright-spawner.ts` and is exported as `PlaywrightSpawner`.
- Experimental services follow the same-named interface plus `Context.GenericTag` convention.
- Features in `src/experimental/` may have different stability guarantees but should follow the same coding standards.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file.
### Breaking Changes

- **Namespaced Playwright API**: All wrapper tags, canonical service types, model classes, option types, and errors now live under the root `Playwright` namespace. Tags and service types share names, so `Playwright.Browser`, `Playwright.Page`, and the other service names work in both value and type positions. Implementation-only `*Service` names are no longer exported, and wrapper constructors use names such as `Playwright.makeBrowser` and `Playwright.makePage` instead of static `make` methods. Browser engine exports remain top-level.
- **PlaywrightSpawner**: Renamed the experimental `Environment` namespace and service to `PlaywrightSpawner`. Use `PlaywrightSpawner.PlaywrightSpawner`, `PlaywrightSpawner.layer`, and `PlaywrightSpawner.withBrowser`.
- **PlaywrightSpawner**: Renamed the `Environment` namespace and service to `PlaywrightSpawner` and promoted it to the stable root entrypoint. Import `PlaywrightSpawner` from `effect-playwright`, then use `PlaywrightSpawner.PlaywrightSpawner`, `PlaywrightSpawner.layer`, and `PlaywrightSpawner.withBrowser`.


## 0.6.0
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,14 @@ const program = Effect.gen(function* () {
});
```

## Playwright Spawner (Experimental)
## Playwright Spawner

`PlaywrightSpawner` configures how browsers are launched and spawns browsers scoped to the current lifetime.

### Usage

```ts
import { Playwright, chromium } from "effect-playwright";
import { PlaywrightSpawner } from "effect-playwright/experimental";
import { Playwright, PlaywrightSpawner, chromium } from "effect-playwright";
import { Effect } from "effect";

const liveLayer = PlaywrightSpawner.layer(chromium, {
Expand Down
2 changes: 1 addition & 1 deletion src/browser-context.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { assert, layer } from "@effect/vitest";
import { Effect, Option } from "effect";
import { PlaywrightSpawner } from "effect-playwright";
import { chromium } from "playwright-core";
import { Browser } from "./browser";
import { PlaywrightSpawner } from "./experimental";

type TestWindow = Window & {
magicValue?: number;
Expand Down
2 changes: 1 addition & 1 deletion src/common.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { assert, layer } from "@effect/vitest";
import { Chunk, Effect, Fiber, Option, Stream } from "effect";
import { PlaywrightSpawner } from "effect-playwright";
import { chromium } from "playwright-core";
import { Browser } from "./browser";
import { PlaywrightSpawner } from "./experimental";

layer(PlaywrightSpawner.layer(chromium))("PlaywrightCommon", (it) => {
it.scoped("Request and Response", () =>
Expand Down
2 changes: 1 addition & 1 deletion src/event-stream.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { layer } from "@effect/vitest";
import { Effect, Stream } from "effect";
import { PlaywrightSpawner } from "effect-playwright";
import { chromium } from "playwright-core";
import { Browser } from "./browser";
import { PlaywrightSpawner } from "./experimental";

layer(PlaywrightSpawner.layer(chromium))("eventStream", (it) => {
it.scoped("should complete when the page closes", () =>
Expand Down
6 changes: 0 additions & 6 deletions src/experimental/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,3 @@
* @since 0.2.0
*/
export * as BrowserUtils from "./browser-utils";
/**
* Scoped browser provisioning for Effect programs.
*
* @since 0.7.0
*/
export * as PlaywrightSpawner from "./playwright-spawner";
2 changes: 1 addition & 1 deletion src/frame.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { assert, layer } from "@effect/vitest";
import { Effect, Option } from "effect";
import { PlaywrightSpawner } from "effect-playwright";
import { chromium } from "playwright-core";
import { Browser } from "./browser";
import { PlaywrightSpawner } from "./experimental";
import type { Frame } from "./frame";

layer(PlaywrightSpawner.layer(chromium))("Frame", (it) => {
Expand Down
14 changes: 10 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/**
* Effect services and value wrappers for browser automation with Playwright.
*
* The root entrypoint exposes Playwright's browser engines directly and groups
* the Effect-based services, models, constructors, and errors under the
* {@link Playwright} namespace. Scoped acquisition APIs close browsers and
* contexts automatically, while fallible operations report {@link Playwright.PlaywrightError}.
* The root entrypoint exposes Effect-based services, models, constructors, and
* errors under the {@link Playwright} namespace. {@link PlaywrightSpawner}
* provides scoped browser acquisition. Fallible operations report
* {@link Playwright.PlaywrightError}.
*
* @since 0.1.0
* @packageDocumentation
Expand All @@ -22,3 +22,9 @@ export { chromium, firefox, webkit } from "playwright-core";
* @since 0.7.0
*/
export * as Playwright from "./playwright-api";
/**
* Scoped browser provisioning for Effect programs.
*
* @since 0.7.0
*/
export * as PlaywrightSpawner from "./playwright-spawner";
2 changes: 1 addition & 1 deletion src/locator.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/// <reference lib="dom" />
import { assert, layer } from "@effect/vitest";
import { Effect, Option } from "effect";
import { PlaywrightSpawner } from "effect-playwright";
import { chromium } from "playwright-core";
import { Browser } from "./browser";
import { PlaywrightSpawner } from "./experimental";

layer(PlaywrightSpawner.layer(chromium))("Locator", (it) => {
it.scoped("should work", () =>
Expand Down
3 changes: 1 addition & 2 deletions src/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,7 @@ export interface Locator {
* ```ts
* import { chromium } from "@playwright/test";
* import { Effect } from "effect";
* import { Playwright } from "effect-playwright";
* import { PlaywrightSpawner } from "effect-playwright/experimental";
* import { Playwright, PlaywrightSpawner } from "effect-playwright";
*
* const program = Effect.gen(function* () {
* const browser = yield* Playwright.Browser;
Expand Down
2 changes: 1 addition & 1 deletion src/page.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { assert, layer } from "@effect/vitest";
import { Effect, Fiber, Option, Ref, Stream } from "effect";
import { PlaywrightSpawner } from "effect-playwright";
import { chromium } from "playwright-core";
import { Browser } from "./browser";
import { PlaywrightSpawner } from "./experimental";

type TestWindow = Window & {
timerFired?: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { assert, layer } from "@effect/vitest";
import { Effect } from "effect";
import { Playwright } from "effect-playwright";
import { PlaywrightSpawner } from "effect-playwright/experimental";
import { Playwright, PlaywrightSpawner } from "effect-playwright";
import { chromium } from "playwright-core";

const accessFirst = Effect.gen(function* () {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/**
* Experimental service for provisioning a scoped Playwright browser.
* Service for provisioning a scoped Playwright browser.
*
* @since 0.7.0
*/

import { Context, Effect, Layer } from "effect";
import type { Scope } from "effect/Scope";
import { Playwright } from "effect-playwright";
import type { BrowserType, LaunchOptions } from "playwright-core";
import type { PlaywrightError } from "../errors";
import type { PlaywrightError } from "./errors";
import * as Playwright from "./playwright-api";

/**
* Deferred acquisition of a browser scoped to the caller's lifetime.
Expand All @@ -32,7 +32,7 @@ export interface PlaywrightSpawner {
* @since 0.7.0
*/
export const PlaywrightSpawner = Context.GenericTag<PlaywrightSpawner>(
"effect-playwright/experimental/playwright-spawner/PlaywrightSpawner",
"effect-playwright/playwright-spawner/PlaywrightSpawner",
);

/**
Expand All @@ -48,8 +48,7 @@ export const PlaywrightSpawner = Context.GenericTag<PlaywrightSpawner>(
*
* ```ts
* import { Effect } from "effect";
* import { chromium } from "effect-playwright";
* import { PlaywrightSpawner } from "effect-playwright/experimental";
* import { PlaywrightSpawner, chromium } from "effect-playwright";
*
* const program = Effect.gen(function* () {
* const spawner = yield* PlaywrightSpawner.PlaywrightSpawner;
Expand Down Expand Up @@ -104,8 +103,7 @@ const withBrowserUnscoped = Effect.provideServiceEffect(
*
* ```ts
* import { Effect } from "effect";
* import { Playwright, chromium } from "effect-playwright";
* import { PlaywrightSpawner } from "effect-playwright/experimental";
* import { Playwright, PlaywrightSpawner, chromium } from "effect-playwright";
*
* const program = Effect.gen(function* () {
* const browser = yield* Playwright.Browser;
Expand Down
2 changes: 1 addition & 1 deletion src/scratchpad/setup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PlaywrightSpawner } from "effect-playwright/experimental";
import { PlaywrightSpawner } from "effect-playwright";
import { chromium } from "playwright-core";

export const liveLayer = PlaywrightSpawner.layer(chromium, {
Expand Down
Loading