@dotcms/client 1.5.1-next.1965 → 1.5.1-next.1977
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 +13 -8
- package/index.cjs.js +83 -38
- package/index.esm.js +83 -38
- package/package.json +1 -1
- package/src/lib/client/page/utils.d.ts +2 -1
package/README.md
CHANGED
|
@@ -699,13 +699,14 @@ createDotCMSClient(config: DotCMSClientConfig): DotCMSClient
|
|
|
699
699
|
|
|
700
700
|
#### Parameters
|
|
701
701
|
|
|
702
|
-
| Option | Type
|
|
703
|
-
| ---------------- |
|
|
704
|
-
| `dotcmsUrl` | string
|
|
705
|
-
| `authToken` | string
|
|
706
|
-
| `siteId` | string
|
|
707
|
-
| `requestOptions` | DotRequestOptions
|
|
708
|
-
| `httpClient` | DotHttpClient
|
|
702
|
+
| Option | Type | Required | Description |
|
|
703
|
+
| ---------------- | -------------------------- | -------- | ------------------------------------------------------------- |
|
|
704
|
+
| `dotcmsUrl` | string | ✅ | Your dotCMS instance URL |
|
|
705
|
+
| `authToken` | string | ✅ | Authentication token |
|
|
706
|
+
| `siteId` | string | ❌ | Site identifier (falls back to default site if not specified) |
|
|
707
|
+
| `requestOptions` | DotRequestOptions | ❌ | Additional request options |
|
|
708
|
+
| `httpClient` | DotHttpClient | ❌ | Custom HTTP client implementation |
|
|
709
|
+
| `logLevel` | `'default'` \| `'verbose'` | ❌ | Controls log verbosity. `'verbose'` adds status, code, and variables to error logs. Defaults to `'default'` |
|
|
709
710
|
|
|
710
711
|
#### Example
|
|
711
712
|
```typescript
|
|
@@ -713,10 +714,14 @@ const client = createDotCMSClient({
|
|
|
713
714
|
dotcmsUrl: 'https://your-dotcms-instance.com',
|
|
714
715
|
authToken: 'your-auth-token',
|
|
715
716
|
siteId: 'your-site-id',
|
|
716
|
-
httpClient: customHttpClient // Optional: provide custom HTTP client
|
|
717
|
+
httpClient: customHttpClient, // Optional: provide custom HTTP client
|
|
718
|
+
logLevel: 'verbose' // Optional: enable detailed error logs
|
|
717
719
|
});
|
|
718
720
|
```
|
|
719
721
|
|
|
722
|
+
> [!TIP]
|
|
723
|
+
> Enable `logLevel: 'verbose'` during development to see HTTP status codes, error codes, and request variables in error logs. In verbose mode, error logs also include a hint to access the full GraphQL query via `error.graphql.query`. Keep it at `'default'` (or omit it) in production to avoid noisy logs.
|
|
724
|
+
|
|
720
725
|
### HTTP Client Configuration
|
|
721
726
|
|
|
722
727
|
The SDK now supports custom HTTP client implementations for advanced use cases. By default, it uses the built-in `FetchHttpClient` based on the native Fetch API.
|
package/index.cjs.js
CHANGED
|
@@ -2107,9 +2107,9 @@ const DEFAULT_PAGE_CONTENTLETS_CONTENT = `
|
|
|
2107
2107
|
* @param {string} additionalQueries - Additional GraphQL queries to include in the main query
|
|
2108
2108
|
* @returns {string} Complete GraphQL query string for page content
|
|
2109
2109
|
*/
|
|
2110
|
-
const buildPageQuery = ({ page, fragments, additionalQueries }) => {
|
|
2111
|
-
if (!page) {
|
|
2112
|
-
|
|
2110
|
+
const buildPageQuery = ({ page, fragments, additionalQueries, verbose = false }) => {
|
|
2111
|
+
if (!page && verbose) {
|
|
2112
|
+
console.warn("[DotCMS Client]: No page query was found, so we're loading all content using _map. This might slow things down. For better performance, we recommend adding a specific query in the page attribute.");
|
|
2113
2113
|
}
|
|
2114
2114
|
return `
|
|
2115
2115
|
fragment DotCMSPage on DotPage {
|
|
@@ -2316,11 +2316,8 @@ function mapContentResponse(responseData, keys) {
|
|
|
2316
2316
|
* @internal
|
|
2317
2317
|
*/
|
|
2318
2318
|
async function fetchStyleEditorSchemas(pageId, config, requestOptions, httpClient) {
|
|
2319
|
-
if (typeof window === 'undefined') {
|
|
2320
|
-
return [];
|
|
2321
|
-
}
|
|
2322
2319
|
if (!pageId) {
|
|
2323
|
-
|
|
2320
|
+
console.warn('[DotCMS PageClient]: fetchStyleEditorSchemas called without a pageId — ' +
|
|
2324
2321
|
'make sure "identifier" is included in your GraphQL page fragment.');
|
|
2325
2322
|
return [];
|
|
2326
2323
|
}
|
|
@@ -2343,11 +2340,11 @@ async function fetchStyleEditorSchemas(pageId, config, requestOptions, httpClien
|
|
|
2343
2340
|
}
|
|
2344
2341
|
catch (error) {
|
|
2345
2342
|
if (error instanceof types.DotHttpError && (error.status === 401 || error.status === 403)) {
|
|
2346
|
-
|
|
2343
|
+
console.warn(`[DotCMS PageClient]: Style editor schemas request failed with ${error.status} — ` +
|
|
2347
2344
|
'make sure your DotCMS client is configured with a valid authToken that has READ access to the page.');
|
|
2348
2345
|
}
|
|
2349
2346
|
else {
|
|
2350
|
-
|
|
2347
|
+
console.warn('[DotCMS PageClient]: Skipping style editor schemas:', error);
|
|
2351
2348
|
}
|
|
2352
2349
|
return [];
|
|
2353
2350
|
}
|
|
@@ -2373,6 +2370,11 @@ async function fetchGraphQL({ baseURL, body, headers, httpClient }) {
|
|
|
2373
2370
|
});
|
|
2374
2371
|
}
|
|
2375
2372
|
|
|
2373
|
+
function logVerboseError(url, message, details) {
|
|
2374
|
+
const statusLine = details.status !== undefined ? `\n status: ${details.status} | code: ${details.code}` : '';
|
|
2375
|
+
const variables = JSON.stringify(details.variables, null, 2).replace(/\n/g, '\n ');
|
|
2376
|
+
consola.consola.error(`[DotCMS GraphQL Error] ${url}: ${message}${statusLine}\n\n variables:\n ${variables}\n\n (full query available at error.graphql.query)`);
|
|
2377
|
+
}
|
|
2376
2378
|
/**
|
|
2377
2379
|
* Client for interacting with the DotCMS Page API.
|
|
2378
2380
|
* Provides methods to retrieve and manipulate pages.
|
|
@@ -2458,15 +2460,18 @@ class PageClient extends BaseApiClient {
|
|
|
2458
2460
|
async get(url, options) {
|
|
2459
2461
|
const { languageId = '1', mode = 'LIVE', siteId = this.siteId, fireRules = false, personaId, publishDate, variantName, graphql = {} } = options || {};
|
|
2460
2462
|
const { page, content = {}, variables, fragments } = graphql;
|
|
2463
|
+
const verbose = this.config.logLevel === 'verbose';
|
|
2461
2464
|
const contentQuery = buildQuery(content);
|
|
2462
2465
|
const completeQuery = buildPageQuery({
|
|
2463
2466
|
page,
|
|
2464
2467
|
fragments,
|
|
2465
|
-
additionalQueries: contentQuery
|
|
2468
|
+
additionalQueries: contentQuery,
|
|
2469
|
+
verbose
|
|
2466
2470
|
});
|
|
2471
|
+
const normalizedUrl = url.startsWith('/') ? url : `/${url}`;
|
|
2467
2472
|
const requestVariables = {
|
|
2468
2473
|
// The url is expected to have a leading slash to comply on VanityURL Matching, some frameworks like Angular will not add the leading slash
|
|
2469
|
-
url:
|
|
2474
|
+
url: normalizedUrl,
|
|
2470
2475
|
mode,
|
|
2471
2476
|
languageId,
|
|
2472
2477
|
personaId,
|
|
@@ -2485,33 +2490,77 @@ class PageClient extends BaseApiClient {
|
|
|
2485
2490
|
headers: requestHeaders,
|
|
2486
2491
|
httpClient: this.httpClient
|
|
2487
2492
|
});
|
|
2488
|
-
//
|
|
2489
|
-
if (response.errors) {
|
|
2490
|
-
response.errors
|
|
2491
|
-
|
|
2493
|
+
// 1. Log unstructured GraphQL errors (structured ones are logged with enriched messages below)
|
|
2494
|
+
if (response.errors?.length) {
|
|
2495
|
+
response.errors
|
|
2496
|
+
.filter((error) => !error.extensions?.code)
|
|
2497
|
+
.forEach((error) => {
|
|
2498
|
+
if (verbose) {
|
|
2499
|
+
logVerboseError(normalizedUrl, error.message, {
|
|
2500
|
+
variables: requestVariables
|
|
2501
|
+
});
|
|
2502
|
+
}
|
|
2503
|
+
else {
|
|
2504
|
+
consola.consola.error(`[DotCMS GraphQL Error] ${normalizedUrl}: `, error.message);
|
|
2505
|
+
}
|
|
2492
2506
|
});
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2507
|
+
}
|
|
2508
|
+
// 2. BAD QUERY — data is null/undefined means the entire query failed
|
|
2509
|
+
// (syntax error, unknown type, validation error)
|
|
2510
|
+
// Must check BEFORE accessing response.data.page
|
|
2511
|
+
if (!response.data) {
|
|
2512
|
+
const firstError = response.errors?.[0];
|
|
2513
|
+
throw new types.DotErrorPage(firstError?.message ?? 'GraphQL query failed', 400, 'BAD_REQUEST', new types.DotHttpError({
|
|
2514
|
+
status: 400,
|
|
2515
|
+
statusText: 'Bad Request',
|
|
2516
|
+
message: firstError?.message ?? 'GraphQL query failed',
|
|
2517
|
+
data: response.errors
|
|
2518
|
+
}), { query: completeQuery, variables: requestVariables });
|
|
2519
|
+
}
|
|
2520
|
+
// 3. STRUCTURED ERRORS — check extensions.code for NOT_FOUND, PERMISSION_DENIED, etc.
|
|
2521
|
+
// Only fatal when the page itself failed (data.page is null/undefined).
|
|
2522
|
+
// If data.page exists, partial errors (e.g. secondary content) surface via errors[].
|
|
2523
|
+
if (response.errors?.length && !response.data.page) {
|
|
2524
|
+
const structuredError = response.errors.find((error) => error.extensions?.code);
|
|
2525
|
+
if (structuredError) {
|
|
2526
|
+
const code = structuredError.extensions.code;
|
|
2527
|
+
const status = structuredError.extensions.status ??
|
|
2528
|
+
(code === 'NOT_FOUND' ? 404 : code === 'PERMISSION_DENIED' ? 403 : 400);
|
|
2529
|
+
const message = code === 'NOT_FOUND'
|
|
2530
|
+
? `Page '${normalizedUrl}' was not found`
|
|
2531
|
+
: code === 'PERMISSION_DENIED'
|
|
2532
|
+
? `Permission denied: you do not have access to page '${normalizedUrl}'. Verify the page permissions in dotCMS and that the auth token has sufficient access.`
|
|
2533
|
+
: `Page '${normalizedUrl}' could not be loaded (${code})`;
|
|
2534
|
+
if (verbose) {
|
|
2535
|
+
logVerboseError(normalizedUrl, message, {
|
|
2536
|
+
status,
|
|
2537
|
+
code,
|
|
2538
|
+
variables: requestVariables
|
|
2539
|
+
});
|
|
2540
|
+
}
|
|
2541
|
+
else {
|
|
2542
|
+
consola.consola.error(`[DotCMS GraphQL Error] ${normalizedUrl}: `, message);
|
|
2543
|
+
}
|
|
2544
|
+
throw new types.DotErrorPage(message, status, code, undefined, {
|
|
2545
|
+
query: completeQuery,
|
|
2546
|
+
variables: requestVariables
|
|
2501
2547
|
});
|
|
2502
2548
|
}
|
|
2503
2549
|
}
|
|
2504
|
-
|
|
2550
|
+
// 4. Transform and check page — null page with no structured error = 404
|
|
2551
|
+
const pageResponse = response.data.page
|
|
2552
|
+
? internal.graphqlToPageEntity(response.data.page)
|
|
2553
|
+
: null;
|
|
2505
2554
|
if (!pageResponse) {
|
|
2506
|
-
|
|
2507
|
-
throw new types.DotHttpError({
|
|
2555
|
+
throw new types.DotErrorPage(`Page '${normalizedUrl}' was not found`, 404, 'NOT_FOUND', new types.DotHttpError({
|
|
2508
2556
|
status: 404,
|
|
2509
2557
|
statusText: 'Not Found',
|
|
2510
|
-
message: `Page ${
|
|
2558
|
+
message: `Page '${normalizedUrl}' was not found`,
|
|
2511
2559
|
data: response.errors
|
|
2512
|
-
});
|
|
2560
|
+
}), { query: completeQuery, variables: requestVariables });
|
|
2513
2561
|
}
|
|
2514
2562
|
const styleEditorSchemas = await fetchStyleEditorSchemas(pageResponse.page.identifier, this.config, this.requestOptions, this.httpClient);
|
|
2563
|
+
// 5. Build response — include any non-fatal errors for consumers to inspect
|
|
2515
2564
|
const contentResponse = mapContentResponse(response.data, Object.keys(content));
|
|
2516
2565
|
return {
|
|
2517
2566
|
pageAsset: pageResponse,
|
|
@@ -2520,22 +2569,18 @@ class PageClient extends BaseApiClient {
|
|
|
2520
2569
|
query: completeQuery,
|
|
2521
2570
|
variables: requestVariables
|
|
2522
2571
|
},
|
|
2572
|
+
errors: response.errors?.length ? response.errors : undefined,
|
|
2523
2573
|
...(styleEditorSchemas.length > 0 && { styleEditorSchemas })
|
|
2524
2574
|
};
|
|
2525
2575
|
}
|
|
2526
2576
|
catch (error) {
|
|
2527
|
-
|
|
2577
|
+
if (error instanceof types.DotErrorPage) {
|
|
2578
|
+
throw error;
|
|
2579
|
+
}
|
|
2528
2580
|
if (error instanceof types.DotHttpError) {
|
|
2529
|
-
throw new types.DotErrorPage(`Page request failed for URL '${
|
|
2530
|
-
query: completeQuery,
|
|
2531
|
-
variables: requestVariables
|
|
2532
|
-
});
|
|
2581
|
+
throw new types.DotErrorPage(`Page request failed for URL '${normalizedUrl}': ${error.message}`, error.status, 'UNKNOWN', error, { query: completeQuery, variables: requestVariables });
|
|
2533
2582
|
}
|
|
2534
|
-
|
|
2535
|
-
throw new types.DotErrorPage(`Page request failed for URL '${url}': ${error instanceof Error ? error.message : 'Unknown error'}`, undefined, {
|
|
2536
|
-
query: completeQuery,
|
|
2537
|
-
variables: requestVariables
|
|
2538
|
-
});
|
|
2583
|
+
throw new types.DotErrorPage(`Page request failed for URL '${normalizedUrl}': ${error instanceof Error ? error.message : 'Unknown error'}`, 500, 'UNKNOWN', undefined, { query: completeQuery, variables: requestVariables });
|
|
2539
2584
|
}
|
|
2540
2585
|
}
|
|
2541
2586
|
}
|
package/index.esm.js
CHANGED
|
@@ -2105,9 +2105,9 @@ const DEFAULT_PAGE_CONTENTLETS_CONTENT = `
|
|
|
2105
2105
|
* @param {string} additionalQueries - Additional GraphQL queries to include in the main query
|
|
2106
2106
|
* @returns {string} Complete GraphQL query string for page content
|
|
2107
2107
|
*/
|
|
2108
|
-
const buildPageQuery = ({ page, fragments, additionalQueries }) => {
|
|
2109
|
-
if (!page) {
|
|
2110
|
-
|
|
2108
|
+
const buildPageQuery = ({ page, fragments, additionalQueries, verbose = false }) => {
|
|
2109
|
+
if (!page && verbose) {
|
|
2110
|
+
console.warn("[DotCMS Client]: No page query was found, so we're loading all content using _map. This might slow things down. For better performance, we recommend adding a specific query in the page attribute.");
|
|
2111
2111
|
}
|
|
2112
2112
|
return `
|
|
2113
2113
|
fragment DotCMSPage on DotPage {
|
|
@@ -2314,11 +2314,8 @@ function mapContentResponse(responseData, keys) {
|
|
|
2314
2314
|
* @internal
|
|
2315
2315
|
*/
|
|
2316
2316
|
async function fetchStyleEditorSchemas(pageId, config, requestOptions, httpClient) {
|
|
2317
|
-
if (typeof window === 'undefined') {
|
|
2318
|
-
return [];
|
|
2319
|
-
}
|
|
2320
2317
|
if (!pageId) {
|
|
2321
|
-
|
|
2318
|
+
console.warn('[DotCMS PageClient]: fetchStyleEditorSchemas called without a pageId — ' +
|
|
2322
2319
|
'make sure "identifier" is included in your GraphQL page fragment.');
|
|
2323
2320
|
return [];
|
|
2324
2321
|
}
|
|
@@ -2341,11 +2338,11 @@ async function fetchStyleEditorSchemas(pageId, config, requestOptions, httpClien
|
|
|
2341
2338
|
}
|
|
2342
2339
|
catch (error) {
|
|
2343
2340
|
if (error instanceof DotHttpError && (error.status === 401 || error.status === 403)) {
|
|
2344
|
-
|
|
2341
|
+
console.warn(`[DotCMS PageClient]: Style editor schemas request failed with ${error.status} — ` +
|
|
2345
2342
|
'make sure your DotCMS client is configured with a valid authToken that has READ access to the page.');
|
|
2346
2343
|
}
|
|
2347
2344
|
else {
|
|
2348
|
-
|
|
2345
|
+
console.warn('[DotCMS PageClient]: Skipping style editor schemas:', error);
|
|
2349
2346
|
}
|
|
2350
2347
|
return [];
|
|
2351
2348
|
}
|
|
@@ -2371,6 +2368,11 @@ async function fetchGraphQL({ baseURL, body, headers, httpClient }) {
|
|
|
2371
2368
|
});
|
|
2372
2369
|
}
|
|
2373
2370
|
|
|
2371
|
+
function logVerboseError(url, message, details) {
|
|
2372
|
+
const statusLine = details.status !== undefined ? `\n status: ${details.status} | code: ${details.code}` : '';
|
|
2373
|
+
const variables = JSON.stringify(details.variables, null, 2).replace(/\n/g, '\n ');
|
|
2374
|
+
consola.error(`[DotCMS GraphQL Error] ${url}: ${message}${statusLine}\n\n variables:\n ${variables}\n\n (full query available at error.graphql.query)`);
|
|
2375
|
+
}
|
|
2374
2376
|
/**
|
|
2375
2377
|
* Client for interacting with the DotCMS Page API.
|
|
2376
2378
|
* Provides methods to retrieve and manipulate pages.
|
|
@@ -2456,15 +2458,18 @@ class PageClient extends BaseApiClient {
|
|
|
2456
2458
|
async get(url, options) {
|
|
2457
2459
|
const { languageId = '1', mode = 'LIVE', siteId = this.siteId, fireRules = false, personaId, publishDate, variantName, graphql = {} } = options || {};
|
|
2458
2460
|
const { page, content = {}, variables, fragments } = graphql;
|
|
2461
|
+
const verbose = this.config.logLevel === 'verbose';
|
|
2459
2462
|
const contentQuery = buildQuery(content);
|
|
2460
2463
|
const completeQuery = buildPageQuery({
|
|
2461
2464
|
page,
|
|
2462
2465
|
fragments,
|
|
2463
|
-
additionalQueries: contentQuery
|
|
2466
|
+
additionalQueries: contentQuery,
|
|
2467
|
+
verbose
|
|
2464
2468
|
});
|
|
2469
|
+
const normalizedUrl = url.startsWith('/') ? url : `/${url}`;
|
|
2465
2470
|
const requestVariables = {
|
|
2466
2471
|
// The url is expected to have a leading slash to comply on VanityURL Matching, some frameworks like Angular will not add the leading slash
|
|
2467
|
-
url:
|
|
2472
|
+
url: normalizedUrl,
|
|
2468
2473
|
mode,
|
|
2469
2474
|
languageId,
|
|
2470
2475
|
personaId,
|
|
@@ -2483,33 +2488,77 @@ class PageClient extends BaseApiClient {
|
|
|
2483
2488
|
headers: requestHeaders,
|
|
2484
2489
|
httpClient: this.httpClient
|
|
2485
2490
|
});
|
|
2486
|
-
//
|
|
2487
|
-
if (response.errors) {
|
|
2488
|
-
response.errors
|
|
2489
|
-
|
|
2491
|
+
// 1. Log unstructured GraphQL errors (structured ones are logged with enriched messages below)
|
|
2492
|
+
if (response.errors?.length) {
|
|
2493
|
+
response.errors
|
|
2494
|
+
.filter((error) => !error.extensions?.code)
|
|
2495
|
+
.forEach((error) => {
|
|
2496
|
+
if (verbose) {
|
|
2497
|
+
logVerboseError(normalizedUrl, error.message, {
|
|
2498
|
+
variables: requestVariables
|
|
2499
|
+
});
|
|
2500
|
+
}
|
|
2501
|
+
else {
|
|
2502
|
+
consola.error(`[DotCMS GraphQL Error] ${normalizedUrl}: `, error.message);
|
|
2503
|
+
}
|
|
2490
2504
|
});
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2505
|
+
}
|
|
2506
|
+
// 2. BAD QUERY — data is null/undefined means the entire query failed
|
|
2507
|
+
// (syntax error, unknown type, validation error)
|
|
2508
|
+
// Must check BEFORE accessing response.data.page
|
|
2509
|
+
if (!response.data) {
|
|
2510
|
+
const firstError = response.errors?.[0];
|
|
2511
|
+
throw new DotErrorPage(firstError?.message ?? 'GraphQL query failed', 400, 'BAD_REQUEST', new DotHttpError({
|
|
2512
|
+
status: 400,
|
|
2513
|
+
statusText: 'Bad Request',
|
|
2514
|
+
message: firstError?.message ?? 'GraphQL query failed',
|
|
2515
|
+
data: response.errors
|
|
2516
|
+
}), { query: completeQuery, variables: requestVariables });
|
|
2517
|
+
}
|
|
2518
|
+
// 3. STRUCTURED ERRORS — check extensions.code for NOT_FOUND, PERMISSION_DENIED, etc.
|
|
2519
|
+
// Only fatal when the page itself failed (data.page is null/undefined).
|
|
2520
|
+
// If data.page exists, partial errors (e.g. secondary content) surface via errors[].
|
|
2521
|
+
if (response.errors?.length && !response.data.page) {
|
|
2522
|
+
const structuredError = response.errors.find((error) => error.extensions?.code);
|
|
2523
|
+
if (structuredError) {
|
|
2524
|
+
const code = structuredError.extensions.code;
|
|
2525
|
+
const status = structuredError.extensions.status ??
|
|
2526
|
+
(code === 'NOT_FOUND' ? 404 : code === 'PERMISSION_DENIED' ? 403 : 400);
|
|
2527
|
+
const message = code === 'NOT_FOUND'
|
|
2528
|
+
? `Page '${normalizedUrl}' was not found`
|
|
2529
|
+
: code === 'PERMISSION_DENIED'
|
|
2530
|
+
? `Permission denied: you do not have access to page '${normalizedUrl}'. Verify the page permissions in dotCMS and that the auth token has sufficient access.`
|
|
2531
|
+
: `Page '${normalizedUrl}' could not be loaded (${code})`;
|
|
2532
|
+
if (verbose) {
|
|
2533
|
+
logVerboseError(normalizedUrl, message, {
|
|
2534
|
+
status,
|
|
2535
|
+
code,
|
|
2536
|
+
variables: requestVariables
|
|
2537
|
+
});
|
|
2538
|
+
}
|
|
2539
|
+
else {
|
|
2540
|
+
consola.error(`[DotCMS GraphQL Error] ${normalizedUrl}: `, message);
|
|
2541
|
+
}
|
|
2542
|
+
throw new DotErrorPage(message, status, code, undefined, {
|
|
2543
|
+
query: completeQuery,
|
|
2544
|
+
variables: requestVariables
|
|
2499
2545
|
});
|
|
2500
2546
|
}
|
|
2501
2547
|
}
|
|
2502
|
-
|
|
2548
|
+
// 4. Transform and check page — null page with no structured error = 404
|
|
2549
|
+
const pageResponse = response.data.page
|
|
2550
|
+
? graphqlToPageEntity(response.data.page)
|
|
2551
|
+
: null;
|
|
2503
2552
|
if (!pageResponse) {
|
|
2504
|
-
|
|
2505
|
-
throw new DotHttpError({
|
|
2553
|
+
throw new DotErrorPage(`Page '${normalizedUrl}' was not found`, 404, 'NOT_FOUND', new DotHttpError({
|
|
2506
2554
|
status: 404,
|
|
2507
2555
|
statusText: 'Not Found',
|
|
2508
|
-
message: `Page ${
|
|
2556
|
+
message: `Page '${normalizedUrl}' was not found`,
|
|
2509
2557
|
data: response.errors
|
|
2510
|
-
});
|
|
2558
|
+
}), { query: completeQuery, variables: requestVariables });
|
|
2511
2559
|
}
|
|
2512
2560
|
const styleEditorSchemas = await fetchStyleEditorSchemas(pageResponse.page.identifier, this.config, this.requestOptions, this.httpClient);
|
|
2561
|
+
// 5. Build response — include any non-fatal errors for consumers to inspect
|
|
2513
2562
|
const contentResponse = mapContentResponse(response.data, Object.keys(content));
|
|
2514
2563
|
return {
|
|
2515
2564
|
pageAsset: pageResponse,
|
|
@@ -2518,22 +2567,18 @@ class PageClient extends BaseApiClient {
|
|
|
2518
2567
|
query: completeQuery,
|
|
2519
2568
|
variables: requestVariables
|
|
2520
2569
|
},
|
|
2570
|
+
errors: response.errors?.length ? response.errors : undefined,
|
|
2521
2571
|
...(styleEditorSchemas.length > 0 && { styleEditorSchemas })
|
|
2522
2572
|
};
|
|
2523
2573
|
}
|
|
2524
2574
|
catch (error) {
|
|
2525
|
-
|
|
2575
|
+
if (error instanceof DotErrorPage) {
|
|
2576
|
+
throw error;
|
|
2577
|
+
}
|
|
2526
2578
|
if (error instanceof DotHttpError) {
|
|
2527
|
-
throw new DotErrorPage(`Page request failed for URL '${
|
|
2528
|
-
query: completeQuery,
|
|
2529
|
-
variables: requestVariables
|
|
2530
|
-
});
|
|
2579
|
+
throw new DotErrorPage(`Page request failed for URL '${normalizedUrl}': ${error.message}`, error.status, 'UNKNOWN', error, { query: completeQuery, variables: requestVariables });
|
|
2531
2580
|
}
|
|
2532
|
-
|
|
2533
|
-
throw new DotErrorPage(`Page request failed for URL '${url}': ${error instanceof Error ? error.message : 'Unknown error'}`, undefined, {
|
|
2534
|
-
query: completeQuery,
|
|
2535
|
-
variables: requestVariables
|
|
2536
|
-
});
|
|
2581
|
+
throw new DotErrorPage(`Page request failed for URL '${normalizedUrl}': ${error instanceof Error ? error.message : 'Unknown error'}`, 500, 'UNKNOWN', undefined, { query: completeQuery, variables: requestVariables });
|
|
2537
2582
|
}
|
|
2538
2583
|
}
|
|
2539
2584
|
}
|
package/package.json
CHANGED
|
@@ -7,10 +7,11 @@ import { StyleEditorFormSchema } from '@dotcms/types/internal';
|
|
|
7
7
|
* @param {string} additionalQueries - Additional GraphQL queries to include in the main query
|
|
8
8
|
* @returns {string} Complete GraphQL query string for page content
|
|
9
9
|
*/
|
|
10
|
-
export declare const buildPageQuery: ({ page, fragments, additionalQueries }: {
|
|
10
|
+
export declare const buildPageQuery: ({ page, fragments, additionalQueries, verbose }: {
|
|
11
11
|
page?: string;
|
|
12
12
|
fragments?: string[];
|
|
13
13
|
additionalQueries?: string;
|
|
14
|
+
verbose?: boolean;
|
|
14
15
|
}) => string;
|
|
15
16
|
/**
|
|
16
17
|
* Converts a record of query strings into a single GraphQL query string.
|