Skip to main content

@nubbin/cli

The publish path from a terminal. Compile a document, write the artifact, move the route pointer, and ask whether anything already live still fits the blocks in your code — with no editor running and no React loaded.

npm install -D @nubbin/cli

The config

One file beside the application it configures, found by climbing from where the command ran — no further than the repository root, and no climbing at all outside a repository:

// nubbin.config.ts
import { defineConfig } from "@nubbin/cli";
import { createFsArtifactStore } from "@nubbin/store-fs";
import { catalog } from "./src/nubbin/catalog";
import { registry } from "./src/nubbin/registry";
import { documents } from "./src/nubbin/documents";

export default defineConfig({
catalog,
registry,
store: createFsArtifactStore("./.nubbin"),
document: (route) => documents[route] ?? null,
save: (route, version) => drafts.write(route, version),
});

document is a function rather than a table because where documents live is yours: a directory of fixtures, a database, an API. Returning null says the route has none, which is reported rather than guessed at.

save is document in the other direction, and optional: it is where the editing commands put a document back. A config without one refuses those commands and keeps everything else working, so a project that only publishes fixtures carries nothing it does not use.

The file is loaded through jiti, so it may import the way the rest of your application does — extensionless specifiers, path aliases, TypeScript throughout.

The commands

nubbin compile /pricing # would it publish, and as what hash — writes nothing
nubbin publish /pricing # compile, write the artifact, then move the pointer
nubbin unpublish /pricing # drop the pointer; the artifact stays where it is
nubbin rollback /pricing 9f2c1a8e4b7d0356 # point the route back at an artifact already stored
nubbin rollback /pricing --to 3 # the same, naming the document version instead
nubbin show /pricing # the document as authored — ids, blocks, slots
nubbin add /pricing Card --parent grid --slot cards # mint a node into a slot; prints its id
nubbin remove /pricing 4f9d… # remove the node and everything beneath it
nubbin move /pricing 4f9d… --parent grid --slot cards --index 0 # move a node into a slot
nubbin set /pricing 4f9d… title Spring # set one prop; JSON when it parses, else the string
nubbin history /pricing # what the route has pointed at, newest first
nubbin status # every live route, or one of them
nubbin check # every live route against the registry as it is now
nubbin doctor # diagnose Nubbin's project and live-state contracts
nubbin help # this list, as an answer rather than an error

--config <path> names a config instead of searching for one.

Editing from the terminal

The four write verbs address nodes by the ids nubbin show prints. add mints the id itself — core's operations deliberately do not, so the same composition always produces the same document — and prints it after the arrow, because that id is how every later command names the node.

Each verb applies one operation, compiles the result, and persists it through save. A document that cannot compile is refused with its issue codes and nothing is saved — there is no --force, because the state one command leaves is the state the next reads. A field whose catalog entry carries a data hint is refused by name: it resolves per request, so a value written there would be compiled into a hole and replaced before anyone saw it. The reasoning lives in an edited document goes back where it came from.

Publishing while the application is running

A framework's page cache is invalidated by the process that serves it. Publishing straight into the store while a server is up moves the pointer and leaves that server answering from its cache, so the two disagree until a restart.

--origin <url> publishes through the running application instead:

nubbin publish /pricing --origin http://localhost:3000

It posts to /api/nubbin/publish and /api/nubbin/unpublish, which are two route handlers @nubbin/next provides the bodies for.

Exit codes

CodeMeans
0done — warnings may have been printed
1refused: the document, the rollback, or a page already live
2the command could not be run as given

A refusal prints the code it carries — unknown-block, slot-max, invalid-props — on stderr, so a script branches on the same names your editor would. Warnings go to stderr too, even when the exit is 0. Stdout carries the answer or nothing: HASH=$(nubbin compile /pricing) captures a hash, never a warning beside it, and never a complaint about why there is not one.

In CI

nubbin check reads every route pointer, compares each artifact against the registry it was compiled with, and exits 1 when a page that is live depends on a block that changed version or left the registry. That makes it a required check on a pull request, not a report someone reads.

nubbin doctor is the wider, still read-only diagnostic. It checks that catalog and registry names agree; that the manifest contains one valid pointer per route; that each pointer resolves to an artifact for the same hash and route; and that those artifacts fit the current registry. It does not prescribe or inspect authentication, hosting, databases, caches, or any other consumer-owned implementation behind the callbacks.

Read the Nubbin documentation for the complete command reference. MIT.

Interfaces

Type Aliases

Functions