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

@bffless/workflow

v1.4.0

Published

Authoring CLI for BFFless Workflow implementations — init/rename/add/lint/index/publish (spec apps/workflow/docs/spec/06-discovery-publishing-files.md)

Readme

@bffless/workflow

The authoring CLI for BFFless Workflow implementations (apps#420): workflow init|rename|add|lint|index|publish. A thin CLI over @bffless/workflow-lint, which stays the parser/schema/ resolver library — lint and index delegate straight into its published API (lintFile, resolveRuleSet, buildIndex) rather than re-implementing any of the linting logic.

This package lives outside the bffless platform CLI deliberately: the Workflow toolchain co-versions with the spec, which lives in this monorepo (apps/workflow/docs/spec/), not with platform verbs (2026-08-31 ruling on apps#420).

Status

Every verb — init, rename, add, lint, index and publish — is implemented (Phases 1–3 of the authoring CLI plan).

Install

npx @bffless/workflow --version

Inside the monorepo: pnpm --filter @bffless/workflow build, then node packages/workflow-cli/dist/cli.js <verb> ... (or pnpm workflow:cli).

CLI

workflow --version
workflow init   <alias> --from <owner>/<repo>|<path> [--path <dir>] [--ref <ref>] [options]
workflow rename <old> <new> [--dry-run]
workflow add    <name> [--step <path>]…
workflow lint   <file...> [--json] [--quiet] [--rules <dir>] [--alias <alias>] [--path-prefix <p>]
workflow index  <workflows-dir> --out <dir> --impl <alias> --name <display> [options]
workflow publish [--api-url <url>] [--project <owner/name>] [--alias <alias>]
                 [--harness-alias <alias>] [--path <dir>] [--workflows <dir>]
                 [--rules <dir>] [--name <display name>] [--description <text>] [--dry-run]

Exit codes: 0 clean/success · 1 lint errors/warnings · 2 usage/config/IO error. Every error goes to stderr as workflow: <message>. Every write is preflighted — a fatal precondition is checked and reported before anything touches disk, so a refused command never leaves a partial result behind.

init — start a new implementation from any source repo

workflow init <alias> --from <owner>/<repo>|<path> [--path <dir>] [--ref <ref>] [options]

Clones --from (an <owner>/<repo> GitHub spec, shallow by default; a --ref that isn't a branch/tag falls back to a full clone + checkout) — or, for local testing, reads an existing local directory in place with no clone. Inside that source, .bffless/workflow.json is the discovery contract: --path names where the package lives, or — if omitted — the conventional default (workflows/hello) is tried first, then the whole source tree is searched for candidates.

The found package is staged in a disposable temp directory, run through the boundary-aware rename engine there (old alias → <alias>), and only that already-renamed result is copied into --dest. Staging first — rather than copying straight into the destination and renaming in place — means the rename pass only ever sees the files this command actually copies: it never walks (and therefore never rewrites) anything already sitting in a populated destination, which matters most for --dest . landing a package inside an existing host repo.

On top of the copy, init generates the host repo's deploy-<alias>.yml / preview-<alias>.yml GitHub Actions workflows, plus a third, repo-wide .github/workflows/workflow-drive.yml — the receiver for the harness's driven-run repository_dispatch (event workflow-drive), which runs @bffless/workflow-headless run --wait park or resume depending on the dispatch's mode. Unlike the other two, it carries no <alias> in its name or contents — one copy per repo, generated (or skipped) alongside them. All three are skipped only when a repo-root package is copied into a repo-root destination — forking a whole single-implementation repo verbatim, whose own top-level CI travels with it. Generating them requires --project <owner/name>, the BFFless project this implementation deploys to (often not the same as the GitHub repo it lives in) — a required flag exactly when generation would happen. Existing hand-edited workflow files at those paths are never clobbered; they're reported as skipped.

workflow-drive.yml needs one repo secret: WORKFLOW_APP_TOKEN, an app token from Settings → App Tokens on the harness's admin, minted with the four scopes workflow:read workflow:run workflow:files auth:session. The driver signs the browser in from it through CE's session exchange (auth:session is the scope that gate checks; CE ≥ 0.4.50) and sends it as Bearer on every /api/workflow/* call it makes — no member email or password in the job (apps#588). The dispatch payload also carries drive_key, an optional nonce the template passes to the driver as WORKFLOW_DRIVE_KEY; the driver sends it as the x-workflow-drive-key header on every harness request the driven page makes — nothing is signed. Harmless when absent (env is empty and installs no route).

Options:

| Flag | Default | Meaning | | --- | --- | --- | | --from <owner>/<repo>\|<path> | bffless/workflow-implementations | Source repo or local path | | --path <dir> | searched | The package's location within the source repo | | --ref <ref> | default branch | Branch, tag, or commit SHA to clone | | --dest <dir> | ./<alias> | Where to copy the package; . for a repo-root implementation | | --project <owner/name> | — | The BFFless project this deploys to (required to generate .github/workflows) | | --harness-alias <alias> | workflow | Which harness alias it deploys under | | --skip-existing | off | On a path collision with the destination, keep the host's version and proceed instead of refusing — colliding paths are reported under a "skipped (already exists) — merge by hand" section, never copied. Refused (exit 2, nothing written) when a collision is a load-bearing file — package.json, tsconfig.json, a lockfile, vite.config.* — since skipping those orphans the copy or breaks the host's build; use --dest <subdir> instead | | --dry-run | off | Print the copy/rename/generate plan; write nothing |

Without --skip-existing, any path the copy would overwrite at --dest refuses the whole command up front (exit 2, every colliding path listed) — the error also names --skip-existing as the way past it.

The report (real run and --dry-run alike) also lists every pre-existing destination directory the copy merges files into (e.g. merged into existing scripts/ (1 file added)) — directory-level merges never show up as file conflicts, so this is the only trace they leave.

If the destination isn't ., init prints a reminder to add it to pnpm-workspace.yaml (creating the file if the host repo doesn't have one yet) and re-run pnpm install: the generated workflow builds with pnpm --filter ./<dest> run build, which only resolves once the workspace covers it.

rename — rename an implementation's alias in place

workflow rename <old> <new> [--dry-run]

Run from inside an already-inited implementation directory. <old> must match what .bffless/workflow.json actually declares, or the command refuses rather than guessing which tree was meant. Renames the .bffless/proxy-rules/<old>/ directory and rewrites every non-binary, non-vendored file's text content wherever <old> appears with a word boundary on both sides (so hello-pr-1/hello_jobs are rewritten while othello is left alone).

add — scaffold a new workflow + rule stub

workflow add <name> [--step <path>]…

Run from inside an already-inited implementation directory (the alias — and therefore the rule-set directory it scaffolds into — is read from .bffless/workflow.json). Writes .bffless/workflows/<name>.workflow.yaml (one job, one uses: pipeline step per --step, defaulting to a single step named <name> when --step is omitted) plus a matching rule stub (rule.yaml + .fn.js + .fn.test.yaml) per step path — so workflow lint reports zero rule-missing findings immediately after add, no hand-authoring required first.

lint / index

workflow lint  <file...> [--json] [--quiet] [--rules <dir>] [--alias <alias>] [--path-prefix <p>]
workflow index <workflows-dir> --out <dir> --impl <alias> --name <display> [options]

Same flags and the same exit-code contract as @bffless/workflow-lint's own workflow CLI — see its README for the full flag reference. index here also accepts one flag workflow-lint's own CLI doesn't have:

| Flag | Default | Meaning | | --- | --- | --- | | --driver-repo <owner/name> | GITHUB_REPOSITORY when set, else omitted | ADR-0006: the GitHub repo whose workflow-drive.yml a repository_dispatch reaches; written onto the index as driver.repo |

publish — index, prepare, sync, deploy, attach

workflow publish [--api-url <url>] [--project <owner/name>] [--alias <alias>]
                 [--harness-alias <alias>] [--path <dir>] [--workflows <dir>]
                 [--rules <dir>] [--name <display name>] [--description <text>] [--dry-run]

Run from inside an already-inited implementation directory. Drives the same four moves bffless/publish-workflow's GitHub Action makes, in process, against a live BFFless instance:

  1. index — buildIndex (the same machinery workflow index uses) builds <path>/.bffless/workflows/index.json from --workflows, checked against --rules.
  2. prepare — an alias-named copy of the rule set is staged under a disposable temp dir, plus a generated /w/<alias>/* forwarder rule (forwardCookies: true, order: 5, and a headerConfig that forwards cookie and authorization — the CE backend strips a caller's credential from an in-process sibling call unless the rule lists it, so an app token reaches the alias) pointing at the alias served in-process by the CE backend — never written into the source tree.
  3. rules push — spawns npx --yes [email protected] rules push against the staged copy, syncing it under /api/<alias>/ on --project.
  4. upload + attach — zips --path and deploys it to the --alias (base-path: /, its own rule set attached by name), then unions the synced rule set's id into --harness-alias's own proxyRuleSetIds — idempotent, so publishing the same implementation twice is a no-op.

The API key comes from BFFLESS_API_KEY only — never a flag (it would otherwise land in the process list). A missing key exits 2 before any network call. --dry-run prints every move with fully resolved values (URLs, alias, rule-set names, paths) and performs none of them.

Options:

| Flag | Default | Meaning | | --- | --- | --- | | --api-url <url> | BFFLESS_API_URL | Base URL of the BFFless instance | | --project <owner/name> | — | The BFFless project the alias + rule set live on (required) | | --alias <alias> | the identity file's alias | The implementation alias | | --harness-alias <alias> | workflow | The harness alias carrying the union of implementation rule sets | | --path <dir> | dist | Built bundle directory, also index's --out | | --workflows <dir> | .bffless/workflows | Directory of authored workflow YAML | | --rules <dir> | .bffless/proxy-rules/<alias> | The implementation rule-set directory | | --name <display name> | the alias | Display name shown on the Implementations screen (move 1's index.json) | | --description <text> | none | One line about the bundle, shown on the Implementations screen (move 1's index.json) | | --driver-repo <owner/name> | GITHUB_REPOSITORY when set, else omitted | ADR-0006: the GitHub repo whose workflow-drive.yml a repository_dispatch reaches; passed through to move 1's index.json as driver.repo | | --dry-run | off | Print the four resolved moves; write nothing, call no network |

Both packages ship a workflow bin

@bffless/workflow-lint cannot drop its bin: workflow (a breaking change under bffless/publish-workflow's ^1.0.0 npx pin); this package claims the same bin name — the intended end state for npx @bffless/workflow. If both are installed as dependencies of the same project, whichever installs last wins the shared node_modules/.bin/workflow shim; invoke this package explicitly (npx @bffless/workflow, or this monorepo's pnpm workflow:cli) when that matters.