@eightyfourthousand/client-graphql 2026.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/codegen.ts +34 -0
  2. package/package.json +25 -0
  3. package/project.json +21 -0
  4. package/src/index.ts +79 -0
  5. package/src/lib/client-ssr.ts +44 -0
  6. package/src/lib/client.ts +58 -0
  7. package/src/lib/config.ts +21 -0
  8. package/src/lib/functions/get-bibliography-entry.ts +40 -0
  9. package/src/lib/functions/get-glossary-entry.ts +136 -0
  10. package/src/lib/functions/get-glossary-instance.ts +48 -0
  11. package/src/lib/functions/get-glossary-terms.ts +44 -0
  12. package/src/lib/functions/get-passage.ts +78 -0
  13. package/src/lib/functions/get-term-passages.ts +53 -0
  14. package/src/lib/functions/get-translation-blocks-around.ts +131 -0
  15. package/src/lib/functions/get-translation-blocks.ts +160 -0
  16. package/src/lib/functions/get-translation-imprint.ts +114 -0
  17. package/src/lib/functions/get-translation-metadata.ts +99 -0
  18. package/src/lib/functions/get-translation-passages-around.ts +153 -0
  19. package/src/lib/functions/get-translation-passages.ts +168 -0
  20. package/src/lib/functions/get-translation-titles.ts +55 -0
  21. package/src/lib/functions/get-translation-toc.ts +115 -0
  22. package/src/lib/functions/get-translation-uuids.ts +63 -0
  23. package/src/lib/functions/get-translations-metadata.ts +81 -0
  24. package/src/lib/functions/get-work-bibliography.ts +47 -0
  25. package/src/lib/functions/get-work-folios.ts +52 -0
  26. package/src/lib/functions/get-work-glossary-terms-around.ts +126 -0
  27. package/src/lib/functions/get-work-glossary-terms.ts +150 -0
  28. package/src/lib/functions/get-work-glossary.ts +54 -0
  29. package/src/lib/functions/get-works.ts +111 -0
  30. package/src/lib/functions/has-permission.ts +38 -0
  31. package/src/lib/functions/index.ts +33 -0
  32. package/src/lib/functions/save-passages.ts +76 -0
  33. package/src/lib/generated/graphql.ts +1318 -0
  34. package/src/lib/graphql/fragments/alignment.graphql +7 -0
  35. package/src/lib/graphql/fragments/annotation.graphql +7 -0
  36. package/src/lib/graphql/fragments/imprint.graphql +34 -0
  37. package/src/lib/graphql/fragments/passage.graphql +29 -0
  38. package/src/lib/graphql/fragments/title.graphql +6 -0
  39. package/src/lib/graphql/fragments/toc.graphql +40 -0
  40. package/src/lib/graphql/fragments/work.graphql +10 -0
  41. package/src/lib/graphql/queries/passages.graphql +116 -0
  42. package/src/lib/graphql/queries/work.graphql +38 -0
  43. package/src/lib/graphql/queries/works.graphql +23 -0
  44. package/src/lib/mappers/alignment.ts +34 -0
  45. package/src/lib/mappers/annotation.ts +204 -0
  46. package/src/lib/mappers/imprint.ts +79 -0
  47. package/src/lib/mappers/index.ts +7 -0
  48. package/src/lib/mappers/passage.ts +80 -0
  49. package/src/lib/mappers/title.ts +35 -0
  50. package/src/lib/mappers/toc.ts +49 -0
  51. package/src/lib/mappers/work.ts +38 -0
  52. package/src/ssr.ts +73 -0
  53. package/tsconfig.json +17 -0
  54. package/tsconfig.lib.json +14 -0
