Skip to content

salute-developers/design-system-builder

Repository files navigation

Design System Builder

A web platform for building, managing, and publishing design systems end-to-end.

Design System Builder lets product teams compose their own design system from a shared component catalog, customize tokens (colors, typography, spacing, etc.), preview the result in real time, and then generate, publish, and document the resulting npm package β€” all from a single UI.

It is the tooling layer behind several Salute design systems built on top of @salutejs/plasma.

What you can do with it

  • Compose a design system from a shared catalog of components and variations
  • Tweak tokens (colors, typography, spacing, shape, etc.) with live preview
  • Generate a ready-to-use npm package from your configuration
  • Publish that package to a private npm registry in one click
  • Generate documentation (Docusaurus site) and deploy it to S3
  • Inspect and query the underlying registry through an admin panel (tables, schema, OpenAPI, NL queries)

Architecture

The platform is a small set of independent services around a central registry. All services are deployed and orchestrated through Docker Compose.

                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                       β”‚       Admin        β”‚  React SPA β€” registry inspector,
                       β”‚   apps/admin :3004 β”‚  schema browser, OpenAPI, NL queries
                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                 β”‚ REST (OpenAPI-typed)
                                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  REST   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚       Client       β”‚ ──────▢ β”‚     DS Registry    │◀──▢│  PostgreSQL      β”‚
β”‚  apps/client :3002 β”‚         β”‚   ds-registry :3008β”‚    β”‚  (postgres-      β”‚
β”‚  (DS builder UI)   β”‚         β”‚  Express + Drizzle β”‚    β”‚   registry :5433)β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β”‚       β”‚                          β–²
      β”‚       β”‚                          β”‚ fetch DS data
      β”‚       β”‚                          β”‚
      β”‚       β”‚                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚       └──────REST─────▢│    DS Generator    │───▢│    Publisher     β”‚
      β”‚                        β”‚  ds-generator :3005β”‚    β”‚  publisher :3007 β”‚
      β”‚                        β”‚  Fastify + CLI     β”‚    β”‚  npm publish     β”‚
      β”‚                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β”‚
      β”‚                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      └─────────REST──────────▢│  Docs Generator    │───▢│      AWS S3      β”‚
                               β”‚ docs-generator:3006β”‚    β”‚  (docs hosting)  β”‚
                               β”‚  NestJS+Docusaurus β”‚    β”‚                  β”‚
                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                         β”‚ fetch DS data
                                         β–Ό
                                  (DS Registry)

Services

Service Path Port Stack Purpose
DS Registry services/ds-registry 3008 Express, Drizzle ORM, Postgres, Zod, OpenAPI Source of truth. Stores design systems, components, variations, tokens. Exposes a typed REST API and an auto-generated OpenAPI spec.
DS Generator services/ds-generator 3005 Fastify Builds an npm package from a design system stored in the registry. Available both as a web service and as a CLI (plasma-ds-generate).
Publisher services/publisher 3007 Express Receives a packaged design system from the generator and publishes it to a configured npm registry.
Docs Generator services/ds-documentation-generator 3006 NestJS, Handlebars, Docusaurus Pulls a design system from the registry, renders a Docusaurus project from templates, builds the static site, and uploads it to S3.
Client apps/client 3002 React 18, Vite, @salutejs/plasma-* The end-user UI for composing a design system, editing tokens, and triggering generation/publication/documentation.
Admin apps/admin 3004 React 18, Vite, OpenAPI types Inspector for the registry: schema view, table browser, OpenAPI reference, saved/NL queries.

How a design system flows through the system

  1. Authoring. A user opens the Client (apps/client) and composes a design system β€” picks components, edits tokens, previews the result. The client persists everything to the DS Registry over REST.
  2. Generation. From the same UI the user triggers generate-download or generate-publish on the DS Generator. The generator fetches the design system from the registry, builds an npm package, and either streams it back to the client or hands it off to the Publisher.
  3. Publication. The Publisher receives the tarball and pushes it to a configured npm registry, making the design system installable as @salutejs-ds/<name>.
  4. Documentation. The user (still from the client UI) triggers documentation/generate on the Docs Generator. It fetches the design system from the registry, renders a Docusaurus project from Handlebars templates, builds the static site, and uploads the result to S3.
  5. Inspection. The Admin app talks to the registry directly through its OpenAPI-typed client, used for debugging schema, browsing tables, and running ad-hoc queries.

Repository layout

