Microsoft rebranet Azure AI Foundry -> Microsoft Foundry; doc-URLer flyttet /azure/ai-foundry/ -> /azure/foundry/. Ny lib/registry-migrate.mjs (rene funksjoner remapKey + migrateRegistry, 10 enhetstester) + one-shot migrate-ai-foundry.mjs (backup via lib/backup.mjs + atomisk saveRegistry). Remap: 164 ai-foundry-URLer -> foundry (2 probede DEAD-sider far eksplisitte mal: evaluation-evaluators -> built-in-evaluators; agent-service -> agents/overview). Remappede entries resettes til status=unpolled/lastmod=null sa neste poll re-evaluerer. 1 kollisjon merget (ref_files unionert). Schema-migrering: authority_source+course lagt til alle 1343 entries. Idempotent. Registry er gitignored (regenererbar) - kun kode committes. kb-update 66 tester gronne, validate 239. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01REiKFhP4w6xGXXqWKpPCJJ
80 lines
3.1 KiB
JavaScript
80 lines
3.1 KiB
JavaScript
// registry-migrate.mjs — One-shot ai-foundry -> foundry URL remap + reserved
|
|
// schema migration. Pure functions (no I/O) so they are unit-testable; the
|
|
// migrate-ai-foundry.mjs script wires backup + atomic write around them.
|
|
//
|
|
// Microsoft rebranded "Azure AI Foundry" -> "Microsoft Foundry"; doc URLs moved
|
|
// /azure/ai-foundry/ -> /azure/foundry/. Two pages probed in Sesjon 1 are DEAD
|
|
// on a naive prefix swap and need explicit targets.
|
|
|
|
const AI_FOUNDRY_PREFIX = 'https://learn.microsoft.com/azure/ai-foundry/';
|
|
const FOUNDRY_PREFIX = 'https://learn.microsoft.com/azure/foundry/';
|
|
|
|
// Probed DEAD pages (Sesjon 1): naive prefix swap 404s, these are the live targets.
|
|
const DEAD_REMAPS = {
|
|
'https://learn.microsoft.com/azure/ai-foundry/concepts/evaluation-evaluators':
|
|
'https://learn.microsoft.com/azure/foundry/concepts/built-in-evaluators',
|
|
'https://learn.microsoft.com/azure/ai-foundry/agent-service':
|
|
'https://learn.microsoft.com/azure/foundry/agents/overview',
|
|
};
|
|
|
|
/**
|
|
* Remap a single registry URL key. DEAD overrides win; otherwise prefix-swap
|
|
* ai-foundry -> foundry; non-ai-foundry URLs are returned unchanged.
|
|
* @param {string} url
|
|
* @returns {string}
|
|
*/
|
|
export function remapKey(url) {
|
|
if (DEAD_REMAPS[url]) return DEAD_REMAPS[url];
|
|
if (url.startsWith(AI_FOUNDRY_PREFIX)) {
|
|
return FOUNDRY_PREFIX + url.slice(AI_FOUNDRY_PREFIX.length);
|
|
}
|
|
return url;
|
|
}
|
|
|
|
/**
|
|
* Migrate a registry: remap ai-foundry keys (resetting their tracking state so
|
|
* the next poll re-evaluates them), merge any target collisions, and ensure the
|
|
* reserved schema fields (authority_source, course) exist on every entry.
|
|
* Pure — does not mutate the input.
|
|
* @param {object} registry
|
|
* @returns {{registry: object, stats: {remapped: number, deadRemapped: number, collisions: number}}}
|
|
*/
|
|
export function migrateRegistry(registry) {
|
|
const out = { ...registry, urls: {} };
|
|
const stats = { remapped: 0, deadRemapped: 0, collisions: 0 };
|
|
|
|
for (const [url, entry] of Object.entries(registry.urls)) {
|
|
const target = remapKey(url);
|
|
const isRemap = target !== url;
|
|
|
|
// Normalize every entry to the full schema (reserved fields included).
|
|
const base = {
|
|
sitemap_lastmod: entry.sitemap_lastmod ?? null,
|
|
reference_files: entry.reference_files ?? [],
|
|
status: entry.status ?? 'unpolled',
|
|
authority_source: entry.authority_source ?? null,
|
|
course: entry.course ?? null,
|
|
};
|
|
|
|
let next = base;
|
|
if (isRemap) {
|
|
stats.remapped++;
|
|
if (DEAD_REMAPS[url]) stats.deadRemapped++;
|
|
// The URL changed — old poll state is invalid; let the next poll decide.
|
|
next = { ...base, sitemap_lastmod: null, status: 'unpolled' };
|
|
}
|
|
|
|
if (out.urls[target]) {
|
|
// Two source keys collapsed onto one target — union the reference files.
|
|
stats.collisions++;
|
|
const merged = [
|
|
...new Set([...out.urls[target].reference_files, ...next.reference_files]),
|
|
].sort();
|
|
out.urls[target] = { ...out.urls[target], ...next, reference_files: merged };
|
|
} else {
|
|
out.urls[target] = next;
|
|
}
|
|
}
|
|
|
|
return { registry: out, stats };
|
|
}
|