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
- One id per library — stable slug (
chartjs,dayjs) used in manifests, docs, and CI. - Pinned versions — registry entries record the semver of vendored/CDN artifacts.
- Predictable loading —
chromaJson.hooks.loadtells tooling where to inject scripts. - CDN resilience — ordered
cdn.primary+cdn.fallbackswithout hand-maintaining URLs in every app. - Separation from plugins — no
chroma.plugin.json; globals are optional and explicit.
Registry entry shape
Each object in libraries[] must include:
| Field | Required | Meaning |
|---|---|---|
id | yes | Lowercase slug; referenced from chroma.json legacyLibraries[].id |
name | yes | Display / npm product name |
version | yes | Pinned semver for files and CDN URLs |
categories | yes | Tags from the schema enum (e.g. charts, date-time, utility) |
files | yes | App-relative vendor paths when source is vendor |
cdn | yes | primary URL plus ordered fallbacks |
chromaJson | yes | global name and hooks for loaders / HTML injection |
license | no | SPDX id for compliance |
notes | no | Maintainer guidance (migration, deprecations) |
files[]
| Subfield | Meaning |
|---|---|
path | e.g. vendor/chart.js/chart.umd.min.js |
role | script, style, or worker |
format | umd, iife, esm, or css |
integrity | Optional 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
| Subfield | Meaning |
|---|---|
global | Property on window after the script runs (Chart, dayjs, _) |
hooks.load | Injection point (see load hooks) |
hooks.order | Sort key when multiple libraries are declared (lower = earlier) |
hooks.staticBuild | append-scripts merges files[].path into static build.scripts |
dynamic.esbuildExternal | Reserved: treat global as external during chroma build (dynamic mode) |
Load hooks (chromaJson.hooks.load)
| Hook | Use when |
|---|---|
indexHtml:head | Classic <script defer> in <head> before the module graph |
indexHtml:body-start | Script at top of <body> |
indexHtml:body-end | Script after main markup, before type="module" entry |
static:scripts | Only 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"
}
]| Field | Required | Meaning |
|---|---|---|
id | yes | Must match a registry libraries[].id |
source | no | vendor (copy paths from registry), cdn (network script tags), or auto (vendor if files exist, else CDN). Default: auto |
version | no | Override registry pin; must match a registry entry for that id when set |
url | no | Single URL override (skips CDN list; still uses chromaJson.global and hooks) |
disabled | no | When 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
- Pick
idfrom the registry (or add a new entry inChroma/cli/legacy-libs/registry.jsonand bumpregistryVersionif semantics change). - Create paths listed in
files[]under your app root (commonlyvendor/<name>/). - Declare
legacyLibrariesinchroma.json. - Run
chroma validate— manifest shape is schema-checked; file presence checks may be added in the CLI later. - For air-gapped deploys, use
source: "vendor"only and commit vendored bytes.
Adding a new library to the registry
- Edit
Chroma/cli/legacy-libs/registry.json(validate with AJV againstlegacy-lib-registry.schema.json). - Choose an unused
idand categories. - Pin
versionand mirror that version incdnURLs. - Set
chromaJson.globalto the actual global (inspect the UMD build in a browser console if unsure). - Document breaking changes in
notesand, when needed, addaudit-<yyyy-mm>.mdxunder this folder. - Regenerate the docs catalog:
npm run legacy-libs:catalog(from the workspace root). CI may runnode scripts/generate-legacy-libs-catalog.mjs --checkto ensurecatalog.mdxmatches 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=draft2020Relationship to other Chroma artifacts
| Artifact | Legacy libs | Plugins |
|---|---|---|
| Manifest | chroma.json → legacyLibraries[] | chroma.json → plugins[] |
| Package manifest | — | plugins/<id>/chroma.plugin.json |
| Loader | index.html / future CLI injector | plugins/loader.js |
| App code | globalThis.* or thin wrappers | import from plugin entry |
Current registry entries (v1)
| id | name | version | categories |
|---|---|---|---|
chartjs | Chart.js | 4.4.1 | charts, visualization |
dayjs | dayjs | 1.11.13 | date-time, utility |
lodash | lodash | 4.17.21 | utility |
echarts | Apache ECharts | 6.1.0 | charts, visualization |
anime | anime.js | 3.2.2 | utility, visualization |
lucide | Lucide | 0.469.0 | ui |
See the generated catalog and registry.json for CDN URLs, vendor paths, and hook metadata.