catalox-ui
v1.2.0
Published
Single-app Catalox catalog workspace. Standalone: set CATALOX_UI_* / APP_ID in .env (see .env.example). Embedded host: import CatalogManagerApp from "catalox-ui/app" and pass config={{ appId, cataloxApiBaseUrl, ... }}. Requires peers react, react-dom, @x1
Readme
catalox-ui
catalox-ui is the Catalox-backed catalog workspace shipped from this repository (@x12i/catalox via the embedded BFF under server/). It provides a normalized item surface, a shared shell (CataloxUiShell), and shared list/detail/create flows driven by catalog descriptors from the backend.
The catalox-ui-core package-in-tree still holds reusable contracts, registry, and adapter helpers for hosts that compose a custom registry; the default app entry is CatalogManagerApp (src/catalox-ui-app/CatalogManagerApp.tsx).
What it is
catalox-ui is a frontend package for applications that need to:
- browse, search, and filter multiple kinds of catalog data in one place
- view normalized item details with shared JSON, validation, and reference surfaces
- create new items through a shared generic flow
- display type-specific detail UI when a module provides it
- remain fully functional in stub/local mode before real sources are available
It is not specific to any domain, editor, or backend system.
What it is not
- Not a graph navigator
- Not specific to any workflow engine
- Not a backend service or schema engine
- Not hardcoded to any catalog family
Core concepts
| Term | Definition |
|---|---|
| Catalog item | Any managed item normalized into CatalogItemEnvelope |
| Catalog type | A registered family key, e.g. policy-template, taxonomy-node |
| Envelope | The shared normalized shape all items must conform to |
| Adapter | Data behavior for one type — loads, creates, validates, returns references |
| Module | UI behavior for one type — label, capabilities, and optional detail/create rendering |
| Registry | A map binding each catalog type key to its adapter + module |
Architecture
The framework has four layers:
┌────────────────────────────────────────────────────────┐
│ Generic shell CataloxUiShell │ Layer 2 (UI)
│ Shared components Sidebar / Inventory / Detail / … │
├────────────────────────────────────────────────────────┤
│ Registry + adapter helpers │ Layer 2 (logic)
│ loadAllCatalogItems / createCatalogItemFromRegistry … │
├────────────────────┬───────────────────────────────────┤
│ Catalog type A │ Catalog type B (add yours here) │ Layer 3
│ adapter + module │ adapter + module │
└────────────────────┴───────────────────────────────────┘
│ Shared contracts types.ts / contracts.ts │ Layer 1
└────────────────────────────────────────────────────────┘See ARCHITECTURE.md for the full layer breakdown, import rules, and data ownership reference.
Core contracts reference
Core types and contracts live in src/catalox-ui-core/.
Primitive types (types.ts)
| Type | Values |
|---|---|
| CatalogItemStatus | draft · defined · in-use · gap · deprecated · invalid |
| CatalogItemSourceKind | stub · derived · live |
| CatalogManagerView | inventory · gaps · recent · json-stubs |
| CatalogValidationSeverity | error · warning · info |
| CatalogReferenceRelationType | uses · declares · requests · maps-to · derived-from |
Entity contracts (contracts.ts)
| Type | Purpose |
|---|---|
| CatalogItemEnvelope | Normalized item shape all adapters must produce |
| CatalogManagerState | Shell state shape |
| CatalogManagerCoreProps | Props for <CatalogManagerCore> |
| CatalogModule | UI behavior contract per type |
| CatalogAdapter | Data behavior contract per type |
| CatalogModuleCapabilities | Feature flags per module |
| RegisteredCatalogModule | { module, adapter } pair |
| CatalogModuleRegistry | Record<string, RegisteredCatalogModule> |
| CatalogTypeOption | Type option used in create flow |
| CatalogReference | Cross-item reference shape |
| CatalogValidationIssue | Single validation issue |
| CatalogValidationResult | { ok, issues } result |
| CreateCatalogItemInput | Input for item creation |
| UpdateCatalogItemInput | Input for item updates |
Registry API reference
Registry helpers are exported from src/catalox-ui-core/registry.ts.
createCatalogModuleRegistry(entries)
Creates a registry from an array of { module, adapter } pairs.
- Throws if
module.type !== adapter.typefor any entry - Throws on duplicate type keys
- Returns
CatalogModuleRegistry
getCatalogModule(registry, type)
Returns the CatalogModule for a type, or undefined if not registered.
getCatalogAdapter(registry, type)
Returns the CatalogAdapter for a type, or undefined if not registered.
getCatalogTypeOptions(registry)
Returns CatalogTypeOption[] for all registered types — used by the create flow.
getAllRegisteredCatalogModules(registry)
Returns all RegisteredCatalogModule entries as an array.
Adapter helpers reference
Adapter helpers are exported from src/catalox-ui-core/adapters.ts.
loadAllCatalogItems(registry)
Loads items from all registered adapters. Returns { items, errors }. One failing adapter does not prevent others from loading.
createCatalogItemFromRegistry(registry, input)
Creates an item by calling the adapter's createItem if available. Falls back to a default stub envelope if not.
getCatalogItemReferences(registry, item)
Returns references for an item via the adapter's getReferences. Returns [] if not implemented.
validateCatalogItem(registry, item)
Validates an item via the adapter's validateItem. Falls back to getFallbackCatalogValidationIssues if not implemented.
Bootstrapping
With your own types
import { createCatalogModuleRegistry } from './src/catalox-ui-core/registry';
// Illustrative: your shell that consumes `registry` (this repo ships CataloxUiShell + BFF by default).
import { YourCatalogShell } from './your-app/YourCatalogShell';
const registry = createCatalogModuleRegistry([
{ module: PolicyTemplateCatalogModule, adapter: policyTemplateCatalogAdapter },
{ module: TaxonomyNodeCatalogModule, adapter: taxonomyNodeCatalogAdapter },
]);<YourCatalogShell registry={registry} />Default Catalox workspace (this repo)
import CatalogManagerApp from 'catalox-ui/app';
// or from source:
import CatalogManagerApp from './src/catalox-ui-app/CatalogManagerApp';
<CatalogManagerApp />Configure appId / API base URL via host props, public/catalox-ui.config.json, or CATALOX_UI_* / APP_ID in .env (see .env.example). For tenancy and identity questions directed at Catalox, see docs/catalox-inquiry-tenancy-and-identity.md.
Note: CATALOX_APP_ID / CATALOX_STORE_ID are for the @x12i/catalox CLI and tooling; the browser resolves appId from CATALOX_UI_APP_ID or APP_ID (and store from CATALOX_UI_STORE_ID). Set the CATALOX_UI_* / APP_ID pair if you expect the UI to default without Settings.
If GET /api/catalox/bootstrap returns 409 with catalox_metadata_incomplete, Firestore catalog rows are missing required metadata (often catalogs/{id}.metadata.status). Use operator auth and POST /api/catalox/operator/repair/metadata (dry-run by default; see server/catalox/metadataRepair.ts and docs/troubleshooting-catalox-empty-inventory.md).
The embedded BFF wires createCatalox() in server/catalox/init.ts. Enable optional Catalox stores with CATALOX_UI_RENDERER_SNIPPETS=1, CATALOX_UI_PRESENTATION_PROFILES=1, and CATALOX_UI_DESIGN_OBJECTS=1 (server env) so snippet-backed renderers, presentation profiles, and scoped design objects resolve when descriptors reference them.
Stub mode vs live mode
The framework supports both modes through adapters.
Stub mode — adapters return hardcoded CatalogItemEnvelope arrays from local data. No network, no backend. Ideal for early development.
Live mode — adapters fetch real sources, normalize the results into CatalogItemEnvelope, and return them. The shell, components, and registry are unchanged.
The adapter is the only thing that changes. The core shell, shared UI, and registry API behave identically in both modes.
Adding a new catalog type
See ADDING_A_CATALOG_TYPE.md for a step-by-step guide.
The short version: implement an adapter, implement a module, register both, pass the registry to the shell.
Display layer (Catalox v2.0)
Every list card, filter chip, and detail row is driven by the catalog descriptor — presentationSpec, filterSpec, and customRenderer. The shell enriches each descriptor once at bootstrap and routes list / filters / detail through the display components in src/catalox-ui-shell/components/display/.
Precedence for what drives the UI: optional host override > backend descriptor > default derived from queryableFields + identity.
Catalog appearance is owned by the Catalox descriptor (presentationSpec, filterSpec, identity.titleField, etc.). This shell calls enrichDescriptor(descriptor, undefined) with no built-in override map. Host integrations may merge a partial CatalogDescriptorOverride at bootstrap time if needed.
For fully custom list or item UIs, register React components in src/catalox-ui-app/custom-renderer-registry.ts and reference them from the descriptor’s customRenderer.
See ARCHITECTURE.md for the full pipeline diagram, import rules, and how to add a custom renderer component.
Google service account (GOOGLE_SERVICE_ACCOUNT_BASE64)
For local development, put your Firebase service account JSON on disk only inside .env, not in source control:
- Set
GOOGLE_SERVICE_ACCOUNT_BASE64to the base64 encoding of the raw JSON (one line, no line breaks in the env value). You can generate it from a downloaded key file with your platform’s base64 tooling.
You do not add code in this repo to read it. The Catalox / @x12i stack (via @x12i/helpers Firebase initialization) already resolves credentials from the environment: it accepts GOOGLE_SERVICE_ACCOUNT_BASE64 and the FIREBASE_SERVICE_ACCOUNT_BASE64 alias, alongside path- or JSON-based options. If this variable is set when the BFF or tooling loads .env, authentication is picked up automatically.
Operational rule: .env is for your machine and deploy secrets only. It must stay gitignored and must never be included in an npm package tarball. Prefer GOOGLE_SERVICE_ACCOUNT_BASE64 over committing a JSON file path so CI and laptops can inject the same variable without shipping key files.
Extension roadmap
The following extension points are planned for future implementation:
- Type-specific create forms — via
module.renderCreate() - Type-specific edit forms — via
module.renderEdit(item) - Advanced cross-reference view — visual relationship browser
- JSON import/export — bulk stub creation and export
- Permissions per catalog type — capability-gated actions
- History/versioning panel — optional per-module version tracking
- Live fetch testing — adapter health check panel
Framework positioning
catalox-ui (and the catalox-ui-core contracts it ships) are designed to be used as a base or embed for products that manage multiple catalog types through one consistent UI, with the default path wired to Catalox and the BFF.
Fork it, register your types, and extend per-type behavior through adapters and modules — without touching the core shell.
