diff --git a/.prettierignore b/.prettierignore
index 32acf1b2e..304c757ee 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -4,3 +4,8 @@
**/*.md
content/api-specs/
pnpm-lock.yaml
+
+# Generated by UpdateFeatureSupportFromDaikonWorkflow; the generator owns its
+# one-entity-per-line format (prettier reflowing it would make every daily bot
+# write a diff)
+content/feature-support.json
diff --git a/content/api-reference/pricing-resources/resources/feature-support-by-chain.mdx b/content/api-reference/pricing-resources/resources/feature-support-by-chain.mdx
index a761592c3..167eb6121 100644
--- a/content/api-reference/pricing-resources/resources/feature-support-by-chain.mdx
+++ b/content/api-reference/pricing-resources/resources/feature-support-by-chain.mdx
@@ -5,8 +5,43 @@ subtitle: Alchemy's current feature availability for each of its supported chain
slug: reference/feature-support-by-chain
---
-
- Check the [Chains](https://dashboard.alchemy.com/chains) page for details about product and chain support!
+See the [Chains page](https://dashboard.alchemy.com/chains) in the dashboard for Alchemy's current product and chain support.
- 
-
+{/*
+ The visible sentence above is a placeholder so this nav-listed page is
+ never blank: the stacked follow-up replaces it with the interactive
+ widget when the generated data lands.
+
+ Data freshness: the widget's data is server-rendered, not fetched by the
+ browser and not baked into this MDX. The docs-site renders
+ on the server from content/feature-support.json
+ in this repo, cached under the "feature-support" Next.js cache tag. When
+ that file changes on main — the daily Daikon bot PR merge — the
+ revalidate-content workflow calls the docs-site
+ /api/revalidate/feature-support endpoint to invalidate the tag, so the
+ statically generated page re-renders with fresh data (an hourly TTL
+ backstops a missed webhook). JSON-only daily updates therefore reach
+ readers without this MDX being edited.
+
+ The summary table below cannot drift from the JSON: the daily workflow
+ regenerates both from the same resolved data and commits them together,
+ so any change that affects the table is itself an MDX edit and
+ revalidates through the markdown path. A JSON-only commit means the
+ table's coarser product-by-chain view genuinely did not change.
+
+ The summary block is intentionally wrapped in a hidden div: it exists for
+ search engines, LLMs, and markdown consumers, while the interactive
+ widget is the human-facing view. The h4 keeps it
+ out of the page table of contents.
+*/}
+
+
+
+#### Product support by chain
+
+Every Alchemy product and the chains it supports, at a glance. A product counts as supported on a chain when at least one of its networks supports at least one of the product's methods.
+
+{/* feature-support:auto */}
+{/* feature-support:auto end */}
+
+
diff --git a/src/content-indexer/utils/__tests__/truncate-record.test.ts b/src/content-indexer/utils/__tests__/truncate-record.test.ts
index 4489f2feb..596c6564c 100644
--- a/src/content-indexer/utils/__tests__/truncate-record.test.ts
+++ b/src/content-indexer/utils/__tests__/truncate-record.test.ts
@@ -21,6 +21,54 @@ describe("truncateRecord", () => {
expect(result.content).toBe("Short content");
});
+ test("should strip non-rendered MDX comments from indexed content", () => {
+ const record: AlgoliaRecord = {
+ objectID: "abc123",
+ indexerType: "docs",
+ path: "reference/feature-support-by-chain",
+ pageType: "Guide",
+ title: "Feature Support By Chain",
+ content: [
+ "Visible intro.",
+ "{/*\n Data freshness: operational note that never renders.\n*/}",
+ "{/* feature-support:auto */}",
+ "| Product | Chains |",
+ "{/* feature-support:auto end */}",
+ "Visible outro.",
+ ].join("\n\n"),
+ breadcrumbs: ["Reference"],
+ };
+
+ const result = truncateRecord(record);
+ expect(result.content).toContain("Visible intro.");
+ expect(result.content).toContain("Visible outro.");
+ expect(result.content).not.toContain("Data freshness");
+ expect(result.content).not.toContain("feature-support:auto");
+ });
+
+ test("should preserve JSX comments inside fenced code examples", () => {
+ const record: AlgoliaRecord = {
+ objectID: "abc123",
+ indexerType: "docs",
+ path: "wallets/react/mfa/email-otp",
+ pageType: "Guide",
+ title: "Email OTP",
+ content: [
+ "{/* operational note that never renders */}",
+ "```tsx\nreturn (\n \n {/* OTP input component */}\n \n
\n);\n```",
+ "After the example.",
+ ].join("\n\n"),
+ breadcrumbs: ["Wallets"],
+ };
+
+ const result = truncateRecord(record);
+ // The comment in the rendered code block is real page content...
+ expect(result.content).toContain("OTP input component");
+ // ...while the non-rendered MDX comment outside it is stripped.
+ expect(result.content).not.toContain("operational note");
+ expect(result.content).toContain("After the example.");
+ });
+
test("should truncate content for oversized record", () => {
// Create a record with content exceeding 100KB
const largeContent = "x".repeat(150_000);
diff --git a/src/content-indexer/utils/truncate-record.ts b/src/content-indexer/utils/truncate-record.ts
index 3fa7d8a75..348adc376 100644
--- a/src/content-indexer/utils/truncate-record.ts
+++ b/src/content-indexer/utils/truncate-record.ts
@@ -6,13 +6,33 @@ const MAX_RECORD_BYTES = 100_000; // Algolia imposes a 100KB limit on each recor
const BUFFER_BYTES = 1_000;
const MAX_ITERATIONS = 5;
+/**
+ * MDX expression comments never render on the page, but remove-markdown
+ * leaves them intact, so implementation notes and generator markers
+ * (cu:auto, feature-support:auto, data-freshness notes) would otherwise be
+ * uploaded into public search records and snippets.
+ *
+ * Fenced code blocks are skipped: a JSX comment inside a rendered code
+ * example (e.g. `{/* OTP input component *\/}` in the wallets MFA guides)
+ * is real page content that must stay searchable.
+ */
+const stripMdxComments = (content: string): string =>
+ content
+ .split(/(```[\s\S]*?```)/)
+ .map((segment, index) =>
+ // Odd indexes are the captured fenced code blocks.
+ index % 2 === 1 ? segment : segment.replace(/\{\/\*[\s\S]*?\*\/\}/g, ""),
+ )
+ .join("");
+
/**
* Truncate record content to ensure entire JSON payload fits within Algolia limit.
* Also strips markdown formatting from content for better search experience.
*/
export const truncateRecord = (rawRecord: AlgoliaRecord): AlgoliaRecord => {
- // Strip markdown formatting to get clean, searchable text
- const cleanedContent = removeMd(rawRecord.content);
+ // Strip non-rendered MDX comments and markdown formatting to get clean,
+ // searchable text
+ const cleanedContent = removeMd(stripMdxComments(rawRecord.content));
const record = { ...rawRecord, content: cleanedContent };
const fullRecordJson = JSON.stringify(record);