1faba62f9b
Memind CI / Test, build, and release guards (push) Failing after 6s
Production WeChat delivery strips localPath from artifacts, so share-preview auto-repair never wrote back to HTML. Resolve paths via publishDir + relativePath, repair at materialize and delivery prep, and pass h5Root from Portal so page.generate can patch missing meta before sending links. Co-authored-by: Cursor <cursoragent@cursor.com>
109 lines
3.3 KiB
JavaScript
109 lines
3.3 KiB
JavaScript
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 };
|
|
}
|