diff --git a/AGENTS.md b/AGENTS.md index e350391..11fdb34 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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). @@ -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 @@ -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) => { @@ -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. diff --git a/CHANGELOG.md b/CHANGELOG.md index 44ada3b..d09b2b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 109a164..208ae4a 100644 --- a/README.md +++ b/README.md @@ -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, { diff --git a/src/browser-context.test.ts b/src/browser-context.test.ts index 66573fe..5d46c7d 100644 --- a/src/browser-context.test.ts +++ b/src/browser-context.test.ts @@ -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; diff --git a/src/common.test.ts b/src/common.test.ts index c6a2d47..e5712ce 100644 --- a/src/common.test.ts +++ b/src/common.test.ts @@ -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", () => diff --git a/src/event-stream.test.ts b/src/event-stream.test.ts index fd072e2..d6c2a32 100644 --- a/src/event-stream.test.ts +++ b/src/event-stream.test.ts @@ -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", () => diff --git a/src/experimental/index.ts b/src/experimental/index.ts index d9487ea..b04c1a9 100644 --- a/src/experimental/index.ts +++ b/src/experimental/index.ts @@ -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"; diff --git a/src/frame.test.ts b/src/frame.test.ts index f597b4c..cbf84e6 100644 --- a/src/frame.test.ts +++ b/src/frame.test.ts @@ -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) => { diff --git a/src/index.ts b/src/index.ts index ce550f0..19c83b7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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 @@ -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"; diff --git a/src/locator.test.ts b/src/locator.test.ts index 13a4786..563eaff 100644 --- a/src/locator.test.ts +++ b/src/locator.test.ts @@ -1,9 +1,9 @@ /// 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", () => diff --git a/src/locator.ts b/src/locator.ts index 731b03d..844f5ae 100644 --- a/src/locator.ts +++ b/src/locator.ts @@ -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; diff --git a/src/page.test.ts b/src/page.test.ts index bcb8f0e..bf28cba 100644 --- a/src/page.test.ts +++ b/src/page.test.ts @@ -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; diff --git a/src/experimental/playwright-spawner.test.ts b/src/playwright-spawner.test.ts similarity index 96% rename from src/experimental/playwright-spawner.test.ts rename to src/playwright-spawner.test.ts index 85f0f30..55bbf01 100644 --- a/src/experimental/playwright-spawner.test.ts +++ b/src/playwright-spawner.test.ts @@ -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* () { diff --git a/src/experimental/playwright-spawner.ts b/src/playwright-spawner.ts similarity index 86% rename from src/experimental/playwright-spawner.ts rename to src/playwright-spawner.ts index 4f2e0c2..855f153 100644 --- a/src/experimental/playwright-spawner.ts +++ b/src/playwright-spawner.ts @@ -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. @@ -32,7 +32,7 @@ export interface PlaywrightSpawner { * @since 0.7.0 */ export const PlaywrightSpawner = Context.GenericTag( - "effect-playwright/experimental/playwright-spawner/PlaywrightSpawner", + "effect-playwright/playwright-spawner/PlaywrightSpawner", ); /** @@ -48,8 +48,7 @@ export const PlaywrightSpawner = Context.GenericTag( * * ```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; @@ -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; diff --git a/src/scratchpad/setup.ts b/src/scratchpad/setup.ts index 84e8204..5ec634e 100644 --- a/src/scratchpad/setup.ts +++ b/src/scratchpad/setup.ts @@ -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, {