diff --git a/.changeset/v4-content-parity.md b/.changeset/v4-content-parity.md
new file mode 100644
index 0000000..3fee64b
--- /dev/null
+++ b/.changeset/v4-content-parity.md
@@ -0,0 +1,5 @@
+---
+"@quranjs/api": minor
+---
+
+Add typed v4 hadith reference SDK methods and expose the full chapter info response with resource selection support.
diff --git a/packages/api/mocks/handlers.ts b/packages/api/mocks/handlers.ts
index 2e98e76..f29b051 100644
--- a/packages/api/mocks/handlers.ts
+++ b/packages/api/mocks/handlers.ts
@@ -662,18 +662,62 @@ export const handlers = [
http.get(
"https://apis.quran.foundation/content/api/v4/chapters/:chapter_id/info",
- () => {
+ ({ request }) => {
+ const url = new URL(request.url);
+ const includeResources =
+ url.searchParams.get("include_resources") === "true";
+ const resourceId = url.searchParams.get("resource_id");
+ const chapterInfoResourceId =
+ resourceId === "en-tafsir-ibn-ashur"
+ ? 167
+ : resourceId
+ ? Number(resourceId) || 58
+ : 58;
+ const chapterInfo =
+ resourceId === "missing-resource"
+ ? null
+ : {
+ id: 1,
+ chapter_id: 1,
+ language_name: "english",
+ resource_id: chapterInfoResourceId,
+ short_text:
+ "This Surah is named Al-Fatihah because of its subject-matter. Fatihah is that which opens a subject or a book or any other thing. In other words, Al-Fatihah is a sort of preface.",
+ source:
+ "Sayyid Abul Ala Maududi - Tafhim al-Qur'an - The Meaning of the Quran",
+ text: "
Name
\r\nThis Surah is named Al-Fatihah because of its subject-matter. Fatihah is that which opens a subject or a book or any other thing. In other words, Al-Fatihah is a sort of preface.
\r\nPeriod of Revelation
...",
+ };
+
return HttpResponse.json({
- chapterInfo: {
- id: 1,
- chapter_id: 1,
- language_name: "english",
- short_text:
- "This Surah is named Al-Fatihah because of its subject-matter. Fatihah is that which opens a subject or a book or any other thing. In other words, Al-Fatihah is a sort of preface.",
- source:
- "Sayyid Abul Ala Maududi - Tafhim al-Qur'an - The Meaning of the Quran",
- text: "Name
\r\nThis Surah is named Al-Fatihah because of its subject-matter. Fatihah is that which opens a subject or a book or any other thing. In other words, Al-Fatihah is a sort of preface.
\r\nPeriod of Revelation
...",
- },
+ chapter_info: chapterInfo,
+ ...(includeResources
+ ? {
+ resources: [
+ {
+ id: 58,
+ name: "Chapter Info",
+ author_name: "Sayyid Abul Ala Maududi",
+ slug: "",
+ language_name: "english",
+ translated_name: {
+ name: "Chapter Info",
+ language_name: "english",
+ },
+ },
+ {
+ id: 167,
+ name: "Tafsir al-Tahrir wa'l-Tanwir",
+ author_name: "Ibn Ashur",
+ slug: "en-tafsir-ibn-ashur",
+ language_name: "english",
+ translated_name: {
+ name: "Tafsir al-Tahrir wa'l-Tanwir",
+ language_name: "english",
+ },
+ },
+ ],
+ }
+ : {}),
});
},
),
@@ -747,6 +791,91 @@ export const handlers = [
},
),
+ http.get(
+ "https://apis.quran.foundation/content/api/v4/hadith_references/by_ayah/:ayah_key",
+ ({ params }) => {
+ return HttpResponse.json({
+ verse_key: params.ayah_key,
+ verse_number: 12,
+ chapter_number: 12,
+ language: "en",
+ direction: "ltr",
+ hadith_references: [
+ {
+ id: 10,
+ collection: "bukhari",
+ hadith_number: "1",
+ our_hadith_number: 1,
+ arabic_urn: 111,
+ english_urn: 211,
+ surah_number: 12,
+ ayah_start_number: 11,
+ ayah_end_number: 12,
+ },
+ {
+ id: 11,
+ collection: "muslim",
+ hadith_number: "3",
+ our_hadith_number: 1,
+ arabic_urn: 113,
+ english_urn: 213,
+ surah_number: 12,
+ ayah_start_number: 12,
+ ayah_end_number: 14,
+ },
+ ],
+ });
+ },
+ ),
+
+ http.get(
+ "https://apis.quran.foundation/content/api/v4/hadith_references/by_ayah/:ayah_key/hadiths",
+ () => {
+ return HttpResponse.json({
+ hadiths: [
+ {
+ urn: 201,
+ collection: "bukhari",
+ bookNumber: "1",
+ chapterId: "1",
+ hadithNumber: "1",
+ name: "Sahih al-Bukhari",
+ hadith: [
+ {
+ lang: "en",
+ chapterNumber: "1",
+ chapterTitle: "Revelation",
+ body: "Narrated Umar bin Al-Khattab:...",
+ urn: 31,
+ grades: [
+ {
+ graded_by: "Ahmad Muhammad Shakir",
+ grade: "Sahih",
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ page: 1,
+ limit: 4,
+ has_more: true,
+ language: "en",
+ direction: "ltr",
+ });
+ },
+ ),
+
+ http.get(
+ "https://apis.quran.foundation/content/api/v4/hadith_references/count_within_range",
+ () => {
+ return HttpResponse.json({
+ "12:12": 2,
+ "12:13": 2,
+ });
+ },
+ ),
+
http.get(
"https://apis.quran.foundation/content/api/v4/resources/verse_media",
() => {
diff --git a/packages/api/src/runtime/create-client.ts b/packages/api/src/runtime/create-client.ts
index 53e558f..21d583c 100644
--- a/packages/api/src/runtime/create-client.ts
+++ b/packages/api/src/runtime/create-client.ts
@@ -1,3 +1,4 @@
+import type { AuthService } from "@/generated/public-contracts";
import type {
ApiParams,
ChapterId,
@@ -14,13 +15,13 @@ import type {
VerseKey,
} from "@/types";
import { operationCatalog } from "@/generated/contracts";
-import type { AuthService } from "@/generated/public-contracts";
import { toUserSession } from "@/lib/http-utils";
import { createGeneratedGroups, createRawClient } from "@/lib/runtime-utils";
import { replacePathParams } from "@/lib/url";
import { QuranAudio } from "@/sdk/audio";
import { QuranChapters } from "@/sdk/chapters";
import { QuranFetcher } from "@/sdk/fetcher";
+import { QuranHadithReferences } from "@/sdk/hadith-references";
import { QuranJuzs } from "@/sdk/juzs";
import { QuranResources } from "@/sdk/resources";
import { QuranSearch } from "@/sdk/search";
@@ -348,6 +349,7 @@ const createContentFacade = (
verses: QuranVerses,
juzs: QuranJuzs,
audio: QuranAudio,
+ hadithReferences: QuranHadithReferences,
resources: QuranResources,
raw: Record,
) => {
@@ -374,8 +376,18 @@ const createContentFacade = (
get: (id: ChapterId, query?: ApiParams) => chapters.findById(id, query),
getInfo: (id: ChapterId, query?: ApiParams) =>
chapters.findInfoById(id, query),
+ getInfoResponse: (id: ChapterId, query?: ApiParams) =>
+ chapters.findInfoResponseById(id, query),
list: (query?: ApiParams) => chapters.findAll(query),
},
+ hadithReferences: {
+ byAyah: (key: VerseKey, query?: ApiParams) =>
+ hadithReferences.findByAyah(key, query),
+ countWithinRange: (from: VerseKey, to: VerseKey, query?: ApiParams) =>
+ hadithReferences.countWithinRange(from, to, query),
+ hadithsByAyah: (key: VerseKey, query?: ApiParams) =>
+ hadithReferences.findHadithsByAyah(key, query),
+ },
juzs: {
list: () => juzs.findAll(),
},
@@ -439,6 +451,7 @@ export const createRuntimeClient = (
const verses = new QuranVerses(fetcher);
const juzs = new QuranJuzs(fetcher);
const audio = new QuranAudio(fetcher);
+ const hadithReferences = new QuranHadithReferences(fetcher);
const resources = new QuranResources(fetcher);
const searchClient = new QuranSearch(fetcher);
@@ -465,6 +478,7 @@ export const createRuntimeClient = (
verses,
juzs,
audio,
+ hadithReferences,
resources,
raw.content.v4,
);
@@ -501,6 +515,7 @@ export const createRuntimeClient = (
v4: contentV4,
},
getUserSession: () => fetcher.getUserSession(),
+ hadithReferences,
juzs,
oauth2: {
...oauth2V1,
diff --git a/packages/api/src/runtime/create-public-client.ts b/packages/api/src/runtime/create-public-client.ts
index e281eee..3bebedd 100644
--- a/packages/api/src/runtime/create-public-client.ts
+++ b/packages/api/src/runtime/create-public-client.ts
@@ -1,3 +1,4 @@
+import type { AuthService } from "@/generated/public-contracts";
import type {
ApiParams,
HTTPMethod,
@@ -5,7 +6,6 @@ import type {
PublicClientConfig,
TokenResponse,
} from "@/types";
-import type { AuthService } from "@/generated/public-contracts";
import { publicOperationCatalog } from "@/generated/public-contracts";
import { toUserSession } from "@/lib/http-utils";
import { createGeneratedGroups, createRawClient } from "@/lib/runtime-utils";
@@ -357,6 +357,7 @@ export const createPublicRuntimeClient = (config: PublicClientConfig) => {
clearCachedTokens: () => fetcher.clearCachedTokens(),
content: serverOnlyGuard,
getUserSession: () => fetcher.getUserSession(),
+ hadithReferences: serverOnlyGuard,
juzs: serverOnlyGuard,
oauth2: {
...oauth2V1,
diff --git a/packages/api/src/sdk/chapters.ts b/packages/api/src/sdk/chapters.ts
index 4179fb0..5632cfd 100644
--- a/packages/api/src/sdk/chapters.ts
+++ b/packages/api/src/sdk/chapters.ts
@@ -3,11 +3,16 @@ import type {
Chapter,
ChapterId,
ChapterInfo,
+ ChapterInfoResponse,
QuranFetchClient,
} from "@/types";
import { isValidChapterId } from "@/utils";
type GetChapterOptions = BaseApiParams;
+type GetChapterInfoOptions = BaseApiParams & {
+ resourceId?: string | number;
+ includeResources?: boolean;
+};
/**
* Chapters API methods
@@ -55,21 +60,38 @@ export class QuranChapters {
* Get chapter info by id.
* @description https://api-docs.quran.com/docs/quran.com_versioned/4.0.0/get-chapter-info
* @param {ChapterId} id chapter id, minimum 1, maximum 114
- * @param {GetChapterOptions} options
+ * @param {GetChapterInfoOptions} options
* @example
* client.chapters.findInfoById('1')
* client.chapters.findInfoById('114')
*/
async findInfoById(
id: ChapterId,
- options?: GetChapterOptions,
- ): Promise {
- if (!isValidChapterId(id)) throw new Error("Invalid chapter id");
-
- const { chapterInfo } = await this.fetcher.fetch<{
- chapterInfo: ChapterInfo;
- }>(`/content/api/v4/chapters/${id}/info`, options);
+ options?: GetChapterInfoOptions,
+ ): Promise {
+ const { chapterInfo } = await this.findInfoResponseById(id, options);
return chapterInfo;
}
+
+ /**
+ * Get the full chapter info response, including available resources when requested.
+ * @description https://api-docs.quran.com/docs/quran.com_versioned/4.0.0/get-chapter-info
+ * @param {ChapterId} id chapter id, minimum 1, maximum 114
+ * @param {GetChapterInfoOptions} options
+ * @example
+ * client.chapters.findInfoResponseById('1', { includeResources: true })
+ * client.chapters.findInfoResponseById('1', { resourceId: 'en-tafsir-ibn-ashur' })
+ */
+ async findInfoResponseById(
+ id: ChapterId,
+ options?: GetChapterInfoOptions,
+ ): Promise {
+ if (!isValidChapterId(id)) throw new Error("Invalid chapter id");
+
+ return this.fetcher.fetch(
+ `/content/api/v4/chapters/${id}/info`,
+ options,
+ );
+ }
}
diff --git a/packages/api/src/sdk/client.ts b/packages/api/src/sdk/client.ts
index b7d96bc..5524f66 100644
--- a/packages/api/src/sdk/client.ts
+++ b/packages/api/src/sdk/client.ts
@@ -14,6 +14,7 @@ import humps from "humps";
import { QuranAudio } from "./audio";
import { QuranChapters } from "./chapters";
+import { QuranHadithReferences } from "./hadith-references";
import { QuranJuzs } from "./juzs";
import { QuranResources } from "./resources";
import { QuranSearch } from "./search";
@@ -132,6 +133,7 @@ export class QuranClient {
public readonly verses: QuranVerses;
public readonly juzs: QuranJuzs;
public readonly audio: QuranAudio;
+ public readonly hadithReferences: QuranHadithReferences;
public readonly resources: QuranResources;
public readonly search: QuranSearch;
@@ -153,6 +155,7 @@ export class QuranClient {
this.verses = new QuranVerses(this.fetcher);
this.juzs = new QuranJuzs(this.fetcher);
this.audio = new QuranAudio(this.fetcher);
+ this.hadithReferences = new QuranHadithReferences(this.fetcher);
this.resources = new QuranResources(this.fetcher);
this.search = new QuranSearch(this.fetcher);
}
diff --git a/packages/api/src/sdk/hadith-references.ts b/packages/api/src/sdk/hadith-references.ts
new file mode 100644
index 0000000..0fd5467
--- /dev/null
+++ b/packages/api/src/sdk/hadith-references.ts
@@ -0,0 +1,90 @@
+import type {
+ BaseApiParams,
+ HadithCountWithinRangeResponse,
+ HadithReferencesByAyahResponse,
+ HadithsByAyahResponse,
+ QuranFetchClient,
+ VerseKey,
+} from "@/types";
+import { isValidVerseKey } from "@/utils";
+
+type GetHadithReferenceOptions = BaseApiParams;
+
+type GetHadithsByAyahOptions = BaseApiParams & {
+ page?: number;
+ limit?: number;
+};
+
+type CountHadithsWithinRangeOptions = BaseApiParams;
+
+/**
+ * Hadith reference API methods.
+ */
+export class QuranHadithReferences {
+ constructor(private fetcher: QuranFetchClient) {}
+
+ /**
+ * Get ordered hadith reference records linked to a specific ayah.
+ * @param {VerseKey} key verse key in format "chapter:verse" (e.g., "12:12")
+ * @param {GetHadithReferenceOptions} options
+ * @example
+ * client.hadithReferences.findByAyah("12:12")
+ */
+ async findByAyah(
+ key: VerseKey,
+ options?: GetHadithReferenceOptions,
+ ): Promise {
+ if (!isValidVerseKey(key)) throw new Error("Invalid verse key");
+
+ return this.fetcher.fetch(
+ `/content/api/v4/hadith_references/by_ayah/${key}`,
+ options,
+ );
+ }
+
+ /**
+ * Get paginated hadith payloads linked to a specific ayah.
+ * @param {VerseKey} key verse key in format "chapter:verse" (e.g., "12:12")
+ * @param {GetHadithsByAyahOptions} options
+ * @example
+ * client.hadithReferences.findHadithsByAyah("12:12", { limit: 4 })
+ */
+ async findHadithsByAyah(
+ key: VerseKey,
+ options?: GetHadithsByAyahOptions,
+ ): Promise {
+ if (!isValidVerseKey(key)) throw new Error("Invalid verse key");
+
+ return this.fetcher.fetch(
+ `/content/api/v4/hadith_references/by_ayah/${key}/hadiths`,
+ options,
+ );
+ }
+
+ /**
+ * Get a verse-key to hadith-count map within an inclusive ayah range.
+ * @param {VerseKey} from start verse key in format "chapter:verse"
+ * @param {VerseKey} to end verse key in format "chapter:verse"
+ * @param {CountHadithsWithinRangeOptions} options
+ * @example
+ * client.hadithReferences.countWithinRange("12:12", "12:13")
+ */
+ async countWithinRange(
+ from: VerseKey,
+ to: VerseKey,
+ options?: CountHadithsWithinRangeOptions,
+ ): Promise {
+ if (!isValidVerseKey(from) || !isValidVerseKey(to)) {
+ throw new Error("Invalid verse key");
+ }
+
+ return this.fetcher.fetch(
+ "/content/api/v4/hadith_references/count_within_range",
+ {
+ ...options,
+ from,
+ to,
+ },
+ );
+ }
+}
diff --git a/packages/api/src/types/api/ChapterInfo.ts b/packages/api/src/types/api/ChapterInfo.ts
index 88ae9b0..30db388 100644
--- a/packages/api/src/types/api/ChapterInfo.ts
+++ b/packages/api/src/types/api/ChapterInfo.ts
@@ -1,3 +1,5 @@
+import type { ChapterInfoResource } from "./Resources";
+
export interface ChapterInfo {
id: number;
chapterId: number;
@@ -5,4 +7,10 @@ export interface ChapterInfo {
shortText: string;
source: string;
languageName?: string;
+ resourceId?: number;
+}
+
+export interface ChapterInfoResponse {
+ chapterInfo: ChapterInfo | null;
+ resources?: ChapterInfoResource[];
}
diff --git a/packages/api/src/types/api/HadithReference.ts b/packages/api/src/types/api/HadithReference.ts
new file mode 100644
index 0000000..b1aa1e5
--- /dev/null
+++ b/packages/api/src/types/api/HadithReference.ts
@@ -0,0 +1,57 @@
+import type { VerseKey } from "../common/verse-key";
+
+export interface HadithReference {
+ id: number;
+ collection: string;
+ hadithNumber: string;
+ ourHadithNumber: number;
+ arabicUrn: number;
+ englishUrn: number;
+ surahNumber: number;
+ ayahStartNumber: number;
+ ayahEndNumber: number;
+}
+
+export interface HadithReferencesByAyahResponse {
+ verseKey: VerseKey;
+ verseNumber: number;
+ chapterNumber: number;
+ language: string;
+ direction: string;
+ hadithReferences: HadithReference[];
+}
+
+export interface HadithGrade {
+ gradedBy?: string;
+ grade?: string;
+}
+
+export interface HadithContent {
+ lang: string;
+ chapterNumber: string;
+ chapterTitle: string;
+ body: string;
+ urn: number;
+ grades: HadithGrade[];
+}
+
+export interface Hadith {
+ urn: number;
+ collection: string;
+ bookNumber: string;
+ chapterId: string;
+ hadithNumber: string;
+ name: string;
+ hadith: HadithContent[];
+}
+
+export interface HadithsByAyahResponse {
+ hadiths: Hadith[];
+ page: number;
+ limit: number;
+ hasMore: boolean;
+ language: string;
+ direction: string;
+}
+
+export type HadithCountWithinRangeResponse = Record;
diff --git a/packages/api/src/types/api/index.ts b/packages/api/src/types/api/index.ts
index ced9d6d..d9283a0 100644
--- a/packages/api/src/types/api/index.ts
+++ b/packages/api/src/types/api/index.ts
@@ -4,6 +4,7 @@ export * from './AudioResponse';
export * from './Chapter';
export * from './ChapterInfo';
export * from './Footnote';
+export * from './HadithReference';
export * from './Reciter';
export * from './Segment';
export * from './Tafsir';
diff --git a/packages/api/test/chapters.test.ts b/packages/api/test/chapters.test.ts
index ac76fc6..d381ef6 100644
--- a/packages/api/test/chapters.test.ts
+++ b/packages/api/test/chapters.test.ts
@@ -29,6 +29,34 @@ describe("Chapters API", () => {
it("should return chapter info for valid chapter ID", async () => {
const response = await testClient.chapters.findInfoById("1");
expect(response).toBeDefined();
+ expect(response?.resourceId).toBe(58);
+ });
+
+ it("should return the full chapter info response with resources", async () => {
+ const response = await testClient.chapters.findInfoResponseById("1", {
+ includeResources: true,
+ resourceId: 58,
+ });
+
+ expect(response.chapterInfo?.resourceId).toBe(58);
+ expect(response.resources).toBeInstanceOf(Array);
+ expect(response.resources?.[0]?.id).toBe(58);
+ });
+
+ it("should support resource slugs when selecting chapter info", async () => {
+ const response = await testClient.chapters.findInfoResponseById("1", {
+ resourceId: "en-tafsir-ibn-ashur",
+ });
+
+ expect(response.chapterInfo?.resourceId).toBe(167);
+ });
+
+ it("should support null chapter info for missing resources", async () => {
+ const response = await testClient.chapters.findInfoResponseById("1", {
+ resourceId: "missing-resource",
+ });
+
+ expect(response.chapterInfo).toBeNull();
});
it("should throw error for invalid chapter ID", async () => {
diff --git a/packages/api/test/contracts.test.ts b/packages/api/test/contracts.test.ts
index 84efba6..ec23c67 100644
--- a/packages/api/test/contracts.test.ts
+++ b/packages/api/test/contracts.test.ts
@@ -1,9 +1,26 @@
import { describe, expect, it } from "vitest";
-import { getOperationByName, operationCatalog } from "../src/generated/contracts";
+import {
+ getOperationByName,
+ operationCatalog,
+} from "../src/generated/contracts";
const representativeOperations = [
["content", "v4", "getChapter", "get", "/chapters/{id}"],
+ [
+ "content",
+ "v4",
+ "hadithReferencesByAyah",
+ "get",
+ "/hadith_references/by_ayah/{ayah_key}",
+ ],
+ [
+ "content",
+ "v4",
+ "listSurahTafsirs",
+ "get",
+ "/tafsirs/{resource_id}/by_chapter/{chapter_number}",
+ ],
["search", "v1", "searchControllerSearch", "get", "/v1/search"],
["auth", "v1", "getV1NotesByNoteId", "get", "/v1/notes/{noteId}"],
["quranReflect", "v1", "postsControllerFeed", "get", "/v1/posts/feed"],
@@ -14,20 +31,22 @@ describe("operation contracts", () => {
it.each(representativeOperations)(
"loads %s %s %s from the synced OpenAPI contracts",
(service, version, operationName, method, path) => {
- expect(getOperationByName(service, version, operationName)).toMatchObject({
- method,
- operationName,
- path,
- service,
- version,
- });
+ expect(getOperationByName(service, version, operationName)).toMatchObject(
+ {
+ method,
+ operationName,
+ path,
+ service,
+ version,
+ },
+ );
},
);
it("normalizes colon params into curly-brace params", () => {
- const noteOperation = Object.values(operationCatalog.auth.v1.operations).find(
- (operation) => operation.path.includes("/v1/notes/{noteId}"),
- );
+ const noteOperation = Object.values(
+ operationCatalog.auth.v1.operations,
+ ).find((operation) => operation.path.includes("/v1/notes/{noteId}"));
expect(noteOperation).toBeDefined();
});
diff --git a/packages/api/test/hadith-references.test.ts b/packages/api/test/hadith-references.test.ts
new file mode 100644
index 0000000..ce91110
--- /dev/null
+++ b/packages/api/test/hadith-references.test.ts
@@ -0,0 +1,58 @@
+import { describe, expect, it } from "vitest";
+
+import { testClient } from "./test-client";
+
+describe("Hadith References API", () => {
+ describe("findByAyah()", () => {
+ it("should return hadith references for a valid ayah key", async () => {
+ const response = await testClient.hadithReferences.findByAyah("12:12");
+
+ expect(response.verseKey).toBe("12:12");
+ expect(response.hadithReferences).toHaveLength(2);
+ expect(response.hadithReferences[0]).toMatchObject({
+ collection: "bukhari",
+ hadithNumber: "1",
+ });
+ });
+
+ it("should throw for invalid ayah keys", async () => {
+ await expect(
+ // @ts-expect-error - invalid verse key
+ testClient.hadithReferences.findByAyah("0:1"),
+ ).rejects.toThrowError("Invalid verse key");
+ });
+ });
+
+ describe("findHadithsByAyah()", () => {
+ it("should return paginated hadith payloads for a valid ayah key", async () => {
+ const response = await testClient.hadithReferences.findHadithsByAyah(
+ "12:12",
+ { limit: 4 },
+ );
+
+ expect(response.hadiths[0]?.collection).toBe("bukhari");
+ expect(response.hadiths[0]?.hadith[0]?.grades[0]?.gradedBy).toBe(
+ "Ahmad Muhammad Shakir",
+ );
+ expect(response.hasMore).toBe(true);
+ });
+ });
+
+ describe("countWithinRange()", () => {
+ it("should return hadith counts for a verse range", async () => {
+ await expect(
+ testClient.hadithReferences.countWithinRange("12:12", "12:13"),
+ ).resolves.toEqual({
+ "12:12": 2,
+ "12:13": 2,
+ });
+ });
+
+ it("should expose the same methods through content.v4", async () => {
+ const response =
+ await testClient.content.v4.hadithReferences.byAyah("12:12");
+
+ expect(response.hadithReferences[1]?.collection).toBe("muslim");
+ });
+ });
+});