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

efficient-web-builder

v30.1.2

Published

The EFFICIENT website builder: the website editor, the general sections and the renderer that draws them, compiled. Hostable by any EFFICIENT product; nothing here imports a host.

Readme

efficient-web-builder

The EFFICIENT website builder: the website editor, the general sections, and the renderer that draws a published page from them. It has no host in it. The EFFICIENT storefront, the admin portal and Foyerboard each mount it and hand it what is theirs.

This repository was the folder packages/web-builder of efficient-storefront until 2026-10-07, and carries that folder's history: each file's log goes back to the day it was written there (doc/adr/0001).

The one rule

Nothing in src/ imports anything outside src/ except the packages package.json declares as peer dependencies. Not a commerce block, not a page, not a host's API client — by value or by type.

What a host adds, it hands over on the editor's session (src/lib/editor-session.ts), read once for the life of the mount:

| The host's | Declared in | What it is | |---|---|---| | block registry | src/lib/puck/registry.ts, general-registry.tsx | Which blocks exist, in what order. The builder's own registry is the general blocks; a host registers its own among them. | | backend | src/lib/web-backend.ts | Everything the editor asks a server for: pages, the edit lock, the site record, media, and the optional groups (blog, chat, social, theme, …). | | chrome | src/lib/editor-chrome.tsx | The host's furniture round the editor, as components: a wrapper, panels, Pages-panel rows, its products and its row under a Link field. | | document hooks | src/lib/editor-document-hooks.ts | What the host does to a document as it opens and as an edit is stored. | | failure reader | src/lib/web-backend.ts (provideHostFailures) | How to read the host client's own errors. | | record types | src/lib/content/record-types.ts (registerRecordTypes) | What a dynamic page of this host may draw besides a product: a key, a label and the fields a block may read. Declared once, for the app and not for one mount. | | block sources | src/lib/content/block-sources.ts (registerBlockSources) | Where a block of this host may read its rows from instead of holding them: a key, a label, the blocks it fills and sample rows for the editor. The host's backend puts the rows in as a page is served. Declared once, for the app and not for one mount. |

The storefront's are assembled in its lib/storefront-editor.ts.

The editor's words in a host's language

The editor speaks English, and a host that runs in another language lays its own words over it (6.1.0). The English stays in the code, where it is said, and many of the words are also keys (a row is filed under its tab and given its glyph by its English label), so it is translated where it is SHOWN:

  • The words are keyed by the English they replace. efficient-web-builder/format/lib/editor-words-catalogue exports WEB_BUILDER_WORDS, every phrase the editor says (a settings row's label, hint and choices, a group heading, a panel's and a dialog's words, a block's name and its Add-panel blurb, a placeholder drawn on the canvas while editing), and the type of its keys, WebBuilderPhrase. It is written from the source by pnpm run words and held to it by scripts/test-editor-words.ts, so a host typing its catalogue as EditorWordsFor<WebBuilderPhrase> fails its typecheck on a phrase it lacks.
  • A sentence with a value names it in braces, in the English and in the host's word alike: "Delete {name}": "{name} löschen". A host may give a function of the values instead, for a plural or an agreement the braces cannot say ("{n} pages": ({ n }) => …). A count is two phrases (sayCount("{n} page", "{n} pages", n)).
  • Names of a host's places are phrases of their own, so a host renames them once, in English too: the editor says "Storefront ▸ Sites ▸ Settings" and "the admin", where an EFFICIENT admin has them, and a host whose menu is elsewhere says its own.
  • The host hands its words once, while its frame renders, with configureEditorWords(words, locale) from lib/editor-words — as it configures lib/web-runtime. The words are fixed for the life of a page (harness UI-25), one editor is mounted at a time, and they are read by plain functions as well as components, so they are the module's state, not a React context. A word left out reads in English.
  • What is written into a page stays English: a block's default content, a saved name. And what a visitor reads on the published page is never the editor's words.
  • The engine's words are efficient-easel's (EaselStringsProvider); a host wraps the editor in it. The parts the builder draws itself that take easel's words as a prop (a picture box, a Locations tree) are handed useEaselStrings()'s.

efficient-storefront-blocks adds the phrases of its own blocks the same way and takes a host's words on its WebsiteEditor (words, locale).

Two checks hold the rule, both in CI and both without an allow-list:

  • pnpm run typecheck — @/… points at src/ and nowhere else, so an import of a host does not resolve.
  • pnpm run test:boundary — the packages the sources import are exactly the manifest's peer dependencies, and the manifest declares no module type.

Layout

src/ keeps the paths these files had in the storefront — src/lib/…, src/components/… — and imports are written @/lib/…, @/components/…. Comments that name a file say lib/puck/controls; read them from src/.