design-system-builder/
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ admin/                          # React admin (registry inspector)
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ client.ts           # typed API client (openapi-fetch)
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ openapi.json        # generated OpenAPI spec (artifact)
β”‚   β”‚   β”‚   β”‚   └── types.gen.ts        # generated TS types (artifact)
β”‚   β”‚   β”‚   β”œβ”€β”€ pages/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ TablesPage.tsx      # /tables β€” DB table browser
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ QueriesPage.tsx     # /queries β€” saved query catalog
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ SchemaPage.tsx      # /schema β€” ER diagram
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ DocsPage.tsx        # /docs β€” Scalar API reference
β”‚   β”‚   β”‚   β”‚   └── SettingsPage.tsx    # /settings
β”‚   β”‚   β”‚   β”œβ”€β”€ App.tsx
β”‚   β”‚   β”‚   └── main.tsx
β”‚   β”‚   β”œβ”€β”€ vite.config.ts
β”‚   β”‚   └── package.json
β”‚   └── client/                         # React DS builder UI (Plasma-based)
β”‚       β”œβ”€β”€ src/
β”‚       β”œβ”€β”€ vite.config.ts
β”‚       └── package.json
β”œβ”€β”€ services/
β”‚   β”œβ”€β”€ ds-registry/                    # Express + Drizzle, central registry API
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ db/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ index.ts            # DB connection (postgres-js + Drizzle)
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ schema.ts           # Drizzle schema
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ seed-dev.ts         # dev seeding entry point
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ seed-prod.ts        # prod seeding entry point
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ reset.ts            # full schema reset
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ clear.ts            # truncate tables
β”‚   β”‚   β”‚   β”‚   └── seeds/              # per-table seed files (data/dev/prod)
β”‚   β”‚   β”‚   β”œβ”€β”€ routes/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ index.ts            # root router
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ api/                # CRUD routes (one file per resource)
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ utils.ts        # assertFound, tryCatch
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ design-systems.ts
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ components.ts
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ tokens.ts
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ legacy.ts       # /api/legacy/design-systems/:name/*
β”‚   β”‚   β”‚   β”‚   β”‚   └── …               # the rest of the resources
β”‚   β”‚   β”‚   β”‚   └── misc/               # auxiliary routes
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ tables.ts       # /api/tables
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ queries.ts      # /api/queries
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ nl-query.ts     # /api/nl-query
β”‚   β”‚   β”‚   β”‚       └── schema.ts       # /api/schema
β”‚   β”‚   β”‚   β”œβ”€β”€ validation/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ schema.ts           # Zod schemas for all tables
β”‚   β”‚   β”‚   β”‚   └── middleware.ts       # validateBody, validateParams
β”‚   β”‚   β”‚   β”œβ”€β”€ openapi/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ spec.ts             # OpenAPI 3 spec (auto-built from Drizzle + Zod)
β”‚   β”‚   β”‚   β”‚   └── generate.ts         # writes openapi.json
β”‚   β”‚   β”‚   β”œβ”€β”€ queries/
β”‚   β”‚   β”‚   β”‚   └── catalog.ts          # named query catalog
β”‚   β”‚   β”‚   β”œβ”€β”€ nl-query/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ llm-service.ts      # Z.AI API client
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ schema-context.ts   # schema context for the LLM
β”‚   β”‚   β”‚   β”‚   └── sql-validator.ts    # validation (SELECT only)
β”‚   β”‚   β”‚   β”œβ”€β”€ zod-extend.ts           # extendZodWithOpenApi (called once)
β”‚   β”‚   β”‚   └── index.ts                # Express server entry
β”‚   β”‚   β”œβ”€β”€ drizzle/                    # generated SQL migrations
β”‚   β”‚   β”œβ”€β”€ drizzle.config.ts
β”‚   β”‚   β”œβ”€β”€ .env.example
β”‚   β”‚   └── package.json
β”‚   β”œβ”€β”€ ds-generator/                   # Fastify, builds DS npm packages
β”‚   β”œβ”€β”€ ds-documentation-generator/     # NestJS, builds Docusaurus + S3 deploy
β”‚   └── publisher/                      # Express, npm publishing
β”œβ”€β”€ docs/                               # Internal docs (schema, status, todo)
β”œβ”€β”€ .github/
β”‚   β”œβ”€β”€ workflows/                      # CI: build & deploy apps to S3
β”‚   └── actions/
β”œβ”€β”€ .claude/
β”‚   └── skills/                         # Claude Code skills
β”‚       β”œβ”€β”€ sync-all/                   # /sync-all β€” full sync
β”‚       β”œβ”€β”€ sync-validation/            # /sync-validation β€” refresh Zod schemas
β”‚       β”œβ”€β”€ sync-routes/                # /sync-routes β€” create/remove routes
β”‚       β”œβ”€β”€ sync-spec/                  # /sync-spec β€” refresh OpenAPI spec
β”‚       └── sync-api-types/             # /sync-api-types β€” regenerate TS types
β”œβ”€β”€ docker-compose.yml                  # Production compose (subset)
β”œβ”€β”€ docker-compose.dev.yml              # Development compose (full stack)
β”œβ”€β”€ setup-docker.sh                     # One-command dev setup
└── package.json                        # Root scripts (dev / build / install:all)

Quick start

Recommended path: Docker. It is the only setup that's actively used and tested. Local (non-Docker) development still works for individual services but you have to wire ports and env vars yourself.

Prerequisites

  • Docker and Docker Compose
  • Node.js β‰₯ 18 (only if you want to run services outside of Docker)

Run everything with Docker (recommended)

# Clone
git clone <repo-url>
cd design-system-builder

# Start everything (postgres + registry + admin + client + generator + publisher + docs-generator)
./setup-docker.sh

The script builds the dev images, starts the stack via docker-compose.dev.yml, runs the registry migrations, seeds dev data, and prints a health summary.

