All files / lib indexnow.ts

100% Statements 14/14
100% Branches 11/11
100% Functions 2/2
100% Lines 13/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64                          6x                                 22x 22x 22x     6x 6x   1x     5x 6x         5x 5x                   4x   1x      
/**
 * IndexNow — instantly notify Bing / Yandex (and other IndexNow-participating
 * engines) that a marketplace URL was published or updated, instead of waiting
 * for the next crawl.
 *
 * Fire-and-forget: call via `c.executionCtx.waitUntil(...)` so it never blocks
 * the request or surfaces an error to the caller. No-ops when INDEXNOW_KEY or
 * MARKETPLACE_URL is unset (dev/preview safe), so it stays inert until the
 * production secret is configured via `wrangler secret put INDEXNOW_KEY`.
 *
 * Key file: the key string must be served at `${MARKETPLACE_URL}/indexnow-key.txt`
 * (see apps/marketplace/src/pages/indexnow-key.txt.ts).
 */
const INDEXNOW_ENDPOINT = "https://api.indexnow.org/indexnow";
 
export interface IndexNowEnv {
	INDEXNOW_KEY?: string;
	/** Public marketplace origin, e.g. "https://www.interioring.com". */
	MARKETPLACE_URL?: string;
}
 
/**
 * Submit marketplace paths ("/pros/acme") or absolute URLs to IndexNow.
 * Returns true when the engine accepted the batch, false on no-op (missing
 * config / empty list) or any failure. Never throws.
 */
export async function pingIndexNow(
	env: IndexNowEnv,
	paths: string[],
): Promise<boolean> {
	const key = env.INDEXNOW_KEY;
	const site = env.MARKETPLACE_URL?.replace(/\/$/, "");
	if (!key || !site || paths.length === 0) return false;
 
	let host: string;
	try {
		host = new URL(site).host;
	} catch {
		return false;
	}
 
	const urlList = paths.map((p) =>
		p.startsWith("http://") || p.startsWith("https://")
			? p
			: `${site}${p.startsWith("/") ? "" : "/"}${p}`,
	);
 
	try {
		const res = await fetch(INDEXNOW_ENDPOINT, {
			method: "POST",
			headers: { "Content-Type": "application/json; charset=utf-8" },
			body: JSON.stringify({
				host,
				key,
				keyLocation: `${site}/indexnow-key.txt`,
				urlList,
			}),
		});
		return res.ok;
	} catch {
		return false;
	}
}