b5a88d9746
Backfill missing platform-brand footer at serve time and block service- account links when the HTML file omits data-mindspace-page-tag. Co-authored-by: Cursor <cursoragent@cursor.com>
71 lines
2.5 KiB
JavaScript
71 lines
2.5 KiB
JavaScript
import fs from 'node:fs';
|
|
import { artifactFileExists } from './page-artifact.mjs';
|
|
import { hasPlatformBrandMarker } from '../../mindspace-page-tag.mjs';
|
|
|
|
export { hasPlatformBrandMarker };
|
|
|
|
const RASTER_IMAGE_PATTERN = /\.(png|jpe?g|webp|gif)(?:[?#]|$)/i;
|
|
|
|
export function hasMindspaceCoverMeta(html) {
|
|
return /<meta[^>]*name=["']mindspace-cover["']/i.test(String(html ?? ''));
|
|
}
|
|
|
|
export function hasShareDescription(html) {
|
|
const source = String(html ?? '');
|
|
return (
|
|
/<meta[^>]+property=["']og:description["']/i.test(source) ||
|
|
/<meta[^>]+name=["']description["']/i.test(source)
|
|
);
|
|
}
|
|
|
|
export function hasRasterShareImageHint(html) {
|
|
const source = String(html ?? '');
|
|
if (/<meta[^>]+property=["']og:image["'][^>]+content=["'][^"']+/i.test(source)) {
|
|
const match =
|
|
source.match(/<meta[^>]+property=["']og:image["'][^>]+content=["']([^"']+)["']/i) ??
|
|
source.match(/<meta[^>]+content=["']([^"']+)["'][^>]+property=["']og:image["']/i);
|
|
const url = match?.[1] ?? '';
|
|
if (url && !/\.svg(?:[?#]|$)/i.test(url)) return true;
|
|
}
|
|
const coverMatch = source.match(/<meta[^>]*name=["']mindspace-cover["'][^>]*content=(["'])([\s\S]*?)\1/i);
|
|
if (!coverMatch?.[2]) return false;
|
|
try {
|
|
const meta = JSON.parse(coverMatch[2].replaceAll('"', '"'));
|
|
const cover = String(meta.cover ?? meta.image ?? '').trim();
|
|
if (!cover) return false;
|
|
if (/^https?:\/\//i.test(cover)) return RASTER_IMAGE_PATTERN.test(cover);
|
|
return RASTER_IMAGE_PATTERN.test(cover) || !/\.svg(?:[?#]|$)/i.test(cover);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* WeChat link cards need mindspace-cover (for thumbnail.png) + description.
|
|
* Visual pages should also reference a raster hero in cover/image when possible.
|
|
*/
|
|
export function verifySharePreviewMeta(html) {
|
|
if (!hasMindspaceCoverMeta(html)) {
|
|
return { ok: false, reason: 'missing_mindspace_cover' };
|
|
}
|
|
if (!hasShareDescription(html)) {
|
|
return { ok: false, reason: 'missing_description' };
|
|
}
|
|
if (!hasPlatformBrandMarker(html)) {
|
|
return { ok: false, reason: 'missing_platform_brand' };
|
|
}
|
|
return { ok: true, reason: null };
|
|
}
|
|
|
|
export function verifyArtifactSharePreview(artifact) {
|
|
if (!artifactFileExists(artifact)) {
|
|
return { ok: false, reason: 'missing_file' };
|
|
}
|
|
const content = fs.readFileSync(artifact.localPath, 'utf8');
|
|
return verifySharePreviewMeta(content);
|
|
}
|
|
|
|
export function filterSharePreviewReadyArtifacts(artifacts = []) {
|
|
return artifacts.filter((artifact) => verifyArtifactSharePreview(artifact).ok);
|
|
}
|