Skip to content
 
 

Repository files navigation

English | Korean | Japanese

Glendam

Glendam

⛭ Multipurpose headless browser automation for Gleam and the BEAM ⛭

Package Version Hex Docs Target: Erlang

Project status and lineage

Glendam started as a fork of Chrobot. That origin is a core part of the project's history: Chrobot established the original design, high-level API, and Chrome DevTools Protocol foundation on which Glendam builds. Glendam is independently maintained and is not an official release from Chrobot's original author.

Today, most Glendam changes support web integration in the Glendix ecosystem, including browser workflows used by mxpak and the family test harnesses. This is the current development focus, not the limit of the project. Glendam's long-term goal is to become a multifunction, general-purpose headless browser for automation, testing, scraping, rendering, document generation, and other browser-driven workflows in Gleam and on the BEAM.

The project is not kept in lockstep with Chrobot. Features, dependency upgrades, compatibility fixes, and releases are developed here under the Glendam identity. The package and Gleam module namespace are glendam, and environment variables use the GLENDAM_ prefix.

About

Glendam provides typed bindings to the stable Chrome DevTools Protocol, plus higher-level browser automation APIs. It manages a Chrome or Chromium instance through an Erlang Port and handles protocol communication for you.

Typical uses include:

  • generating PDFs from HTML;
  • taking screenshots;
  • web scraping and archiving;
  • browser integration tests; and
  • direct, typed Chrome DevTools Protocol calls.

Warning

The generated protocol surface is large and not every binding is covered by tests. Treat untested protocol calls as experimental.

Requirements

  • Gleam 1.17.0 or newer
  • Erlang/OTP 29 (the tested development version is 29.0.4)
  • rebar3 3.27.0 or newer
  • Google Chrome or Chromium for browser-backed operations

The versions used by contributors and CI are recorded in .mise.toml and the GitHub Actions workflow.

Installation

Gleam

gleam add glendam@1

Elixir

# mix.exs
defp deps do
  [
    {:glendam, "~> 1.0", app: false, manager: :rebar3}
  ]
end

Browser setup

Glendam can use an existing Google Chrome or Chromium installation. Set GLENDAM_BROWSER_PATH when automatic discovery is not suitable.

The package also includes a utility that installs Chrome for Testing inside the current project:

gleam run -m glendam/install

From Elixir:

mix run -e :glendam@install.main

The installer can be configured with GLENDAM_TARGET_VERSION and GLENDAM_TARGET_PATH. Browser launch configuration can be supplied with:

  • GLENDAM_BROWSER_PATH
  • GLENDAM_BROWSER_ARGS
  • GLENDAM_BROWSER_TIMEOUT
  • GLENDAM_LOG_LEVEL
  • GLENDAM_TRANSPORT (auto, pipe, websocket, or ws)

Examples

Take a screenshot

import glendam
import glendam/chrome
import gleam/result

pub type ExampleError {
  BrowserCouldNotLaunch(reason: chrome.LaunchError)
  BrowserWorkflowFailed(
    reason: glendam.DeferredBrowserError(chrome.RequestError),
  )
}

pub fn main() -> Result(Nil, ExampleError) {
  use browser <- result.try(
    glendam.launch()
    |> result.map_error(BrowserCouldNotLaunch),
  )
  glendam.defer_quit(browser, fn() {
    use page <- result.try(
      glendam.open(browser, "https://gleam.run", 30_000),
    )
    use _ <- result.try(glendam.await_selector(page, "body"))
    use screenshot <- result.try(glendam.screenshot(page))
    glendam.to_file(screenshot, "hi_lucy")
  })
  |> result.map_error(BrowserWorkflowFailed)
}

Generate a PDF with Lustre

import glendam
import glendam/chrome
import gleam/result
import lustre/element
import lustre/element/html

pub type ExampleError {
  BrowserCouldNotLaunch(reason: chrome.LaunchError)
  BrowserWorkflowFailed(
    reason: glendam.DeferredBrowserError(chrome.RequestError),
  )
}

fn build_page() -> String {
  html.body([], [
    html.h1([], [element.text("Spanakorizo")]),
    html.p([], [element.text("A page rendered by Lustre and printed by Glendam.")]),
  ])
  |> element.to_document_string()
}

pub fn main() -> Result(Nil, ExampleError) {
  use browser <- result.try(
    glendam.launch()
    |> result.map_error(BrowserCouldNotLaunch),
  )
  glendam.defer_quit(browser, fn() {
    use page <- result.try(
      glendam.create_page(browser, build_page(), 10_000),
    )
    use document <- result.try(glendam.pdf(page))
    glendam.to_file(document, "recipe")
  })
  |> result.map_error(BrowserWorkflowFailed)
}

Scrape a page

import glendam
import glendam/chrome
import gleam/io
import gleam/list
import gleam/result

pub type ExampleError {
  BrowserCouldNotLaunch(reason: chrome.LaunchError)
  BrowserWorkflowFailed(
    reason: glendam.DeferredBrowserError(chrome.RequestError),
  )
}

pub fn main() -> Result(Nil, ExampleError) {
  use browser <- result.try(
    glendam.launch()
    |> result.map_error(BrowserCouldNotLaunch),
  )
  glendam.defer_quit(browser, fn() {
    use page <- result.try(
      glendam.open(
        browser,
        "https://books.toscrape.com/",
        30_000,
      ),
    )
    use _ <- result.try(glendam.await_selector(page, "body"))
    use items <- result.try(
      glendam.select_all(page, ".product_pod h3 a"),
    )
    use titles <- result.try(
      list.map(items, fn(item) {
        glendam.get_attribute(page, item, "title")
      })
      |> result.all(),
    )

    io.debug(titles)
    Ok(Nil)
  })
  |> result.map_error(BrowserWorkflowFailed)
}

Please respect websites' terms of service, robots policies, and capacity when scraping.

Use from Elixir

{:ok, browser} = :glendam.launch()
{:ok, page} = :glendam.open(browser, "https://example.com", 10_000)
{:ok, object} = :glendam.select(page, "h1")
{:ok, text} = :glendam.get_text(page, object)
:ok = case :glendam.quit(browser) do
  {:ok, nil} -> :ok
  other -> other
end

Migrating from Chrobot

The rename is intentionally visible because chrobot and glendam are separate Hex packages:

- gleam add chrobot
+ gleam add glendam

- import chrobot
- import chrobot/protocol/page
+ import glendam
+ import glendam/protocol/page

- CHROBOT_BROWSER_PATH=/path/to/chrome
+ GLENDAM_BROWSER_PATH=/path/to/chrome

Most API names remain familiar, but major Glendam releases may introduce breaking changes as the maintained fork evolves. Read CHANGELOG.md before upgrading.

Development

gleam deps download
gleam check
gleam test
gleam format --check src test dev vendor/justin_fork/src vendor/justin_fork/test

Browser-backed tests run when GLENDAM_TEST_BROWSER_PATH points to a Chrome or Chromium executable. Without it, the test entry point explains how to configure the browser.

Protocol source files are maintained in this repository. If the checked-in bindings must be regenerated from the JSON schemas in assets/, run the Gleam development module directly:

gleam run -m codegen/generate_bindings
gleam format src test dev

This is a maintenance operation, not an upstream synchronization workflow.

Documentation

License

Glendam remains available under the MIT License. See LICENSE for the original copyright notice and the continuation copyright notice.

About

Multipurpose headless browser automation for Gleam and the BEAM, forked from Chrobot.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages