Skip to content

Drupal content-source registry (#343, reshaped by #1099)

One config-driven place that declares every nanawall.com content type NanaSelect ingests: the JSON:API endpoint its rows are attributed to and the store its rows land in. Adding a new content type is a registry entry (configuration), not code duplicated across the codebase.

The typed source of truth is src/lib/server/drupal/registry.ts.

Since #1099 the registry is reporting-only: the per-entry run wiring that once triggered JSON:API syncs was retired when the DB-dump ingest (#1094) proved coverage parity. Rows now arrive via npm run drupal:extractdrupal:load (and, for corpus content, the /admin/corpus tools). The registry's remaining job is provenance attribution: declaring which store each source feeds so the freshness view and admin status surface can report per-source counts and staleness.

Target-store routing

Each registry entry declares where its rows belong:

Source (key)Target storeTableRows arrive via
productsattributessystem_attribute_valuesdump load (#1097)
projectsprojectsdrupal_projects (+ links)dump load (#1097)
media-imagemediamedia_assets (+ tags)dump load (#1097)
resourcescorpuscorpus_items (tech-doc)corpus tools → corpus-ingest (#64)
blogcorpuscorpus_items (blog)corpus tools → corpus-ingest (#64)

The corpus-routed entries come from config: each is a DrupalTextSourceConfig in text-contract.ts or a DrupalViewSourceConfig in view-contract.ts. Adding a text/view type is an entry there — no code.

Provenance & freshness

Each store carries per-item provenance (a last-synced stamp, and a source URL where meaningful). getSourceFreshness(db) (provenance.ts) unifies them into one row per source — count, oldest / newest stamp, and a sample source URL — across all four stores at once. The per-store columns:

StoreLast-synced columnSource URL
attributessystem_attribute_values.drupal_synced_at(system pdp_path)
projectsdrupal_projects.source_synced_at
mediamedia_assets.source_synced_atremote_url
corpuscorpus_items.fetched_atsource_url

Rows loaded by the dump path carry source = 'db-dump' on the stores that discriminate origin and surface as their own freshness rows. The last bulk-load event — dump date, Drupal version, source file, per-store counts — is recorded to the audit trail by the loader (recordBulkLoadEvent) and read back via getLastBulkLoad(db); the Content sources panel on /admin/drupal-sync leads with it.

See also the cross-cutting data-provenance.md, and drupal-taxonomy-facet-mapping.md — how the taxonomy terms this ingested content carries are mapped onto the app's attributes/factors (the shared facet vocabulary).

History

The registry originally generalized three hand-coded sync trios (products #111, projects #378, media-image #400) plus config-generated corpus fetch arms (#343, #415), each entry wrapping its sync in a run that normalized bespoke counts into one UnifiedSyncResult for the admin runner. That run machinery — and the trios themselves — were deleted in #1099; the archived running-drupal-sync how-to records how the channel worked.