general-eval-card / lib /model-url-redirects-build.ts
j-chim's picture
Update about (#8)
599471d
Raw
History Blame
5 kB
// Redirect map builder (pure, testable).
//
// The producer's warehouse makes the GROUP the addressable model (its
// `model_id` / `model_route_id`). Every variant, dated snapshot, or
// older-cased spelling the producer folds into a group lives only in that group
// row's `raw_model_ids` list and has no page of its own. This builds the
// old-route -> canonical-group-route 301 map straight from `models_view`, so a
// link to any folded spelling lands on the real group page. Every redirect
// target is an addressable `model_route_id` by construction.
//
// Kept separate from the generated data file (`lib/model-url-redirects.ts`) so
// the generator script AND unit tests can exercise the build logic without
// importing the (large) generated map.
//
// Route id form: RFC 3986 percent-encoded whole id (`/` -> `%2F`), matching the
// producer's `route_id` column and middleware's `encodeURIComponent` lookup key.
export interface ModelsViewRow {
/** canonical encoded route for this group — the redirect TARGET. */
model_route_id: string
model_id?: string | null
route_id?: string | null
model_key?: string | null
model_group_id?: string | null
/** raw spellings folded into this group (HF-cased, may contain `/`). */
raw_model_ids?: string[] | null
}
/** Encode a plain model id into the producer `route_id` form. */
export function routeId(id: string): string {
return encodeURIComponent(id.trim())
}
export interface BuildResult {
/** old encoded route id -> canonical (group) encoded route id. */
redirects: Map<string, string>
/**
* Raw spellings that fan out to MORE THAN ONE group (would be ambiguous to
* redirect). Excluded from the map and surfaced for review rather than
* picking one arbitrarily. Expected to be empty for a well-formed warehouse.
*/
ambiguous: Map<string, Set<string>>
}
/**
* Build the redirect map from models_view rows. Pure and deterministic.
*
* A folded spelling is redirected to its owning group's `model_route_id` unless
* (a) it already equals that route, or (b) it is itself an addressable page
* (some row's own id) — we never hijack a working URL. A spelling that maps to
* multiple distinct groups is recorded as ambiguous and excluded.
*/
export function buildRedirectsFromModelsView(rows: ModelsViewRow[]): BuildResult {
// Pass 1: every route form that resolves to a real page (mirrors the
// getModelSummaryById lookup columns), so we never redirect a live URL.
const addressable = new Set<string>()
for (const r of rows) {
if (r.model_route_id) addressable.add(r.model_route_id)
if (r.route_id) addressable.add(r.route_id)
if (r.model_id) addressable.add(routeId(r.model_id))
if (r.model_group_id) addressable.add(routeId(r.model_group_id))
if (r.model_key) addressable.add(routeId(r.model_key))
}
// Pass 2: map each folded raw spelling to its owning group route.
const candidates = new Map<string, Set<string>>()
for (const r of rows) {
const target = r.model_route_id
if (!target) continue
for (const raw of r.raw_model_ids ?? []) {
if (!raw) continue
const old = routeId(raw)
if (old === target) continue // already the canonical route
if (addressable.has(old)) continue // has its own page; don't hijack it
const set = candidates.get(old) ?? new Set<string>()
set.add(target)
candidates.set(old, set)
}
}
const redirects = new Map<string, string>()
const ambiguous = new Map<string, Set<string>>()
for (const [old, targets] of candidates) {
if (targets.size === 1) redirects.set(old, [...targets][0])
else ambiguous.set(old, targets)
}
return { redirects, ambiguous }
}
/** Serialise a redirect map to the generated TS module source. */
export function serializeRedirectModule(
redirects: Map<string, string>,
meta: { source: string },
): string {
const sorted = [...redirects.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
const entries = sorted.map(([o, n]) => ` [${JSON.stringify(o)}, ${JSON.stringify(n)}],`).join("\n")
return `// GENERATED by scripts/generate-model-redirects.ts — do not edit by hand.
//
// Derived from the warehouse models_view (${meta.source}). Each entry maps a
// folded model spelling (a variant / dated snapshot / older-cased id the
// producer collapses into a group) to that group's canonical route, so a link
// to any folded spelling 301s to the real group page. Every target is an
// addressable model_route_id by construction.
//
// Entries: ${redirects.size}.
//
// Old encoded route id -> canonical encoded route id (RFC 3986, \`/\` -> %2F).
// middleware.ts looks up the incoming route here and issues a 301 (query params
// preserved).
/** old encoded route id -> canonical (group) encoded route id. */
export const MODEL_URL_REDIRECTS = new Map<string, string>([
${entries}
])
export function resolveModelRedirect(routeId: string): string | undefined {
return MODEL_URL_REDIRECTS.get(routeId)
}
`
}