Remove debug instrumentation and unused webmentions lib
All checks were successful
Deploy / deploy (push) Successful in 1m18s
All checks were successful
Deploy / deploy (push) Successful in 1m18s
Drop the hidden data-webmentions-debug div, the console.log in astro.config.mjs, and src/lib/webmentions.ts (which was superseded by the inline fetch in Webmentions.astro). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
7de0a815f4
commit
df59a1405f
3 changed files with 7 additions and 143 deletions
|
|
@ -10,7 +10,6 @@ import node from '@astrojs/node';
|
||||||
const envMode = process.env.NODE_ENV === 'production' ? 'production' : 'development';
|
const envMode = process.env.NODE_ENV === 'production' ? 'production' : 'development';
|
||||||
const envVars = loadEnv(envMode, process.cwd(), '');
|
const envVars = loadEnv(envMode, process.cwd(), '');
|
||||||
const WEBMENTION_TOKEN = envVars.WEBMENTION_TOKEN || process.env.WEBMENTION_TOKEN || '';
|
const WEBMENTION_TOKEN = envVars.WEBMENTION_TOKEN || process.env.WEBMENTION_TOKEN || '';
|
||||||
console.log(`[astro.config] loadEnv mode=${envMode} token-len=${WEBMENTION_TOKEN.length}`);
|
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
|
|
||||||
|
|
@ -31,15 +31,9 @@ interface Props {
|
||||||
|
|
||||||
const { target, locale = getLocaleFromUrl(Astro.url) ?? DEFAULT_LOCALE } = Astro.props;
|
const { target, locale = getLocaleFromUrl(Astro.url) ?? DEFAULT_LOCALE } = Astro.props;
|
||||||
|
|
||||||
interface FetchResult {
|
async function fetchMentions(target: string): Promise<WMEntry[]> {
|
||||||
mentions: WMEntry[];
|
|
||||||
debug: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchMentions(target: string): Promise<FetchResult> {
|
|
||||||
const token = WEBMENTION_TOKEN;
|
const token = WEBMENTION_TOKEN;
|
||||||
const tokenLen = typeof token === 'string' ? token.length : 0;
|
if (!token) return [];
|
||||||
if (!token) return { mentions: [], debug: `no-token(len=${tokenLen})` };
|
|
||||||
const withSlash = target.endsWith('/') ? target : `${target}/`;
|
const withSlash = target.endsWith('/') ? target : `${target}/`;
|
||||||
const withoutSlash = target.replace(/\/+$/, '');
|
const withoutSlash = target.replace(/\/+$/, '');
|
||||||
const fetchOne = async (t: string) => {
|
const fetchOne = async (t: string) => {
|
||||||
|
|
@ -48,27 +42,24 @@ async function fetchMentions(target: string): Promise<FetchResult> {
|
||||||
url.searchParams.set('token', token);
|
url.searchParams.set('token', token);
|
||||||
url.searchParams.set('per-page', '100');
|
url.searchParams.set('per-page', '100');
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
if (!res.ok) return { entries: [] as WMEntry[], status: res.status };
|
if (!res.ok) return [] as WMEntry[];
|
||||||
const json = (await res.json()) as { children?: WMEntry[] };
|
const json = (await res.json()) as { children?: WMEntry[] };
|
||||||
return { entries: json.children ?? [], status: 200 };
|
return json.children ?? [];
|
||||||
};
|
};
|
||||||
const [a, b] = await Promise.all([fetchOne(withSlash), fetchOne(withoutSlash)]);
|
const [a, b] = await Promise.all([fetchOne(withSlash), fetchOne(withoutSlash)]);
|
||||||
const seen = new Set<number>();
|
const seen = new Set<number>();
|
||||||
const merged: WMEntry[] = [];
|
const merged: WMEntry[] = [];
|
||||||
for (const m of [...a.entries, ...b.entries]) {
|
for (const m of [...a, ...b]) {
|
||||||
const id = m['wm-id'];
|
const id = m['wm-id'];
|
||||||
if (id == null || seen.has(id)) continue;
|
if (id == null || seen.has(id)) continue;
|
||||||
seen.add(id);
|
seen.add(id);
|
||||||
merged.push(m);
|
merged.push(m);
|
||||||
}
|
}
|
||||||
return {
|
return merged;
|
||||||
mentions: merged,
|
|
||||||
debug: `ok(len=${tokenLen}) slash=${a.status}:${a.entries.length} noslash=${b.status}:${b.entries.length}`,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetStr = target.toString();
|
const targetStr = target.toString();
|
||||||
const { mentions: all, debug: fetchDebug } = await fetchMentions(targetStr);
|
const all = await fetchMentions(targetStr);
|
||||||
|
|
||||||
const likes = all.filter((m) => m['wm-property'] === 'like-of');
|
const likes = all.filter((m) => m['wm-property'] === 'like-of');
|
||||||
const reposts = all.filter((m) => m['wm-property'] === 'repost-of');
|
const reposts = all.filter((m) => m['wm-property'] === 'repost-of');
|
||||||
|
|
@ -95,8 +86,6 @@ function formatDate(iso?: string) {
|
||||||
const hasAny = facepile.length > 0 || replies.length > 0 || mentions.length > 0;
|
const hasAny = facepile.length > 0 || replies.length > 0 || mentions.length > 0;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div data-webmentions-debug data-target={targetStr} data-fetch={fetchDebug} data-all={all.length} data-facepile={facepile.length} data-hasany={String(hasAny)} hidden></div>
|
|
||||||
|
|
||||||
{
|
{
|
||||||
hasAny && (
|
hasAny && (
|
||||||
<section class="webmentions" aria-labelledby="webmentions-heading">
|
<section class="webmentions" aria-labelledby="webmentions-heading">
|
||||||
|
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
/**
|
|
||||||
* Build-time Webmention fetcher via webmention.io API.
|
|
||||||
*
|
|
||||||
* Requires WEBMENTION_TOKEN in the environment. Token is read-only and
|
|
||||||
* issued per registered webmention.io domain.
|
|
||||||
*
|
|
||||||
* Fetches per target URL (not domain-wide) to avoid inconsistencies when
|
|
||||||
* a domain is registered more than once at webmention.io.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export type WMProperty = 'in-reply-to' | 'like-of' | 'repost-of' | 'bookmark-of' | 'mention-of';
|
|
||||||
|
|
||||||
export interface WMAuthor {
|
|
||||||
type?: string;
|
|
||||||
name?: string;
|
|
||||||
url?: string;
|
|
||||||
photo?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WMEntry {
|
|
||||||
type: 'entry';
|
|
||||||
author?: WMAuthor;
|
|
||||||
url: string;
|
|
||||||
published?: string;
|
|
||||||
'wm-received'?: string;
|
|
||||||
'wm-id'?: number;
|
|
||||||
'wm-source'?: string;
|
|
||||||
'wm-target'?: string;
|
|
||||||
'wm-property'?: WMProperty;
|
|
||||||
'wm-private'?: boolean;
|
|
||||||
content?: { text?: string; html?: string };
|
|
||||||
name?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WMResponse {
|
|
||||||
type: 'feed';
|
|
||||||
name?: string;
|
|
||||||
children: WMEntry[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const API = 'https://webmention.io/api/mentions.jf2';
|
|
||||||
|
|
||||||
let invocationCounter = 0;
|
|
||||||
|
|
||||||
async function fetchForTarget(target: string): Promise<WMEntry[]> {
|
|
||||||
const token = import.meta.env.WEBMENTION_TOKEN;
|
|
||||||
if (!token) {
|
|
||||||
console.warn('[webmentions] WEBMENTION_TOKEN is not set — skipping fetch.');
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
console.log(`[webmentions] fetching ${target} (token length: ${token.length})`);
|
|
||||||
const url = new URL(API);
|
|
||||||
url.searchParams.set('target', target);
|
|
||||||
url.searchParams.set('token', token);
|
|
||||||
url.searchParams.set('per-page', '100');
|
|
||||||
const res = await fetch(url);
|
|
||||||
if (!res.ok) {
|
|
||||||
console.warn(`[webmentions] API ${res.status} ${res.statusText} for ${target}`);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const json = (await res.json()) as WMResponse;
|
|
||||||
console.log(`[webmentions] → ${json.children?.length ?? 0} mentions`);
|
|
||||||
return json.children ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all webmentions targeting a given absolute URL. Tries both the
|
|
||||||
* canonical URL and its trailing-slash variant, since webmention.io
|
|
||||||
* indexes targets verbatim.
|
|
||||||
*/
|
|
||||||
export async function getMentionsFor(target: string | URL): Promise<WMEntry[]> {
|
|
||||||
const canonical = typeof target === 'string' ? target : target.toString();
|
|
||||||
const withSlash = canonical.endsWith('/') ? canonical : `${canonical}/`;
|
|
||||||
const withoutSlash = canonical.replace(/\/+$/, '');
|
|
||||||
|
|
||||||
const invocation = ++invocationCounter;
|
|
||||||
console.log(`[webmentions getMentionsFor #${invocation}] target=${canonical}`);
|
|
||||||
|
|
||||||
const [a, b] = await Promise.all([fetchForTarget(withSlash), fetchForTarget(withoutSlash)]);
|
|
||||||
const seen = new Set<number>();
|
|
||||||
const merged: WMEntry[] = [];
|
|
||||||
for (const m of [...a, ...b]) {
|
|
||||||
const id = m['wm-id'];
|
|
||||||
if (id == null || seen.has(id)) continue;
|
|
||||||
seen.add(id);
|
|
||||||
merged.push(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`[webmentions getMentionsFor #${invocation}] returning ${merged.length} mentions for ${canonical}`);
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function groupMentions(mentions: WMEntry[]): {
|
|
||||||
likes: WMEntry[];
|
|
||||||
reposts: WMEntry[];
|
|
||||||
bookmarks: WMEntry[];
|
|
||||||
replies: WMEntry[];
|
|
||||||
mentions: WMEntry[];
|
|
||||||
} {
|
|
||||||
const likes: WMEntry[] = [];
|
|
||||||
const reposts: WMEntry[] = [];
|
|
||||||
const bookmarks: WMEntry[] = [];
|
|
||||||
const replies: WMEntry[] = [];
|
|
||||||
const others: WMEntry[] = [];
|
|
||||||
for (const m of mentions) {
|
|
||||||
switch (m['wm-property']) {
|
|
||||||
case 'like-of':
|
|
||||||
likes.push(m);
|
|
||||||
break;
|
|
||||||
case 'repost-of':
|
|
||||||
reposts.push(m);
|
|
||||||
break;
|
|
||||||
case 'bookmark-of':
|
|
||||||
bookmarks.push(m);
|
|
||||||
break;
|
|
||||||
case 'in-reply-to':
|
|
||||||
replies.push(m);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
others.push(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { likes, reposts, bookmarks, replies, mentions: others };
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue