Skip to content

alt-docs-registry

Single source of truth for the registry of ALT documentation products, sections and versions. One YAML schema products.yaml, a Vite plugin with the virtual module virtual:alt-docs-registry, the runtime composable useRegistry() and the CLI alt-docs.

The package replaces the previous PRODUCT_NAMES, ALIASES, ALL_DISTROS hardcodes scattered across the theme and 22 distribution config branches. The same registry is read at build time by the theme, by the publican-docbook-to-vitepress-markdown converter and by the runtime navigation components.

Installation

bash
pnpm add @ampernic/alt-docs-registry

The @ampernic scope from Forgejo is wired via .npmrc at the project root:

@ampernic:registry=https://altlinux.space/api/packages/ampernic/npm/

The alt-docs CLI is installed through the package bin, run pnpm dlx alt-docs ... or, locally, pnpm exec alt-docs ....

Source: products.yaml

The format is described in schema.ts. A minimal example:

yaml
schema: 1
locales: [ru, en]
site:
  hostname: https://docs.altlinux.space
  organization:
    name: Базальт СПО
    url: https://www.basealt.ru
sections:
  - id: server
    name: { ru: "Серверные", en: "Server" }
    products: [alt-server, alt-domain]
  - id: workstation
    name: { ru: "Рабочие станции", en: "Workstation" }
    products: [alt-workstation, alt-kworkstation]
products:
  alt-workstation:
    name: { ru: "Альт Рабочая станция" }
    tagline: { ru: "Универсальная система для офиса" }
    product_link: https://www.basealt.ru/alt-workstation
    versions: ["11.2", "11.1", "11.0", "10.4"]
    branding:
      logo:
        col: branding/alt-workstation/logo-col.svg
        dark: branding/alt-workstation/logo-dark.svg
      accent: "#fa814c"
    meta:
      isPublic: true

The head of versions is treated as the current one. The family field merges a legacy branch (alt-domain-p10) with the main one under a single card. site.hostname propagates into ResolvedRegistry.hostname and then into vitepress-plugin-meta for canonical URLs; site.organization becomes the schema.org Organization node on the landing.

Wiring in the VitePress config

ts
// .vitepress/config.mts
import { defineConfig } from 'vitepress'
import { AltDocsRegistryPlugin, createRegistry } from '@ampernic/alt-docs-registry'
import { createSharedConfig } from '@ampernic/vitepress-theme-alt-docs/config'

const { plugin: registryPlugin, resolved } = await createRegistry({
  source: 'products.yaml',
  fallback: 'https://raw.altlinux.space/alt-docs/docs-vitepress/index/products.yaml',
  distro: 'alt-workstation',
  locale: 'ru',
  emitProductsJson: true,
  emitSprite: true,
})

const shared = createSharedConfig({
  distroName: 'alt-workstation',
  allDistros: resolved.allDistros,
  sections: resolved.sections,
  productNames: resolved.productNames,
  productLinks: resolved.productLinks,
})

export default defineConfig({
  ...shared,
  vite: {
    plugins: [registryPlugin, ...(shared.vite?.plugins ?? [])],
  },
})

createRegistry warms the snapshot once and returns both the plugin (for vite.plugins) and a ready-resolved ResolvedRegistry object. AltDocsRegistryPlugin can be used on its own when the snapshot is only needed at runtime.

Usage in components

ts
import { useRegistry } from '@ampernic/alt-docs-registry/composables'

const { registry, merged, refresh } = useRegistry()

// registry.value: build-time snapshot from virtual:alt-docs-registry
// merged.value:   runtime cache | build; runtime wins
// refresh():      pulls a fresh /products.json from the site root

The composable returns shallow refs so refreshing the registry does not re-render half the theme. One module-scoped cache per page: several components calling useRegistry() see the same fetched snapshot.

CLI alt-docs

File operations only, no auto-commit; the git wrapper stays with gear-* or CI:

bash
alt-docs product add alt-orchestra \
  --section orchestra \
  --logoCol ./logo.svg \
  --logoDark ./logo-mono.svg

alt-docs version add alt-workstation 11.3 --from 11.2
alt-docs product publish --repo alt-docs/docs-vitepress --token "$ALT_DOCS_TOKEN"
alt-docs check --strict
  • product add adds an entry to products.yaml, copies the logos into docs/public/branding/<slug>/, attaches the product to a section.
  • product remove drops the entry from the registry (leaves the branch alone).
  • product publish triggers the full-rebuild.yml workflow via the Forgejo API. Requires --token or ALT_DOCS_TOKEN.
  • version add writes the new version into products.yaml (first in the list = current) and scaffolds docs/ru/<X.Y>/ from a template or from the previous version.
  • check validates the schema and the on-disk presence of branding files. --strict promotes warnings to exit-code 1.

Plugin options

OptionTypeDefaultDescription
sourcestring--Required. Path to products.yaml or a https://... URL
fallbackstring--Fallback source, tried when source is unreadable
distrostring | nullnullSlug of the current distribution. null = index build showing every product
localeLocale'ru'Locale into which LocalizedString fields are resolved
emitProductsJsonbooleantrueWrite <outDir>/products.json, consumed by useRegistry().refresh() at runtime
productsJsonNamestring'products.json'Name of the emitted JSON
brandingRootsstring[]['docs/public', 'public']Roots where branding.logo.col/dark is looked up. Order = priority
emitSpritebooleanfalseAssemble an SVG sprite of every logo as <outDir>/products-sprite.svg
spriteFileNamestring'products-sprite.svg'Name of the emitted sprite

What ends up in ResolvedRegistry

The virtual module virtual:alt-docs-registry and products.json carry a locale-resolved snapshot:

FieldDescription
hostnameCopy of site.hostname from the YAML
organizationCopy of site.organization: the publisher, emitted by vitepress-plugin-meta as a schema.org Organization node on the landing
allDistrosSorted list of every product slug
sections{ id, name, products }, names already resolved into the current locale
productNamesslug -> localized name
productBrandingKeysslug -> sprite key (branding.sharedWith is honored)
productTaglinesslug -> localized tagline
productLinksslug -> product_link (external product page)
productFamilyPrimaryslug -> primary slug of the family (self when not in a family)
productFamilyMembersprimary slug -> [primary, ...secondaries]
productVersionsslug -> versions[] (lets the primary landing show every branch without a cross-site fetch)
currentDistroCopy of the distro option
localeCopy of the locale option

Notes

  • The parser requires schema: 1 in the YAML. Extra fields do not break it, only a major version mismatch does.
  • parseRegistry may return an issues[] list with severity: 'error'; in that case the plugin fails during configResolved instead of silently building a broken snapshot.
  • A distribution with an empty versions[] is not treated as an error, it renders as a placeholder in the switcher without versions (useful as a spacer for a future product).
  • Enable emitSprite only when the theme actually uses products-sprite.svg, otherwise you pay kilobytes in the bundle for nothing.
  • parseRegistry handles unknown fields softly: warns, but validates the rest.
  • The site field is restored manually when merging from YAML, older parser versions used to drop it. Currently fixed upstream; look at test/site-field.test.ts before patching the loader.