Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ no-ranking promise the README makes, that `/llms.txt`, `/sitemap.xml`, `/robots.
npm run audit:live-promise
```

To preview (no network writes) or run the credential-free IndexNow submission
that tells Bing — and Bing-derived DuckDuckGo — to crawl every public URL
(the key file must be live on production first; the script refuses otherwise):

```bash
npm run submit:indexnow -- --dry-run # preview URL set + endpoints
npm run submit:indexnow # verify key file, then submit to Bing/IndexNow
Comment on lines +90 to +96

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

State the dry-run prerequisite accurately.

main() returns code 2 for a missing key file only when dryRun is false. With --dry-run, it logs failed key checks, fetches the sitemap, and returns code 0. State that the key file must be live before a real submission, not before a preview.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 89 - 95, Update the README instructions around the
IndexNow commands to clarify that the production key file is required before a
real submission, while --dry-run can preview without it and still completes
successfully.

```

For the repeatable live walk of the private-beta funnel (the backlog item
"Live-surface walk of the private-beta funnel"): a real-browser (Playwright
Chromium) walk of the funnel stops home → `/demo` → `/packages`, with the
Expand Down
135 changes: 135 additions & 0 deletions docs/growth/search-index-coverage-2026-08-11.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# SEO Fix Kit — full search-index coverage (Google + Bing/DuckDuckGo)

Packet for the lane-1 item: **Establish full search-index coverage: all 7 public
pages indexed on Google plus a Bing/DuckDuckGo presence**
(backlog owner:
`/home/nish/workspaces/agent-state/seo-fix-kit-improvement-loop/backlog.md`,
[scout 2026-08-09, rank: 2, risk: green]).
Comment on lines +5 to +7

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Remove the local workstation path.

This public document exposes a developer identifier and local directory layout. Replace it with a repository-relative backlog reference or an issue identifier.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/growth/search-index-coverage-2026-08-11.md` around lines 5 - 7, Replace
the absolute local workstation path in the document’s backlog owner reference
with a repository-relative backlog path or issue identifier, while preserving
the scout date and ranking metadata.


Status: **agent-completable half shipped, engine half needs owner credentials +
time.** This packet (2026-08-11) re-verified the gap live, shipped the
credential-free IndexNow submission path (key file routes in the Worker,
submission script, tests, wrangler routing) that gets Bing — and therefore
DuckDuckGo — crawling all public URLs the moment the release lands, and
Comment on lines +12 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require submission before claiming crawling starts

When the release is deployed but npm run submit:indexnow has not yet been run, it only exposes the verification key; no request is sent to either IndexNow endpoint because the POST occurs exclusively inside the manually invoked script. Saying crawling begins “the moment the release lands” can therefore cause the operator to skip the required resume command and leave the coverage task stalled indefinitely. Describe the release as enabling submission and make running the command a prerequisite for any crawl notification claim.

AGENTS.md reference: AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

documented the two owner-only legs (Google Search Console request-indexing,
Bing Webmaster ownership) as an exact manual kit. Search-engine indexing is
externally owned and time-dependent; the acceptance outcome ("all 7 pages on
Google, seofixkit.com first on Bing/DDG") cannot be produced from a lane
without credentials, but every lever that does not need credentials is now
built and one command away.
Comment on lines +9 to +19

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

What do the current official IndexNow documentation and FAQ state about whether a successful IndexNow submission guarantees crawling or indexing, and whether DuckDuckGo is an IndexNow participant?

💡 Result:

The official IndexNow documentation and FAQ explicitly state that a successful submission does not guarantee crawling or indexing [1][2]. When a URL is submitted, search engines receive an HTTP 200 response, which only confirms that the search engine has received the notification [3][4]. After receiving a submission, a search engine evaluates the URL based on its own criteria, including crawl quota, scheduling logic, and content quality signals [1][2]. IndexNow serves to alert search engines that content has changed, which increases the likelihood of faster discovery and prioritization compared to pages not submitted through the protocol, but final decisions on whether to crawl or index remain at the discretion of the search engine's algorithms [1][2]. Regarding DuckDuckGo, the official IndexNow documentation does not list it as a direct participant [5]. However, because DuckDuckGo incorporates results from Bing’s index, it benefits indirectly from IndexNow submissions made to Bing [6][7][8]. Therefore, while DuckDuckGo is not a participant that independently receives and processes IndexNow pings, updates submitted to IndexNow-participating engines like Bing can still be reflected in DuckDuckGo search results [9][7].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- target document ---'
sed -n '1,150p' docs/growth/search-index-coverage-2026-08-11.md
printf '%s\n' '--- related README wording ---'
rg -n -i -C 3 'IndexNow|DuckDuckGo|Bing.*crawl|crawl.*Bing|all 7 pages|seofixkit' --glob 'README*' --glob '*.md' .

Repository: nish3451/seo-fix-kit

Length of output: 50377


Qualify IndexNow and DuckDuckGo outcomes

IndexNow acceptance confirms receipt of a notification. It requests discovery but does not guarantee crawling or indexing. Remove claims that IndexNow gets Bing or DuckDuckGo crawling, that DuckDuckGo follows from Bing, or that IndexNow owns first-result presence. State that Bing and DuckDuckGo independently decide whether to crawl and index URLs. Apply the same correction to any README copy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/growth/search-index-coverage-2026-08-11.md` around lines 9 - 19, Revise
the status text in the documentation to describe IndexNow only as requesting URL
discovery and confirming notification receipt, not as guaranteeing Bing or
DuckDuckGo crawling, indexing, or search-result placement. State that Bing and
DuckDuckGo independently determine whether to crawl and index URLs, and remove
any claim that DuckDuckGo follows from Bing or that IndexNow controls
first-result presence; apply the same wording correction to corresponding README
content.


