diff --git a/scripts/build-portal-runtime.mjs b/scripts/build-portal-runtime.mjs index f0ccda5..0ea8ddb 100755 --- a/scripts/build-portal-runtime.mjs +++ b/scripts/build-portal-runtime.mjs @@ -235,10 +235,6 @@ async function copyMindspacePublicLinkTools() { path.join(root, 'scripts', 'repair-mindspace-public-downloads.mjs'), path.join(runtimeRoot, 'scripts', 'repair-mindspace-public-downloads.mjs'), ); - await fs.copyFile( - path.join(root, 'mindspace-public-finish-sync.mjs'), - path.join(runtimeRoot, 'mindspace-public-finish-sync.mjs'), - ); } async function writeMetadata() { diff --git a/scripts/repair-mindspace-public-downloads.mjs b/scripts/repair-mindspace-public-downloads.mjs index 575d0c1..19e6126 100755 --- a/scripts/repair-mindspace-public-downloads.mjs +++ b/scripts/repair-mindspace-public-downloads.mjs @@ -1,29 +1,32 @@ #!/usr/bin/env node /** * Repair missing public/*.docx download targets before release link checks. - * 1) sync from oa/ when a safe source exists - * 2) remove broken docx anchors from HTML when no source is available + * Runtime-safe: no imports from the full MindSpace finish-sync graph. */ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { syncPublicDocxDownloads } from '../mindspace-public-finish-sync.mjs'; const DOCX_HREF_RE = /]*\bhref=(["'])([^"']+\.docx)\1[^>]*>[\s\S]*?<\/a>/gi; const DOCX_HREF_CAPTURE_RE = /]*\bhref=(["'])([^"']+\.docx)\1/gi; -function listMissingDocxTargets(html, publicDir) { - const missing = new Set(); +function listDocxHrefBasenames(html) { + const basenames = new Set(); let match = DOCX_HREF_CAPTURE_RE.exec(html); while (match) { - const docxPath = path.resolve(publicDir, path.basename(String(match[2] ?? ''))); - if (!fs.existsSync(docxPath)) { - missing.add(path.basename(docxPath).toLowerCase()); - } + const base = path.basename(String(match[2] ?? '')).trim(); + if (base) basenames.add(base); match = DOCX_HREF_CAPTURE_RE.exec(html); } DOCX_HREF_CAPTURE_RE.lastIndex = 0; - return [...missing]; + return [...basenames]; +} + +function listMissingDocxTargets(html, publicDir) { + return listDocxHrefBasenames(html).filter((basename) => { + const docxPath = path.join(publicDir, basename); + return !fs.existsSync(docxPath); + }); } function stripBrokenDocxAnchors(html, missingBasenames) { @@ -39,23 +42,39 @@ function stripBrokenDocxAnchors(html, missingBasenames) { return { html: next, changed }; } +function tryCopyDocxFromOa(userRoot, publicDir, basename) { + const destination = path.join(publicDir, basename); + if (fs.existsSync(destination)) return false; + const source = path.join(userRoot, 'oa', basename); + if (!fs.existsSync(source) || !fs.statSync(source).isFile()) return false; + fs.mkdirSync(path.dirname(destination), { recursive: true }); + fs.copyFileSync(source, destination); + return true; +} + export function repairMindspacePublicDownloads({ publishDir } = {}) { const root = path.resolve(String(publishDir ?? '')); if (!root || !fs.existsSync(root)) { - return { synced: [], missing: [], stripped: [] }; + return { synced: [], stripped: [] }; } - const syncResult = syncPublicDocxDownloads({ publishDir: root, minCompleteSize: 1024 }); + const synced = []; const stripped = []; for (const userDir of fs.readdirSync(root, { withFileTypes: true })) { if (!userDir.isDirectory()) continue; - const publicDir = path.join(root, userDir.name, 'public'); + const userRoot = path.join(root, userDir.name); + const publicDir = path.join(userRoot, 'public'); if (!fs.existsSync(publicDir)) continue; for (const entry of fs.readdirSync(publicDir, { withFileTypes: true })) { if (!entry.isFile() || !entry.name.endsWith('.html')) continue; const htmlPath = path.join(publicDir, entry.name); const html = fs.readFileSync(htmlPath, 'utf8'); + for (const basename of listDocxHrefBasenames(html)) { + if (tryCopyDocxFromOa(userRoot, publicDir, basename)) { + synced.push(path.join(userDir.name, 'public', basename)); + } + } const missingBasenames = listMissingDocxTargets(html, publicDir); const { html: nextHtml, changed } = stripBrokenDocxAnchors(html, missingBasenames); if (!changed) continue; @@ -64,11 +83,7 @@ export function repairMindspacePublicDownloads({ publishDir } = {}) { } } - return { - synced: syncResult.synced, - missing: syncResult.missing, - stripped, - }; + return { synced, stripped }; } function main() {