@eightyfourthousand/client-graphql 2026.3.0 → 2026.4.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.
package/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # @eightyfourthousand/client-graphql
2
+
3
+ GraphQL client helpers and typed query functions for 84000 applications.
4
+
5
+ Use this package for shared GraphQL request helpers, typed query access, and response mappers used by the frontend apps.
6
+
7
+ ## Running unit tests
8
+
9
+ Run `nx test client-graphql` to execute the unit tests via [Jest](https://jestjs.io).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eightyfourthousand/client-graphql",
3
- "version": "2026.3.0",
3
+ "version": "2026.4.0",
4
4
  "description": "GraphQL client helpers and typed query functions for 84000 applications.",
5
5
  "private": false,
6
6
  "repository": {
@@ -17,7 +17,7 @@
17
17
  "access": "public"
18
18
  },
19
19
  "dependencies": {
20
- "@eightyfourthousand/data-access": "^2026.3.0"
20
+ "@eightyfourthousand/data-access": "^2026.3.1"
21
21
  },
22
22
  "sideEffects": false,
23
23
  "keywords": [],
package/src/index.ts CHANGED
@@ -8,8 +8,6 @@ export {
8
8
  // Functions
9
9
  export {
10
10
  getPassage,
11
- getTranslationPassages,
12
- getTranslationPassagesAround,
13
11
  getTranslationBlocks,
14
12
  getTranslationBlocksAround,
15
13
  getTranslationTitles,
@@ -19,10 +17,7 @@ export {
19
17
  getTranslationUuids,
20
18
  getTranslationToc,
21
19
  getTranslationImprint,
22
- getGlossaryTerms,
23
- getGlossaryEntry,
24
20
  getGlossaryInstance,
25
- getWorkGlossary,
26
21
  getWorkGlossaryTerms,
27
22
  getWorkGlossaryTermsAround,
28
23
  type GlossaryTermsPage,
@@ -31,12 +26,13 @@ export {
31
26
  getBibliographyEntry,
32
27
  getWorkBibliography,
33
28
  getWorkFolios,
34
- getWorks,
35
29
  hasPermission,
30
+ replace,
36
31
  savePassages,
37
32
  type TranslationBlocksPage,
38
- type WorksPage,
39
33
  type Permission,
34
+ type ReplaceType,
35
+ type ReplacedPassage,
40
36
  } from './lib/functions';
41
37
 
42
38
  // Re-export types from @eightyfourthousand/data-access for convenience
@@ -15,6 +15,7 @@ const GET_GLOSSARY_INSTANCE = gql`
15
15
  pali
16
16
  chinese
17
17
  wylie
18
+ alternatives
18
19
  }
19
20
  }
20
21
  }
@@ -40,8 +40,8 @@ const GET_PASSAGE = gql`
40
40
  }
41
41
  }
42
42
 
43
- query GetPassage($uuid: ID!) {
44
- passage(uuid: $uuid) {
43
+ query GetPassage($uuid: ID, $xmlId: String) {
44
+ passage(uuid: $uuid, xmlId: $xmlId) {
45
45
  ...PassageWithAnnotations
46
46
  }
47
47
  }
@@ -52,18 +52,21 @@ type GetPassageResponse = {
52
52
  };
53
53
 
54
54
  /**
55
- * Get a single passage by UUID.
55
+ * Get a single passage by UUID or XML ID.
56
56
  */
57
57
  export async function getPassage({
58
58
  client,
59
59
  uuid,
60
+ xmlId,
60
61
  }: {
61
62
  client: GraphQLClient;
62
- uuid: string;
63
+ uuid?: string;
64
+ xmlId?: string;
63
65
  }): Promise<Passage | undefined> {
64
66
  try {
65
67
  const response = await client.request<GetPassageResponse>(GET_PASSAGE, {
66
68
  uuid,
69
+ xmlId,
67
70
  });
68
71
 
69
72
  if (!response.passage) {
@@ -70,9 +70,7 @@ type GetPassagesAroundWithJsonResponse = {
70
70
  /**
71
71
  * Get translation blocks (TipTap JSON) around a specific passage UUID.
72
72
  *
73
- * Unlike getTranslationPassagesAround which returns Passage objects,
74
- * this function returns pre-transformed TipTap editor blocks
75
- * ready for rendering.
73
+ * Returns pre-transformed TipTap editor blocks ready for rendering.
76
74
  */
77
75
  export async function getTranslationBlocksAround({
78
76
  client,
@@ -96,9 +96,7 @@ function mapDirection(
96
96
  /**
97
97
  * Get paginated translation blocks (TipTap JSON) for a work.
98
98
  *
99
- * Unlike getTranslationPassages which returns Passage objects,
100
- * this function returns pre-transformed TipTap editor blocks
101
- * ready for rendering.
99
+ * Returns pre-transformed TipTap editor blocks ready for rendering.
102
100
  */
103
101
  export async function getTranslationBlocks({
104
102
  client,
@@ -29,6 +29,7 @@ const GET_WORK_GLOSSARY_TERMS_AROUND = gql`
29
29
  pali
30
30
  chinese
31
31
  wylie
32
+ alternatives
32
33
  }
33
34
  passages(first: 10) {
34
35
  items {
@@ -1,6 +1,9 @@
1
1
  import type { GraphQLClient } from 'graphql-request';
2
2
  import { gql } from 'graphql-request';
3
- import type { GlossaryTermInstance, PaginationDirection } from '@eightyfourthousand/data-access';
3
+ import type {
4
+ GlossaryTermInstance,
5
+ PaginationDirection,
6
+ } from '@eightyfourthousand/data-access';
4
7
 
5
8
  const GET_WORK_GLOSSARY_TERMS = gql`
6
9
  query GetWorkGlossaryTerms(
@@ -29,6 +32,7 @@ const GET_WORK_GLOSSARY_TERMS = gql`
29
32
  pali
30
33
  chinese
31
34
  wylie
35
+ alternatives
32
36
  }
33
37
  passages(first: 10) {
34
38
  items {
@@ -1,6 +1,4 @@
1
1
  export { getPassage } from './get-passage';
2
- export { getTranslationPassages } from './get-translation-passages';
3
- export { getTranslationPassagesAround } from './get-translation-passages-around';
4
2
  export {
5
3
  getTranslationBlocks,
6
4
  type TranslationBlocksPage,
@@ -15,10 +13,7 @@ export { getTranslationsMetadata } from './get-translations-metadata';
15
13
  export { getTranslationUuids } from './get-translation-uuids';
16
14
  export { getTranslationToc } from './get-translation-toc';
17
15
  export { getTranslationImprint } from './get-translation-imprint';
18
- export { getGlossaryTerms } from './get-glossary-terms';
19
- export { getGlossaryEntry } from './get-glossary-entry';
20
16
  export { getGlossaryInstance } from './get-glossary-instance';
21
- export { getWorkGlossary } from './get-work-glossary';
22
17
  export {
23
18
  getWorkGlossaryTerms,
24
19
  type GlossaryTermsPage,
@@ -28,6 +23,6 @@ export { getTermPassages, type GlossaryPassagesPage } from './get-term-passages'
28
23
  export { getBibliographyEntry } from './get-bibliography-entry';
29
24
  export { getWorkBibliography } from './get-work-bibliography';
30
25
  export { getWorkFolios } from './get-work-folios';
31
- export { getWorks, type WorksPage } from './get-works';
32
26
  export { hasPermission, type Permission } from './has-permission';
27
+ export { replace, type ReplaceType, type ReplacedPassage } from './replace';
33
28
  export { savePassages } from './save-passages';
@@ -0,0 +1,91 @@
1
+ import type { GraphQLClient } from 'graphql-request';
2
+ import { gql } from 'graphql-request';
3
+ import type { TranslationEditorContentItem } from '@eightyfourthousand/data-access';
4
+
5
+ export type ReplaceType = 'PASSAGE';
6
+
7
+ export interface ReplacedPassage {
8
+ json?: TranslationEditorContentItem | null;
9
+ uuid: string;
10
+ }
11
+
12
+ interface ReplaceResponse {
13
+ replace: {
14
+ deletedAnnotationCount: number;
15
+ error?: string;
16
+ nextOccurrenceStart?: number | null;
17
+ nextPassageUuid?: string | null;
18
+ passages: ReplacedPassage[];
19
+ replacedOccurrenceCount: number;
20
+ success: boolean;
21
+ updatedAnnotationCount: number;
22
+ updatedCount: number;
23
+ };
24
+ }
25
+
26
+ const REPLACE_MUTATION = gql`
27
+ mutation Replace(
28
+ $searchText: String!
29
+ $replaceText: String!
30
+ $targetUuids: [ID!]!
31
+ $type: ReplaceType
32
+ $occurrenceIndex: Int
33
+ $cursorPassageUuid: ID
34
+ $cursorStart: Int
35
+ ) {
36
+ replace(
37
+ searchText: $searchText
38
+ replaceText: $replaceText
39
+ targetUuids: $targetUuids
40
+ type: $type
41
+ occurrenceIndex: $occurrenceIndex
42
+ cursorPassageUuid: $cursorPassageUuid
43
+ cursorStart: $cursorStart
44
+ ) {
45
+ success
46
+ updatedCount
47
+ replacedOccurrenceCount
48
+ updatedAnnotationCount
49
+ deletedAnnotationCount
50
+ nextPassageUuid
51
+ nextOccurrenceStart
52
+ error
53
+ passages {
54
+ uuid
55
+ json
56
+ }
57
+ }
58
+ }
59
+ `;
60
+
61
+ export const replace = async ({
62
+ client,
63
+ cursorPassageUuid,
64
+ cursorStart,
65
+ occurrenceIndex,
66
+ replaceText,
67
+ searchText,
68
+ targetUuids,
69
+ type = 'PASSAGE',
70
+ }: {
71
+ client: GraphQLClient;
72
+ occurrenceIndex?: number;
73
+ replaceText: string;
74
+ searchText: string;
75
+ cursorPassageUuid?: string;
76
+ cursorStart?: number;
77
+ targetUuids: string[];
78
+ type?: ReplaceType;
79
+ }) => {
80
+ const response = await client.request<ReplaceResponse>(REPLACE_MUTATION, {
81
+ searchText,
82
+ replaceText,
83
+ targetUuids,
84
+ type,
85
+ occurrenceIndex,
86
+ cursorPassageUuid,
87
+ cursorStart,
88
+ });
89
+
90
+ return response.replace;
91
+ };
@@ -115,94 +115,6 @@ export type FolioSide =
115
115
  | 'a'
116
116
  | 'b';
117
117
 
118
- /** Related entity for a glossary entry */
119
- export type GlossaryEntity = {
120
- __typename?: 'GlossaryEntity';
121
- /** Source entity headword */
122
- sourceHeadword: Scalars['String']['output'];
123
- /** Source entity UUID */
124
- sourceUuid: Scalars['ID']['output'];
125
- /** Target entity headword */
126
- targetHeadword: Scalars['String']['output'];
127
- /** Target entity UUID */
128
- targetUuid: Scalars['ID']['output'];
129
- };
130
-
131
- /** Detailed glossary entry for the detail page */
132
- export type GlossaryEntry = {
133
- __typename?: 'GlossaryEntry';
134
- /** Authority UUID */
135
- authorityUuid: Scalars['ID']['output'];
136
- /** Chinese names */
137
- chinese: Array<Scalars['String']['output']>;
138
- /** Classifications/types */
139
- classifications: Array<Scalars['String']['output']>;
140
- /** Definition of the term */
141
- definition?: Maybe<Scalars['String']['output']>;
142
- /** English names/translations */
143
- english: Array<Scalars['String']['output']>;
144
- /** Primary headword */
145
- headword: Scalars['String']['output'];
146
- /** Primary language of the headword */
147
- language: Scalars['String']['output'];
148
- /** Pali names */
149
- pali: Array<Scalars['String']['output']>;
150
- /** Related entities */
151
- relatedEntities: Array<GlossaryEntity>;
152
- /** Related instances across works */
153
- relatedInstances: Array<GlossaryInstance>;
154
- /** Sanskrit names */
155
- sanskrit: Array<Scalars['String']['output']>;
156
- /** Tibetan names */
157
- tibetan: Array<Scalars['String']['output']>;
158
- /** XML ID from source */
159
- xmlId?: Maybe<Scalars['String']['output']>;
160
- };
161
-
162
- /** A glossary instance appearing in a specific work */
163
- export type GlossaryInstance = {
164
- __typename?: 'GlossaryInstance';
165
- /** Canon the work belongs to */
166
- canon?: Maybe<Scalars['String']['output']>;
167
- /** Chinese names */
168
- chinese: Array<Scalars['String']['output']>;
169
- /** Creators/translators */
170
- creators: Array<Scalars['String']['output']>;
171
- /** Definition for this instance */
172
- definition?: Maybe<Scalars['String']['output']>;
173
- /** English names/translations */
174
- english: Array<Scalars['String']['output']>;
175
- /** Pali names */
176
- pali: Array<Scalars['String']['output']>;
177
- /** Sanskrit names */
178
- sanskrit: Array<Scalars['String']['output']>;
179
- /** Tibetan names */
180
- tibetan: Array<Scalars['String']['output']>;
181
- /** Tohoku catalog entry */
182
- toh: Scalars['String']['output'];
183
- /** UUID of the work containing this instance */
184
- workUuid: Scalars['ID']['output'];
185
- };
186
-
187
- /** A glossary item for the landing page listing */
188
- export type GlossaryLandingItem = {
189
- __typename?: 'GlossaryLandingItem';
190
- /** Definition of the term */
191
- definition: Scalars['String']['output'];
192
- /** Primary headword for the term */
193
- headword: Scalars['String']['output'];
194
- /** Primary language of the headword */
195
- language: Scalars['String']['output'];
196
- /** Name variants for the term */
197
- nameVariants: Scalars['String']['output'];
198
- /** Number of glossary entries across works */
199
- numGlossaryEntries: Scalars['Int']['output'];
200
- /** Type/classification of the term */
201
- type: Scalars['String']['output'];
202
- /** Authority UUID */
203
- uuid: Scalars['ID']['output'];
204
- };
205
-
206
118
  /** Paginated passage references for a glossary term */
207
119
  export type GlossaryPassagesPage = {
208
120
  __typename?: 'GlossaryPassagesPage';
@@ -214,6 +126,17 @@ export type GlossaryPassagesPage = {
214
126
  nextCursor?: Maybe<Scalars['String']['output']>;
215
127
  };
216
128
 
129
+ /** Paginated connection of glossary term instances for a work */
130
+ export type GlossaryTermConnection = {
131
+ __typename?: 'GlossaryTermConnection';
132
+ /** List of glossary term instances in this page */
133
+ nodes: Array<GlossaryTermInstance>;
134
+ /** Pagination information */
135
+ pageInfo: PageInfo;
136
+ /** Total number of glossary terms in this work */
137
+ totalCount: Scalars['Int']['output'];
138
+ };
139
+
217
140
  /** A glossary term instance within a specific work */
218
141
  export type GlossaryTermInstance = {
219
142
  __typename?: 'GlossaryTermInstance';
@@ -223,12 +146,21 @@ export type GlossaryTermInstance = {
223
146
  definition?: Maybe<Scalars['String']['output']>;
224
147
  /** Names in different languages */
225
148
  names: GlossaryTermNames;
226
- /** Passages where this term appears */
227
- passages: Array<Passage>;
149
+ /** Paginated passage references where this term appears */
150
+ passages: GlossaryPassagesPage;
151
+ /** Absolute position of this term in the work's glossary (1-based) */
152
+ termNumber: Scalars['Int']['output'];
228
153
  /** Unique identifier */
229
154
  uuid: Scalars['ID']['output'];
230
155
  };
231
156
 
157
+
158
+ /** A glossary term instance within a specific work */
159
+ export type GlossaryTermInstancePassagesArgs = {
160
+ after?: InputMaybe<Scalars['String']['input']>;
161
+ first?: InputMaybe<Scalars['Int']['input']>;
162
+ };
163
+
232
164
  /** Names for a glossary term instance in different languages */
233
165
  export type GlossaryTermNames = {
234
166
  __typename?: 'GlossaryTermNames';
@@ -302,6 +234,11 @@ export type Mutation = {
302
234
  __typename?: 'Mutation';
303
235
  /** Placeholder for schema validation - not used */
304
236
  _empty?: Maybe<Scalars['Boolean']['output']>;
237
+ /**
238
+ * Replace text within one or more targets.
239
+ * Requires editor.edit permission.
240
+ */
241
+ replace: ReplaceResult;
305
242
  /**
306
243
  * Save one or more passages.
307
244
  * Requires editor.edit permission.
@@ -310,6 +247,18 @@ export type Mutation = {
310
247
  };
311
248
 
312
249
 
250
+ /** Root Mutation type - extend this in other schema files */
251
+ export type MutationReplaceArgs = {
252
+ cursorPassageUuid?: InputMaybe<Scalars['ID']['input']>;
253
+ cursorStart?: InputMaybe<Scalars['Int']['input']>;
254
+ occurrenceIndex?: InputMaybe<Scalars['Int']['input']>;
255
+ replaceText: Scalars['String']['input'];
256
+ searchText: Scalars['String']['input'];
257
+ targetUuids: Array<Scalars['ID']['input']>;
258
+ type?: InputMaybe<ReplaceType>;
259
+ };
260
+
261
+
313
262
  /** Root Mutation type - extend this in other schema files */
314
263
  export type MutationSavePassagesArgs = {
315
264
  deletedUuids?: InputMaybe<Array<Scalars['ID']['input']>>;
@@ -372,7 +321,7 @@ export type Passage = {
372
321
  uuid: Scalars['ID']['output'];
373
322
  /** UUID of the work this passage belongs to */
374
323
  workUuid: Scalars['ID']['output'];
375
- /** XML ID from the source document */
324
+ /** The Legacy XML ID from the source document */
376
325
  xmlId?: Maybe<Scalars['String']['output']>;
377
326
  };
378
327
 
@@ -437,7 +386,7 @@ export type PassageInput = {
437
386
  uuid: Scalars['ID']['input'];
438
387
  /** UUID of the work this passage belongs to */
439
388
  workUuid: Scalars['ID']['input'];
440
- /** XML ID from the source document */
389
+ /** The Legacy XML ID from the source document */
441
390
  xmlId?: InputMaybe<Scalars['String']['input']>;
442
391
  };
443
392
 
@@ -455,14 +404,10 @@ export type Query = {
455
404
  __typename?: 'Query';
456
405
  /** Get a single bibliography entry by UUID */
457
406
  bibliographyEntry?: Maybe<BibliographyEntryItem>;
458
- /** Get a single glossary entry by UUID (for detail page) */
459
- glossaryEntry?: Maybe<GlossaryEntry>;
460
407
  /** Get a single glossary instance by UUID */
461
408
  glossaryInstance?: Maybe<GlossaryTermInstance>;
462
409
  /** Get paginated passage references for a glossary term (per-term, on-demand) */
463
410
  glossaryTermPassages: GlossaryPassagesPage;
464
- /** Get all glossary terms for the landing page */
465
- glossaryTerms: Array<GlossaryLandingItem>;
466
411
  /**
467
412
  * Check if the current user has a specific permission.
468
413
  * Returns false if not authenticated.
@@ -472,7 +417,10 @@ export type Query = {
472
417
  health: HealthStatus;
473
418
  /** Get the currently authenticated user (null if not authenticated) */
474
419
  me?: Maybe<CurrentUser>;
475
- /** Fetch a single passage by UUID */
420
+ /**
421
+ * Fetch a single passage by UUID or XML ID.
422
+ * At least one of uuid or legacy xmlId must be provided. Prefers uuid if both are present.
423
+ */
476
424
  passage?: Maybe<Passage>;
477
425
  /** Get the current API version */
478
426
  version: Scalars['String']['output'];
@@ -493,12 +441,6 @@ export type QueryBibliographyEntryArgs = {
493
441
  };
494
442
 
495
443
 
496
- /** Root Query type - extend this in other schema files */
497
- export type QueryGlossaryEntryArgs = {
498
- uuid: Scalars['ID']['input'];
499
- };
500
-
501
-
502
444
  /** Root Query type - extend this in other schema files */
503
445
  export type QueryGlossaryInstanceArgs = {
504
446
  uuid: Scalars['ID']['input'];
@@ -513,12 +455,6 @@ export type QueryGlossaryTermPassagesArgs = {
513
455
  };
514
456
 
515
457
 
516
- /** Root Query type - extend this in other schema files */
517
- export type QueryGlossaryTermsArgs = {
518
- uuids?: InputMaybe<Array<Scalars['ID']['input']>>;
519
- };
520
-
521
-
522
458
  /** Root Query type - extend this in other schema files */
523
459
  export type QueryHasPermissionArgs = {
524
460
  permission: Permission;
@@ -527,7 +463,8 @@ export type QueryHasPermissionArgs = {
527
463
 
528
464
  /** Root Query type - extend this in other schema files */
529
465
  export type QueryPassageArgs = {
530
- uuid: Scalars['ID']['input'];
466
+ uuid?: InputMaybe<Scalars['ID']['input']>;
467
+ xmlId?: InputMaybe<Scalars['String']['input']>;
531
468
  };
532
469
 
533
470
 
@@ -545,6 +482,33 @@ export type QueryWorksArgs = {
545
482
  limit?: InputMaybe<Scalars['Int']['input']>;
546
483
  };
547
484
 
485
+ /** Result of a replace mutation */
486
+ export type ReplaceResult = {
487
+ __typename?: 'ReplaceResult';
488
+ /** Number of annotations deleted because the replacement removed them */
489
+ deletedAnnotationCount: Scalars['Int']['output'];
490
+ /** Error message if the replace failed */
491
+ error?: Maybe<Scalars['String']['output']>;
492
+ /** Character offset within nextPassageUuid where the next single replace should continue */
493
+ nextOccurrenceStart?: Maybe<Scalars['Int']['output']>;
494
+ /** Passage UUID after which the next single replace should continue */
495
+ nextPassageUuid?: Maybe<Scalars['ID']['output']>;
496
+ /** Updated target passages for client-side refresh */
497
+ passages: Array<Passage>;
498
+ /** Number of occurrences replaced */
499
+ replacedOccurrenceCount: Scalars['Int']['output'];
500
+ /** Whether the replace was successful */
501
+ success: Scalars['Boolean']['output'];
502
+ /** Number of annotations whose ranges changed */
503
+ updatedAnnotationCount: Scalars['Int']['output'];
504
+ /** Number of target records updated */
505
+ updatedCount: Scalars['Int']['output'];
506
+ };
507
+
508
+ /** Supported target types for search-and-replace operations */
509
+ export type ReplaceType =
510
+ | 'PASSAGE';
511
+
548
512
  /** Result of saving passages */
549
513
  export type SavePassagesResult = {
550
514
  __typename?: 'SavePassagesResult';
@@ -664,8 +628,13 @@ export type Work = {
664
628
  bibliography: Array<BibliographyEntry>;
665
629
  /** Folios for this work, filtered by toh and paginated */
666
630
  folios: Array<Folio>;
667
- /** Glossary term instances for this work */
631
+ /**
632
+ * Glossary term instances for this work
633
+ * @deprecated Use glossaryTerms for paginated access
634
+ */
668
635
  glossary: Array<GlossaryTermInstance>;
636
+ /** Paginated glossary term instances for this work */
637
+ glossaryTerms: GlossaryTermConnection;
669
638
  /**
670
639
  * Publication imprint information.
671
640
  * If the work query specified a toh argument, that value will be used.
@@ -715,6 +684,15 @@ export type WorkGlossaryArgs = {
715
684
  };
716
685
 
717
686
 
687
+ /** A published translation work from the 84000 canon */
688
+ export type WorkGlossaryTermsArgs = {
689
+ cursor?: InputMaybe<Scalars['String']['input']>;
690
+ direction?: InputMaybe<PaginationDirection>;
691
+ limit?: InputMaybe<Scalars['Int']['input']>;
692
+ withAttestations?: InputMaybe<Scalars['Boolean']['input']>;
693
+ };
694
+
695
+
718
696
  /** A published translation work from the 84000 canon */
719
697
  export type WorkPassagesArgs = {
720
698
  cursor?: InputMaybe<Scalars['String']['input']>;
@@ -867,7 +845,8 @@ export type GetPassagesAroundWithJsonQuery = { __typename?: 'Query', work?: { __
867
845
  )>, pageInfo: { __typename?: 'PageInfo', nextCursor?: string | null, prevCursor?: string | null, hasMoreAfter: boolean, hasMoreBefore: boolean } } } | null };
868
846
 
869
847
  export type GetPassageQueryVariables = Exact<{
870
- uuid: Scalars['ID']['input'];
848
+ uuid?: InputMaybe<Scalars['ID']['input']>;
849
+ xmlId?: InputMaybe<Scalars['String']['input']>;
871
850
  }>;
872
851
 
873
852
 
@@ -1183,8 +1162,8 @@ export const GetPassagesAroundWithJsonDocument = gql`
1183
1162
  }
1184
1163
  ${PassageWithJsonFragmentDoc}`;
1185
1164
  export const GetPassageDocument = gql`
1186
- query GetPassage($uuid: ID!) {
1187
- passage(uuid: $uuid) {
1165
+ query GetPassage($uuid: ID, $xmlId: String) {
1166
+ passage(uuid: $uuid, xmlId: $xmlId) {
1188
1167
  ...PassageWithAnnotations
1189
1168
  }
1190
1169
  }
@@ -1289,7 +1268,7 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
1289
1268
  GetPassagesAroundWithJson(variables: GetPassagesAroundWithJsonQueryVariables, requestHeaders?: GraphQLClientRequestHeaders, signal?: RequestInit['signal']): Promise<GetPassagesAroundWithJsonQuery> {
1290
1269
  return withWrapper((wrappedRequestHeaders) => client.request<GetPassagesAroundWithJsonQuery>({ document: GetPassagesAroundWithJsonDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), 'GetPassagesAroundWithJson', 'query', variables);
1291
1270
  },
1292
- GetPassage(variables: GetPassageQueryVariables, requestHeaders?: GraphQLClientRequestHeaders, signal?: RequestInit['signal']): Promise<GetPassageQuery> {
1271
+ GetPassage(variables?: GetPassageQueryVariables, requestHeaders?: GraphQLClientRequestHeaders, signal?: RequestInit['signal']): Promise<GetPassageQuery> {
1293
1272
  return withWrapper((wrappedRequestHeaders) => client.request<GetPassageQuery>({ document: GetPassageDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), 'GetPassage', 'query', variables);
1294
1273
  },
1295
1274
  GetWorkByUuid(variables: GetWorkByUuidQueryVariables, requestHeaders?: GraphQLClientRequestHeaders, signal?: RequestInit['signal']): Promise<GetWorkByUuidQuery> {
@@ -109,8 +109,8 @@ query GetPassagesAroundWithJson(
109
109
  }
110
110
  }
111
111
 
112
- query GetPassage($uuid: ID!) {
113
- passage(uuid: $uuid) {
112
+ query GetPassage($uuid: ID, $xmlId: String) {
113
+ passage(uuid: $uuid, xmlId: $xmlId) {
114
114
  ...PassageWithAnnotations
115
115
  }
116
116
  }
package/src/ssr.ts CHANGED
@@ -7,8 +7,6 @@ export {
7
7
  // Functions (same as browser)
8
8
  export {
9
9
  getPassage,
10
- getTranslationPassages,
11
- getTranslationPassagesAround,
12
10
  getTranslationBlocks,
13
11
  getTranslationBlocksAround,
14
12
  getTranslationTitles,
@@ -18,19 +16,14 @@ export {
18
16
  getTranslationUuids,
19
17
  getTranslationToc,
20
18
  getTranslationImprint,
21
- getGlossaryTerms,
22
- getGlossaryEntry,
23
19
  getGlossaryInstance,
24
- getWorkGlossary,
25
20
  getWorkGlossaryTerms,
26
21
  getWorkGlossaryTermsAround,
27
22
  type GlossaryTermsPage,
28
23
  getBibliographyEntry,
29
24
  getWorkBibliography,
30
25
  getWorkFolios,
31
- getWorks,
32
26
  type TranslationBlocksPage,
33
- type WorksPage,
34
27
  } from './lib/functions';
35
28
 
36
29
  // Re-export types from @eightyfourthousand/data-access for convenience
@@ -1,136 +0,0 @@
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
- }
@@ -1,44 +0,0 @@
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
- }
@@ -1,153 +0,0 @@
1
- import type { GraphQLClient } from 'graphql-request';
2
- import { gql } from 'graphql-request';
3
- import type { BodyItemType, PassagesPage } from '@eightyfourthousand/data-access';
4
- import { passagesPageFromGraphQL } from '../mappers';
5
-
6
- const GET_PASSAGES_AROUND = 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 PassageWithAnnotations on Passage {
24
- uuid
25
- workUuid
26
- content
27
- label
28
- sort
29
- type
30
- xmlId
31
- annotations {
32
- ...AnnotationFields
33
- }
34
- alignments {
35
- ...AlignmentFields
36
- }
37
- }
38
-
39
- query GetPassagesAround(
40
- $uuid: ID!
41
- $cursor: String!
42
- $limit: Int
43
- $filter: PassageFilter
44
- ) {
45
- work(uuid: $uuid) {
46
- uuid
47
- passages(
48
- cursor: $cursor
49
- limit: $limit
50
- direction: AROUND
51
- filter: $filter
52
- ) {
53
- nodes {
54
- ...PassageWithAnnotations
55
- }
56
- pageInfo {
57
- nextCursor
58
- prevCursor
59
- hasMoreAfter
60
- hasMoreBefore
61
- }
62
- }
63
- }
64
- }
65
- `;
66
-
67
- type GetPassagesAroundResponse = {
68
- work: {
69
- uuid: string;
70
- passages: {
71
- nodes: Array<{
72
- uuid: string;
73
- workUuid: string;
74
- content: string;
75
- label: string;
76
- sort: number;
77
- type: string;
78
- xmlId?: string | null;
79
- annotations: Array<{
80
- uuid: string;
81
- type: string;
82
- start: number;
83
- end: number;
84
- metadata?: Record<string, unknown> | null;
85
- }>;
86
- alignments: Array<{
87
- folioUuid: string;
88
- toh: string;
89
- tibetan: string;
90
- folioNumber: number;
91
- volumeNumber: number;
92
- }>;
93
- }>;
94
- pageInfo: {
95
- nextCursor?: string | null;
96
- prevCursor?: string | null;
97
- hasMoreAfter: boolean;
98
- hasMoreBefore: boolean;
99
- };
100
- };
101
- } | null;
102
- };
103
-
104
- /**
105
- * Get passages around a specific passage UUID.
106
- *
107
- * NOTE: maxCharacters is not supported by the GraphQL API yet.
108
- * This parameter is accepted for API compatibility but is ignored.
109
- */
110
- export async function getTranslationPassagesAround({
111
- client,
112
- uuid,
113
- passageUuid,
114
- type,
115
- maxPassages,
116
- maxCharacters: _maxCharacters,
117
- }: {
118
- client: GraphQLClient;
119
- uuid: string;
120
- passageUuid: string;
121
- type?: BodyItemType;
122
- maxPassages?: number;
123
- maxCharacters?: number;
124
- }): Promise<PassagesPage> {
125
- try {
126
- const response = await client.request<GetPassagesAroundResponse>(
127
- GET_PASSAGES_AROUND,
128
- {
129
- uuid,
130
- cursor: passageUuid,
131
- limit: maxPassages,
132
- filter: type ? { type } : undefined,
133
- },
134
- );
135
-
136
- if (!response.work) {
137
- return {
138
- passages: [],
139
- hasMoreAfter: false,
140
- hasMoreBefore: false,
141
- };
142
- }
143
-
144
- return passagesPageFromGraphQL(response.work.passages, response.work.uuid);
145
- } catch (error) {
146
- console.error('Error fetching translation passages around:', error);
147
- return {
148
- passages: [],
149
- hasMoreAfter: false,
150
- hasMoreBefore: false,
151
- };
152
- }
153
- }
@@ -1,168 +0,0 @@
1
- import type { GraphQLClient } from 'graphql-request';
2
- import { gql } from 'graphql-request';
3
- import type {
4
- BodyItemType,
5
- PaginationDirection,
6
- PassagesPage,
7
- } from '@eightyfourthousand/data-access';
8
- import { passagesPageFromGraphQL } from '../mappers';
9
-
10
- const GET_PASSAGES = gql`
11
- fragment AnnotationFields on Annotation {
12
- uuid
13
- type
14
- start
15
- end
16
- metadata
17
- }
18
-
19
- fragment AlignmentFields on Alignment {
20
- folioUuid
21
- toh
22
- tibetan
23
- folioNumber
24
- volumeNumber
25
- }
26
-
27
- fragment PassageWithAnnotations on Passage {
28
- uuid
29
- workUuid
30
- content
31
- label
32
- sort
33
- type
34
- xmlId
35
- annotations {
36
- ...AnnotationFields
37
- }
38
- alignments {
39
- ...AlignmentFields
40
- }
41
- }
42
-
43
- query GetPassages(
44
- $uuid: ID!
45
- $cursor: String
46
- $limit: Int
47
- $direction: PaginationDirection
48
- $filter: PassageFilter
49
- ) {
50
- work(uuid: $uuid) {
51
- uuid
52
- passages(
53
- cursor: $cursor
54
- limit: $limit
55
- direction: $direction
56
- filter: $filter
57
- ) {
58
- nodes {
59
- ...PassageWithAnnotations
60
- }
61
- pageInfo {
62
- nextCursor
63
- prevCursor
64
- hasMoreAfter
65
- hasMoreBefore
66
- }
67
- }
68
- }
69
- }
70
- `;
71
-
72
- type GetPassagesResponse = {
73
- work: {
74
- uuid: string;
75
- passages: {
76
- nodes: Array<{
77
- uuid: string;
78
- workUuid: string;
79
- content: string;
80
- label: string;
81
- sort: number;
82
- type: string;
83
- xmlId?: string | null;
84
- annotations: Array<{
85
- uuid: string;
86
- type: string;
87
- start: number;
88
- end: number;
89
- metadata?: Record<string, unknown> | null;
90
- }>;
91
- alignments: Array<{
92
- folioUuid: string;
93
- toh: string;
94
- tibetan: string;
95
- folioNumber: number;
96
- volumeNumber: number;
97
- }>;
98
- }>;
99
- pageInfo: {
100
- nextCursor?: string | null;
101
- prevCursor?: string | null;
102
- hasMoreAfter: boolean;
103
- hasMoreBefore: boolean;
104
- };
105
- };
106
- } | null;
107
- };
108
-
109
- /**
110
- * Map internal pagination direction to GraphQL enum
111
- */
112
- function mapDirection(
113
- direction?: PaginationDirection,
114
- ): 'FORWARD' | 'BACKWARD' | undefined {
115
- if (!direction) return undefined;
116
- return direction === 'forward' ? 'FORWARD' : 'BACKWARD';
117
- }
118
-
119
- /**
120
- * Get paginated passages for a translation work.
121
- *
122
- * NOTE: maxCharacters is not supported by the GraphQL API yet.
123
- * This parameter is accepted for API compatibility but is ignored.
124
- */
125
- export async function getTranslationPassages({
126
- client,
127
- uuid,
128
- type,
129
- cursor,
130
- maxPassages = 100,
131
- maxCharacters: _maxCharacters,
132
- direction,
133
- }: {
134
- client: GraphQLClient;
135
- uuid: string;
136
- type?: BodyItemType;
137
- cursor?: string;
138
- maxPassages?: number;
139
- maxCharacters?: number;
140
- direction?: PaginationDirection;
141
- }): Promise<PassagesPage> {
142
- try {
143
- const response = await client.request<GetPassagesResponse>(GET_PASSAGES, {
144
- uuid,
145
- cursor,
146
- limit: maxPassages,
147
- direction: mapDirection(direction),
148
- filter: type ? { type } : undefined,
149
- });
150
-
151
- if (!response.work) {
152
- return {
153
- passages: [],
154
- hasMoreAfter: false,
155
- hasMoreBefore: false,
156
- };
157
- }
158
-
159
- return passagesPageFromGraphQL(response.work.passages, response.work.uuid);
160
- } catch (error) {
161
- console.error('Error fetching translation passages:', error);
162
- return {
163
- passages: [],
164
- hasMoreAfter: false,
165
- hasMoreBefore: false,
166
- };
167
- }
168
- }
@@ -1,54 +0,0 @@
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_WORK_GLOSSARY = gql`
6
- query GetWorkGlossary($uuid: ID!, $withAttestations: Boolean) {
7
- work(uuid: $uuid) {
8
- glossary(withAttestations: $withAttestations) {
9
- uuid
10
- authority
11
- definition
12
- names {
13
- english
14
- tibetan
15
- sanskrit
16
- pali
17
- chinese
18
- wylie
19
- }
20
- }
21
- }
22
- }
23
- `;
24
-
25
- type GetWorkGlossaryResponse = {
26
- work: {
27
- glossary: GlossaryTermInstance[];
28
- } | null;
29
- };
30
-
31
- /**
32
- * Get glossary term instances for a specific work.
33
- */
34
- export async function getWorkGlossary({
35
- client,
36
- uuid,
37
- withAttestations = false,
38
- }: {
39
- client: GraphQLClient;
40
- uuid: string;
41
- withAttestations?: boolean;
42
- }): Promise<GlossaryTermInstance[]> {
43
- try {
44
- const response = await client.request<GetWorkGlossaryResponse>(
45
- GET_WORK_GLOSSARY,
46
- { uuid, withAttestations },
47
- );
48
-
49
- return response.work?.glossary ?? [];
50
- } catch (error) {
51
- console.error('Error fetching work glossary:', error);
52
- return [];
53
- }
54
- }
@@ -1,111 +0,0 @@
1
- import type { GraphQLClient } from 'graphql-request';
2
- import { gql } from 'graphql-request';
3
- import type { Work } from '@eightyfourthousand/data-access';
4
-
5
- const GET_WORKS = gql`
6
- query GetWorks($cursor: String, $limit: Int) {
7
- works(cursor: $cursor, limit: $limit) {
8
- items {
9
- uuid
10
- title
11
- toh
12
- publicationDate
13
- publicationVersion
14
- pages
15
- restriction
16
- section
17
- }
18
- pageInfo {
19
- nextCursor
20
- prevCursor
21
- hasMoreAfter
22
- hasMoreBefore
23
- }
24
- totalCount
25
- }
26
- }
27
- `;
28
-
29
- type GraphQLWork = {
30
- uuid: string;
31
- title: string;
32
- toh: string[];
33
- publicationDate: string;
34
- publicationVersion: string;
35
- pages: number;
36
- restriction: boolean;
37
- section: string;
38
- };
39
-
40
- type PageInfo = {
41
- nextCursor: string | null;
42
- prevCursor: string | null;
43
- hasMoreAfter: boolean;
44
- hasMoreBefore: boolean;
45
- };
46
-
47
- type GetWorksResponse = {
48
- works: {
49
- items: GraphQLWork[];
50
- pageInfo: PageInfo;
51
- totalCount: number;
52
- };
53
- };
54
-
55
- export type WorksPage = {
56
- items: Work[];
57
- pageInfo: PageInfo;
58
- totalCount: number;
59
- };
60
-
61
- function workFromGraphQL(gqlWork: GraphQLWork): Work {
62
- return {
63
- uuid: gqlWork.uuid,
64
- title: gqlWork.title,
65
- toh: gqlWork.toh as Work['toh'],
66
- publicationDate: new Date(gqlWork.publicationDate),
67
- publicationVersion:
68
- gqlWork.publicationVersion as Work['publicationVersion'],
69
- pages: gqlWork.pages,
70
- restriction: gqlWork.restriction,
71
- section: gqlWork.section,
72
- };
73
- }
74
-
75
- /**
76
- * Get paginated works.
77
- */
78
- export async function getWorks({
79
- client,
80
- cursor,
81
- limit = 50,
82
- }: {
83
- client: GraphQLClient;
84
- cursor?: string;
85
- limit?: number;
86
- }): Promise<WorksPage> {
87
- try {
88
- const response = await client.request<GetWorksResponse>(GET_WORKS, {
89
- cursor,
90
- limit,
91
- });
92
-
93
- return {
94
- items: response.works.items.map(workFromGraphQL),
95
- pageInfo: response.works.pageInfo,
96
- totalCount: response.works.totalCount,
97
- };
98
- } catch (error) {
99
- console.error('Error fetching works:', error);
100
- return {
101
- items: [],
102
- pageInfo: {
103
- nextCursor: null,
104
- prevCursor: null,
105
- hasMoreAfter: false,
106
- hasMoreBefore: false,
107
- },
108
- totalCount: 0,
109
- };
110
- }
111
- }