Skip to main content

Architecture

Understand the registry-build lifecycle, build context, cache model, and extension boundaries.

Lifecycle

Loading diagram...

Build context

The runtime context is the shared contract between the config loader, the core phases, and every extension.

It carries:

  • resolved config
  • resolved collections
  • config path and config directory
  • output paths
  • path registry helpers
  • collected build artifacts
  • output registration records
  • a per-build ts-morph project
  • cache access
  • changed-only flags and resolved changed paths

That keeps the pipeline explicit. Phases and extensions do not need to rediscover global state on their own.


Core responsibilities

Collections model

Collections are the domain-neutral input surface. A collection can hold file-backed data, named source trees, and arbitrary metadata. Extensions can read those collections without caring whether the consumer is building UI artifacts, package repositories, or search indexes.

Legacy compatibility layer

Older UI-centric fields such as sources and registries are still supported. During config resolution they are translated into generic collection artifacts so extensions and future pipeline work can target one shared model instead of two unrelated APIs.

Config loader

The loader finds registry-build.config.*, resolves extends, normalizes config-relative paths immediately, materializes external file-backed data, applies defaults, derives compatibility collections, and validates the final config with Zod.

Extension-driven phases

The core runner has no built-in phases. All processing — index building, component generation, validation, colors/themes, component indexes, banners — is performed by extensions registered in the extensions array.

For UI registries, indexBuildExtension() materializes registry entries into index.json and componentsExtension() transforms each indexed item into its generated JSON payload. Both run as afterBuild extensions and are included automatically by uiRegistryPreset().

Extension boundary

The core runner only provides the build context, cache management, and extension execution loop. All domain-specific outputs belong in extensions, not in the runner itself.

Artifact model

The runner exposes two related concepts:

ConceptMeaning
artifactsIn-memory values shared between phases and extensions
outputsFiles or file groups registered as generated outputs

Typical examples:

  • artifact: collections
  • artifact: index
  • output: public/r/index.json
  • output: public/r/components/*.json
  • output: __ui_registry__/index.tsx
  • output: dist/arch/repos/core.db.json
  • output: public/r/themes.css

This split is what lets extensions stay data-first. They can read artifacts and register outputs without pretending they are part of the core phase graph.


Cache model

The cache stores:

  • file hashes
  • phase-specific manifest data

The cache is local, incremental state. It is not source of truth and should always be safe to delete.

What the cache is for

  • skip rehashing unchanged files
  • skip rematerializing unchanged registry items
  • skip rewriting identical generated outputs
  • support --changed-only local rebuilds

What the cache is not for

  • long-term artifact storage
  • cross-machine shared caching
  • replacing deterministic generation

Design rules