Skip to content
Open
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
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,43 @@ subtitle: Alchemy's current feature availability for each of its supported chain
slug: reference/feature-support-by-chain
---

<Info>
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.

![](https://alchemyapi-res.cloudinary.com/image/upload/v1764179964/docs/api-reference/alchemy-transact/transaction-simulation/523fb8a9a9d899921ee1046d0ff1b389967a9976d1c6112ebbbe071ddd1ef374-image.png)
</Info>
{/*
The visible sentence above is a placeholder so this nav-listed page is
never blank: the stacked follow-up replaces it with the interactive
<FeatureSupportMatrix /> 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
<FeatureSupportMatrix /> on the server from content/feature-support.json
Comment thread
SahilAujla marked this conversation as resolved.
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
<FeatureSupportMatrix /> widget is the human-facing view. The h4 keeps it
out of the page table of contents.
*/}

<div hidden>
Comment thread
SahilAujla marked this conversation as resolved.

#### 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 */}

</div>
48 changes: 48 additions & 0 deletions src/content-indexer/utils/__tests__/truncate-record.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <div>\n {/* OTP input component */}\n <OtpInput />\n </div>\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);
Expand Down
24 changes: 22 additions & 2 deletions src/content-indexer/utils/truncate-record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading