From f632f5d7982bd08b4a47d50426732bc93d28033f Mon Sep 17 00:00:00 2001 From: shadcn Date: Mon, 20 Apr 2026 11:55:06 +0400 Subject: [PATCH] feat: rename header --- packages/shadcn/src/registry/fetcher.test.ts | 64 +++++++++++++++++++- packages/shadcn/src/registry/fetcher.ts | 16 +++-- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/packages/shadcn/src/registry/fetcher.test.ts b/packages/shadcn/src/registry/fetcher.test.ts index 608672ed97..7d30448d3e 100644 --- a/packages/shadcn/src/registry/fetcher.test.ts +++ b/packages/shadcn/src/registry/fetcher.test.ts @@ -10,6 +10,7 @@ import { http, HttpResponse } from "msw" import { setupServer } from "msw/node" import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest" +import { clearRegistryContext, setRegistryHeaders } from "./context" import { clearRegistryCache, fetchRegistry } from "./fetcher" const server = setupServer( @@ -223,7 +224,68 @@ describe("fetchRegistry", () => { expect(acceptHeader).toBe( "application/vnd.shadcn.v1+json, application/json;q=0.9" ) - expect(userAgentHeader).toBe("shadcn-ui") + expect(userAgentHeader).toBe("shadcn") + }) + + it("should allow per-registry headers to override the default Accept and User-Agent", async () => { + let acceptHeader: string | null = null + let userAgentHeader: string | null = null + server.use( + http.get(`${REGISTRY_URL}/override-test.json`, ({ request }) => { + acceptHeader = request.headers.get("accept") + userAgentHeader = request.headers.get("user-agent") + return HttpResponse.json({ + name: "override-test", + type: "registry:ui", + }) + }) + ) + + setRegistryHeaders({ + [`${REGISTRY_URL}/override-test.json`]: { + Accept: "application/custom+json", + "User-Agent": "custom-client/1.0", + }, + }) + + await fetchRegistry(["override-test.json"], { useCache: false }) + + expect(acceptHeader).toBe("application/custom+json") + expect(userAgentHeader).toBe("custom-client/1.0") + + clearRegistryContext() + }) + + it("should allow lowercase per-registry headers to override the default Accept and User-Agent", async () => { + let acceptHeader: string | null = null + let userAgentHeader: string | null = null + server.use( + http.get( + `${REGISTRY_URL}/lowercase-override-test.json`, + ({ request }) => { + acceptHeader = request.headers.get("accept") + userAgentHeader = request.headers.get("user-agent") + return HttpResponse.json({ + name: "lowercase-override-test", + type: "registry:ui", + }) + } + ) + ) + + setRegistryHeaders({ + [`${REGISTRY_URL}/lowercase-override-test.json`]: { + accept: "application/custom+json", + "user-agent": "custom-client/1.0", + }, + }) + + await fetchRegistry(["lowercase-override-test.json"], { useCache: false }) + + expect(acceptHeader).toBe("application/custom+json") + expect(userAgentHeader).toBe("custom-client/1.0") + + clearRegistryContext() }) it("should send specific Accept header for direct external URLs", async () => { diff --git a/packages/shadcn/src/registry/fetcher.ts b/packages/shadcn/src/registry/fetcher.ts index 33d34e3ac9..8bc76265df 100644 --- a/packages/shadcn/src/registry/fetcher.ts +++ b/packages/shadcn/src/registry/fetcher.ts @@ -14,7 +14,7 @@ import { } from "@/src/registry/errors" import { registryItemSchema } from "@/src/schema" import { HttpsProxyAgent } from "https-proxy-agent" -import fetch from "node-fetch" +import fetch, { Headers } from "node-fetch" import { z } from "zod" const agent = process.env.https_proxy @@ -50,14 +50,18 @@ export async function fetchRegistry( const fetchPromise = (async () => { // Get headers from context for this URL. const headers = getRegistryHeadersFromContext(url) + const requestHeaders = new Headers({ + Accept: "application/vnd.shadcn.v1+json, application/json;q=0.9", + "User-Agent": "shadcn", + }) + + for (const [key, value] of Object.entries(headers)) { + requestHeaders.set(key, value) + } const response = await fetch(url, { agent, - headers: { - Accept: "application/vnd.shadcn.v1+json, application/json;q=0.9", - "User-Agent": "shadcn-ui", - ...headers, - }, + headers: requestHeaders, }) if (!response.ok) {