Skip to content

blobatar-cli — the terminal surface (CLI-only subset of #4) - #5

Open
guillermolg00 wants to merge 5 commits into
Alain00:mainfrom
guillermolg00:feat/blobatar-cli
Open

blobatar-cli — the terminal surface (CLI-only subset of #4)#5
guillermolg00 wants to merge 5 commits into
Alain00:mainfrom
guillermolg00:feat/blobatar-cli

Conversation

@guillermolg00

@guillermolg00 guillermolg00 commented Aug 18, 2026

Copy link
Copy Markdown

What this is

The CLI slice of #4, standing on its own: packages/cli (publishes as blobatar-cli, bin blobatar) plus the private packages/render-core param table. No service, nothing touching apps/ — zero overlap with apps/site/worker.

Opened separately so the two contributions can be judged independently: if you take #4, close this one; if you'd rather keep your worker as the only URL surface, this PR has the piece that doesn't compete with it.

The CLI

blobatar <name>                      # SVG to stdout
blobatar <name> -o alain.png --size 512
blobatar --stdin -d ./blobatars/    # batch: one name per line
  • Flags speak the same vocabulary as a URL surface (--size, --background, --hue, --tone, --expression, --no-normalize) through render-core — one validation table, one set of error messages.
  • SVG to stdout by default; PNG never lands on a TTY (-o, or pipe + --format png); with -o the extension decides the format and a contradicting --format errors.
  • Batch: deterministic filenames (the render's own normalization, then [^A-Za-z0-9._-]_, capped at 200 chars); every filename is precomputed and a collision aborts the whole batch listing the conflicting names — nothing partially written. Exact duplicate lines are deduped.
  • Errors to stderr only, stdout carries only image bytes; exit codes 0/1.
  • Plain-Node portable source (#!/usr/bin/env node, Node ≥ 18) — a deliberate, documented exception to the repo's Bun-first rule, because npx is where the non-JS-project audience lives. bunx works too.
  • Published as plain JS with render-core inlined at build; runtime deps are exactly blobatar + @resvg/resvg-js (napi prebuilds, no postinstall).
  • Determinism promise, stated honestly in the README: SVG byte-identical per visual-contract major; PNG bytes tied to the resvg version in the lockfile. The documented CI pattern: generate the team's blobatars in the build, commit them, diffs stay clean until a visual major.

render-core

Private (private: true, never published), bundled into the CLI's dist at build time. Contains only parseParams (strings → BlobatarOptions) and the expression string→value map — no raster, no I/O. CONTEXT.md gains the one-line glossary exception for it. The param table currently mirrors the roster this branch was written against (happy|sad|mad|idle); extending to the 9 new expressions is a two-line change if this lands. The expression lookup is guarded with Object.hasOwn — a plain-object roster answers truthily to __proto__/constructor (details in the #4 thread; your worker's oneOf has the same exposure via in).

Testing

  • 46 tests across the two members: the full param table in render-core, and the CLI's whole flag/output/batch matrix in-process through an injected process seam (argv, stdin, TTY-ness, file writes) — no spawning in unit tests.
  • One smoke script spawns the built bin under Node to produce a real SVG and a real PNG (napi prebuild) — same pattern as the library package's own smoke.mjs.
  • Everything is wired into the member check scripts, so bun --filter '*' check covers both like every other member. Full workspace on this branch: 222 pass, 0 fail.

Open questions

  • Is blobatar-cli the name you want? The npm name is free, and the @blobatar scope is unclaimed if you'd rather have an org.
  • Release wiring is deliberately untouched: release.yml validates tags against the library only. Happy to extend it for the CLI once the name is confirmed — your tag convention, your call.

One private workspace package turns the six-param string vocabulary
(size, background, hue, tone, expression, normalize) into BlobatarOptions
with a single set of validation rules and error messages, so the URL
service and the CLI cannot drift apart. Glossary gains the one-line
exception: render-core is a private support package, never published.
A command-line tool publishing as blobatar-cli with the bin blobatar:
SVG to stdout, PNG to files or pipes (never a TTY), and batch generation
from stdin with deterministic filenames that abort on collision before
anything is written. Flags mirror the /v1 params through render-core, so
validation and error messages are identical across both surfaces.

The source is plain-Node portable (npx is the audience; a documented
exception to the repo's Bun-first rule) and the build inlines the private
render-core, leaving exactly two runtime deps: blobatar and
@resvg/resvg-js. The whole flag/output/batch matrix runs in-process
through an injected process seam under bun test; a smoke script spawns
the built bin under node, mirroring the library package's own.
A plain-object lookup answers truthily to __proto__ and constructor,
letting a crafted URL bypass the roster and crash downstream — a 400
turned into a 500. Own-property check plus regression tests. Also swaps
the CLI batch dedupe from O(n²) includes to a Set.
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

@guillermolg00 is attempting to deploy a commit to the alain00's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new terminal-only surface for blobatar by introducing a CLI package plus a small private “render-core” package to keep parameter parsing/validation consistent across string-based surfaces.

Changes:

  • Introduces packages/render-core (private) with parseParams() + a shared param allowlist and canonicalization behavior.
  • Adds packages/cli (blobatar-cli, bin blobatar) with in-process unit tests, a Bun build step that inlines render-core, and a Node-spawned smoke test.
  • Updates repo context docs and lockfile to include the new workspace members and @resvg/resvg-js.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/render-core/tsconfig.json Adds member TS config (Bun types) for render-core.
packages/render-core/package.json Declares private support package exported from TS source for bundler consumers.
packages/render-core/src/index.ts Implements shared param parsing + canonicalization (parseParams).
packages/render-core/test/params.test.ts Verifies the param table behavior and error messages.
packages/cli/tsconfig.json Adds member TS config (Bun types + JSON module support).
packages/cli/package.json Defines blobatar-cli packaging, build, smoke, and check scripts.
packages/cli/README.md Documents CLI usage, flags, batch behavior, and determinism guarantees.
packages/cli/src/cli.ts Implements pure CLI logic behind injected IO/deps seam (unit-testable).
packages/cli/src/main.ts Wires real Node process + resvg + blobatar into the pure CLI runner.
packages/cli/scripts/build.ts Bun build script producing dist/blobatar.mjs with Node shebang.
packages/cli/scripts/smoke.mjs Node-spawned smoke test validating real SVG/PNG outputs and exit behavior.
packages/cli/test/cli.test.ts In-process test matrix for flags/output/batch behavior.
CONTEXT.md Documents render-core as the deliberate exception to “packages are publishable”.
bun.lock Adds workspace entries and locks @resvg/resvg-js + its optional prebuilds.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/cli/src/cli.ts Outdated
Comment thread packages/render-core/src/index.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3fbff517ad

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/cli.ts Outdated
Comment thread packages/cli/src/cli.ts Outdated
Comment thread packages/cli/src/cli.ts
Comment thread packages/cli/package.json
… packability

tone=100 mapped to exactly 1, which the library's half-open bucketing
wraps to the FIRST swatch — verified: same bytes as tone=0. Now clamps
to the library's own 0.999999. Batch -d keeps the dir verbatim (trimming
made "/" into mkdir("") and a drive root relative) and the collision
preflight groups filenames case-insensitively, since default macOS and
Windows volumes merge casings — exactly when --no-normalize preserves
them. "--" ends option parsing so hyphen-led names render. render-core
gets a version: packing the CLI resolves every workspace range and an
unversioned member failed bun pm pack.
blobatar as a real semver range instead of workspace:* — bun still links
the workspace member, while a tarball built by npm pack (which does not
rewrite the workspace protocol) stays installable. stdout EPIPE exits 0:
piping into head is the reader being done, not an error.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants