import fs from 'node:fs'; import path from 'node:path'; const STUB_MARKERS = ['临时补出', '服务号兜底', '服务号自动补出简版页面']; export function resolveArtifactLocalPath(artifact, publishDir = '') { const localPath = String(artifact?.localPath ?? '').trim(); if (localPath) return localPath; const relativePath = String(artifact?.relativePath ?? '').trim(); const root = String(publishDir ?? '').trim(); if (!relativePath || !root) return ''; const resolved = path.resolve(root, relativePath); const normalizedRoot = path.resolve(root); if ( resolved !== normalizedRoot && !resolved.startsWith(`${normalizedRoot}${path.sep}`) ) { return ''; } return resolved; } export function isStubPublicHtmlContent(content) { const value = String(content ?? ''); return STUB_MARKERS.some((marker) => value.includes(marker)); } export function artifactFileExists(artifact, { publishDir = '' } = {}) { const localPath = resolveArtifactLocalPath(artifact, publishDir); if (localPath) { try { return fs.existsSync(localPath) && fs.statSync(localPath).isFile(); } catch { return false; } } if (typeof artifact?.exists === 'boolean') { return artifact.exists; } return false; } export function isStubPublicHtmlArtifact(artifact, { publishDir = '' } = {}) { if (typeof artifact?.isStub === 'boolean') { return artifact.isStub; } const localPath = resolveArtifactLocalPath(artifact, publishDir); if (!localPath) return false; try { return isStubPublicHtmlContent(fs.readFileSync(localPath, 'utf8')); } catch { return false; } } /** Real HTML artifacts only — never fall back to stub placeholders. */ export function filterSendableHtmlArtifacts(artifacts = [], options = {}) { return artifacts.filter( (artifact) => artifactFileExists(artifact, options) && !isStubPublicHtmlArtifact(artifact, options), ); } export function selectSendableHtmlArtifacts({ verifiedArtifacts = [], confirmedArtifacts = [], publishDir = '', } = {}) { const options = { publishDir }; const candidates = verifiedArtifacts.length > 0 ? verifiedArtifacts : confirmedArtifacts; return filterSendableHtmlArtifacts(candidates, options); } export function verifyPageArtifactContent(artifact, { minBytes = 512, publishDir = '' } = {}) { const localPath = resolveArtifactLocalPath(artifact, publishDir); const resolvedArtifact = localPath ? { ...artifact, localPath } : artifact; if (!artifactFileExists(resolvedArtifact, { publishDir })) { return { ok: false, reason: 'missing_file' }; } if (isStubPublicHtmlArtifact(resolvedArtifact, { publishDir })) { return { ok: false, reason: 'stub_placeholder' }; } if (!localPath) { if (Number(artifact?.sizeBytes ?? 0) < minBytes) { return { ok: false, reason: 'too_small', }; } if (artifact?.isHtmlDocument !== true) { return { ok: false, reason: 'not_html_document', }; } return { ok: true, reason: null }; } const size = fs.statSync(localPath).size; if (size < minBytes) { return { ok: false, reason: 'too_small' }; } const content = fs.readFileSync(localPath, 'utf8'); if (!/<(?:html|body|main|article)\b/i.test(content)) { return { ok: false, reason: 'not_html_document' }; } return { ok: true, reason: null }; }