How the storefront uses it

The storefront installs this repository's npm package, like any host, at one exact version in its package.json (efficient-storefront doc/adr/0011, 2026-10-11). Its commerce and service blocks are written with the builder's control builders and passes, which they import by package name and side (efficient-web-builder/render/…, /format/…, /editor/…; 24.0.0). Their @/… resolves in the storefront's own tree and in its blocks only.

  • A change to the builder is made HERE and reaches the storefront as a release. Merge it, cut a GitHub Release vX.Y.Z (which publishes to npm), move efficient-storefront-blocks to that release, then open one storefront pull request that moves the version in its package.json and its blocks pin together. The storefront's check fails while it and the blocks' repository install two different builders.

  • To try a branch against the storefront's suite before a release, install its build there as a tarball:

    pnpm run build && pnpm pack --pack-destination /tmp/builder-trial
    # in an efficient-storefront worktree
    pnpm add "efficient-web-builder@file:/tmp/builder-trial/efficient-web-builder-<version>.tgz"
    just check

    Exactly one storefront guard fails on a trial, the one that will not let an unreleased builder merge.

  • What a host receives is compiled. A function's own name is a letter in dist/, so a host's test cannot find a component by that name: it imports the component and compares, or asks what it draws. Export what a host has to be able to find.

So this repository's CI proves the builder stands alone, and the storefront's proves it still does what it did, on the package. The guards that run the editor assembled with a host stay in the host's repository.

From 2026-10-07 to 2026-10-11 the storefront kept a vendored copy of src/ instead, pinned to a commit here (doc/adr/0001, doc/adr/0002).

Published as efficient-web-builder

From 1.0.0 the npm package efficient-web-builder is this repository and nothing else, published from here. (Up to 0.10.0 that name was built in the storefront: these sources assembled WITH the storefront's blocks and EFFICIENT's backend, for the admin portal. That assembly is now efficient-storefront-blocks.)

The source is closed, so the package is compiled: pnpm run build writes one minified .js and one .d.ts for every file under src/, with no source map and no comment. There is no bundle.

Three sides (24.0.0, doc/adr/0005). Every module is on one side, and ships under it at the path it has in src/:

| Side | What it is | A host imports it as | |---|---|---| | render | what a published page draws from | efficient-web-builder/render/lib/puck/page-frame | | format | pure data and rules, no React | efficient-web-builder/format/lib/puck/section-catalog | | editor | the settings panel, the editor's registries and screen parts | efficient-web-builder/editor/lib/puck/controls |

import { PublishedPageFrame } from "efficient-web-builder/render/lib/puck/page-frame";
import { createEditorSession } from "efficient-web-builder/render/lib/editor-session";
import type { WebsiteBackend } from "efficient-web-builder/editor/lib/web-backend";
import "efficient-web-builder/editor/components/storefront-editor.css";

The exports map is the three sides and nothing else, so a module is only reachable through its own side: an editor module has no file under render/, and a page that imports through render/ cannot reach the editor in any bundler. Which side a module is on is decided once, in scripts/entry-sides.mjs, and the build fails when the render side reaches the editor. To find a module's side, look for it under dist/<side>/.

Inside the package @/… is already a relative path, so a host needs no mapping of its own. It installs the peer dependencies the manifest lists.

The sheets load in one order. Since efficient-easel 1.0 the editor's controls are efficient-shell/ui's, and easel's tokens resolve to the shell's with no fallback, so efficient-shell/ui.css comes first, compiled by the host's own Tailwind (its @source scans the shell's dist/). Then easel's sheets (efficient-easel/screen.css), then components/storefront-editor.css.

Releasing

A merge is not a release. The pull request that wants hosts to see a change raises version in package.json and adds a section to CHANGELOG.md. After it merges, publish a GitHub Release tagged vX.Y.Z on the merge commit:

gh release create vX.Y.Z --target <full merge sha> --title vX.Y.Z --notes-file <notes>

.github/workflows/release.yml runs the whole gate again and publishes through npm trusted publishing. There is no token anywhere; npm accepts the publish only from that workflow file in this repository. The registry can answer with the previous version for a couple of minutes after a green run.

Working here

pnpm install
pnpm run verify

verify is the typecheck, the boundary check, the screen rule (scripts/test-screen-in-easel.mjs, doc/adr/0004), the build and scripts/verify-dist.mjs, which reads dist/ as a host would: every file there, every import resolving, every "use client" kept, no source in it.

Node 26 (.node-version), pnpm 11. Do not add "type": "module" to package.json: the storefront loads these files beside its own, and a module type here makes its test runner load every one of them twice.

Shared conventions — commits, branches, CI, naming — are the organisation's harness (efficient-harness), as in every platform repository.