Authoring, validation, build, sample-test, and local Studio host for jsonspecs rules projects.
npm install --global jsonspecs-clijsonspecs init <project-name>
jsonspecs validate
jsonspecs test
jsonspecs build
jsonspecs studio| Command | Purpose |
|---|---|
init |
Creates a minimal rules project with manifest, example rules, samples, local operator pack, and output directories. |
validate |
Loads artifacts from rules/ and reports structured diagnostics from the jsonspecs compiler. |
test |
Runs every JSON sample in samples/ against the compiled project. |
build |
Writes deterministic snapshot.json and build-info.json into dist/. |
studio |
Starts the local SPA Studio and JSON API for exploration and playground runs. |
Human-readable CLI output is colorized automatically when stdout/stderr is a TTY. Use
--color=always, --color=never, or --color=auto to override detection. NO_COLOR
disables color and FORCE_COLOR enables it. --json output is always plain machine-readable
JSON without ANSI escape codes, and --quiet suppresses human output.
manifest.json
rules/
library/
entrypoints/
internal/
dictionaries/
operators/
node/
samples/
docs/
dist/
docs/ is reserved for hand-written project documentation. The CLI no longer generates Markdown or Confluence-style documentation from pipelines. Studio is an exploration/playground UI; it does not expose /api/docs/* endpoints.
manifest.json must contain an explicit SemVer ruleset version:
{
"project": {
"id": "checkout-rules",
"version": "1.0.0",
"title": "Checkout rules",
"description": "Checkout validation rules",
"language": "ru"
}
}project.version is copied to:
snapshot.meta.rulesetVersion;build-info.json.rulesetVersion;- runtime result
ruleset.rulesetVersionafterjsonspecs.compileSnapshot().
Increment it whenever the rules package is released. Projects created before project.version became required must add it before running validate, test, build, or Studio.
The manifest also drives Studio display metadata:
catalog.fields[field].titleis the primary human-readable field label;catalog.fields[field].descriptionis secondary explanatory text;catalog.entrypoints[id]andcatalog.artifacts[id]provide titles/descriptions for pages and flow views;catalog.operatorsand operator-packmeta.operatorsprovide operator descriptions.
jsonspecs build writes a deterministic snapshot suitable for jsonspecs.compileSnapshot():
{
"format": "jsonspecs-snapshot",
"formatVersion": 1,
"sourceHash": "...",
"engine": { "minVersion": "2.1.1" },
"artifacts": [],
"meta": {
"projectId": "checkout-rules",
"projectTitle": "Checkout rules",
"description": "Checkout validation rules",
"rulesetVersion": "1.0.0"
}
}build-info.json duplicates deployment metadata useful for CI, Docker images, and runtime services: project id/title, ruleset version, engine version, snapshot format/version, source hash, artifact count, entrypoints, and local Node operator packs.
Each samples/*.json file is a complete execution case:
{
"context": {
"pipelineId": "entrypoints.order.validation",
"currentDate": "2026-07-12"
},
"payload": {
"order": { "amount": 1500 }
},
"expect": {
"status": "OK",
"exact": true,
"issues": []
}
}expect.status is exact. expect.issues uses subset matching, so a sample can assert only stable fields such as code, field, and level. expect.exact: true rejects additional issues.
Project-local custom operators are loaded from manifest.json:
{
"operatorPacks": {
"node": ["./operators/node"]
}
}A local Node operator pack exports check, predicate, and optional meta:
module.exports = {
check: {
amount_gt_zero(rule, ctx) {
const got = ctx.get(rule.field);
if (!got.ok) return { status: "FAIL", actual: undefined };
const value = Number(got.value);
return {
status: Number.isFinite(value) && value > 0 ? "OK" : "FAIL",
actual: got.value,
};
},
},
predicate: {},
meta: {
operators: {
amount_gt_zero: {
description: "должно быть больше нуля",
},
},
},
};Project-local operator packs should use the runtime context passed by jsonspecs:
ctx.get(path)— stable payload/context field access;ctx.has(path)— presence check;ctx.payloadKeys— flattened payload keys;ctx.getDictionary(id)— dictionary lookup.
Do not import jsonspecs or deepGet from project-local operator packs.
jsonspecs studio serves a bundled SPA from / and a JSON API under /api/*.
Current Studio capabilities:
- entrypoint list and project summary;
- pipeline flow, nested conditions, and stats;
- rule, condition, dictionary, and generic artifact pages;
- playground execution against sample payloads;
- safe
basictrace rendering in the playground; - SPA deep-link fallback for routes such as
/rules/<id>and/pipelines/<id>/playground.
Studio binds to 127.0.0.1 by default and uses same-origin requests. It is a local development tool and must not be exposed as a production service.
The bundled frontend is built from the separate jsonspecs-studio-ui repository and copied into static/.
The source checkout intentionally depends on a sibling ../jsonspecs checkout:
git clone https://github.com/catindev/jsonspecs.git
git clone https://github.com/catindev/jsonspecs-cli.git
cd jsonspecs-cli
npm ci
npm run verifypackage.json pins the coordinated engine release in:
{
"config": {
"jsonspecsVersion": "2.1.1",
"jsonspecsGitRef": "v2.1.1"
}
}Advance both fields deliberately when the CLI needs a newer engine. Dependabot/Renovate will not update this pair automatically because the source dependency is intentionally a sibling checkout for reproducible local and CI builds.
npm test
npm run test:pack
npm run verifynpm run test:pack creates real tarballs, installs them in a clean CommonJS consumer, and runs the installed CLI through init, validate, test, and build.
Current coverage and recommended additions are tracked in TESTING.md.
- Publish the matching
jsonspecsversion first. - Update
config.jsonspecsVersionandconfig.jsonspecsGitRefif needed. - Tag
jsonspecs-cliwithv<version>.
The tag workflow downloads the exact engine release, builds a sanitized registry-safe tarball whose dependency is ^<jsonspecsVersion>, repeats the pack/install smoke test, publishes to npm, and creates a GitHub release.
Direct publication from the source checkout is blocked by private: true and a prepublishOnly guard.