Targ Apps Docs
ChromaLegacy libraries

Legacy library standard

Registry schema, chroma.json legacyLibraries field, CDN fallbacks, and load hooks for third-party global scripts.

This standard describes how Chroma apps declare third-party legacy JavaScript (UMD/IIFE globals). It complements the Plugins, which covers ESM packages under plugins/<id>/.

Source of truth (monorepo): Chroma/cli/legacy-libs/registry.json (bundled with the Chroma CLI; copied to $CHROMA_HOME/legacy-libs/ on install).

First-party plugins (maintainers): plugins-registry.json sits beside the globals registry under Chroma/cli/legacy-libs/. It lists canonical workspace paths, build hints, and pinned versions for browser plugins (chroma-fs, chroma-style, …) — not UMD entries in libraries[]. The CLI release bundle copies it when present; npm run legacy-lib:list -- --plugins and refresh-version read it. Apps install plugins with chroma install and chroma.json → plugins[]; see the Plugins.

JSON Schema: legacy-lib-registry.schema.json

chroma.json schema (canonical): targ-apps-docs/public/schemas/chroma/chroma.schema.json in the monorepo (published at /schemas/chroma/chroma.schema.json); Chroma and Chroma-Linter do not ship a duplicate copy.

Goals

  1. One id per library — stable slug (chartjs, dayjs) used in manifests, docs, and CI.
  2. Pinned versions — registry entries record the semver of vendored/CDN artifacts.
  3. Predictable loading — chromaJson.hooks.load tells tooling where to inject scripts.
  4. CDN resilience — ordered cdn.primary + cdn.fallbacks without hand-maintaining URLs in every app.
  5. Separation from plugins — no chroma.plugin.json; globals are optional and explicit.

Registry entry shape

Each object in libraries[] must include:

FieldRequiredMeaning
idyesLowercase slug; referenced from chroma.json legacyLibraries[].id
nameyesDisplay / npm product name
versionyesPinned semver for files and CDN URLs
categoriesyesTags from the schema enum (e.g. charts, date-time, utility)
filesyesApp-relative vendor paths when source is vendor
cdnyesprimary URL plus ordered fallbacks
chromaJsonyesglobal name and hooks for loaders / HTML injection
licensenoSPDX id for compliance
notesnoMaintainer guidance (migration, deprecations)

files[]

SubfieldMeaning
pathe.g. vendor/chart.js/chart.umd.min.js
rolescript, style, or worker
formatumd, iife, esm, or css
integrityOptional SRI hash for <script integrity="…">

cdn

"cdn": {
  "primary": "https://cdn.jsdelivr.net/npm/dayjs@1.11.13/dayjs.min.js",
  "fallbacks": [
    "https://unpkg.com/dayjs@1.11.13/dayjs.min.js"
  ]
}

Tooling should try primary first, then each fallback in order. Apps may override with legacyLibraries[].url for a self-hosted mirror (see below).

chromaJson

SubfieldMeaning
globalProperty on window after the script runs (Chart, dayjs, _)
hooks.loadInjection point (see load hooks)
hooks.orderSort key when multiple libraries are declared (lower = earlier)
hooks.staticBuildappend-scripts merges files[].path into static build.scripts
dynamic.esbuildExternalReserved: treat global as external during chroma build (dynamic mode)

Load hooks (chromaJson.hooks.load)

HookUse when
indexHtml:headClassic <script defer> in <head> before the module graph
indexHtml:body-startScript at top of <body>
indexHtml:body-endScript after main markup, before type="module" entry
static:scriptsOnly static mode — paths appended to build.scripts

Dynamic apps typically use indexHtml:head or indexHtml:body-end so globals exist before bundle.js executes. Static apps should set staticBuild: "append-scripts" so chroma build minifies vendor files with the rest of the script list.

chroma.json — legacyLibraries

Optional top-level array on the app manifest (see chroma.schema.json):

"legacyLibraries": [
  {
    "id": "chartjs",
    "source": "vendor",
    "version": "4.4.1"
  },
  {
    "id": "dayjs",
    "source": "auto"
  }
]
FieldRequiredMeaning
idyesMust match a registry libraries[].id
sourcenovendor (copy paths from registry), cdn (network script tags), or auto (vendor if files exist, else CDN). Default: auto
versionnoOverride registry pin; must match a registry entry for that id when set
urlnoSingle URL override (skips CDN list; still uses chromaJson.global and hooks)
disablednoWhen true, entry is ignored (feature flags / gradual removal)

Example with mixed sources:

{
  "$schema": "https://docs.targapps.xyz/schemas/chroma/chroma.schema.json",
  "name": "reports-dashboard",
  "mode": "dynamic",
  "entry": "index.html",
  "legacyLibraries": [
    { "id": "lodash", "source": "vendor" },
    { "id": "chartjs", "source": "cdn" }
  ],
  "build": {
    "jsEntry": "src/main.chr",
    "outfile": "bundle.js",
    "format": "esm",
    "minify": true
  }
}

From .chr code, read globals only after scripts load (e.g. on first render or after DOMContentLoaded):

const chart = globalThis.Chart;

Prefer wrapping globals in a small module under src/legacy/ when migrating toward ESM.

Vendoring workflow

  1. Pick id from the registry (or add a new entry in Chroma/cli/legacy-libs/registry.json and bump registryVersion if semantics change).
  2. Create paths listed in files[] under your app root (commonly vendor/<name>/).
  3. Declare legacyLibraries in chroma.json.
  4. Run chroma validate — manifest shape is schema-checked; file presence checks may be added in the CLI later.
  5. For air-gapped deploys, use source: "vendor" only and commit vendored bytes.

Adding a new library to the registry

  1. Edit Chroma/cli/legacy-libs/registry.json (validate with AJV against legacy-lib-registry.schema.json).
  2. Choose an unused id and categories.
  3. Pin version and mirror that version in cdn URLs.
  4. Set chromaJson.global to the actual global (inspect the UMD build in a browser console if unsure).
  5. Document breaking changes in notes and, when needed, add audit-<yyyy-mm>.mdx under this folder.
  6. Regenerate the docs catalog: npm run legacy-libs:catalog (from the workspace root). CI may run node scripts/generate-legacy-libs-catalog.mjs --check to ensure catalog.mdx matches the registry.

Validation (local)

cd targ-apps-docs/public/schemas
npx --yes -p ajv-cli@5 -p ajv@8 ajv validate \
  -s chroma/legacy-lib-registry.schema.json \
  -d ../../../../Chroma/cli/legacy-libs/registry.json \
  --spec=draft2020

Relationship to other Chroma artifacts

ArtifactLegacy libsPlugins
Manifestchroma.json → legacyLibraries[]chroma.json → plugins[]
Package manifest—plugins/<id>/chroma.plugin.json
Loaderindex.html / future CLI injectorplugins/loader.js
App codeglobalThis.* or thin wrappersimport from plugin entry

Current registry entries (v1)

idnameversioncategories
chartjsChart.js4.4.1charts, visualization
dayjsdayjs1.11.13date-time, utility
lodashlodash4.17.21utility
echartsApache ECharts6.1.0charts, visualization
animeanime.js3.2.2utility, visualization
lucideLucide0.469.0ui

See the generated catalog and registry.json for CDN URLs, vendor paths, and hook metadata.

On this page