package/codegen.ts ADDED
@@ -0,0 +1,34 @@
1
+ import type { CodegenConfig } from '@graphql-codegen/cli';
2
+ import { join } from 'path';
3
+
4
+ // Resolve paths from repo root
5
+ const repoRoot = join(__dirname, '../..');
6
+ const schemaPath = join(repoRoot, 'apps/api-graphql/src/graphql/schema/**/*.graphql');
7
+ const documentsPath = join(__dirname, 'src/lib/graphql/**/*.graphql');
8
+ const outputPath = join(__dirname, 'src/lib/generated/graphql.ts');
9
+
10
+ const config: CodegenConfig = {
11
+ schema: schemaPath,
12
+ documents: documentsPath,
13
+ generates: {
14
+ [outputPath]: {
15
+ plugins: [
16
+ 'typescript',
17
+ 'typescript-operations',
18
+ 'typescript-graphql-request',
19
+ ],
20
+ config: {
21
+ rawRequest: false,
22
+ inlineFragmentTypes: 'combine',
23
+ skipTypename: false,
24
+ exportFragmentSpreadSubTypes: true,
25
+ dedupeFragments: true,
26
+ enumsAsTypes: true,
27
+ useTypeImports: true,
28
+ },
29
+ },
30
+ },
31
+ ignoreNoDocuments: false,
32
+ };
33
+
34
+ export default config;
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@eightyfourthousand/client-graphql",
3
+ "version": "2026.3.0",
4
+ "description": "GraphQL client helpers and typed query functions for 84000 applications.",
5
+ "private": false,
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/84000/84000-apps.git",
9
+ "directory": "libs/client-graphql"
10
+ },
11
+ "homepage": "https://github.com/84000/84000-apps",
12
+ "bugs": {
13
+ "url": "https://github.com/84000/84000-apps/issues"
14
+ },
15
+ "license": "ISC",
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "dependencies": {
20
+ "@eightyfourthousand/data-access": "^2026.3.0"
21
+ },
22
+ "sideEffects": false,
23
+ "keywords": [],
24
+ "author": ""
25
+ }
package/project.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "client-graphql",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "libs/client-graphql/src",
5
+ "projectType": "library",
6
+ "tags": [],
7
+ "targets": {
8
+ "build": {
9
+ "executor": "@nx/js:tsc",
10
+ "outputs": ["{options.outputPath}"],
11
+ "options": {
12
+ "outputPath": "dist/libs/client-graphql",
13
+ "main": "libs/client-graphql/src/index.ts",
14
+ "tsConfig": "libs/client-graphql/tsconfig.lib.json",
15
+ "rootDir": "libs/client-graphql/src",
16
+ "generateExportsField": true,
17
+ "additionalEntryPoints": ["libs/client-graphql/src/ssr.ts"]
18
+ }
19
+ }
20
+ }
21
+ }
package/src/index.ts ADDED
@@ -0,0 +1,79 @@
1
+ // Client
2
+ export {
3
+ createGraphQLClient,
4
+ resetGraphQLClient,
5
+ setAccessTokenProvider,
6
+ } from './lib/client';
7
+
8
+ // Functions
9
+ export {
10
+ getPassage,
11
+ getTranslationPassages,
12
+ getTranslationPassagesAround,
13
+ getTranslationBlocks,
14
+ getTranslationBlocksAround,
15
+ getTranslationTitles,
16
+ getTranslationMetadataByUuid,
17
+ getTranslationMetadataByToh,
18
+ getTranslationsMetadata,
19
+ getTranslationUuids,
20
+ getTranslationToc,
21
+ getTranslationImprint,
22
+ getGlossaryTerms,
23
+ getGlossaryEntry,
24
+ getGlossaryInstance,
25
+ getWorkGlossary,
26
+ getWorkGlossaryTerms,
27
+ getWorkGlossaryTermsAround,
28
+ type GlossaryTermsPage,
29
+ getTermPassages,
30
+ type GlossaryPassagesPage,
31
+ getBibliographyEntry,
32
+ getWorkBibliography,
33
+ getWorkFolios,
34
+ getWorks,
35
+ hasPermission,
36
+ savePassages,
37
+ type TranslationBlocksPage,
38
+ type WorksPage,
39
+ type Permission,
40
+ } from './lib/functions';
41
+
42
+ // Re-export types from @eightyfourthousand/data-access for convenience
43
+ export type {
44
+ Passage,
45
+ Passages,
46
+ PassagesPage,
47
+ PaginationDirection,
48
+ BodyItemType,
49
+ Annotation,
50
+ Annotations,
51
+ Alignment,
52
+ Work,
53
+ Title,
54
+ Titles,
55
+ TitleType,
56
+ Toc,
57
+ TocEntry,
58
+ Imprint,
59
+ GlossaryTermInstance,
60
+ GlossaryLandingItem,
61
+ GlossaryPageItem,
62
+ BibliographyEntry,
63
+ BibliographyEntryItem,
64
+ BibliographyEntries,
65
+ Folio,
66
+ } from '@eightyfourthousand/data-access';
67
+
68
+ // Re-export constants for passage filters
69
+ export {
70
+ BODY_ITEM_TYPES,
71
+ FRONT_MATTER,
72
+ FRONT_MATTER_FILTER,
73
+ BODY_MATTER,
74
+ BODY_MATTER_FILTER,
75
+ BACK_MATTER,
76
+ BACK_MATTER_FILTER,
77
+ COMPARE_MODE,
78
+ COMPARE_MODE_FILTER,
79
+ } from '@eightyfourthousand/data-access';
@@ -0,0 +1,44 @@
1
+ import 'server-only';
2
+ import { GraphQLClient } from 'graphql-request';
3
+ import { cookies } from 'next/headers';
4
+ import { getGraphQLUrl } from './config';
5
+
6
+ /**
7
+ * Create a GraphQL client for server-side requests (SSR/RSC).
8
+ * Reads auth cookies from the request and forwards them to the GraphQL API.
9
+ *
10
+ * Must be called within a Server Component or Server Action context.
11
+ */
12
+ export async function createServerGraphQLClient(): Promise<GraphQLClient> {
13
+ const url = getGraphQLUrl();
14
+
15
+ // Get all cookies from the request
16
+ const cookieStore = await cookies();
17
+ const allCookies = cookieStore.getAll();
18
+
19
+ // Build cookie header string with encoded values to ensure HTTP header compatibility
20
+ // Cookie values may contain non-ASCII characters which are invalid in HTTP headers (ByteString)
21
+ const cookieHeader = allCookies
22
+ .map((cookie) => `${cookie.name}=${encodeURIComponent(cookie.value)}`)
23
+ .join('; ');
24
+
25
+ const client = new GraphQLClient(url, {
26
+ headers: {
27
+ // Forward cookies for authentication
28
+ ...(cookieHeader ? { cookie: cookieHeader } : {}),
29
+ },
30
+ });
31
+
32
+ return client;
33
+ }
34
+
35
+ /**
36
+ * Create a GraphQL client for build-time requests (generateStaticParams).
37
+ * Does not use cookies since there's no request context during builds.
38
+ *
39
+ * Use this in generateStaticParams and other build-time contexts.
40
+ */
41
+ export function createBuildGraphQLClient(): GraphQLClient {
42
+ const url = getGraphQLUrl();
43
+ return new GraphQLClient(url);
44
+ }
@@ -0,0 +1,58 @@
1
+ import { GraphQLClient } from 'graphql-request';
2
+ import { getGraphQLUrl } from './config';
3
+
4
+ let clientInstance: GraphQLClient | null = null;
5
+ let getAccessToken: (() => Promise<string | null>) | null = null;
6
+
7
+ /**
8
+ * Set the access token provider function.
9
+ * This should be called once at app initialization.
10
+ */
11
+ export function setAccessTokenProvider(
12
+ provider: () => Promise<string | null>
13
+ ): void {
14
+ getAccessToken = provider;
15
+ }
16
+
17
+ /**
18
+ * Create a GraphQL client for browser-side requests.
19
+ * Uses Authorization header with dynamic token retrieval for cross-domain requests.
20
+ */
21
+ export function createGraphQLClient(): GraphQLClient {
22
+ if (clientInstance) {
23
+ return clientInstance;
24
+ }
25
+
26
+ const url = getGraphQLUrl();
27
+
28
+ clientInstance = new GraphQLClient(url, {
29
+ // Dynamic headers - called on each request
30
+ requestMiddleware: async (request) => {
31
+ const headers: Record<string, string> = {
32
+ 'Content-Type': 'application/json',
33
+ 'apollo-require-preflight': 'true',
34
+ };
35
+
36
+ if (getAccessToken) {
37
+ const token = await getAccessToken();
38
+ if (token) {
39
+ headers['Authorization'] = `Bearer ${token}`;
40
+ }
41
+ }
42
+
43
+ return {
44
+ ...request,
45
+ headers,
46
+ };
47
+ },
48
+ });
49
+
50
+ return clientInstance;
51
+ }
52
+
53
+ /**
54
+ * Reset the client instance (useful for testing)
55
+ */
56
+ export function resetGraphQLClient(): void {
57
+ clientInstance = null;
58
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * GraphQL API configuration
3
+ */
4
+
5
+ /**
6
+ * Get the GraphQL API URL from environment variables
7
+ * Falls back to localhost for development
8
+ */
9
+ export function getGraphQLUrl(): string {
10
+ const url = process.env.NEXT_PUBLIC_GRAPHQL_URL;
11
+
12
+ if (!url) {
13
+ // Default to localhost in development
14
+ if (process.env.NODE_ENV === 'development') {
15
+ return 'http://localhost:3001/api/graphql';
16
+ }
17
+ throw new Error('NEXT_PUBLIC_GRAPHQL_URL environment variable is not set');
18
+ }
19
+
20
+ return url;
21
+ }
@@ -0,0 +1,40 @@
1
+ import type { GraphQLClient } from 'graphql-request';
2
+ import { gql } from 'graphql-request';
3
+ import type { BibliographyEntryItem } from '@eightyfourthousand/data-access';
4
+
5
+ const GET_BIBLIOGRAPHY_ENTRY = gql`
6
+ query GetBibliographyEntry($uuid: ID!) {
7
+ bibliographyEntry(uuid: $uuid) {
8
+ uuid
9
+ html
10
+ workUuid
11
+ }
12
+ }
13
+ `;
14
+
15
+ type GetBibliographyEntryResponse = {
16
+ bibliographyEntry: BibliographyEntryItem | null;
17
+ };
18
+
19
+ /**
20
+ * Get a single bibliography entry by UUID.
21
+ */
22
+ export async function getBibliographyEntry({
23
+ client,
24
+ uuid,
25
+ }: {
26
+ client: GraphQLClient;
27
+ uuid: string;
28
+ }): Promise<BibliographyEntryItem | null> {
29
+ try {
30
+ const response = await client.request<GetBibliographyEntryResponse>(
31
+ GET_BIBLIOGRAPHY_ENTRY,
32
+ { uuid },
33
+ );
34
+
35
+ return response.bibliographyEntry;
36
+ } catch (error) {
37
+ console.error('Error fetching bibliography entry:', error);
38
+ return null;
39
+ }
40
+ }
@@ -0,0 +1,136 @@
1
+ import type { GraphQLClient } from 'graphql-request';
2
+ import { gql } from 'graphql-request';
3
+ import type { GlossaryPageItem, TohokuCatalogEntry } from '@eightyfourthousand/data-access';
4
+
5
+ const GET_GLOSSARY_ENTRY = gql`
6
+ query GetGlossaryEntry($uuid: ID!) {
7
+ glossaryEntry(uuid: $uuid) {
8
+ authorityUuid
9
+ headword
10
+ language
11
+ classifications
12
+ definition
13
+ xmlId
14
+ english
15
+ tibetan
16
+ sanskrit
17
+ pali
18
+ chinese
19
+ relatedInstances {
20
+ workUuid
21
+ toh
22
+ definition
23
+ canon
24
+ english
25
+ tibetan
26
+ sanskrit
27
+ pali
28
+ chinese
29
+ creators
30
+ }
31
+ relatedEntities {
32
+ sourceHeadword
33
+ sourceUuid
34
+ targetHeadword
35
+ targetUuid
36
+ }
37
+ }
38
+ }
39
+ `;
40
+
41
+ type GraphQLGlossaryEntry = {
42
+ authorityUuid: string;
43
+ headword: string;
44
+ language: string;
45
+ classifications: string[];
46
+ definition: string | null;
47
+ xmlId: string | null;
48
+ english: string[];
49
+ tibetan: string[];
50
+ sanskrit: string[];
51
+ pali: string[];
52
+ chinese: string[];
53
+ relatedInstances: Array<{
54
+ workUuid: string;
55
+ toh: string;
56
+ definition: string | null;
57
+ canon: string | null;
58
+ english: string[];
59
+ tibetan: string[];
60
+ sanskrit: string[];
61
+ pali: string[];
62
+ chinese: string[];
63
+ creators: string[];
64
+ }>;
65
+ relatedEntities: Array<{
66
+ sourceHeadword: string;
67
+ sourceUuid: string;
68
+ targetHeadword: string;
69
+ targetUuid: string;
70
+ }>;
71
+ };
72
+
73
+ type GetGlossaryEntryResponse = {
74
+ glossaryEntry: GraphQLGlossaryEntry | null;
75
+ };
76
+
77
+ /**
78
+ * Map GraphQL glossary entry to the GlossaryPageItem type.
79
+ */
80
+ function glossaryEntryFromGraphQL(
81
+ entry: GraphQLGlossaryEntry,
82
+ ): GlossaryPageItem {
83
+ return {
84
+ authorityUuid: entry.authorityUuid,
85
+ headword: entry.headword,
86
+ language: entry.language as GlossaryPageItem['language'],
87
+ classifications: entry.classifications,
88
+ definition: entry.definition ?? undefined,
89
+ xmlId: entry.xmlId ?? undefined,
90
+ english: entry.english,
91
+ tibetan: entry.tibetan,
92
+ sanskrit: entry.sanskrit,
93
+ pali: entry.pali,
94
+ chinese: entry.chinese,
95
+ relatedInstances: entry.relatedInstances.map((instance) => ({
96
+ workUuid: instance.workUuid,
97
+ toh: instance.toh as TohokuCatalogEntry,
98
+ definition: instance.definition ?? undefined,
99
+ canon: instance.canon ?? undefined,
100
+ english: instance.english,
101
+ tibetan: instance.tibetan,
102
+ sanskrit: instance.sanskrit,
103
+ pali: instance.pali,
104
+ chinese: instance.chinese,
105
+ creators: instance.creators,
106
+ })),
107
+ relatedEntities: entry.relatedEntities,
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Get a single glossary entry by UUID (for the detail page).
113
+ */
114
+ export async function getGlossaryEntry({
115
+ client,
116
+ uuid,
117
+ }: {
118
+ client: GraphQLClient;
119
+ uuid: string;
120
+ }): Promise<GlossaryPageItem | null> {
121
+ try {
122
+ const response = await client.request<GetGlossaryEntryResponse>(
123
+ GET_GLOSSARY_ENTRY,
124
+ { uuid },
125
+ );
126
+
127
+ if (!response.glossaryEntry) {
128
+ return null;
129
+ }
130
+
131
+ return glossaryEntryFromGraphQL(response.glossaryEntry);
132
+ } catch (error) {
133
+ console.error('Error fetching glossary entry:', error);
134
+ return null;
135
+ }
136
+ }
@@ -0,0 +1,48 @@
1
+ import type { GraphQLClient } from 'graphql-request';
2
+ import { gql } from 'graphql-request';
3
+ import type { GlossaryTermInstance } from '@eightyfourthousand/data-access';
4
+
5
+ const GET_GLOSSARY_INSTANCE = gql`
6
+ query GetGlossaryInstance($uuid: ID!) {
7
+ glossaryInstance(uuid: $uuid) {
8
+ uuid
9
+ authority
10
+ definition
11
+ names {
12
+ english
13
+ tibetan
14
+ sanskrit
15
+ pali
16
+ chinese
17
+ wylie
18
+ }
19
+ }
20
+ }
21
+ `;
22
+
23
+ type GetGlossaryInstanceResponse = {
24
+ glossaryInstance: GlossaryTermInstance | null;
25
+ };
26
+
27
+ /**
28
+ * Get a single glossary instance by UUID.
29
+ */
30
+ export async function getGlossaryInstance({
31
+ client,
32
+ uuid,
33
+ }: {
34
+ client: GraphQLClient;
35
+ uuid: string;
36
+ }): Promise<GlossaryTermInstance | null> {
37
+ try {
38
+ const response = await client.request<GetGlossaryInstanceResponse>(
39
+ GET_GLOSSARY_INSTANCE,
40
+ { uuid },
41
+ );
42
+
43
+ return response.glossaryInstance;
44
+ } catch (error) {
45
+ console.error('Error fetching glossary instance:', error);
46
+ return null;
47
+ }
48
+ }
@@ -0,0 +1,44 @@
1
+ import type { GraphQLClient } from 'graphql-request';
2
+ import { gql } from 'graphql-request';
3
+ import type { GlossaryLandingItem } from '@eightyfourthousand/data-access';
4
+
5
+ const GET_GLOSSARY_TERMS = gql`
6
+ query GetGlossaryTerms($uuids: [ID!]) {
7
+ glossaryTerms(uuids: $uuids) {
8
+ uuid
9
+ headword
10
+ type
11
+ language
12
+ nameVariants
13
+ definition
14
+ numGlossaryEntries
15
+ }
16
+ }
17
+ `;
18
+
19
+ type GetGlossaryTermsResponse = {
20
+ glossaryTerms: GlossaryLandingItem[];
21
+ };
22
+
23
+ /**
24
+ * Get all glossary terms for the landing page, optionally filtered by UUIDs.
25
+ */
26
+ export async function getGlossaryTerms({
27
+ client,
28
+ uuids,
29
+ }: {
30
+ client: GraphQLClient;
31
+ uuids?: string[];
32
+ }): Promise<GlossaryLandingItem[]> {
33
+ try {
34
+ const response = await client.request<GetGlossaryTermsResponse>(
35
+ GET_GLOSSARY_TERMS,
36
+ { uuids },
37
+ );
38
+
39
+ return response.glossaryTerms;
40
+ } catch (error) {
41
+ console.error('Error fetching glossary terms:', error);
42
+ return [];
43
+ }
44
+ }
@@ -0,0 +1,78 @@
1
+ import type { GraphQLClient } from 'graphql-request';
2
+ import { gql } from 'graphql-request';
3
+ import type { Passage } from '@eightyfourthousand/data-access';
4
+ import { passageFromGraphQL, type GraphQLPassage } from '../mappers';
5
+
6
+ const GET_PASSAGE = gql`
7
+ fragment AnnotationFields on Annotation {
8
+ uuid
9
+ type
10
+ start
11
+ end
12
+ metadata
13
+ }
14
+
15
+ fragment AlignmentFields on Alignment {
16
+ folioUuid
17
+ toh
18
+ tibetan
19
+ folioNumber
20
+ volumeNumber
21
+ }
22
+
23
+ fragment PassageFields on Passage {
24
+ uuid
25
+ workUuid
26
+ content
27
+ label
28
+ sort
29
+ type
30
+ xmlId
31
+ }
32
+
33
+ fragment PassageWithAnnotations on Passage {
34
+ ...PassageFields
35
+ annotations {
36
+ ...AnnotationFields
37
+ }
38
+ alignments {
39
+ ...AlignmentFields
40
+ }
41
+ }
42
+
43
+ query GetPassage($uuid: ID!) {
44
+ passage(uuid: $uuid) {
45
+ ...PassageWithAnnotations
46
+ }
47
+ }
48
+ `;
49
+
50
+ type GetPassageResponse = {
51
+ passage: GraphQLPassage | null;
52
+ };
53
+
54
+ /**
55
+ * Get a single passage by UUID.
56
+ */
57
+ export async function getPassage({
58
+ client,
59
+ uuid,
60
+ }: {
61
+ client: GraphQLClient;
62
+ uuid: string;
63
+ }): Promise<Passage | undefined> {
64
+ try {
65
+ const response = await client.request<GetPassageResponse>(GET_PASSAGE, {
66
+ uuid,
67
+ });
68
+
69
+ if (!response.passage) {
70
+ return undefined;
71
+ }
72
+
73
+ return passageFromGraphQL(response.passage);
74
+ } catch (error) {
75
+ console.error('Error fetching passage:', error);
76
+ return undefined;
77
+ }
78
+ }
@@ -0,0 +1,53 @@
1
+ import type { GraphQLClient } from 'graphql-request';
2
+ import { gql } from 'graphql-request';
3
+
4
+ const GET_TERM_PASSAGES = gql`
5
+ query GetTermPassages($uuid: ID!, $first: Int, $after: String) {
6
+ glossaryTermPassages(uuid: $uuid, first: $first, after: $after) {
7
+ items {
8
+ uuid
9
+ type
10
+ label
11
+ sort
12
+ }
13
+ nextCursor
14
+ hasMore
15
+ }
16
+ }
17
+ `;
18
+
19
+ export type GlossaryPassagesPage = {
20
+ items: Array<{ uuid: string; type: string; label: string; sort: number }>;
21
+ nextCursor: string | null;
22
+ hasMore: boolean;
23
+ };
24
+
25
+ type GetTermPassagesResponse = {
26
+ glossaryTermPassages: GlossaryPassagesPage;
27
+ };
28
+
29
+ /**
30
+ * Fetch a page of passage references for a single glossary term instance.
31
+ */
32
+ export async function getTermPassages({
33
+ client,
34
+ uuid,
35
+ first = 10,
36
+ after,
37
+ }: {
38
+ client: GraphQLClient;
39
+ uuid: string;
40
+ first?: number;
41
+ after?: string;
42
+ }): Promise<GlossaryPassagesPage> {
43
+ try {
44
+ const response = await client.request<GetTermPassagesResponse>(
45
+ GET_TERM_PASSAGES,
46
+ { uuid, first, after },
47
+ );
48
+ return response.glossaryTermPassages;
49
+ } catch (error) {
50
+ console.error('Error fetching term passages:', error);
51
+ return { items: [], nextCursor: null, hasMore: false };
52
+ }
53
+ }