diff --git a/packages/hub/src/lib/collection-info.spec.ts b/packages/hub/src/lib/collection-info.spec.ts new file mode 100644 index 000000000..e152f31a2 --- /dev/null +++ b/packages/hub/src/lib/collection-info.spec.ts @@ -0,0 +1,190 @@ +import { describe, expect, it } from "vitest"; +import { TEST_HUB_URL } from "../test/consts"; +import { collectionInfo } from "./collection-info"; + +describe("collectionInfo", () => { + it("should return the collection info", async () => { + const collection = await collectionInfo({ + collectionSlug: "quanghuynt14/test-collection-6866ff686ca2d2e0a1931507", + hubUrl: TEST_HUB_URL, + }); + + if (typeof collection.lastUpdated === "string") { + collection.lastUpdated = "2025-07-03T22:18:56.239Z"; + } + + if (collection.items && Array.isArray(collection.items)) { + collection.items.map((item) => { + if ("lastModified" in item && typeof item.lastModified === "string") { + item.lastModified = "2025-07-01T00:36:29.000Z"; + } + if ("lastUpdated" in item && typeof item.lastUpdated === "string") { + item.lastUpdated = "2025-07-01T00:41:27.525Z"; + } + }); + } + + const items = collection.items; + collection.items = []; + + // Check all properties of the collection except items + expect(collection).deep.equal({ + slug: "quanghuynt14/test-collection-6866ff686ca2d2e0a1931507", + title: "Test Collection", + description: "This collection is only for test", + gating: false, + lastUpdated: "2025-07-03T22:18:56.239Z", + owner: { + _id: "6866ff3936a7677f427f99e3", + avatarUrl: "/avatars/b51088e22fb7194888551365b1bafada.svg", + fullname: "Quang-Huy Tran", + name: "quanghuynt14", + type: "user", + isPro: false, + isHf: false, + isHfAdmin: false, + isMod: false, + }, + items: [], + theme: "purple", + position: 1, + private: false, + shareUrl: "https://hub-ci.huggingface.co/collections/quanghuynt14/test-collection-6866ff686ca2d2e0a1931507", + upvotes: 0, + isUpvotedByUser: false, + }); + + // Check for item type model + expect(items[0]).deep.equal({ + _id: "686700086ca2d2e0a193150b", + position: 0, + type: "model", + author: "quanghuynt14", + authorData: { + _id: "6866ff3936a7677f427f99e3", + avatarUrl: "/avatars/b51088e22fb7194888551365b1bafada.svg", + fullname: "Quang-Huy Tran", + name: "quanghuynt14", + type: "user", + isPro: false, + isHf: false, + isHfAdmin: false, + isMod: false, + }, + downloads: 0, + gated: false, + id: "quanghuynt14/TestModel", + availableInferenceProviders: [], + lastModified: "2025-07-01T00:36:29.000Z", + likes: 0, + private: false, + repoType: "model", + isLikedByUser: false, + }); + + // Check for item type dataset + expect(items[1]).deep.equal({ + _id: "686701cd86ea6972ba6c9da5", + position: 1, + type: "dataset", + author: "quanghuynt14", + downloads: 0, + gated: false, + id: "quanghuynt14/TestDataset", + lastModified: "2025-07-01T00:36:29.000Z", + private: false, + repoType: "dataset", + likes: 0, + isLikedByUser: false, + }); + + // Check for item type space + expect(items[2]).deep.equal({ + _id: "6867000f6ca2d2e0a193150e", + position: 2, + type: "space", + author: "quanghuynt14", + authorData: { + _id: "6866ff3936a7677f427f99e3", + avatarUrl: "/avatars/b51088e22fb7194888551365b1bafada.svg", + fullname: "Quang-Huy Tran", + name: "quanghuynt14", + type: "user", + isPro: false, + isHf: false, + isHfAdmin: false, + isMod: false, + }, + colorFrom: "pink", + colorTo: "indigo", + createdAt: "2025-07-03T22:10:39.000Z", + emoji: "🏆", + id: "quanghuynt14/TestSpace", + lastModified: "2025-07-01T00:36:29.000Z", + likes: 0, + pinned: false, + private: false, + sdk: "docker", + repoType: "space", + runtime: { + stage: "BUILDING", + hardware: { + current: null, + requested: "cpu-basic", + }, + storage: null, + gcTimeout: 172800, + replicas: { + current: 0, + requested: 1, + }, + }, + shortDescription: "This space is only for test", + title: "TestSpace", + isLikedByUser: false, + trendingScore: 0, + tags: ["docker", "region:us"], + }); + + // Check for item type collection + expect(items[3]).deep.equal({ + _id: "68670014f25517a0a7eaf505", + position: 3, + type: "collection", + id: "6866ff686ca2d2e0a1931507", + slug: "quanghuynt14/test-collection-6866ff686ca2d2e0a1931507", + title: "Test Collection", + description: "This collection is only for test", + lastUpdated: "2025-07-01T00:41:27.525Z", + numberItems: 5, + owner: { + _id: "6866ff3936a7677f427f99e3", + avatarUrl: "/avatars/b51088e22fb7194888551365b1bafada.svg", + fullname: "Quang-Huy Tran", + name: "quanghuynt14", + type: "user", + isPro: false, + isHf: false, + isHfAdmin: false, + isMod: false, + }, + theme: "purple", + shareUrl: "https://hub-ci.huggingface.co/collections/quanghuynt14/test-collection-6866ff686ca2d2e0a1931507", + upvotes: 0, + isUpvotedByUser: false, + }); + + // Check for item type paper + expect(items[4]).deep.equal({ + _id: "6867002186ea6972ba6c9b54", + position: 4, + type: "paper", + id: "1910.09700", + title: "Quantifying the Carbon Emissions of Machine Learning", + publishedAt: "2019-10-21T23:57:32.000Z", + thumbnailUrl: "https://cdn-thumbnails.huggingface.co/social-thumbnails/papers/1910.09700.png", + upvotes: 0, + isUpvotedByUser: false, + }); + }); +}); diff --git a/packages/hub/src/lib/collection-info.ts b/packages/hub/src/lib/collection-info.ts new file mode 100644 index 000000000..398d32ff3 --- /dev/null +++ b/packages/hub/src/lib/collection-info.ts @@ -0,0 +1,34 @@ +import { HUB_URL } from "../consts"; +import { createApiError } from "../error"; +import type { ApiCollectionInfo } from "../types/api/api-collection"; +import type { CredentialsParams } from "../types/public"; +import { checkCredentials } from "../utils/checkCredentials"; + +export async function collectionInfo( + params: { + /** + * The slug of the collection. + */ + collectionSlug: string; + hubUrl?: string; + /** + * Custom fetch function to use instead of the default one, for example to use a proxy or edit headers. + */ + fetch?: typeof fetch; + } & Partial +): Promise { + const accessToken = checkCredentials(params); + + const res = await (params.fetch ?? fetch)(`${params.hubUrl ?? HUB_URL}/api/collections/${params.collectionSlug}`, { + headers: { + "Content-Type": "application/json", + ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : undefined), + }, + }); + + if (!res.ok) { + throw await createApiError(res); + } + + return await res.json(); +} diff --git a/packages/hub/src/lib/list-collections.ts b/packages/hub/src/lib/list-collections.ts index e66780341..d35c02bad 100644 --- a/packages/hub/src/lib/list-collections.ts +++ b/packages/hub/src/lib/list-collections.ts @@ -5,6 +5,10 @@ import { checkCredentials } from "../utils/checkCredentials"; import { parseLinkHeader } from "../utils/parseLinkHeader"; import type { ApiCollectionInfo } from "../types/api/api-collection"; +/* + * When listing collections, the item list per collection is truncated to 4 items maximum. + * To retrieve all items from a collection, you need to make an additional call using its collection slug. + */ export async function* listCollections( params?: { search?: { diff --git a/packages/hub/src/types/api/api-collection.ts b/packages/hub/src/types/api/api-collection.ts index 740bf6b8c..b1f63ebf5 100644 --- a/packages/hub/src/types/api/api-collection.ts +++ b/packages/hub/src/types/api/api-collection.ts @@ -21,10 +21,6 @@ export interface ApiCollectionInfo { } ); owner: ApiAuthor; - /* - * The items list per collection is truncated to 4 items maximum. - * To retrieve all items from a collection, you need to make an additional call using its collection slug. - */ items: ApiCollectionItem[]; theme: "orange" | "blue" | "green" | "purple" | "pink" | "indigo"; private: boolean;