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
42 changes: 42 additions & 0 deletions .github/workflows/monitor-llms-md-endpoints.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Monitor LLM-friendly Docs Endpoints

on:
schedule:
# Run every 5 minutes
- cron: '*/5 * * * *'
workflow_dispatch: # Allow manual triggering

jobs:
monitor:
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: |
npm install -g tsx
npm install node-fetch

- name: Run monitoring script
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_DOCS_INCIDENTS }}
INCIDENT_IO_API_KEY: ${{ secrets.INCIDENT_IO_API_KEY }}
run: |
tsx scripts/monitor/check-llms-md-endpoints.ts

- name: Report status
if: always()
run: |
if [ $? -eq 0 ]; then
echo "✅ All endpoints healthy"
else
echo "❌ Some endpoints failed - alerts sent"
fi
Comment on lines +35 to +42
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- name: Report status
if: always()
run: |
if [ $? -eq 0 ]; then
echo "✅ All endpoints healthy"
else
echo "❌ Some endpoints failed - alerts sent"
fi

The status check using $? will always return 0 because it checks the exit code of the if command, not the monitoring script.

View Details

Analysis

Incorrect exit code check in monitoring workflow always shows success

What fails: The "Report status" step in .github/workflows/monitor-llms-md-endpoints.yml uses $? to check the monitoring script's exit code, but $? refers to the if command (always 0), not the previous step's tsx script

How to reproduce:

# Simulate the workflow structure:
# Step 1: tsx script fails
false
# Step 2 (separate shell): Report status 
bash -c 'if [ $? -eq 0 ]; then echo "✅ All endpoints healthy"; else echo "❌ Failed"; fi'

Result: Always prints "✅ All endpoints healthy" even when monitoring script fails

Expected: Should show failure status when monitoring script exits with code 1

Fix: Removed the misleading "Report status" step since the job's overall status already correctly indicates success/failure and the monitoring script logs appropriate messages

3 changes: 2 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,8 @@
"packages/workers/proxy/src/websocket.ts",
"packages/generator-cli/src/**/*.ts",
"packages/commons/visual-editor-server/src/mongodb-client.ts",
"servers/fdr-lambda/src/index.ts"
"servers/fdr-lambda/src/index.ts",
"scripts/monitor/**/*.ts"
],
"linter": {
"rules": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { NextRequest } from "next/server";
import { describe, expect, it, vi } from "vitest";

describe("markdown route slug handling", () => {
const createMockRequest = (pathname: string, searchParams: Record<string, string> = {}) => {
const url = new URL(`https://example.com${pathname}`);
Object.entries(searchParams).forEach(([key, value]) => {
url.searchParams.set(key, value);
});
return new NextRequest(url);
};

it("should prefer slug from search params over pathname", () => {
const request = createMockRequest("/api/fern-docs/markdown", { slug: "docs/quickstart" });

const slugParam = request.nextUrl.searchParams.get("slug");
const slug = slugParam ?? request.nextUrl.pathname.replace(/\.(md|mdx)$/, "");
Comment on lines +14 to +17
Copy link
Contributor

Choose a reason for hiding this comment

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

The test is validating behavior that doesn't exist in the actual route implementation - the route doesn't use search parameters for slug extraction.

View Details
📝 Patch Details
diff --git a/packages/fern-docs/bundle/src/app/[host]/[domain]/[lang]/fern-docs/markdown/route.ts b/packages/fern-docs/bundle/src/app/[host]/[domain]/[lang]/fern-docs/markdown/route.ts
index 35eb56b3c..d8092dda6 100644
--- a/packages/fern-docs/bundle/src/app/[host]/[domain]/[lang]/fern-docs/markdown/route.ts
+++ b/packages/fern-docs/bundle/src/app/[host]/[domain]/[lang]/fern-docs/markdown/route.ts
@@ -27,7 +27,8 @@ export async function GET(req: NextRequest, props: { params: Promise<BaseParams>
     const fernToken = (await cookies()).get(COOKIE_FERN_TOKEN)?.value;
 
     const path = req.nextUrl.pathname;
-    const slug = path.replace(MARKDOWN_PATTERN, "");
+    const slugParam = req.nextUrl.searchParams.get("slug");
+    const slug = slugParam ?? path.replace(MARKDOWN_PATTERN, "");
     const cleanSlug = removeLeadingSlash(slug);
 
     const loader = await createCachedDocsLoader(host, domain, fernToken);

Analysis

Route ignores slug parameter from middleware, causing incorrect markdown resolution

What fails: The markdown route handler in route.ts line 30 ignores the slug search parameter passed by middleware and incorrectly extracts slug from the rewritten pathname

How to reproduce:

  1. Request /docs/quickstart.md
  2. Middleware correctly extracts slug docs/quickstart and rewrites to /api/fern-docs/markdown?slug=docs/quickstart
  3. Route handler calls path.replace(MARKDOWN_PATTERN, "") on /api/fern-docs/markdown

Result: Route resolves slug as api/fern-docs/markdown instead of docs/quickstart, causing incorrect page lookup

Expected: Route should check req.nextUrl.searchParams.get("slug") first, then fall back to pathname extraction, matching the test expectations in route.test.ts


expect(slug).toBe("docs/quickstart");
});

it("should fallback to pathname parsing when slug param is not present", () => {
const request = createMockRequest("/docs/quickstart.md");

const slugParam = request.nextUrl.searchParams.get("slug");
const slug = slugParam ?? request.nextUrl.pathname.replace(/\.(md|mdx)$/, "");

expect(slug).toBe("/docs/quickstart");
});

it("should handle .mdx extension in pathname fallback", () => {
const request = createMockRequest("/docs/quickstart.mdx");

const slugParam = request.nextUrl.searchParams.get("slug");
const slug = slugParam ?? request.nextUrl.pathname.replace(/\.(md|mdx)$/, "");

expect(slug).toBe("/docs/quickstart");
});

it("should handle nested paths in slug param", () => {
const request = createMockRequest("/api/fern-docs/markdown", {
slug: "learn/sdks/overview/quickstart"
});

const slugParam = request.nextUrl.searchParams.get("slug");
const slug = slugParam ?? request.nextUrl.pathname.replace(/\.(md|mdx)$/, "");

expect(slug).toBe("learn/sdks/overview/quickstart");
});

it("should handle empty slug param", () => {
const request = createMockRequest("/api/fern-docs/markdown", { slug: "" });

const slugParam = request.nextUrl.searchParams.get("slug");
const slug = slugParam ?? request.nextUrl.pathname.replace(/\.(md|mdx)$/, "");

expect(slug).toBe("");
});
});
Loading