## Fresh live evidence (2026-08-11, ~10:40 IST)

- **Google: homepage only.** Startpage (a live Google-index proxy) for
`site:seofixkit.com` returns exactly one organic result:
`https://seofixkit.com` "SEO Fix Kit - Proof-Backed SEO Repair Beta". Per-path
probes `site:seofixkit.com/{terms,demo,methodology,packages,check,privacy,support}`
all return zero results. Direct Google is CAPTCHA-walled from this VPS
(`/sorry/index`, screenshot `/tmp/serde2-google_gbv1_site.png`), so Startpage
is the reproducible Google-index receipt. Backlog evidence (2026-08-09, real
anti-detection browser) showed `/` + `/terms`; the `/terms` result has since
dropped out or been consolidated — either way, 7 of 8 public routes remain
unindexed on Google.
- **Bing: zero presence.** Real-browser `bing.com/search?q=seofixkit.com`
returns ~82,200 results, none of them seofixkit.com (all unrelated
"Inn at the Market" hotel pages — Bing is fuzzy-matching the query text).
Screenshot `/tmp/seo-serde-bing_domain.png`.
- **DuckDuckGo: blocked this run, zero by construction.** DDG html/lite/main
endpoints all bot-walled this IP (418/error page; screenshots
`/tmp/serde2-duckduckgo_main.png`). DDG's web index is Bing-derived, and
prior real-browser evidence (2026-08-09) recorded a zero-results SERP for
`seofixkit.com`. No change in DDG coverage is possible before Bing crawls.
- **Site surfaces are crawl-ready:** `https://seofixkit.com/robots.txt` HTTP 200
(`Allow: /`, `Sitemap: https://seofixkit.com/sitemap.xml`);
`https://seofixkit.com/sitemap.xml` HTTP 200 listing 8 public URLs
(`/`, `/demo`, `/check`, `/methodology`, `/packages`, `/privacy`, `/support`,
`/terms` — the item said 7; `/check` was added to the sitemap after the item
was filed, so the target set is now 8). Every page has unique title/meta/OG/
canonical; homepage carries Organization/WebSite/SoftwareApplication/FAQPage
schema; `/check` carries WebPage/FAQ JSON-LD.
- **No ownership artifacts observable:** DNS TXT on seofixkit.com carries only
SPF (`v=spf1 include:_spf.porkbun.com include:_spf.mx.cloudflare.net ~all`);
no `google-site-verification` meta, no `bing-site-verification` meta, no
BingSiteAuth.xml, no IndexNow key file live (both candidate paths return the
SPA fallback HTML with body mismatch — the exact bug `run_worker_first`
routing fixes in this packet).

## What this packet ships

- `shared/index-now.js` — committed IndexNow key (`3219d564f9f914772e178f33ae543e60`),
key file paths, payload builder, endpoints (`api.indexnow.org`,
`www.bing.com/indexnow`). Key is world-readable by spec, not a credential.
- `shared/audit-engine.js` — hoisted the canonical route list to
`ROOT_PUBLIC_PATHS`, shared by `rootSitemap()` and IndexNow so the sitemap
and the submission set can never drift.
- `worker/index.js` — serves `GET /{key}.txt` and `GET /.well-known/{key}.txt`
(text/plain, `x-robots-tag: noindex`), apex-only like every other public
surface; www requests keep 301ing to apex.
- `wrangler.jsonc` — both key paths added to `run_worker_first` so the SPA
asset fallback cannot shadow them (verified live: without this, both paths
currently return the SPA HTML with HTTP 200).
- `server/index.js` — local dev-server parity for both key paths.
- `scripts/submit-indexnow.mjs` + `scripts/submit-indexnow.test.mjs` —
`node scripts/submit-indexnow.mjs` verifies the key file is live at both
locations (refuses otherwise, exit 2), parses the live sitemap locs, POSTs
the payload to both endpoints, and reports per-endpoint accept/reject
(exit 0/3). `--dry-run` previews with no network writes.
- `package.json` — `submit:indexnow` script + `test:indexnow` wired into the
canonical `check` gate.
- `worker/index.test.mjs` — apex key file 200 with exact key body + noindex,
www 301 to apex, for both paths.

## Owner manual legs (credentials required — cannot be done by an agent)

Both are one-time, copy-paste steps that materially accelerate the outcome:

1. **Google Search Console — request indexing (accelerates Google re-crawl).**
- Add property `https://seofixkit.com` (URL-prefix) at
https://search.google.com/search-console (any Google account).
- Verify: DNS TXT record `google-site-verification=...` at Porkbun (or the
HTML meta tag; DNS is preferred and survives redeploys).
- Submit `https://seofixkit.com/sitemap.xml` in Sitemaps.
- Open URL Inspection for each of the 8 sitemap URLs and click
"Request indexing" (spread over a few days; Google throttles).
- Expected outcome: `site:seofixkit.com` on Google returns all 8 pages.
Without GSC, Google re-crawls on its own schedule via the sitemap and
the homepage's internal links (already shipped: `/check` is linked from
`/`, `/demo`, `/packages`, `/methodology`, `/support`, `/terms`,
`/privacy`).
2. **Bing Webmaster Tools — ownership + sitemap (long-term Bing health).**
- Add site at https://www.bing.com/webmasters (Microsoft account).
- Verify: DNS TXT `ms=...` at Porkbun.
- Submit `https://seofixkit.com/sitemap.xml`.
- IndexNow (shipped here) covers the crawl trigger; Webmaster Tools adds
the dashboard, crawl logs, and index coverage reports.

## Resume path (agent-completable, after this PR is merged AND released)

1. Confirm release landed: `curl -s https://seofixkit.com/{key}.txt` returns
the key text (not the SPA HTML).
2. `node scripts/submit-indexnow.mjs` — verify 2x "key matches", 8 URLs listed,
ACCEPTED on both endpoints. Bing crawls on its schedule (typically minutes
to hours); DDG follows from Bing's index.
3. Re-run the verification queries from "Fresh live evidence" above; record
receipts in this file under a "Re-verified" section. Google's leg moves
only via owner step 1 or its own re-crawl cadence.

## Acceptance / verification mapping

- Bing/DDG first-result presence: owned by IndexNow submission (this packet)
+ crawler schedule; externally observable via
`bing.com/search?q=seofixkit.com` and `duckduckgo.com/?q=seofixkit.com`.
- All public pages on Google: owned by Search Console request-indexing (owner)
+ sitemap re-crawl; externally observable via `site:seofixkit.com`.
- No index bloat / duplicate-host junk: already enforced — www 301s to apex,
every emitted URL (canonical, og:url, sitemap, robots, key file) is
apex-only; submission set is exactly the sitemap set with no query strings.
- Rollback: remove the worker key-file routes + `run_worker_first` entries and
delete `shared/index-now.js`; no product surface or public copy changes.

## Files changed in this packet

- `shared/index-now.js` (new), `scripts/submit-indexnow.mjs` (new),
`scripts/submit-indexnow.test.mjs` (new), `docs/growth/search-index-coverage-2026-08-11.md` (this file)
- `shared/audit-engine.js`, `worker/index.js`, `worker/index.test.mjs`,
`server/index.js`, `wrangler.jsonc`, `package.json`
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"cf:dry-run": "npm run build && node scripts/wrangler-dry-run.mjs",
"ops:audit-owned": "node scripts/run-live-audit-batch.mjs",
"audit:live-promise": "node scripts/live-promise-spot-check.mjs",
"submit:indexnow": "node scripts/submit-indexnow.mjs",
"test:billing": "node server/dodo-payment-smoke-test.js",
"test:billing-route": "node --test worker/routes/billing.test.mjs",
"test:product-truth": "node server/product-truth-smoke-test.js",
Expand Down Expand Up @@ -41,9 +42,10 @@
"test:promise-audit": "node --test shared/promise-audit.test.mjs",
"test:live-promise-spot-check": "node --test scripts/live-promise-spot-check.test.mjs",
"test:app-contract": "node --test src/app-contract.test.mjs",
"test:indexnow": "node --test scripts/submit-indexnow.test.mjs",
"test:canary-dry-run": "node --test scripts/wrangler-dry-run.test.mjs",
"test:check-inventory": "node --test scripts/check-chain-inventory.test.mjs",
"check": "npm run test:billing && npm run test:billing-route && npm run test:product-truth && npm run test:audit && npm run test:large-crawl-security && npm run test:report-retention-security && npm run test:local-developer-api-security && npm run test:worker-dispatch && npm run test:worker-email && npm run test:public-pages && npm run test:public-check && npm run test:audit-engine && npm run test:account && npm run test:ai-answer-readiness && npm run test:growth-opportunities && npm run test:repair-queue && npm run test:repair-proof-receipt && npm run test:repair-implementation-pack && npm run test:repair-agent && npm run test:developer-api && npm run test:remediation-brief && npm run test:audit-batch-runner && npm run test:webhooks && npm run test:app-contract && npm run test:promise-audit && npm run test:live-promise-spot-check && npm run test:funnel-walk && npm run test:large-crawl && npm run test:canary-dry-run && npm run test:check-inventory && npm run build",
"check": "npm run test:billing && npm run test:billing-route && npm run test:product-truth && npm run test:audit && npm run test:large-crawl-security && npm run test:report-retention-security && npm run test:local-developer-api-security && npm run test:worker-dispatch && npm run test:worker-email && npm run test:public-pages && npm run test:public-check && npm run test:audit-engine && npm run test:account && npm run test:ai-answer-readiness && npm run test:growth-opportunities && npm run test:repair-queue && npm run test:repair-proof-receipt && npm run test:repair-implementation-pack && npm run test:repair-agent && npm run test:developer-api && npm run test:remediation-brief && npm run test:audit-batch-runner && npm run test:webhooks && npm run test:app-contract && npm run test:promise-audit && npm run test:live-promise-spot-check && npm run test:indexnow && npm run test:funnel-walk && npm run test:large-crawl && npm run test:canary-dry-run && npm run test:check-inventory && npm run build",
"audit:funnel-walk": "node scripts/run-private-beta-funnel-walk.mjs",
"test:funnel-walk": "node --test scripts/run-private-beta-funnel-walk.test.mjs"
},
Expand Down
115 changes: 115 additions & 0 deletions scripts/submit-indexnow.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Submit the live sitemap URL set to IndexNow (Bing/Naver/Seznam/Yandex).
//
// Usage:
// node scripts/submit-indexnow.mjs # verify key file live, then submit
// node scripts/submit-indexnow.mjs --dry-run # print what would happen, no network writes
//
// Requirements: the IndexNow key file must already be live on the production
// host (the worker route in worker/index.js serves it once the repo change is
// released). The script refuses to submit until both key-file locations return
// the exact key text, because a submission with an unreachable key is silently
// discarded by the engines.
//
// Exit codes: 0 = accepted by every endpoint, 2 = key file not live yet,
// 3 = one or more endpoints rejected the submission.
import { INDEX_NOW_ENDPOINTS, INDEX_NOW_HOST, indexNowKeyFileBody, indexNowKeyFilePaths, buildIndexNowPayload } from "../shared/index-now.js";

const ORIGIN = `https://${INDEX_NOW_HOST}`;

export function parseSitemapLocs(xml) {
const locs = [];
const re = /<loc>([^<]+)<\/loc>/g;
let m;
while ((m = re.exec(xml)) !== null) locs.push(m[1]);
return locs;
}

export async function fetchSitemapUrls({ fetchImpl = globalThis.fetch } = {}) {
const res = await fetchImpl(`${ORIGIN}/sitemap.xml`);
if (!res.ok) throw new Error(`sitemap fetch failed: HTTP ${res.status}`);
const locs = parseSitemapLocs(await res.text());
if (locs.length === 0) throw new Error("sitemap returned zero <loc> entries");
return locs;
}

export async function verifyKeyFileLive({ fetchImpl = globalThis.fetch } = {}) {
const expected = indexNowKeyFileBody().trim();
const results = [];
for (const path of indexNowKeyFilePaths()) {
const url = `${ORIGIN}${path}`;
let ok = false;
let detail = "";
try {
const res = await fetchImpl(url);
const body = (await res.text()).trim();
ok = res.status === 200 && body === expected;
detail = ok ? "key matches" : `HTTP ${res.status}, body mismatch`;
} catch (err) {
detail = `fetch failed: ${err.message}`;
}
results.push({ url, ok, detail });
}
return results;
}

export async function submitUrlList(urlList, { endpoints = INDEX_NOW_ENDPOINTS, fetchImpl = globalThis.fetch } = {}) {
const payload = buildIndexNowPayload(urlList);
const outcomes = [];
for (const endpoint of endpoints) {
try {
const res = await fetchImpl(endpoint, {
method: "POST",
headers: { "content-type": "application/json; charset=utf-8" },
body: JSON.stringify(payload)
});
outcomes.push({ endpoint, status: res.status, ok: res.status === 200 || res.status === 202 });
} catch (err) {
outcomes.push({ endpoint, status: 0, ok: false, error: err.message });
}
}
return outcomes;
Comment on lines +55 to +70

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

According to the current official IndexNow FAQ, can a website submit the same URL-list payload to both the global IndexNow endpoint and the Bing endpoint, or should it submit to only one endpoint?

💡 Result:

According to the official IndexNow FAQ, you should submit your URL-list payload to only one of the participating IndexNow endpoints [1][2]. The protocol is designed such that all search engines adopting IndexNow have agreed that URLs submitted to any one participating endpoint will be automatically shared with all other participating search engines [3][4][5]. Therefore, submitting the same list to multiple endpoints is unnecessary [6]. The FAQ explicitly states: "You may submit your request to only one of the following participating endpoints. Each endpoint sends your submission directly to its respective search engine, and your submission will be shared across all IndexNow-enabled search engines" [1][2]. Using the global IndexNow endpoint (api.indexnow.org) is a common and efficient way to reach all participating engines with a single request [7][8][9].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- target file ---'
cat -n scripts/submit-indexnow.mjs | sed -n '1,140p'

printf '%s\n' '--- related references ---'
rg -n -S --glob '!node_modules' 'INDEX_NOW_ENDPOINTS|submitUrlList|IndexNow|api\.indexnow\.org|www\.bing\.com/indexnow|status.*3' .

Repository: nish3451/seo-fix-kit

Length of output: 18101


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- shared IndexNow contract ---'
cat -n shared/index-now.js | sed -n '1,100p'

printf '%s\n' '--- submit-indexnow tests ---'
cat -n scripts/submit-indexnow.test.mjs | sed -n '1,130p'

printf '%s\n' '--- operational documentation ---'
cat -n README.md | sed -n '80,105p'
cat -n docs/growth/search-index-coverage-2026-08-11.md | sed -n '48,70p;95,125p'

Repository: nish3451/seo-fix-kit

Length of output: 13128


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path
import re

source = Path("scripts/submit-indexnow.mjs").read_text()
shared = Path("shared/index-now.js").read_text()

endpoints = re.findall(r'"(https://[^"]+/indexnow)"', shared)
loop = re.search(r'for \(const endpoint of endpoints\)', source)
accepted = re.search(r'res\.status === 200 \|\| res\.status === 202', source)
exit_logic = re.search(r'code: allOk \? 0 : 3', source)

print("configured_endpoints:", endpoints)
print("default_endpoint_count:", len(endpoints))
print("submitUrlList_iterates_each_endpoint:", bool(loop))
print("200_or_202_are_accepted:", bool(accepted))
print("main_returns_3_when_not_all_outcomes_are_ok:", bool(exit_logic))

# Model the source-level aggregation for one accepted response and one rejection.
statuses = [200, 403]
outcomes = [{"status": s, "ok": s in (200, 202)} for s in statuses]
all_ok = all(item["ok"] for item in outcomes)
print("mixed_statuses:", statuses)
print("mixed_outcomes:", outcomes)
print("modeled_exit_code:", 0 if all_ok else 3)
PY

Repository: nish3451/seo-fix-kit

Length of output: 524


Submit through one IndexNow endpoint.

IndexNow shares submissions across participating search engines. Configure one endpoint and do not require every endpoint to accept the payload. Update the shared contract, tests, and operational documentation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/submit-indexnow.mjs` around lines 55 - 70, The submitUrlList function
currently posts to every configured endpoint; change it to submit once through a
single configured IndexNow endpoint while preserving the existing outcome shape
and error handling. Update the shared endpoint configuration contract, related
tests, and operational documentation to reflect that only one endpoint is
required and used.

}

export async function main({ dryRun = false, fetchImpl = globalThis.fetch } = {}) {
const keyChecks = await verifyKeyFileLive({ fetchImpl });
const keyLive = keyChecks.every((c) => c.ok);
console.log(`IndexNow key file check (${ORIGIN}):`);
for (const c of keyChecks) console.log(` ${c.ok ? "OK " : "MISS"} ${c.url} (${c.detail})`);

if (!keyLive && !dryRun) {
console.error(
"\nKey file is not live yet. Merge + release the worker change that serves the key file, then re-run this script."
);
return { ok: false, code: 2, keyChecks };
}

const urls = await fetchSitemapUrls({ fetchImpl });
console.log(`\nSitemap URL set (${urls.length} URLs):`);
for (const u of urls) console.log(` ${u}`);

if (dryRun) {
console.log("\nDry run: would submit the above URL set to:");
for (const e of INDEX_NOW_ENDPOINTS) console.log(` POST ${e}`);
return { ok: true, code: 0, keyChecks, urls, dryRun: true };
}

const outcomes = await submitUrlList(urls, { fetchImpl });
console.log("\nSubmission results:");
let allOk = true;
for (const o of outcomes) {
allOk = allOk && o.ok;
console.log(` ${o.ok ? "ACCEPTED" : "REJECTED"} ${o.endpoint} (HTTP ${o.status}${o.error ? `, ${o.error}` : ""})`);
}
return { ok: allOk, code: allOk ? 0 : 3, keyChecks, urls, outcomes };
}

if (import.meta.url === `file://${process.argv[1]}`) {
const dryRun = process.argv.includes("--dry-run");
try {
const result = await main({ dryRun });
process.exitCode = result.code;
} catch (err) {
console.error(`submit-indexnow failed: ${err.message}`);
process.exitCode = 1;
}
}
Loading
Loading