@dotcms/client 1.0.6-next.3 → 1.0.6-next.4

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/index.cjs.js CHANGED
@@ -1,8 +1,117 @@
1
1
  'use strict';
2
2
 
3
3
  var consola = require('consola');
4
+ var types = require('@dotcms/types');
4
5
  var internal = require('./internal.cjs.js');
5
6
 
7
+ /**
8
+ * HTTP client implementation using the Fetch API.
9
+ *
10
+ * Extends BaseHttpClient to provide a standard interface for making HTTP requests.
11
+ * This implementation uses the native Fetch API and handles:
12
+ * - JSON and non-JSON response parsing
13
+ * - HTTP error response parsing and conversion to DotHttpError
14
+ * - Network error handling and wrapping
15
+ * - Content-Type detection for proper response handling
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const client = new FetchHttpClient();
20
+ *
21
+ * // JSON request
22
+ * const data = await client.request<MyType>('/api/data', {
23
+ * method: 'GET',
24
+ * headers: { 'Authorization': 'Bearer token' }
25
+ * });
26
+ *
27
+ * // Non-JSON request (e.g., file download)
28
+ * const response = await client.request<Response>('/api/file.pdf', {
29
+ * method: 'GET'
30
+ * });
31
+ * ```
32
+ */
33
+ class FetchHttpClient extends types.BaseHttpClient {
34
+ /**
35
+ * Sends an HTTP request using the Fetch API.
36
+ *
37
+ * Implements the abstract request method from BaseHttpClient using the native Fetch API.
38
+ * Automatically handles response parsing based on Content-Type headers and converts
39
+ * HTTP errors to standardized DotHttpError instances.
40
+ *
41
+ * @template T - The expected response type. For JSON responses, T should be the parsed object type.
42
+ * For non-JSON responses, T should be Response or the expected response type.
43
+ * @param url - The URL to send the request to.
44
+ * @param options - Optional fetch options including method, headers, body, etc.
45
+ * @returns Promise that resolves with the parsed response data or the Response object for non-JSON.
46
+ * @throws {DotHttpError} - Throws DotHttpError for HTTP errors (4xx/5xx status codes).
47
+ * @throws {DotHttpError} - Throws DotHttpError for network errors (connection issues, timeouts).
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * // JSON API request
52
+ * const user = await client.request<User>('/api/users/123', {
53
+ * method: 'GET',
54
+ * headers: { 'Accept': 'application/json' }
55
+ * });
56
+ *
57
+ * // POST request with JSON body
58
+ * const result = await client.request<CreateResult>('/api/users', {
59
+ * method: 'POST',
60
+ * headers: { 'Content-Type': 'application/json' },
61
+ * body: JSON.stringify({ name: 'John', email: 'john@example.com' })
62
+ * });
63
+ *
64
+ * // File download (non-JSON response)
65
+ * const response = await client.request<Response>('/api/files/document.pdf', {
66
+ * method: 'GET'
67
+ * });
68
+ * ```
69
+ */
70
+ async request(url, options) {
71
+ try {
72
+ // Use native fetch API - no additional configuration needed
73
+ const response = await fetch(url, options);
74
+ if (!response.ok) {
75
+ // Parse response body for error context
76
+ let errorBody;
77
+ try {
78
+ const contentType = response.headers.get('content-type');
79
+ if (contentType?.includes('application/json')) {
80
+ errorBody = await response.json();
81
+ }
82
+ else {
83
+ errorBody = await response.text();
84
+ }
85
+ }
86
+ catch {
87
+ errorBody = response.statusText;
88
+ }
89
+ // Convert headers to plain object
90
+ const headers = {};
91
+ response.headers.forEach((value, key) => {
92
+ headers[key] = value;
93
+ });
94
+ throw this.createHttpError(response.status, response.statusText, headers, errorBody);
95
+ }
96
+ // Handle different response types
97
+ const contentType = response.headers.get('content-type');
98
+ if (contentType?.includes('application/json')) {
99
+ return response.json();
100
+ }
101
+ // For non-JSON responses, return the response object
102
+ // Sub-clients can handle specific response types as needed
103
+ return response;
104
+ }
105
+ catch (error) {
106
+ // Handle network errors (fetch throws TypeError for network issues)
107
+ if (error instanceof TypeError) {
108
+ throw this.createNetworkError(error);
109
+ }
110
+ throw error;
111
+ }
112
+ }
113
+ }
114
+
6
115
  /******************************************************************************
7
116
  Copyright (c) Microsoft Corporation.
8
117
 
@@ -637,7 +746,7 @@ class QueryBuilder {
637
746
  }
638
747
  _QueryBuilder_query = new WeakMap();
639
748
 
640
- var _CollectionBuilder_page, _CollectionBuilder_limit, _CollectionBuilder_depth, _CollectionBuilder_render, _CollectionBuilder_sortBy, _CollectionBuilder_contentType, _CollectionBuilder_defaultQuery, _CollectionBuilder_query, _CollectionBuilder_rawQuery, _CollectionBuilder_languageId, _CollectionBuilder_draft, _CollectionBuilder_requestOptions, _CollectionBuilder_config;
749
+ var _CollectionBuilder_page, _CollectionBuilder_limit, _CollectionBuilder_depth, _CollectionBuilder_render, _CollectionBuilder_sortBy, _CollectionBuilder_contentType, _CollectionBuilder_defaultQuery, _CollectionBuilder_query, _CollectionBuilder_rawQuery, _CollectionBuilder_languageId, _CollectionBuilder_draft, _CollectionBuilder_requestOptions, _CollectionBuilder_httpClient, _CollectionBuilder_config;
641
750
  /**
642
751
  * Creates a Builder to filter and fetch content from the content API for a specific content type.
643
752
  *
@@ -651,9 +760,10 @@ class CollectionBuilder {
651
760
  * @param {ClientOptions} requestOptions Options for the client request.
652
761
  * @param {DotCMSClientConfig} config The client configuration.
653
762
  * @param {string} contentType The content type to fetch.
763
+ * @param {DotHttpClient} httpClient HTTP client for making requests.
654
764
  * @memberof CollectionBuilder
655
765
  */
