Vocab Bloom Hub
此页面仅提供英文版本。

Contributing to Vocab Bloom Hub

First of all, thank you for considering contributing to Vocab Bloom Hub ❤️

We welcome all kinds of contributions, including:

  • Bug fixes
  • New features
  • Documentation improvements
  • UI/UX enhancements
  • Performance optimizations
  • Refactoring
  • Tests and tooling

Getting Started

Prerequisites

  • Node.js >= 22.13 to run it; the Jest suites need 24.9+ (NestJS 12 is ESM-only and Jest's require(ESM) needs it)
  • Yarn 4 (the repo pins the version via packageManager; enable it with corepack enable)
  • PostgreSQL — optional for development: without DATABASE_URL the server uses a local dev.sqlite whose schema follows the entities automatically, so a change to the data model needs no migration until it is ready for Postgres (docs/migrations.md). A production run and the Postgres-only suites need a real one (docs/database.md)

1. Fork the Repository

Create your own fork of the repository on GitHub.

2. Clone Your Fork

git clone https://github.com/Fristail27/vocab-bloom-hub.git
cd vocab-bloom-hub

3. Install Dependencies

yarn

4. Start Development Server

printf 'NODE_ENV=development\nADMIN_USERNAME=admin\nADMIN_PASSWORD=change-me\nNEXT_PUBLIC_BASE_API_URL=http://localhost:3010/api\n' > .env
yarn dev

The dictionary is empty until Import dictionary in the admin UI loads it. The production run of the same checkout (yarn build && yarn start, Postgres) is step 3 of the README.

Coding agents

Configure only the clients you use, then start the agent in the repository root:

yarn agents:setup codex          # or cursor, claude, gemini, copilot, kiro, amp, opencode
yarn agents:setup codex cursor   # several clients

docs/agents.md covers setup, MCP, trust, existing local settings, skills and first-run checks. AGENTS.md contains the shared project instructions; .agents/ holds the common configuration and skills. Generated client files are ignored by Git.

Tech Stack

LayerTechnology
FrontendNext.js 16 (App Router), React, Ant Design, Sass modules, next-intl
BackendNestJS 12, TypeORM, Swagger (OpenAPI)
DatabasePostgreSQL (production) / SQLite via better-sqlite3 (development)
TestingJest, Supertest, Playwright
ToolingTypeScript, Yarn 4 workspaces, ESLint 10, Prettier, Husky + lint-staged, Dependabot

Scripts

All commands run from the repository root.

CommandWhat it does
yarn devRun the API, the admin UI and the website together (with watch)
yarn server:dev / yarn front:devRun only the API (port SERVER_PORT, default 3010) or only the UI (port FRONT_PORT, default 3000)
yarn site:dev / yarn site:build / yarn start:siteThe website: dev server / production build / start of that build (port SITE_PORT, default 3020)
yarn build / yarn startProduction build of the server and the admin UI / start both builds (NODE_ENV=production, Postgres)
yarn testAll unit tests (server, frontend, site, npm SDK)
yarn jest --selectProjects serverOnly server tests (or frontend, site, sdk)
yarn workspace server test:covServer unit tests with coverage; CI fails below the thresholds in apps/server/jest.config.ts
yarn workspace server test:e2eServer e2e tests (Supertest against an in-memory SQLite)
yarn workspace server test:postgresThe Postgres-only suites: query-plan guard and trigram search (need a postgres:// DATABASE_URL)
yarn e2e / yarn e2e:uiBrowser e2e: production frontend build + Playwright (API :3011, UI :3001)
yarn e2e:siteBrowser e2e of the website (API :3012, site :3021)
yarn lint / yarn lint:fixESLint
yarn format / yarn format:checkPrettier
yarn checklint + format:check + peers:check (unmet peer dependencies) — run before opening a PR

Database migrations (Postgres only, need DATABASE_URL):

DATABASE_URL=postgres://... yarn workspace server migration:generate src/db/migrations/MyChange
DATABASE_URL=postgres://... yarn workspace server migration:run      # also migration:revert / migration:show

The public API contract and the two SDKs have generators of their own (openapi:generate, generate, generate_models.py); the chain and when to run it are in docs/api-tools.md.

Server HTTP tests start the app with await app.listen(0, '127.0.0.1') and close it in teardown with await app.close(). Keep the server listening throughout the suite: when Supertest starts it itself, its wildcard bind and IPv4 requests can reach another local process on macOS (Supertest #906). For upload tests that expect an early authorization rejection, attach a small in-memory buffer or send no file: a file stream may still be writing when the server answers and closes the connection, causing EPIPE instead of the expected response.

Project Philosophy

Vocab Bloom Hub aims to be:

  • Simple and maintainable
  • Beginner-friendly
  • Open for collaboration
  • Focused on learning and productivity

Please try to keep contributions aligned with these goals.

Branch Naming

Use descriptive branch names whenever possible.

Examples:

  • feature/add-authentication
  • fix/mobile-layout
  • docs/update-readme

Commit Messages

Please write clear and meaningful commit messages.

Examples:

  • feat: add spaced repetition algorithm
  • fix: resolve vocabulary sorting issue
  • docs: improve installation guide

Pull Requests

Before submitting a pull request:

  • Ensure the project builds successfully and yarn check passes
  • Keep PRs focused and minimal
  • Avoid unrelated changes in the same PR
  • Update documentation if needed

Licensing of contributions

There is no contributor agreement to sign. By opening a pull request you agree that your contribution is licensed under the terms of what it changes: code and documentation under the MIT license, dictionary data — an entry edited in the admin UI, a correction sent through Report a mistake on a word page and applied by the owner, a dataset revision — under CC BY 4.0 for the project's own dictionary. Other datasets retain their source terms; edits in an own dataset or fork carry its contribution license without replacing inherited licenses. Include the original source, version, links and required notices when transferring data. Keep the model label (generated_by_model) truthful on data you generate: every record names the model behind it, see docs/data.md.

Documentation languages

English is the source language of the documentation. A page may have translated versions next to it, named <name>.<lang>.md (docs/api.md ↔ docs/api.ru.md; the README's live under docs/: README.md ↔ docs/README.ru.md, README.es.md, README.fr.md, README.pt.md, README.de.md, README.zh.md, README.ar.md) and registered under translations in apps/site/src/content/registry.ts, where the website renders them under /ru, /es, /fr, /pt, /de, /zh, /ar. The README exists in every interface language of the apps; the other pages have a Russian version at most. A page without a translation shows the English text on the site with a notice. Code comments, issue and PR templates and the pages without a translation are English only.

重要

Any change to a page with translations must be applied to every one of them in the same PR so they stay in sync section by section.

Reviewing a translation

The translated texts — the READMEs under docs/, the translated pages, the interface messages of both apps (apps/site/messages/*.ts, apps/frontend/messages/*.ts) — are machine-made until a native speaker has read them. A review is a pull request: the fixes, if any, plus one line at the top of the file with the language, your login, the date and the commit of main you read — <!-- reviewed: de · @login · 2026-10-03 · 32148f5 --> in Markdown (after the title), // reviewed: de · @login · 2026-10-03 · 32148f5 first in a .ts file. Keep the lines of earlier reviews above yours. A review with nothing to fix is that one line, and it is welcome.

Pull Request Checklist

  • Code builds successfully
  • Changes were tested
  • Documentation updated if necessary
  • A translation you read as a native speaker carries its reviewed: line (above)
  • No unnecessary files included

The check-pull-request workflow runs the linters, the typecheck of every workspace, the unit and e2e suites (SQLite and Postgres), the browser suites, the production and Docker smokes, and two quality gates: the server unit coverage must not drop below the thresholds in apps/server/jest.config.ts (yarn workspace server test:cov reproduces the check), and yarn npm audit --all --recursive --severity high plus pip-audit on the Python SDK must find nothing — fix an advisory by upgrading (yarn up -R <package>), not by silencing it.

Coding Guidelines

Please follow the existing project structure and coding style.

General recommendations:

  • Write readable and maintainable code
  • Use meaningful variable and function names
  • Prefer reusable components
  • Avoid unnecessary dependencies
  • Keep functions and components small when possible

Dependencies

  • Declare a dependency in the workspace that imports it (apps/frontend, apps/server, …), not at the root; the root holds only the tooling shared by every workspace.
  • Peer dependencies must be met. yarn peers:check (part of yarn check, run in CI) fails on any unmet one — Yarn itself only warns. When a package declares a range that lags behind the version the monorepo runs and CI exercises the combination anyway, list it in KNOWN_MISMATCHES in scripts/check-peer-requirements.mjs with the reason; the check also fails when a listed mismatch is gone, so the list stays current. Yarn's packageExtensions cannot widen a range a package already declares, which is why the exceptions live there.
  • Dependabot (.github/dependabot.yml) watches every workspace manifest, the Python SDK (uv) and the GitHub Actions, grouped per directory into one weekly PR each. A new workspace must be added to its directories list.

Reporting Bugs

When creating a bug report, please include:

  • Steps to reproduce
  • Expected behavior
  • Actual behavior
  • Screenshots if applicable
  • Environment information

Suggesting Features

Feature requests are welcome.

Please describe:

  • The problem you are trying to solve
  • Your proposed solution
  • Possible alternatives

Code of Conduct

By participating in this project, you agree to follow the Code of Conduct.

Releasing

One version covers the whole monorepo; the dataset keeps its own (manifest.version). The repeatable part of a release:

  1. The release PR. Bump the version everywhere with the only tool allowed to touch it — node scripts/bump-version.mjs <version> (six files; a CI test keeps them equal) — and curate the new CHANGELOG.md entry (the release-changelog agent skill in .agents/skills/ — /release-changelog <version> in Codex, Cursor or Copilot, "run the release-changelog skill" in Claude Code — assembles the draft from everything merged since the previous tag); the section is dated with the day the tag will be pushed. The same PR moves VBH_TAG to the new version in .env.example, docs/deployment/docker.md and the install sections of the READMEs — the images exist minutes after the merge, once the tag is pushed. Merge on green CI.
  2. The tag — the whole release. On the merged main: git tag -a v<version> -m "..." && git push origin v<version>. The one push does everything: publishes the three Docker images with that version (docker.yml; a prerelease tag never gets latest) and runs .github/workflows/release.yml, which verifies the tag matches the version, publishes @vocab-bloom-hub/client to npm and vocab-bloom-hub to PyPI via OIDC trusted publishing — no tokens — and creates the GitHub Release with generated notes (categories in .github/release.yml; a hyphen in the tag marks it a prerelease). A prerelease publishes to npm under its channel dist-tag (alpha, beta, …), never latest — npm refuses a bare npm publish of a prerelease version. An SDK is published only when its package or the public OpenAPI spec changed since the previous tag (the version bump alone does not count): a release that touched neither leaves the registries at the version they have, so the SDK versions may lag the app's. A publish job that failed on the tag push is repeated from the Actions tab: release → Run workflow with the tag (the npm and PyPI checkboxes pick the jobs); a plain re-run would use the workflow file as it was at that tag. The first npm publish is manual (npm publish --tag <channel> from packages/npm-sdk; npm attaches a trusted publisher only to an existing package — configure it right after, workflow release.yml, environment npm, then re-run the failed npm job). PyPI's pending publisher covers the first publish.
  3. After the tag: export the dictionary (the manifest carries the next dataset version), upload the revision to HuggingFace and git-tag it there with that version.

注意

PyPI normalizes pre-release suffixes per PEP 440 (0.1.0-alpha.1 is served as 0.1.0a1) — cosmetic only, the sources keep the semver spelling. Re-run uv lock in packages/python-sdk after a bump so uv.lock follows.

小心

The tag push is the point of no return: the npm/PyPI publishes cannot be undone — a bad release ships a fixed next version instead. Everything before the push is free to redo.

Documentation

Everything the repository documents, also rendered on the website, vocab-bloom-hub.com:

  • docs/deployment/ — production build and start, probes, graceful stop, systemd / PM2; docker.md: the three images and docker compose with Postgres; reverse-proxy.md: TLS, Caddy / nginx configs, exposure profiles, keeping the admin API private
  • docs/operations.md — operating an instance: what holds state and what to back up, database backup vs dictionary export, upgrading and rolling back, dataset updates vs code updates, sizing
  • docs/upgrading.md — upgrading an instance step by step: the update notice of the admin UI, the published images, a build from a checkout, a start without Docker, the checks after, rolling back
  • docs/database.md — Postgres inside compose or separate, connecting, the migrations, backups and restore, size
  • docs/environment.md — every environment variable, driver selection, startup checks
  • docs/authentication.md — how the single-admin login, login proof and JWT cookie work
  • docs/migrations.md — TypeORM migrations workflow for Postgres, deployment and troubleshooting
  • docs/offline-import.md — moving a dictionary between instances without internet access (export → copy → import from file)
  • docs/datasets.md — catalog installation (server download or manual upload), OpenGloss conversion limits, own datasets, forks, word sources and contribution licenses
  • apps/server/src/converters/README.md — standalone source conversion and adding a catalog adapter
  • docs/observability.md — how the monitoring works, Prometheus + Grafana in one command or your own, every metric, the JSON logs and the request id, shipping the logs to a collector
  • docs/performance.md — latency of the hot reads on the full dictionary (Postgres vs SQLite), the indexes behind them, the benchmark and the query-plan guard
  • docs/api.md — the public /api/v1 contract (envelope, errors, rate limit, caching, OpenAPI export) and the public-only / admin-only switches
  • docs/migrate-dictionaryapi.md and docs/migrate-freedictionaryapi.md — migration from the two compatible dictionary APIs and reading every dataset in their response formats
  • docs/api-tools.md — Swagger UI, the OpenAPI document, the website's reference and playground, the admin Documentation pages: which to open when
  • docs/data.md — where the dictionary data comes from (LLM-generated, generated_by_model), known limitations, how to report errors; the terms are in DATA_LICENSE.md
  • packages/npm-sdk/README.md and packages/python-sdk/README.md — the SDKs
  • docs/README.ru.md, README.es.md, README.fr.md, README.pt.md, README.de.md, README.zh.md, README.ar.md — the README in the other interface languages; docs/api.ru.md, docs/environment.ru.md and docs/deployment/README.ru.md are the other translated pages
  • AGENTS.md — the map of the codebase for coding agents (commands, architecture, conventions), kept in sync with the tree
  • docs/agents.md — local setup for coding agents, shared configuration, MCP and skills

Roadmap

Planned directions, in no particular order (the issues show the current state):

  • Semantic search and a semantic network on top of the dictionary (next major version)
  • Word relations graph beyond synonyms and antonyms: hypernyms / hyponyms, collocations
  • More source languages besides English, and more translation languages beyond Russian, Spanish, French, German, Portuguese, Chinese and Arabic
  • Published linguistic datasets built from the dictionary

Questions

  • GitHub Discussions — questions, ideas, show & tell
  • Issues — bug reports and feature requests, through the templates

Project Structure

.
├── apps/
│   ├── frontend/   → Next.js admin UI (eight interface languages)
│   ├── site/       → Next.js project website: docs, API reference, playground, word pages
│   ├── server/     → NestJS API; also exports the shared types (types/) and constants (core/) the frontend and the site import
│   └── e2e/        → Playwright browser tests that boot the apps against an isolated SQLite database
├── packages/
│   ├── npm-sdk/    → @vocab-bloom-hub/client, the Node.js / TypeScript SDK of the public API
│   └── python-sdk/ → vocab-bloom-hub, the Python SDK (uv, httpx, pydantic)
├── docs/           → the documentation (deployment, database, operations, observability, performance, environment, API, authentication, migrations, offline import, data) and the README in the other seven languages
├── eslint/         → shared ESLint config pieces (base / next / nest)
├── .agents/        → shared agent configuration and skills (docs/agents.md)
├── .github/        → CI workflows, issue / PR templates, Dependabot, CODEOWNERS
├── .env            → the single environment file of every app (not committed; .env.example is the template)
└── package.json    → root workspace scripts

Thank you for contributing to Vocab Bloom Hub 🚀