overengineeringstudio / overengineeringstudio/effect-utils
Add @overeng/effect-playwright package
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 82
- Forks
- 2
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 121
Description
Summary
Create an Effect-native Playwright wrapper package (@overeng/effect-playwright) that provides idiomatic Effect APIs for browser automation and testing.
Motivation
Playwright is a powerful browser automation tool, but its Promise-based API doesn't integrate naturally with Effect's resource management, error handling, and observability patterns. An Effect-native wrapper would provide:
- Resource safety: Automatic cleanup of browser contexts and pages via
Effect.acquireRelease - Typed errors: Custom
TaggedErrortypes instead of generic exceptions - Observability: Built-in spans for tracing browser operations
- Streaming: Console message handling as Effect Streams
- Composability: Layer-based dependency injection for browser contexts
Design Decisions to Align On
1. Service Architecture
Option A: Single BrowserContext service (livestore approach)
export class BrowserContext extends Context.Tag('Playwright.BrowserContext')<
BrowserContext,
{ browserContext: PW.BrowserContext }
>() {}
Option B: Separate Browser and Page services
export class Browser extends Context.Tag('Playwright.Browser')<Browser, PW.Browser>() {}
export class Page extends Context.Tag('Playwright.Page')<Page, PW.Page>() {}
Option C: Full hierarchy (Browser → BrowserContext → Page)
Recommendation: Start with Option A (simple) but design for extensibility to Option C.
2. API Surface
What Playwright APIs should we wrap initially?
Core (must have):
- Browser/context launch and lifecycle
- Page navigation (
goto,reload,goBack,goForward) - Element interactions (
click,fill,type,press) - Waiting (
waitForSelector,waitForNavigation,waitForLoadState) - Screenshots and PDFs
Extended (nice to have):
- Console message streaming
- Network request interception
- File uploads/downloads
- Dialogs handling
- Frames and iframes
3. Error Handling Strategy
Option A: Single PlaywrightError with discriminated union
export class PlaywrightError extends Schema.TaggedError<PlaywrightError>()('PlaywrightError', {
_tag: Schema.Literal('Timeout', 'ElementNotFound', 'NavigationFailed', ...),
message: Schema.String,
cause: Schema.Defect,
}) {}
Option B: Separate error classes per operation type
export class TimeoutError extends Schema.TaggedError<TimeoutError>()('Playwright.TimeoutError', {...}) {}
export class ElementNotFoundError extends Schema.TaggedError<ElementNotFoundError>()('Playwright.ElementNotFoundError', {...}) {}
Recommendation: Option B for better type-level error tracking in Effect channels.
4. Locator Strategy
How to handle Playwright's Locator API?
Option A: Pass-through (use Playwright locators directly)
const click = (locator: PW.Locator) => Effect.tryPromise(() => locator.click())
Option B: Effect-wrapped locators
export class Locator extends Context.Tag('Playwright.Locator')<Locator, PW.Locator>() {}
const getByRole = (role: string) => Effect.map(Page, (page) => page.getByRole(role))
Recommendation: Option A initially - locators are lightweight and don't need resource management.
5. Testing Integration
Should we provide vitest/playwright test runner integration?
// Option: Effect-aware test helper
export const it = {
effect: <E, A>(name: string, effect: Effect.Effect<A, E, BrowserContext>) =>
test(name, () => Effect.runPromise(effect.pipe(Effect.provide(BrowserContextLive))))
}
Reference Implementation
Based on @livestore/effect-playwright:
// Key patterns to adopt:
// 1. Layer.scoped for browser lifecycle
export const browserContextLayer = (params: MakeBrowserContextParams) =>
Layer.scoped(BrowserContext, browserContext(params))
// 2. Effect.addFinalizer for cleanup
yield* Effect.addFinalizer(() => Effect.promise(() => browserContext.close()))
// 3. Stream.asyncPush for event handling
export const pageConsole = ({ page }) =>
Stream.asyncPush<ConsoleMessage, SiteError>((emit) =>
Effect.acquireRelease(
Effect.sync(() => {
page.on('console', (msg) => emit.single(parseMessage(msg)))
return cleanup
}),
() => Effect.sync(() => page.off('console', ...))
)
)
// 4. Schema.TaggedError for typed errors
export class SiteError extends Schema.TaggedError<SiteError>()('Playwright.SiteError', {
label: Schema.String,
messages: Schema.Union(Schema.Array(ConsoleMessage), Schema.Defect),
}) {}
Proposed Package Structure
packages/@overeng/effect-playwright/
├── src/
│ ├── mod.ts # Main exports
│ ├── BrowserContext.ts # Browser context service & layer
│ ├── Page.ts # Page operations
│ ├── Locator.ts # Locator utilities (if wrapped)
│ ├── Console.ts # Console message streaming
│ ├── Errors.ts # Error types
│ └── Testing.ts # Test runner integration
├── package.json
└── tsconfig.json
Questions for Discussion
- Should we support both
@playwright/testandplaywrightpackages? - Do we need Chrome extension testing support (like livestore)?
- Should console streaming be opt-in or always-on?
- How should we handle headless vs headed mode configuration?
- Should we integrate with
@effect/vitestfor test helpers?
Next Steps
- Align on design decisions above
- Create initial package scaffold
- Implement core browser context management
- Add page navigation and basic interactions
- Add console streaming
- Add test helpers
- Write documentation and examples
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing the proposed package structure under packages/@overeng/effect-playwright/ and the referenced @livestore/effect-playwright implementation. Resolve the listed service, API, error, locator, and testing decisions before implementing the scaffold; done means an agreed package with the core lifecycle, navigation, interactions, streaming, and test-helper scope defined.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- testing, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100