656
- constructor(requestOptions, config, contentType) {
766
+ constructor(requestOptions, config, contentType, httpClient) {
657
767
  _CollectionBuilder_page.set(this, 1);
658
768
  _CollectionBuilder_limit.set(this, 10);
659
769
  _CollectionBuilder_depth.set(this, 0);
@@ -666,10 +776,13 @@ class CollectionBuilder {
666
776
  _CollectionBuilder_languageId.set(this, 1);
667
777
  _CollectionBuilder_draft.set(this, false);
668
778
  _CollectionBuilder_requestOptions.set(this, void 0);
779
+ _CollectionBuilder_httpClient.set(this, void 0);
669
780
  _CollectionBuilder_config.set(this, void 0);
670
781
  __classPrivateFieldSet(this, _CollectionBuilder_requestOptions, requestOptions, "f");
671
782
  __classPrivateFieldSet(this, _CollectionBuilder_config, config, "f");
672
783
  __classPrivateFieldSet(this, _CollectionBuilder_contentType, contentType, "f");
784
+ __classPrivateFieldSet(this, _CollectionBuilder_httpClient, httpClient, "f");
785
+ __classPrivateFieldSet(this, _CollectionBuilder_config, config, "f");
673
786
  // Build the default query with the contentType field
674
787
  __classPrivateFieldSet(this, _CollectionBuilder_defaultQuery, new QueryBuilder().field('contentType').equals(__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")), "f");
675
788
  }
@@ -896,26 +1009,36 @@ class CollectionBuilder {
896
1009
  *
897
1010
  * @param {OnFullfilled} [onfulfilled] A callback that is called when the fetch is successful.
898
1011
  * @param {OnRejected} [onrejected] A callback that is called when the fetch fails.
899
- * @return {Promise<GetCollectionResponse<T> | GetCollectionError>} A promise that resolves to the content or rejects with an error.
1012
+ * @return {Promise<GetCollectionResponse<T> | DotErrorContent>} A promise that resolves to the content or rejects with an error.
900
1013
  * @memberof CollectionBuilder
901
1014
  */
902
1015
  then(onfulfilled, onrejected) {
903
- return this.fetch().then(async (response) => {
904
- const data = await response.json();
905
- if (response.ok) {
906
- const formattedResponse = this.formatResponse(data);
907
- const finalResponse = typeof onfulfilled === 'function'
908
- ? onfulfilled(formattedResponse)
909
- : formattedResponse;
910
- return finalResponse;
1016
+ return this.fetch().then((data) => {
1017
+ const formattedResponse = this.formatResponse(data);
1018
+ if (typeof onfulfilled === 'function') {
1019
+ const result = onfulfilled(formattedResponse);
1020
+ // Ensure we always return a value, fallback to formattedResponse if callback returns undefined
1021
+ return result ?? formattedResponse;
1022
+ }
1023
+ return formattedResponse;
1024
+ }, (error) => {
1025
+ // Wrap error in DotCMSContentError
1026
+ let contentError;
1027
+ if (error instanceof types.DotHttpError) {
1028
+ contentError = new types.DotErrorContent(`Content API failed for '${__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")}' (fetch): ${error.message}`, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"), 'fetch', error, this.getFinalQuery());
911
1029
  }
912
1030
  else {
913
- return {
914
- status: response.status,
915
- ...data
916
- };
1031
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1032
+ contentError = new types.DotErrorContent(`Content API failed for '${__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")}' (fetch): ${errorMessage}`, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"), 'fetch', undefined, this.getFinalQuery());
917
1033
  }
918
- }, onrejected);
1034
+ if (typeof onrejected === 'function') {
1035
+ const result = onrejected(contentError);
1036
+ // Ensure we always return a value, fallback to original error if callback returns undefined
1037
+ return result ?? contentError;
1038
+ }
1039
+ // Throw the wrapped error to trigger .catch()
1040
+ throw contentError;
1041
+ });
919
1042
  }
920
1043
  /**
921
1044
  * Formats the response to the desired format.
@@ -945,14 +1068,15 @@ class CollectionBuilder {
945
1068
  * Calls the content API to fetch the content.
946
1069
  *
947
1070
  * @private
948
- * @return {Promise<Response>} The fetch response.
1071
+ * @return {Promise<GetCollectionRawResponse<T>>} The fetch response data.
1072
+ * @throws {DotHttpError} When the HTTP request fails.
949
1073
  * @memberof CollectionBuilder
950
1074
  */
951
1075
  fetch() {
952
1076
  const finalQuery = this.getFinalQuery();
953
1077
  const sanitizedQuery = sanitizeQueryForContentType(finalQuery, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"));
954
1078
  const query = __classPrivateFieldGet(this, _CollectionBuilder_rawQuery, "f") ? `${sanitizedQuery} ${__classPrivateFieldGet(this, _CollectionBuilder_rawQuery, "f")}` : sanitizedQuery;
955
- return fetch(this.url, {
1079
+ return __classPrivateFieldGet(this, _CollectionBuilder_httpClient, "f").request(this.url, {
956
1080
  ...__classPrivateFieldGet(this, _CollectionBuilder_requestOptions, "f"),
957
1081
  method: 'POST',
958
1082
  headers: {
@@ -1024,9 +1148,9 @@ class CollectionBuilder {
1024
1148
  return baseQuery;
1025
1149
  }
1026
1150
  }
1027
- _CollectionBuilder_page = new WeakMap(), _CollectionBuilder_limit = new WeakMap(), _CollectionBuilder_depth = new WeakMap(), _CollectionBuilder_render = new WeakMap(), _CollectionBuilder_sortBy = new WeakMap(), _CollectionBuilder_contentType = new WeakMap(), _CollectionBuilder_defaultQuery = new WeakMap(), _CollectionBuilder_query = new WeakMap(), _CollectionBuilder_rawQuery = new WeakMap(), _CollectionBuilder_languageId = new WeakMap(), _CollectionBuilder_draft = new WeakMap(), _CollectionBuilder_requestOptions = new WeakMap(), _CollectionBuilder_config = new WeakMap();
1151
+ _CollectionBuilder_page = new WeakMap(), _CollectionBuilder_limit = new WeakMap(), _CollectionBuilder_depth = new WeakMap(), _CollectionBuilder_render = new WeakMap(), _CollectionBuilder_sortBy = new WeakMap(), _CollectionBuilder_contentType = new WeakMap(), _CollectionBuilder_defaultQuery = new WeakMap(), _CollectionBuilder_query = new WeakMap(), _CollectionBuilder_rawQuery = new WeakMap(), _CollectionBuilder_languageId = new WeakMap(), _CollectionBuilder_draft = new WeakMap(), _CollectionBuilder_requestOptions = new WeakMap(), _CollectionBuilder_httpClient = new WeakMap(), _CollectionBuilder_config = new WeakMap();
1028
1152
 
1029
- var _Content_requestOptions, _Content_config;
1153
+ var _Content_requestOptions, _Content_httpClient, _Content_config;
1030
1154
  /**
1031
1155
  * Creates a builder to filter and fetch a collection of content items.
1032
1156
  * @param contentType - The content type to retrieve.
@@ -1079,14 +1203,17 @@ var _Content_requestOptions, _Content_config;
1079
1203
  class Content {
1080
1204
  /**
1081
1205
  * Creates an instance of Content.
1082
- * @param {RequestOptions} requestOptions - The options for the client request.
1206
+ * @param {DotRequestOptions} requestOptions - The options for the client request.
1083
1207
  * @param {string} serverUrl - The server URL.
1208
+ * @param {DotHttpClient} httpClient - HTTP client for making requests.
1084
1209
  */
1085
- constructor(config, requestOptions) {
1210
+ constructor(config, requestOptions, httpClient) {
1086
1211
  _Content_requestOptions.set(this, void 0);
1212
+ _Content_httpClient.set(this, void 0);
1087
1213
  _Content_config.set(this, void 0);
1088
1214
  __classPrivateFieldSet(this, _Content_requestOptions, requestOptions, "f");
1089
1215
  __classPrivateFieldSet(this, _Content_config, config, "f");
1216
+ __classPrivateFieldSet(this, _Content_httpClient, httpClient, "f");
1090
1217
  }
1091
1218
  /**
1092
1219
  * Takes a content type and returns a builder to filter and fetch the collection.
@@ -1154,35 +1281,44 @@ class Content {
1154
1281
  *
1155
1282
  */
1156
1283
  getCollection(contentType) {
1157
- return new CollectionBuilder(__classPrivateFieldGet(this, _Content_requestOptions, "f"), __classPrivateFieldGet(this, _Content_config, "f"), contentType);
1284
+ return new CollectionBuilder(__classPrivateFieldGet(this, _Content_requestOptions, "f"), __classPrivateFieldGet(this, _Content_config, "f"), contentType, __classPrivateFieldGet(this, _Content_httpClient, "f"));
1158
1285
  }
1159
1286
  }
1160
- _Content_requestOptions = new WeakMap(), _Content_config = new WeakMap();
1287
+ _Content_requestOptions = new WeakMap(), _Content_httpClient = new WeakMap(), _Content_config = new WeakMap();
1161
1288
 
1162
1289
  class NavigationClient {
1163
- constructor(config, requestOptions) {
1290
+ constructor(config, requestOptions, httpClient) {
1164
1291
  this.requestOptions = requestOptions;
1165
1292
  this.BASE_URL = `${config?.dotcmsUrl}/api/v1/nav`;
1293
+ this.httpClient = httpClient;
1166
1294
  }
1167
1295
  /**
1168
1296
  * Retrieves information about the dotCMS file and folder tree.
1169
- * @param {NavigationApiOptions} options - The options for the Navigation API call. Defaults to `{ depth: 0, path: '/', languageId: 1 }`.
1297
+ * @param {string} path - The path to retrieve navigation for.
1298
+ * @param {DotCMSNavigationRequestParams} params - The options for the Navigation API call.
1170
1299
  * @returns {Promise<DotCMSNavigationItem[]>} - A Promise that resolves to the response from the DotCMS API.
1171
- * @throws {Error} - Throws an error if the options are not valid.
1300
+ * @throws {DotErrorNavigation} - Throws a navigation-specific error if the request fails.
1172
1301
  */
1173
1302
  async get(path, params) {
1174
1303
  if (!path) {
1175
- throw new Error("The 'path' parameter is required for the Navigation API");
1304
+ throw new types.DotErrorNavigation("The 'path' parameter is required for the Navigation API", path);
1176
1305
  }
1177
1306
  const navParams = params ? this.mapToBackendParams(params) : {};
1178
1307
  const urlParams = new URLSearchParams(navParams).toString();
1179
1308
  const parsedPath = path.replace(/^\/+/, '/').replace(/\/+$/, '/');
1180
1309
  const url = `${this.BASE_URL}${parsedPath}${urlParams ? `?${urlParams}` : ''}`;
1181
- const response = await fetch(url, this.requestOptions);
1182
- if (!response.ok) {
1183
- throw new Error(`Failed to fetch navigation data: ${response.statusText} - ${response.status}`);
1310
+ try {
1311
+ const response = await this.httpClient.request(url, this.requestOptions);
1312
+ return response.entity;
1313
+ }
1314
+ catch (error) {
1315
+ // Handle DotHttpError instances from httpClient.request
1316
+ if (error instanceof types.DotHttpError) {
1317
+ throw new types.DotErrorNavigation(`Navigation API failed for path '${parsedPath}': ${error.message}`, parsedPath, error);
1318
+ }
1319
+ // Handle other errors (validation, network, etc.)
1320
+ throw new types.DotErrorNavigation(`Navigation API failed for path '${parsedPath}': ${error instanceof Error ? error.message : 'Unknown error'}`, parsedPath);
1184
1321
  }
1185
- return response.json().then((data) => data.entity);
1186
1322
  }
1187
1323
  mapToBackendParams(params) {
1188
1324
  const backendParams = {};
@@ -1196,26 +1332,6 @@ class NavigationClient {
1196
1332
  }
1197
1333
  }
1198
1334
 
1199
- /**
1200
- * A record of HTTP status codes and their corresponding error messages.
1201
- *
1202
- * @type {Record<number, string>}
1203
- * @property {string} 401 - Unauthorized. Check the token and try again.
1204
- * @property {string} 403 - Forbidden. Check the permissions and try again.
1205
- * @property {string} 404 - Not Found. Check the URL and try again.
1206
- * @property {string} 500 - Internal Server Error. Try again later.
1207
- * @property {string} 502 - Bad Gateway. Try again later.
1208
- * @property {string} 503 - Service Unavailable. Try again later.
1209
- */
1210
- const ErrorMessages = {
1211
- 401: 'Unauthorized. Check the token and try again.',
1212
- 403: 'Forbidden. Check the permissions and try again.',
1213
- 404: 'Not Found. Check the URL and try again.',
1214
- 500: 'Internal Server Error. Try again later.',
1215
- 502: 'Bad Gateway. Try again later.',
1216
- 503: 'Service Unavailable. Try again later.'
1217
- };
1218
-
1219
1335
  const DEFAULT_PAGE_CONTENTLETS_CONTENT = `
1220
1336
  publishDate
1221
1337
  inode
@@ -1431,11 +1547,14 @@ function buildQuery(queryData) {
1431
1547
  /**
1432
1548
  * Filters response data to include only specified keys.
1433
1549
  *
1434
- * @param {Record<string, string>} responseData - Original response data object
1550
+ * @param {Record<string, unknown> | undefined} responseData - Original response data object
1435
1551
  * @param {string[]} keys - Array of keys to extract from the response data
1436
- * @returns {Record<string, string>} New object containing only the specified keys
1552
+ * @returns {Record<string, unknown> | undefined} New object containing only the specified keys
1437
1553
  */
1438
- function mapResponseData(responseData, keys) {
1554
+ function mapContentResponse(responseData, keys) {
1555
+ if (!responseData) {
1556
+ return undefined;
1557
+ }
1439
1558
  return keys.reduce((accumulator, key) => {
1440
1559
  if (responseData[key] !== undefined) {
1441
1560
  accumulator[key] = responseData[key];
@@ -1449,25 +1568,19 @@ function mapResponseData(responseData, keys) {
1449
1568
  * @param {Object} options - Options for the fetch request
1450
1569
  * @param {string} options.body - GraphQL query string
1451
1570
  * @param {Record<string, string>} options.headers - HTTP headers for the request
1452
- * @returns {Promise<any>} Parsed JSON response from the GraphQL API
1453
- * @throws {Error} If the HTTP response is not successful
1571
+ * @param {DotHttpClient} options.httpClient - HTTP client for making requests
1572
+ * @returns {Promise<DotGraphQLApiResponse>} Parsed JSON response from the GraphQL API
1573
+ * @throws {DotHttpError} If the HTTP request fails (non-2xx status or network error)
1454
1574
  */
1455
- async function fetchGraphQL({ baseURL, body, headers }) {
1575
+ async function fetchGraphQL({ baseURL, body, headers, httpClient }) {
1456
1576
  const url = new URL(baseURL);
1457
1577
  url.pathname = '/api/v1/graphql';
1458
- const response = await fetch(url.toString(), {
1578
+ // httpClient.request throws DotHttpError on failure, so we just return the response directly
1579
+ return await httpClient.request(url.toString(), {
1459
1580
  method: 'POST',
1460
1581
  body,
1461
1582
  headers
1462
1583
  });
1463
- if (!response.ok) {
1464
- const error = {
1465
- status: response.status,
1466
- message: ErrorMessages[response.status] || response.statusText
1467
- };
1468
- throw error;
1469
- }
1470
- return await response.json();
1471
1584
  }
1472
1585
 
1473
1586
  /**
@@ -1479,7 +1592,8 @@ class PageClient {
1479
1592
  * Creates a new PageClient instance.
1480
1593
  *
1481
1594
  * @param {DotCMSClientConfig} config - Configuration options for the DotCMS client
1482
- * @param {RequestOptions} requestOptions - Options for fetch requests including authorization headers
1595
+ * @param {DotRequestOptions} requestOptions - Options for fetch requests including authorization headers
1596
+ * @param {DotHttpClient} httpClient - HTTP client for making requests
1483
1597
  * @example
1484
1598
  * ```typescript
1485
1599
  * const pageClient = new PageClient(
@@ -1492,14 +1606,16 @@ class PageClient {
1492
1606
  * headers: {
1493
1607
  * Authorization: 'Bearer your-auth-token'
1494
1608
  * }
1495
- * }
1609
+ * },
1610
+ * httpClient
1496
1611
  * );
1497
1612
  * ```
1498
1613
  */
1499
- constructor(config, requestOptions) {
1614
+ constructor(config, requestOptions, httpClient) {
1500
1615
  this.requestOptions = requestOptions;
1501
1616
  this.siteId = config.siteId || '';
1502
1617
  this.dotcmsUrl = config.dotcmsUrl;
1618
+ this.httpClient = httpClient;
1503
1619
  }
1504
1620
  /**
1505
1621
  * Retrieves a page from DotCMS using GraphQL.
@@ -1508,6 +1624,7 @@ class PageClient {
1508
1624
  * @param {DotCMSPageRequestParams} [options] - Options for the request
1509
1625
  * @template T - The type of the page and content, defaults to DotCMSBasicPage and Record<string, unknown> | unknown
1510
1626
  * @returns {Promise<DotCMSComposedPageResponse<T>>} A Promise that resolves to the page data
1627
+ * @throws {DotErrorPage} - Throws a page-specific error if the request fails or page is not found
1511
1628
  *
1512
1629
  * @example Using GraphQL
1513
1630
  * ```typescript
@@ -1575,21 +1692,26 @@ class PageClient {
1575
1692
  const requestHeaders = this.requestOptions.headers;
1576
1693
  const requestBody = JSON.stringify({ query: completeQuery, variables: requestVariables });
1577
1694
  try {
1578
- const { data, errors } = await fetchGraphQL({
1695
+ const response = await fetchGraphQL({
1579
1696
  baseURL: this.dotcmsUrl,
1580
1697
  body: requestBody,
1581
- headers: requestHeaders
1698
+ headers: requestHeaders,
1699
+ httpClient: this.httpClient
1582
1700
  });
1583
- if (errors) {
1584
- errors.forEach((error) => {
1701
+ // The GQL endpoint can return errors and data, we need to handle both
1702
+ if (response.errors) {
1703
+ response.errors.forEach((error) => {
1585
1704
  consola.consola.error('[DotCMS GraphQL Error]: ', error.message);
1586
1705
  });
1587
1706
  }
1588
- const pageResponse = internal.graphqlToPageEntity(data);
1707
+ const pageResponse = internal.graphqlToPageEntity(response.data.page);
1589
1708
  if (!pageResponse) {
1590
- throw new Error('No page data found');
1709
+ throw new types.DotErrorPage(`Page ${url} not found. Check the page URL and permissions.`, undefined, {
1710
+ query: completeQuery,
1711
+ variables: requestVariables
1712
+ });
1591
1713
  }
1592
- const contentResponse = mapResponseData(data, Object.keys(content));
1714
+ const contentResponse = mapContentResponse(response.data, Object.keys(content));
1593
1715
  return {
1594
1716
  pageAsset: pageResponse,
1595
1717
  content: contentResponse,
@@ -1600,15 +1722,18 @@ class PageClient {
1600
1722
  };
1601
1723
  }
1602
1724
  catch (error) {
1603
- const errorMessage = {
1604
- error,
1605
- message: 'Failed to retrieve page data',
1606
- graphql: {
1725
+ // Handle DotHttpError instances from httpClient.request
1726
+ if (error instanceof types.DotHttpError) {
1727
+ throw new types.DotErrorPage(`Page request failed for URL '${url}': ${error.message}`, error, {
1607
1728
  query: completeQuery,
1608
1729
  variables: requestVariables
1609
- }
1610
- };
1611
- throw errorMessage;
1730
+ });
1731
+ }
1732
+ // Handle other errors (GraphQL errors, validation errors, etc.)
1733
+ throw new types.DotErrorPage(`Page request failed for URL '${url}': ${error instanceof Error ? error.message : 'Unknown error'}`, undefined, {
1734
+ query: completeQuery,
1735
+ variables: requestVariables
1736
+ });
1612
1737
  }
1613
1738
  }
1614
1739
  }
@@ -1649,11 +1774,12 @@ class DotCMSClient {
1649
1774
  */
1650
1775
  constructor(config = defaultConfig) {
1651
1776
  this.config = config;
1777
+ this.httpClient = config.httpClient || new FetchHttpClient();
1652
1778
  this.requestOptions = this.createAuthenticatedRequestOptions(this.config);
1653
- // Initialize clients
1654
- this.page = new PageClient(this.config, this.requestOptions);
1655
- this.nav = new NavigationClient(this.config, this.requestOptions);
1656
- this.content = new Content(this.config, this.requestOptions);
1779
+ // Initialize clients with httpClient
1780
+ this.page = new PageClient(this.config, this.requestOptions, this.httpClient);
1781
+ this.nav = new NavigationClient(this.config, this.requestOptions, this.httpClient);
1782
+ this.content = new Content(this.config, this.requestOptions, this.httpClient);
1657
1783
  }
1658
1784
  /**
1659
1785
  * Creates request options with authentication headers.