Appearance
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:extract → drupal: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 store | Table | Rows arrive via |
|---|---|---|---|
products | attributes | system_attribute_values | dump load (#1097) |
projects | projects | drupal_projects (+ links) | dump load (#1097) |
media-image | media | media_assets (+ tags) | dump load (#1097) |
resources | corpus | corpus_items (tech-doc) | corpus tools → corpus-ingest (#64) |
blog | corpus | corpus_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:
| Store | Last-synced column | Source URL |
|---|---|---|
attributes | system_attribute_values.drupal_synced_at | (system pdp_path) |
projects | drupal_projects.source_synced_at | — |
media | media_assets.source_synced_at | remote_url |
corpus | corpus_items.fetched_at | source_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.