npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@venn-lang/browser

v0.6.0

Published

The browser namespace: sixteen verbs that drive a page, plus two element matchers and the ports the driving actually goes through.

Readme

@venn-lang/browser

The browser namespace: sixteen verbs that drive a page, plus two element matchers and the ports the driving actually goes through.

Venn's grammar knows nothing about browsers. This package adds the vocabulary: browser.visit, browser.click, browser.fill and the rest arrive as a plugin, exactly the way a third-party package would add them. None of them touch a browser directly. Every verb calls a BrowserDriver, a port with two implementations, so a flow can run against a real engine or against a deterministic in-memory model with the same source unchanged.

Install

Nothing to install: the plugin ships inside the CLI's stdlib. Declare it in the file that needs it, and the runner loads it.

import { browser } from "venn/browser"
import { browser as web } from "venn/browser"   # or under an alias

Usage

module demo.signin

import { browser } from "venn/browser"
import { assert } from "venn/assert"

flow "Sign in" {
  step "open the app" {
    const page = browser.newContext { viewport: { width: 1280, height: 800 } }
    browser.visit "/login"
    browser.fill "#email" "[email protected]"
    browser.fill "#password" "hunter2"
    browser.click "button[type=submit]"
    browser.waitFor { text: "Welcome back" }
    browser.waitForUrl "/dashboard"

    const shot = browser.screenshot "after-login"
    expect shot.name == "after-login"
    expect page.url != null
  }
}

venn test runs this offline against the fake driver the stdlib binds by default.

Verbs

The namespace is browser. Positional arguments come first, the options map last.

| Verb | Gives back | | --- | --- | | browser.launch { engine, headless } | browser.Browser | | browser.newContext { locale, viewport } | browser.Page | | browser.visit "url" { headers } | nothing | | browser.click "selector" | nothing | | browser.fill "selector" "value" | nothing | | browser.select "selector" "value" | nothing | | browser.hover "selector" | nothing | | browser.press "key" | nothing | | browser.upload "selector" { file } | nothing | | browser.download "selector" | browser.Download | | browser.screenshot "name" | browser.Screenshot | | browser.waitFor { text, selector, timeout } | nothing | | browser.waitForUrl "url" | nothing | | browser.evaluate "script" | whatever the script returned | | browser.frame "name" | nothing | | browser.clearCookies | nothing |

press takes one argument or two. With one, it is the key and the page has focus. With two, the first is the selector to focus and the second is the key: browser.press "#email" "Control+A".

select matches the option's value, not its label. waitFor takes its condition as options, so the wait is browser.waitFor { text: "Welcome back" }, never a bare argument. Its timeout is written as a string or as a number of milliseconds ({ timeout: "5s" }, { timeout: 5000 }); a bare duration literal is not accepted there in this build.

Types and matchers

The plugin publishes five names under browser.:

| Type | Shape | | --- | --- | | browser.Browser | opaque handle carrying id and engine | | browser.Page | opaque handle carrying id and url | | browser.Element | { visible, text, value }, open | | browser.Download | { path, bytes } | | browser.Screenshot | { name, path } |

Browser and Page stay opaque because they are live things owned by the driver. Download and Screenshot are plain data the verbs hand back, so a flow reads their fields.

Two matchers apply to an Element subject: visible passes when the element model says it is visible, and text passes when the element's text equals or contains the argument.

The plugin also declares two resources, Browser (worker scope) and Page (flow scope). The runtime does not execute resource declarations yet, so they describe the surface rather than open anything.

Ports

Two ports, both with a real implementation and a double, which is the condition for being a port at all.

BrowserDriverPort

venn.port.browser-driver, contract version 1, requires the net capability. Sixteen methods, one per verb. Every action reaches it through ctx.port(BrowserDriverPort), so a verb never knows which engine is underneath.

| Implementation | What it is | | --- | --- | | createFakeBrowserDriver(options?) | A deterministic, offline driver over an in-memory DOM model. Preload elements and a starting URL; read back state and element(selector). | | createRealBrowserDriver() | The real-engine stub. Every method throws VN8090, because engine automation is out of scope for the language repository. |

import { createFakeBrowserDriver } from "@venn-lang/browser";

const driver = createFakeBrowserDriver({ elements: { "#email": { visible: true } } });
await driver.visit({ url: "/dashboard" });
await driver.fill({ selector: "#email", value: "[email protected]" });

driver.state.url;            // "/dashboard"
driver.state.history;        // ["/dashboard"]
driver.element("#email");    // { visible: true, text: "", value: "[email protected]" }

The fake records url, history, clicks, fills, frame and cookiesCleared, which is what a test asserts against.

PreviewProviderPort

venn.port.preview-provider, contract version 1, requires nothing. Three methods, start, stop and latestFrame, for the live frame stream a studio UI renders. Frame streaming is CDP-only on some engines and polled on others, which is why it is a port and not a method on the driver. A frame is the control-plane pointer (seq, width, height, mime, data), not a pixel buffer.

| Implementation | What it is | | --- | --- | | createNonePreviewProvider() | The zero-cost default for CI. Starts nothing, never yields a frame. | | createFakePreviewProvider() | Returns a canned JPEG frame for any worker that has been started. |

API

| Export | What it is | | --- | --- | | browserPlugin (also the default export) | The PluginDefinition: namespace browser, requires net, carrying the actions, matchers, resources and types. | | BrowserDriverPort, BrowserDriver | The port descriptor and its interface. | | PreviewProviderPort, PreviewProvider | The frame-stream port descriptor and its interface. | | createFakeBrowserDriver, createRealBrowserDriver | The two driver implementations. | | createNonePreviewProvider, createFakePreviewProvider | The two preview implementations. | | FakeBrowserDriver, FakeBrowserState, FakeDriverOptions, FakeElement, FillRecord | The fake driver's inspection surface. | | LaunchOptions, ContextOptions, Viewport, VisitArgs, FillArgs, PressArgs, UploadArgs, DownloadArgs, WaitForArgs, EvaluateArgs, FrameArgs | The driver's argument types. | | BrowserHandle, PageHandle, DownloadResult, ScreenshotResult | What the driver returns. | | PreviewTarget, PreviewFrame | The preview port's argument and result types. |

Binding a driver

A host chooses the implementation once, at startup. @venn-lang/stdlib binds the fake for both ports; pass your own binding to override it.

import { BrowserDriverPort, createRealBrowserDriver } from "@venn-lang/browser";
import { createRunner } from "@venn-lang/runtime";

const runner = createRunner({
  host,
  plugins,
  sink,
  ports: [{ port: BrowserDriverPort, impl: createRealBrowserDriver() }],
});

BrowserDriverPort requires net. A host that does not offer it fails at bind time with a readable problem, rather than with a TypeError in the middle of a flow.

See also

  • @venn-lang/sdk for definePlugin, defineAction and defineMatcher, the API this package is built on.
  • @venn-lang/contracts for Port, Host and capability negotiation.
  • @venn-lang/stdlib for the plugin list and the default port bindings the CLI runs with.
  • @venn-lang/mail for the sibling plugin that checks the email a browser flow triggered.