Once it's done:

Service URL
Client (DS builder UI) http://localhost:3002
Admin (registry inspector) http://localhost:3004
DS Registry API http://localhost:3008/api
DS Generator http://localhost:3005
Docs Generator http://localhost:3006
Publisher http://localhost:3007
Postgres (ds-registry) localhost:5433

Common commands:

# Logs
docker-compose -f docker-compose.dev.yml logs -f

# Stop (data persists)
docker-compose -f docker-compose.dev.yml down

# Stop and wipe data
docker-compose -f docker-compose.dev.yml down -v

# Re-run migrations / re-seed manually
docker-compose -f docker-compose.dev.yml exec ds-registry npx drizzle-kit migrate
docker-compose -f docker-compose.dev.yml exec ds-registry npx tsx src/db/seed-dev.ts

Run locally without Docker

You'll need a Postgres instance reachable from ds-registry and the right env vars in each service.

# 1. Start Postgres (you can still use the dev compose for the DB only)
docker-compose -f docker-compose.dev.yml up -d postgres-registry

# 2. Configure ds-registry
cp services/ds-registry/.env.example services/ds-registry/.env
# default values already point at localhost:5433/ds_registry

# 3. Install dependencies for every workspace
npm run install:all

# 4. Apply migrations and seed
cd services/ds-registry
npm run db:migrate
npm run db:seed:dev
cd ../..

# 5. Start everything in parallel
npm run dev

The root package.json exposes per-service scripts so you can also start them one by one:

Script What it runs
npm run dev All services and apps in parallel
npm run dev:services Backend services only (registry, generator, publisher, docs-generator)
npm run dev:ds-registry DS Registry only (port 3008)
npm run dev:ds-generator DS Generator only (port 3005)
npm run dev:publisher Publisher only (port 3007)
npm run dev:ds-documentation-generator Docs Generator only (port 3006)
npm run dev:client Client app only (port 3002)
npm run dev:admin Admin app only (port 3004)
npm run build Production build of every service and app
npm run install:all Install dependencies in every workspace

DS Registry: database

Postgres lives in the dev compose as postgres-registry (host port 5433). The registry connects with:

DATABASE_URL=postgresql://postgres:postgres@localhost:5433/ds_registry
PORT=3008
ZAI_API_KEY=your-zai-api-key-here   # for /api/nl-query

Inside Docker the host becomes postgres-registry:5432. See services/ds-registry/.env.example.

Drizzle ORM commands

Run from services/ds-registry:

Command Description
npm run db:generate Generate a SQL migration after changing schema.ts
npm run db:migrate Apply migrations to the database
npm run db:seed:dev Seed the DB with dev fixtures
npm run db:seed:prod Seed the DB with prod fixtures
npm run db:reset Drop the schema (run migrate + seed afterwards)
npm run db:clear Truncate tables (keeps the schema)
npm run db:studio Open Drizzle Studio

Common scenarios

First run (without Docker):

docker-compose -f docker-compose.dev.yml up -d postgres-registry
cd services/ds-registry
npm run db:migrate
npm run db:seed:dev

Schema (schema.ts) or routes (routes/api/) changed:

# Inside services/ds-registry
npm run db:generate   # only if schema.ts changed
npm run db:migrate    # only if schema.ts changed

# From the repo root, via Claude Code
/sync-all             # updates validation, routes, openapi spec, and admin types

Reset to a clean state:

cd services/ds-registry
npm run db:reset
npm run db:migrate
npm run db:seed:dev

Admin: API types

The admin app uses a typed OpenAPI client. Types are generated from the registry's OpenAPI spec β€” no running backend required:

# Inside apps/admin
npm run api:gen        # uses the spec checked into apps/admin/src/api/openapi.json
npm run api:gen:live   # fetches it from http://localhost:3008/api/openapi.json

After regeneration the typed client in apps/admin/src/api/client.ts is good to use:

import { api } from "../api/client";

const { data } = await api.GET("/users");
const { data: user } = await api.GET("/users/{id}", {
  params: { path: { id: "..." } },
});

Run this after any schema/route changes β€” or just use /sync-all, which does it for you.

Admin: pages

Route Description
/tables Browse all DB tables, paginated
/queries Saved query catalog with parameters
/schema Interactive ER diagram
/docs Scalar API reference (OpenAPI)
/settings Admin settings

Claude Code skills

Skills for keeping the registry's generated artifacts in sync with the schema and routes. Live in .claude/skills/.

Skill What it does
/sync-all Runs the four skills below in order
/sync-validation Updates services/ds-registry/src/validation/schema.ts
/sync-routes Creates/removes files in services/ds-registry/src/routes/api/, updates routes/index.ts
/sync-spec Updates services/ds-registry/src/openapi/spec.ts
/sync-api-types Regenerates apps/admin/src/api/openapi.json and types.gen.ts

CI / Deployment

GitHub Actions in .github/workflows/ build the two frontend apps and sync them to S3:

Backend services (ds-registry, ds-generator, publisher, ds-documentation-generator) are deployed separately from this repo's CI.

Documentation

License

See repository for license information.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors