diff --git a/apps/v4/content/docs/components/base/data-table.mdx b/apps/v4/content/docs/components/base/data-table.mdx index 4cbc976d34..23295d86e5 100644 --- a/apps/v4/content/docs/components/base/data-table.mdx +++ b/apps/v4/content/docs/components/base/data-table.mdx @@ -472,7 +472,7 @@ Let's make the email column sortable. ### Update `` -```tsx showLineNumbers title="app/payments/data-table.tsx" showLineNumbers {3,6,10,18,25-28} +```tsx showLineNumbers title="app/payments/data-table.tsx" showLineNumbers {3,6,10,18,25-29} "use client" import * as React from "react" diff --git a/packages/shadcn/src/registry/errors.ts b/packages/shadcn/src/registry/errors.ts index fff5d4d309..42dd47fdbc 100644 --- a/packages/shadcn/src/registry/errors.ts +++ b/packages/shadcn/src/registry/errors.ts @@ -5,6 +5,7 @@ export const RegistryErrorCode = { // Network errors NETWORK_ERROR: "NETWORK_ERROR", NOT_FOUND: "NOT_FOUND", + GONE: "GONE", UNAUTHORIZED: "UNAUTHORIZED", FORBIDDEN: "FORBIDDEN", FETCH_ERROR: "FETCH_ERROR", @@ -90,6 +91,22 @@ export class RegistryNotFoundError extends RegistryError { } } +export class RegistryGoneError extends RegistryError { + constructor(public readonly url: string, cause?: unknown) { + const message = `The item at ${url} is no longer available. It may have been removed or expired.` + + super(message, { + code: RegistryErrorCode.GONE, + statusCode: 410, + cause, + context: { url }, + suggestion: + "This resource was previously available but has been permanently removed. Check if a newer version exists or contact the registry maintainer.", + }) + this.name = "RegistryGoneError" + } +} + export class RegistryUnauthorizedError extends RegistryError { constructor(public readonly url: string, cause?: unknown) { const message = `You are not authorized to access the item at ${url}. If this is a remote registry, you may need to authenticate.` diff --git a/packages/shadcn/src/registry/fetcher.test.ts b/packages/shadcn/src/registry/fetcher.test.ts index 7061e53e1a..0348db6c0b 100644 --- a/packages/shadcn/src/registry/fetcher.test.ts +++ b/packages/shadcn/src/registry/fetcher.test.ts @@ -2,6 +2,7 @@ import { REGISTRY_URL } from "@/src/registry/constants" import { RegistryFetchError, RegistryForbiddenError, + RegistryGoneError, RegistryNotFoundError, RegistryUnauthorizedError, } from "@/src/registry/errors" @@ -30,6 +31,9 @@ const server = setupServer( http.get(`${REGISTRY_URL}/forbidden.json`, () => { return new HttpResponse(null, { status: 403 }) }), + http.get(`${REGISTRY_URL}/gone.json`, () => { + return new HttpResponse(null, { status: 410 }) + }), http.get("https://external.com/component.json", () => { return HttpResponse.json({ name: "external", @@ -123,6 +127,10 @@ describe("fetchRegistry", () => { ) }) + it("should handle 410 errors", async () => { + await expect(fetchRegistry(["gone.json"])).rejects.toThrow(RegistryGoneError) + }) + it("should handle network errors", async () => { await expect(fetchRegistry(["error.json"])).rejects.toThrow() }) diff --git a/packages/shadcn/src/registry/fetcher.ts b/packages/shadcn/src/registry/fetcher.ts index 0e87aa50c7..e5df988310 100644 --- a/packages/shadcn/src/registry/fetcher.ts +++ b/packages/shadcn/src/registry/fetcher.ts @@ -6,6 +6,7 @@ import { getRegistryHeadersFromContext } from "@/src/registry/context" import { RegistryFetchError, RegistryForbiddenError, + RegistryGoneError, RegistryLocalFileError, RegistryNotFoundError, RegistryParseError, @@ -93,6 +94,10 @@ export async function fetchRegistry( throw new RegistryNotFoundError(url, messageFromServer) } + if (response.status === 410) { + throw new RegistryGoneError(url, messageFromServer) + } + if (response.status === 403) { throw new RegistryForbiddenError(url, messageFromServer) }