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

@digital-gravy/etch-public-api

v0.6.0

Published

MIT-licensed typed client and contract for the Etch builder scripting API (window.etch). Etch itself is a separate proprietary product governed by its own commercial terms.

Readme

@digital-gravy/etch-public-api

Typed client and contract for the Etch builder scripting API exposed on window.etch. Intended for AI assistants and third-party plugins.

The Etch builder injects the runtime onto the page. This package ships the types plus a thin accessor over the global — there is nothing heavy to bundle, and the implementation always comes from the installed Etch version.

Status: experimental (0.x). The surface may change without a major version bump until it stabilizes. Prefer feature detection over version comparison, and don't pin production plugins to 0.x.

Install

npm install @digital-gravy/etch-public-api

Usage

Getting the API object

The Etch builder injects the runtime onto window.etch during its bootstrap. Acquire it with getEtch(), guarded by isEtchAvailable() for code that might run outside the builder:

import { getEtch, isEtchAvailable } from "@digital-gravy/etch-public-api";

function run() {
  if (!isEtchAvailable()) return; // not running inside the Etch builder
  const etch = getEtch();
  const textIds = etch.blocks.find({ type: "text" });
  etch.blocks.setText(textIds[0], "Hello world");
}

getEtch() throws an EtchApiError with code NOT_AVAILABLE when the builder isn't on the page, so you can also try/catch it. If your script may run before the builder has finished loading, wait until it appears:

import { getEtch, isEtchAvailable } from "@digital-gravy/etch-public-api";

async function whenEtchReady(timeoutMs = 10_000) {
  const start = Date.now();
  while (!isEtchAvailable()) {
    if (Date.now() - start > timeoutMs) throw new Error("Etch did not load");
    await new Promise((resolve) => setTimeout(resolve, 100));
  }
  return getEtch();
}

const etch = await whenEtchReady();

Reading and mutating blocks

// Read
const textIds = etch.blocks.find({ type: "etch/text" });
const json = etch.blocks.getJson(textIds[0]);

// Mutate (routes through the same guarded paths as the UI; undo/redo works)
etch.blocks.setText(textIds[0], "Hello world");
etch.blocks.addClass(textIds[0], "lead");

// Persist (blocks/styles wait for save; stylesheets/components/fields persist immediately)
await etch.saveAsync();

Styles

// Create a class or id rule
const styleId = etch.styles.create(".lead", "font-size: 1.25rem;");

// Find existing styles by selector type
const classStyles = etch.styles.list({ type: "class" });
const myStyle = etch.styles.list().find((s) => s.selector === ".lead");
console.log(myStyle?.id); // the id you pass to blocks.addClass etc.

// Global CSS custom properties (default collection)
etch.styles.setVariable("--brand", "#0af");
etch.styles.getVariable("--brand"); // "#0af"

// Variable methods accept an optional collection for multi-collection :root setups
etch.styles.setVariable("--brand", "#0af", "theme-a");
etch.styles.getVariable("--brand", "theme-a"); // "#0af"
etch.styles.listVariables("theme-a");
etch.styles.removeVariable("--brand", "theme-a");

Note: The collection field on style objects (StyleSummary) and the collection argument on create / update are internal implementation details — always omit them for regular styles. The four :root variable methods (listVariables, getVariable, setVariable, removeVariable) are the exception: they accept an optional collection parameter, defaulting to "default" when omitted.

Component edit mode

Use blocks.enterComponentEditMode to open a component's internal block tree for direct inspection or mutation, then save and exit when done:

// Find a component block
const [compId] = etch.blocks.find({ type: "etch/component" });

// Enter edit mode — the component's block tree becomes accessible
etch.blocks.enterComponentEditMode(compId);

// Inspect or mutate the component's children
const tree = etch.blocks.getTree();

// Persist the component definition to the backend
await etch.blocks.saveComponentEditModeAsync();

// Save the page, then exit edit mode
await etch.saveAsync();
etch.blocks.exitComponentEditMode();

To discard in-memory changes and restore the original block:

etch.blocks.exitComponentEditMode({ revert: true });

Error handling

Methods throw a typed EtchApiError with a code, rather than returning sentinels:

import { isEtchApiError } from "@digital-gravy/etch-public-api";

try {
  etch.blocks.getJson("does-not-exist");
} catch (err) {
  if (isEtchApiError(err)) {
    console.warn(err.code, err.message); // e.g. "BLOCK_NOT_FOUND"
  }
}

Version negotiation

Today (0.x): there is no real negotiation yet. The runtime reports etch.apiVersion as the coarse marker '0.x' (not a precise version), and getEtch({ apiVersion }) only does a best-effort major-version check that console.warns on mismatch — it never throws or adapts. While the surface is experimental, prefer feature detection over version comparison:

const etch = getEtch();
if (typeof etch.blocks.someNewMethod === "function") {
  // safe to use
}

In the future (once the contract reaches 1.x): etch.apiVersion will report a real semver, and getEtch() will negotiate against the runtime's native connect() — returning an instance pinned to the version your plugin targets, and failing fast when the runtime can't satisfy it:

// Reserved API — shape of versioned access once the contract is stable:
const etch = getEtch({ apiVersion: "^1.0", id: "my-plugin" });
//    └─ delegates to window.etch.connect({ apiVersion: "^1.0", id }) when present,
//       yielding a version-pinned instance (throws on an incompatible runtime).

getEtch() already accepts { apiVersion, id } today, so plugins can pass them now and have them take effect automatically once the stable runtime ships — no code change needed.

Typed block JSON

Block JSON is a discriminated union on type, so the compiler flags a block whose payload doesn't match its declared type, and getJson() / getTree() results narrow by type:

const block = etch.blocks.getJson(id);

if (block.type === "etch/text") {
  console.log(block.text); // narrowed to the text-block shape
} else if (block.type === "etch/element") {
  console.log(block.tag, block.attributes);
}

// Authoring is checked too — this is a type error (an `etch/text` has no `tag`):
etch.blocks.create({
  type: "etch/text",
  version: 1,
  context: {},
  children: [],
  tag: "div", // ✗ type error
});

Special element attributes

Some block types recognise special attributes in addition to standard HTML:

etch/dynamic-image — rendered as <img>:

  • mediaId — WordPress attachment ID. Etch fetches the media object and uses its URL as src, overriding any explicit src. Supports dynamic expressions (e.g. {post.featured_image_id}).
  • useSrcSet"true" to generate a responsive srcset from the media (requires mediaId).
  • maximumSize — WordPress image size slug (e.g. "large", "full") used when resolving the image. Defaults to "full".
etch.blocks.setAttribute(imgBlockId, "mediaId", "{post.featured_image_id}");
etch.blocks.setAttribute(imgBlockId, "useSrcSet", "true");

etch/svg — inline SVG:

  • src — URL of an external .svg file. Etch fetches and inlines the SVG at render time. Supports dynamic expressions.
  • stripColors"true" to strip fill and stroke colour declarations from the fetched SVG, so CSS can drive its colours instead.
etch.blocks.setAttribute(svgBlockId, "src", "/icons/logo.svg");
etch.blocks.setAttribute(svgBlockId, "stripColors", "true");

Types only

Every contract type is exported and dependency-free, so you can use them directly:

import type {
  PublicBlockJson,
  EtchBlocksApi,
} from "@digital-gravy/etch-public-api";

You can also work against the global directly — the package augments window.etch, so window.etch?.blocks.find(...) is fully typed once the package is imported.

What's here

  • getEtch(options?) / isEtchAvailable() — acquire the API from the page.
  • EtchApiError / isEtchApiError() / EtchApiErrorCode — typed errors.
  • ETCH_API_VERSION — the contract version this package targets (0.x).
  • The full contract as exported types:
    • BlocksEtch, EtchBlocksApi, EtchBlockJson, PublicBlockJson, FindBlocksPredicate, BlockPatch, …
    • StylesEtchStylesApi, StyleSummary, StyleListFilter, StyleSelectorType, StylePatch
    • StylesheetsEtchStylesheetsApi, StylesheetSummary, StylesheetInput, StylesheetPatch
    • ComponentsEtchComponentsApi, PublicComponentSummary, PublicComponentJson, ComponentPatch, ComponentProperty, …
    • LoopsEtchLoopsApi, EtchLoop, EtchLoopConfig, BlockLoopBinding, …
    • NavigationEtchNavigationApi, NavigationPlace, PostSummary, TemplateSummary
    • FieldsEtchFieldsApi, CustomField, CustomFieldGroup, …
    • UI / HistoryEtchUiApi, EtchHistoryApi, ColorScheme

Versioning

This package's npm version tracks the scripting contract, not the Etch product. Additive changes are minor; breaking changes are reserved for a major bump plus a deprecation window — once the contract leaves 0.x.

License

This package — the @digital-gravy/etch-public-api client and type definitions — is released under the MIT License.

The MIT license applies only to this package. It does not extend to the Etch builder or the Etch WordPress plugin, which are separate proprietary products governed by their own commercial license terms. This package merely describes and communicates with that software; installing or using it grants no rights to Etch itself.