@dotcms/client 1.6.0 → 1.7.0-next.37
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 +92 -30
- package/index.cjs.js +36 -66
- package/index.esm.js +37 -67
- package/internal.cjs.js +9 -3
- package/internal.esm.js +9 -3
- package/package.json +2 -5
- package/src/lib/client/page/utils.d.ts +9 -6
package/README.md
CHANGED
|
@@ -84,7 +84,7 @@ The `@dotcms/client` is a powerful JavaScript/TypeScript SDK designed to simplif
|
|
|
84
84
|
**For Local Development:**
|
|
85
85
|
|
|
86
86
|
- 🐳 [Docker setup guide](https://github.com/dotCMS/core/tree/main/docker/docker-compose-examples/single-node-demo-site)
|
|
87
|
-
- 💻 [Local installation guide](https://dev.dotcms.com/
|
|
87
|
+
- 💻 [Local installation guide](https://dev.dotcms.com/getting-started/setup/run-locally)
|
|
88
88
|
|
|
89
89
|
#### Create a dotCMS API Key
|
|
90
90
|
|
|
@@ -100,7 +100,7 @@ This integration requires an API Key with read-only permissions for security bes
|
|
|
100
100
|
|
|
101
101
|
For detailed instructions, please refer to the [dotCMS API Documentation - Read-only token](https://dev.dotcms.com/docs/rest-api-authentication#ReadOnlyToken).
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
### Installation
|
|
104
104
|
|
|
105
105
|
Install the SDK and required dependencies:
|
|
106
106
|
|
|
@@ -121,7 +121,7 @@ import { createDotCMSClient } from '@dotcms/client';
|
|
|
121
121
|
// Create a client instance
|
|
122
122
|
const client = createDotCMSClient({
|
|
123
123
|
dotcmsUrl: 'https://your-dotcms-instance.com',
|
|
124
|
-
authToken: 'your-auth-token',
|
|
124
|
+
authToken: 'your-auth-token',
|
|
125
125
|
siteId: 'your-site-id' // Optional site identifier
|
|
126
126
|
});
|
|
127
127
|
|
|
@@ -361,7 +361,7 @@ const response = await client.ai.search(
|
|
|
361
361
|
);
|
|
362
362
|
|
|
363
363
|
// Access results with match scores
|
|
364
|
-
|
|
364
|
+
response.results.forEach(result => {
|
|
365
365
|
console.log(result.title);
|
|
366
366
|
console.log('Matches:', result.matches); // Distance and extracted text
|
|
367
367
|
});
|
|
@@ -457,11 +457,8 @@ response.contentlets.forEach(post => {
|
|
|
457
457
|
#### Typing AI Search Results
|
|
458
458
|
|
|
459
459
|
```typescript
|
|
460
|
-
import
|
|
461
|
-
|
|
462
|
-
DotCMSBasicContentlet,
|
|
463
|
-
DISTANCE_FUNCTIONS
|
|
464
|
-
} from '@dotcms/types';
|
|
460
|
+
import { DISTANCE_FUNCTIONS } from '@dotcms/types';
|
|
461
|
+
import type { DotCMSAISearchResponse, DotCMSBasicContentlet } from '@dotcms/types';
|
|
465
462
|
|
|
466
463
|
// Define your content type
|
|
467
464
|
interface Article extends DotCMSBasicContentlet {
|
|
@@ -688,6 +685,69 @@ const response = await client.page.get('/about-us', {
|
|
|
688
685
|
});
|
|
689
686
|
```
|
|
690
687
|
|
|
688
|
+
### How to Enable Page Editing
|
|
689
|
+
|
|
690
|
+
The `@dotcms/client` SDK is responsible for **fetching** your page, while a framework SDK ([`@dotcms/react`](https://www.npmjs.com/package/@dotcms/react) or [`@dotcms/angular`](https://www.npmjs.com/package/@dotcms/angular)) makes that page **editable** inside the [Universal Visual Editor (UVE)](https://dev.dotcms.com/docs/uve-headless-config).
|
|
691
|
+
|
|
692
|
+
The flow is always the same three steps:
|
|
693
|
+
|
|
694
|
+
1. **Fetch the page** on the server with `client.page.get()`.
|
|
695
|
+
2. **Connect the page to the editor** with the framework hook/service (`useEditableDotCMSPage` in React, `DotCMSEditablePageService` in Angular).
|
|
696
|
+
3. **Render the layout** with `DotCMSLayoutBody`, mapping your content types to components.
|
|
697
|
+
|
|
698
|
+
#### 1. Fetch the page with `client.page.get()`
|
|
699
|
+
|
|
700
|
+
Fetch the full page response on the server. The complete response object — not just `pageAsset` — must be forwarded to the editor layer, because it carries the data UVE needs to track changes.
|
|
701
|
+
|
|
702
|
+
```typescript
|
|
703
|
+
// server-side, e.g. a Next.js Server Component
|
|
704
|
+
import { createDotCMSClient } from '@dotcms/client';
|
|
705
|
+
|
|
706
|
+
const client = createDotCMSClient({
|
|
707
|
+
dotcmsUrl: 'https://your-dotcms-instance.com',
|
|
708
|
+
authToken: 'your-auth-token',
|
|
709
|
+
siteId: 'your-site-id'
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
// Return the whole response so the framework SDK can make it editable
|
|
713
|
+
export async function getPage(path: string) {
|
|
714
|
+
return await client.page.get(path);
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
#### 2. Make the page editable (React example)
|
|
719
|
+
|
|
720
|
+
Pass the full page response into `useEditableDotCMSPage`. The hook keeps the page in sync with UVE while editing and returns the same `pageAsset` / `content` shape you get from `client.page.get()`, so the component works identically in and out of the editor.
|
|
721
|
+
|
|
722
|
+
```tsx
|
|
723
|
+
'use client';
|
|
724
|
+
|
|
725
|
+
import { DotCMSLayoutBody, useEditableDotCMSPage } from '@dotcms/react';
|
|
726
|
+
import { pageComponents } from '@/components/content-types';
|
|
727
|
+
|
|
728
|
+
export function Page({ pageContent }) {
|
|
729
|
+
// `pageContent` is the full response from client.page.get()
|
|
730
|
+
const { pageAsset } = useEditableDotCMSPage(pageContent);
|
|
731
|
+
|
|
732
|
+
return (
|
|
733
|
+
<DotCMSLayoutBody
|
|
734
|
+
page={pageAsset}
|
|
735
|
+
components={pageComponents}
|
|
736
|
+
mode={process.env.NEXT_PUBLIC_DOTCMS_MODE}
|
|
737
|
+
/>
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
> 💡 Using Angular? Use [`DotCMSEditablePageService`](https://www.npmjs.com/package/@dotcms/angular) together with `DotCMSLayoutBody` — the same fetch → make-editable → render flow applies.
|
|
743
|
+
|
|
744
|
+
#### 3. Render the layout with `DotCMSLayoutBody`
|
|
745
|
+
|
|
746
|
+
`DotCMSLayoutBody` renders the page's rows, columns, and containers, and maps each contentlet to one of your components via the `components` prop. When loaded inside UVE it automatically applies the `data-dot-*` attributes that make the page editable — no extra wiring required.
|
|
747
|
+
|
|
748
|
+
#### Working example
|
|
749
|
+
|
|
750
|
+
See the page-editing flow end to end in the official Next.js example — [`examples/nextjs`](https://github.com/dotCMS/core/tree/main/examples/nextjs). In particular, [`src/views/Page.tsx`](https://github.com/dotCMS/core/blob/main/examples/nextjs/src/views/Page.tsx) uses `useEditableDotCMSPage` and `DotCMSLayoutBody` exactly as shown above.
|
|
691
751
|
|
|
692
752
|
## API Reference
|
|
693
753
|
|
|
@@ -816,14 +876,15 @@ getCollection<T = DotCMSBasicContentlet>(
|
|
|
816
876
|
|
|
817
877
|
#### Builder Methods
|
|
818
878
|
|
|
819
|
-
| Method
|
|
820
|
-
|
|
|
821
|
-
| `query()`
|
|
822
|
-
| `limit()`
|
|
823
|
-
| `page()`
|
|
824
|
-
| `sortBy()`
|
|
825
|
-
| `language()`
|
|
826
|
-
| `depth()`
|
|
879
|
+
| Method | Arguments | Description |
|
|
880
|
+
| --------------------- | ----------------------------- | ------------------------------------------------------------------ |
|
|
881
|
+
| `query()` | `string` \| `BuildQuery` | Filter content using query builder |
|
|
882
|
+
| `limit()` | `number` | Set number of items to return |
|
|
883
|
+
| `page()` | `number` | Set which page of results to fetch |
|
|
884
|
+
| `sortBy()` | `SortBy[]` | Sort by one or more fields |
|
|
885
|
+
| `language()` | `number \| string` | Set content language |
|
|
886
|
+
| `depth()` | `number` | Set depth of related content |
|
|
887
|
+
| `includeSystemHost()` | - | Include content from the System Host alongside the configured site |
|
|
827
888
|
|
|
828
889
|
#### Example
|
|
829
890
|
```typescript
|
|
@@ -1137,12 +1198,13 @@ DotHttpError: "Network request failed"
|
|
|
1137
1198
|
|
|
1138
1199
|
### Choosing the Right Method
|
|
1139
1200
|
|
|
1140
|
-
The dotCMS Client SDK provides
|
|
1201
|
+
The dotCMS Client SDK provides five core methods for fetching data. Use this quick guide to decide which one is best for your use case:
|
|
1141
1202
|
|
|
1142
1203
|
| Method | Use When You Need... | Best For |
|
|
1143
1204
|
| -------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1144
1205
|
| `client.page.get()` | A full page with layout, containers, and related content | **Rendering entire pages** with a single request. Ideal for headless setups, SSR/SSG frameworks, and cases where you want everything—page structure, content, and navigation—tied to a URL path. |
|
|
1145
|
-
| `client.content.getCollection()` | A filtered list of content items from a specific content type | Populating dynamic blocks, lists, search results, widgets, or reusable components.
|
|
1206
|
+
| `client.content.getCollection()` | A filtered list of content items from a specific content type | Populating dynamic blocks, lists, search results, widgets, or reusable components using the fluent query builder. |
|
|
1207
|
+
| `client.content.query()` | Full control over a raw Lucene query string | Advanced search scenarios where you need direct Lucene syntax without the `getCollection()` query-builder DSL or automatic `contentType.` field prefixing. |
|
|
1146
1208
|
| `client.ai.search()` | Semantic/AI-powered content discovery based on natural language | **Intelligent search experiences** where users describe what they're looking for in natural language. Great for search features, content recommendations, and finding relevant content by meaning rather than exact keywords. ⚠️ **Experimental API** |
|
|
1147
1209
|
| `client.navigation.get()` | Only the site's navigation structure (folders and links) | Standalone menus or use cases where navigation is needed outside of page context. |
|
|
1148
1210
|
|
|
@@ -1156,7 +1218,7 @@ For most use cases, `client.page.get()` is all you need. It lets you retrieve:
|
|
|
1156
1218
|
|
|
1157
1219
|
All in a single request using GraphQL.
|
|
1158
1220
|
|
|
1159
|
-
Only use `content.getCollection()` or `navigation.get()` if you have advanced needs, like real-time data fetching or building custom dynamic components.
|
|
1221
|
+
Only use `content.getCollection()`, `content.query()`, or `navigation.get()` if you have advanced needs, like real-time data fetching or building custom dynamic components.
|
|
1160
1222
|
|
|
1161
1223
|
> 🔍 **For comprehensive examples of advanced GraphQL querying including relationships and custom fields,** see the [How to Work with GraphQL](#how-to-work-with-graphql) section.
|
|
1162
1224
|
|
|
@@ -1165,7 +1227,7 @@ Only use `content.getCollection()` or `navigation.get()` if you have advanced ne
|
|
|
1165
1227
|
The SDK follows a client-builder pattern with four main APIs:
|
|
1166
1228
|
|
|
1167
1229
|
- **Page API** (`client.page.get()`) - Fetches complete page content with layout and containers
|
|
1168
|
-
- **Content API** (`client.content.getCollection()`) - Builder pattern for querying content collections
|
|
1230
|
+
- **Content API** (`client.content.getCollection()`, `client.content.query()`) - Builder pattern for querying content collections or raw Lucene queries
|
|
1169
1231
|
- **AI API** (`client.ai.search()`) - AI-powered semantic search using embeddings and vector similarity ⚠️ **Experimental**
|
|
1170
1232
|
- **Navigation API** (`client.navigation.get()`) - Fetches site navigation structure
|
|
1171
1233
|
|
|
@@ -1182,7 +1244,7 @@ We offer multiple channels to get help with the dotCMS Client SDK:
|
|
|
1182
1244
|
- **GitHub Issues**: For bug reports and feature requests, please [open an issue](https://github.com/dotCMS/core/issues/new/choose) in the GitHub repository.
|
|
1183
1245
|
- **Community Forum**: Join our [community discussions](https://community.dotcms.com/) to ask questions and share solutions.
|
|
1184
1246
|
- **Stack Overflow**: Use the tag `dotcms-client` when posting questions.
|
|
1185
|
-
- **Enterprise Support**: Enterprise customers can access premium support through the [dotCMS Support Portal](https://
|
|
1247
|
+
- **Enterprise Support**: Enterprise customers can access premium support through the [dotCMS Support Portal](https://www.dotcms.com/support).
|
|
1186
1248
|
|
|
1187
1249
|
When reporting issues, please include:
|
|
1188
1250
|
|
|
@@ -1203,6 +1265,14 @@ GitHub pull requests are the preferred method to contribute code to dotCMS. We w
|
|
|
1203
1265
|
|
|
1204
1266
|
Please ensure your code follows the existing style and includes appropriate tests.
|
|
1205
1267
|
|
|
1268
|
+
## Licensing
|
|
1269
|
+
|
|
1270
|
+
dotCMS is available under either the [Business Source License 1.1 (BSL)](https://www.dotcms.com/bsl) or a commercial license.
|
|
1271
|
+
|
|
1272
|
+
Under the BSL, dotCMS can be used at no cost by individual developers, small businesses or agencies under $5M in total finances, and by larger organizations in non-production environments. Every BSL release automatically converts to GPL v3 four years after its release date. For full terms and FAQs, visit [dotcms.com/bsl](https://www.dotcms.com/bsl) and [dotcms.com/bsl-faq](https://www.dotcms.com/bsl-faq).
|
|
1273
|
+
|
|
1274
|
+
Production use in larger organizations, along with access to managed cloud, SLAs, support, and enterprise capabilities, is available under a commercial license from dotCMS. For details on commercial plans, features, and support options, see [dotcms.com/pricing](https://www.dotcms.com/pricing).
|
|
1275
|
+
|
|
1206
1276
|
## Changelog
|
|
1207
1277
|
|
|
1208
1278
|
### v1.3.0
|
|
@@ -1362,11 +1432,3 @@ import { RequestOptions } from '@dotcms/types';
|
|
|
1362
1432
|
// After
|
|
1363
1433
|
import { DotRequestOptions } from '@dotcms/types';
|
|
1364
1434
|
```
|
|
1365
|
-
|
|
1366
|
-
## Licensing
|
|
1367
|
-
|
|
1368
|
-
dotCMS comes in multiple editions and as such is dual-licensed. The dotCMS Community Edition is licensed under the GPL 3.0 and is freely available for download, customization, and deployment for use within organizations of all stripes. dotCMS Enterprise Editions (EE) adds several enterprise features and is available via a supported, indemnified commercial license from dotCMS. For the differences between the editions, see [the feature page](http://www.dotcms.com/cms-platform/features).
|
|
1369
|
-
|
|
1370
|
-
This SDK is part of dotCMS's dual-licensed platform (GPL 3.0 for Community, commercial license for Enterprise).
|
|
1371
|
-
|
|
1372
|
-
[Learn more ](https://www.dotcms.com)at [dotcms.com](https://www.dotcms.com).
|
package/index.cjs.js
CHANGED
|
@@ -112,7 +112,7 @@ class FetchHttpClient extends types.BaseHttpClient {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
/*! *****************************************************************************
|
|
116
116
|
Copyright (c) Microsoft Corporation.
|
|
117
117
|
|
|
118
118
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
@@ -126,7 +126,7 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
126
126
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
127
127
|
PERFORMANCE OF THIS SOFTWARE.
|
|
128
128
|
***************************************************************************** */
|
|
129
|
-
/* global Reflect, Promise
|
|
129
|
+
/* global Reflect, Promise */
|
|
130
130
|
|
|
131
131
|
|
|
132
132
|
function __classPrivateFieldGet(receiver, state, kind, f) {
|
|
@@ -140,12 +140,7 @@ function __classPrivateFieldSet(receiver, state, value, kind, f) {
|
|
|
140
140
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
141
141
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
142
142
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
146
|
-
var e = new Error(message);
|
|
147
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
148
|
-
};
|
|
143
|
+
}
|
|
149
144
|
|
|
150
145
|
/**
|
|
151
146
|
* Utility functions for AI search parameter mapping and processing
|
|
@@ -2160,6 +2155,7 @@ const buildPageQuery = ({ page, fragments, additionalQueries, verbose = false })
|
|
|
2160
2155
|
lockedBy
|
|
2161
2156
|
lockedByName
|
|
2162
2157
|
numberContents
|
|
2158
|
+
styleEditorSchemas
|
|
2163
2159
|
urlContentMap {
|
|
2164
2160
|
_map
|
|
2165
2161
|
}
|
|
@@ -2310,44 +2306,17 @@ function mapContentResponse(responseData, keys) {
|
|
|
2310
2306
|
}, {});
|
|
2311
2307
|
}
|
|
2312
2308
|
/**
|
|
2313
|
-
*
|
|
2314
|
-
*
|
|
2309
|
+
* Returns a shallow copy of the object with every key whose value is `undefined` removed.
|
|
2310
|
+
*
|
|
2311
|
+
* `undefined` is not valid JSON, so keeping such keys breaks consumers that serialize the value
|
|
2312
|
+
* (e.g. Next.js Pages Router `getServerSideProps`/`getStaticProps`). `null` and other falsy values
|
|
2313
|
+
* are preserved since they serialize fine.
|
|
2315
2314
|
*
|
|
2316
|
-
* @
|
|
2315
|
+
* @param {Record<string, unknown>} object - Source object to clean
|
|
2316
|
+
* @returns {Record<string, unknown>} New object without `undefined` values
|
|
2317
2317
|
*/
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
console.warn('[DotCMS PageClient]: fetchStyleEditorSchemas called without a pageId — ' +
|
|
2321
|
-
'make sure "identifier" is included in your GraphQL page fragment.');
|
|
2322
|
-
return [];
|
|
2323
|
-
}
|
|
2324
|
-
try {
|
|
2325
|
-
const url = new URL(config.dotcmsUrl);
|
|
2326
|
-
url.pathname = `/api/v1/page/${encodeURIComponent(pageId)}/contenttype-schema`;
|
|
2327
|
-
const data = await httpClient.request(url.toString(), {
|
|
2328
|
-
...requestOptions,
|
|
2329
|
-
method: 'GET',
|
|
2330
|
-
headers: {
|
|
2331
|
-
Accept: 'application/json',
|
|
2332
|
-
...requestOptions.headers
|
|
2333
|
-
}
|
|
2334
|
-
});
|
|
2335
|
-
const { entity } = data ?? {};
|
|
2336
|
-
if (!Array.isArray(entity)) {
|
|
2337
|
-
return [];
|
|
2338
|
-
}
|
|
2339
|
-
return entity;
|
|
2340
|
-
}
|
|
2341
|
-
catch (error) {
|
|
2342
|
-
if (error instanceof types.DotHttpError && (error.status === 401 || error.status === 403)) {
|
|
2343
|
-
console.warn(`[DotCMS PageClient]: Style editor schemas request failed with ${error.status} — ` +
|
|
2344
|
-
'make sure your DotCMS client is configured with a valid authToken that has READ access to the page.');
|
|
2345
|
-
}
|
|
2346
|
-
else {
|
|
2347
|
-
console.warn('[DotCMS PageClient]: Skipping style editor schemas:', error);
|
|
2348
|
-
}
|
|
2349
|
-
return [];
|
|
2350
|
-
}
|
|
2318
|
+
function removeUndefinedValues(object) {
|
|
2319
|
+
return Object.fromEntries(Object.entries(object).filter(([, value]) => value !== undefined));
|
|
2351
2320
|
}
|
|
2352
2321
|
/**
|
|
2353
2322
|
* Executes a GraphQL query against the DotCMS API.
|
|
@@ -2468,11 +2437,10 @@ class PageClient extends BaseApiClient {
|
|
|
2468
2437
|
additionalQueries: contentQuery,
|
|
2469
2438
|
verbose
|
|
2470
2439
|
});
|
|
2471
|
-
const
|
|
2472
|
-
const requestVariables = {
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
mode,
|
|
2440
|
+
const newURL = url.startsWith('/') ? url : `/${url}`;
|
|
2441
|
+
const requestVariables = removeUndefinedValues({
|
|
2442
|
+
url: newURL,
|
|
2443
|
+
mode: types.UVE_MODE[mode], // Translate the UVE_MODE key ('EDIT' | 'PREVIEW' | ...) to the value the backend PageMode enum expects ('EDIT_MODE' | 'PREVIEW_MODE' | ...)
|
|
2476
2444
|
languageId,
|
|
2477
2445
|
personaId,
|
|
2478
2446
|
fireRules,
|
|
@@ -2480,7 +2448,7 @@ class PageClient extends BaseApiClient {
|
|
|
2480
2448
|
siteId,
|
|
2481
2449
|
variantName,
|
|
2482
2450
|
...variables
|
|
2483
|
-
};
|
|
2451
|
+
});
|
|
2484
2452
|
const requestHeaders = this.requestOptions.headers;
|
|
2485
2453
|
const requestBody = JSON.stringify({ query: completeQuery, variables: requestVariables });
|
|
2486
2454
|
try {
|
|
@@ -2496,12 +2464,12 @@ class PageClient extends BaseApiClient {
|
|
|
2496
2464
|
.filter((error) => !error.extensions?.code)
|
|
2497
2465
|
.forEach((error) => {
|
|
2498
2466
|
if (verbose) {
|
|
2499
|
-
logVerboseError(
|
|
2467
|
+
logVerboseError(newURL, error.message, {
|
|
2500
2468
|
variables: requestVariables
|
|
2501
2469
|
});
|
|
2502
2470
|
}
|
|
2503
2471
|
else {
|
|
2504
|
-
consola.consola.error(`[DotCMS GraphQL Error] ${
|
|
2472
|
+
consola.consola.error(`[DotCMS GraphQL Error] ${newURL}: `, error.message);
|
|
2505
2473
|
}
|
|
2506
2474
|
});
|
|
2507
2475
|
}
|
|
@@ -2523,23 +2491,23 @@ class PageClient extends BaseApiClient {
|
|
|
2523
2491
|
if (response.errors?.length && !response.data.page) {
|
|
2524
2492
|
const structuredError = response.errors.find((error) => error.extensions?.code);
|
|
2525
2493
|
if (structuredError) {
|
|
2526
|
-
const code = structuredError.extensions
|
|
2527
|
-
const status = structuredError.extensions
|
|
2494
|
+
const code = structuredError.extensions?.code;
|
|
2495
|
+
const status = structuredError.extensions?.status ??
|
|
2528
2496
|
(code === 'NOT_FOUND' ? 404 : code === 'PERMISSION_DENIED' ? 403 : 400);
|
|
2529
2497
|
const message = code === 'NOT_FOUND'
|
|
2530
|
-
? `Page '${
|
|
2498
|
+
? `Page '${newURL}' was not found`
|
|
2531
2499
|
: code === 'PERMISSION_DENIED'
|
|
2532
|
-
? `Permission denied: you do not have access to page '${
|
|
2533
|
-
: `Page '${
|
|
2500
|
+
? `Permission denied: you do not have access to page '${newURL}'. Verify the page permissions in dotCMS and that the auth token has sufficient access.`
|
|
2501
|
+
: `Page '${newURL}' could not be loaded (${code})`;
|
|
2534
2502
|
if (verbose) {
|
|
2535
|
-
logVerboseError(
|
|
2503
|
+
logVerboseError(newURL, message, {
|
|
2536
2504
|
status,
|
|
2537
2505
|
code,
|
|
2538
2506
|
variables: requestVariables
|
|
2539
2507
|
});
|
|
2540
2508
|
}
|
|
2541
2509
|
else {
|
|
2542
|
-
consola.consola.error(`[DotCMS GraphQL Error] ${
|
|
2510
|
+
consola.consola.error(`[DotCMS GraphQL Error] ${newURL}: `, message);
|
|
2543
2511
|
}
|
|
2544
2512
|
throw new types.DotErrorPage(message, status, code, undefined, {
|
|
2545
2513
|
query: completeQuery,
|
|
@@ -2551,15 +2519,15 @@ class PageClient extends BaseApiClient {
|
|
|
2551
2519
|
const pageResponse = response.data.page
|
|
2552
2520
|
? internal.graphqlToPageEntity(response.data.page)
|
|
2553
2521
|
: null;
|
|
2522
|
+
const styleEditorSchemas = pageResponse ? pageResponse.page.styleEditorSchemas : [];
|
|
2554
2523
|
if (!pageResponse) {
|
|
2555
|
-
throw new types.DotErrorPage(`Page '${
|
|
2524
|
+
throw new types.DotErrorPage(`Page '${newURL}' was not found`, 404, 'NOT_FOUND', new types.DotHttpError({
|
|
2556
2525
|
status: 404,
|
|
2557
2526
|
statusText: 'Not Found',
|
|
2558
|
-
message: `Page '${
|
|
2527
|
+
message: `Page '${newURL}' was not found`,
|
|
2559
2528
|
data: response.errors
|
|
2560
2529
|
}), { query: completeQuery, variables: requestVariables });
|
|
2561
2530
|
}
|
|
2562
|
-
const styleEditorSchemas = await fetchStyleEditorSchemas(pageResponse.page.identifier, this.config, this.requestOptions, this.httpClient);
|
|
2563
2531
|
// 5. Build response — include any non-fatal errors for consumers to inspect
|
|
2564
2532
|
const contentResponse = mapContentResponse(response.data, Object.keys(content));
|
|
2565
2533
|
return {
|
|
@@ -2569,8 +2537,10 @@ class PageClient extends BaseApiClient {
|
|
|
2569
2537
|
query: completeQuery,
|
|
2570
2538
|
variables: requestVariables
|
|
2571
2539
|
},
|
|
2572
|
-
|
|
2573
|
-
|
|
2540
|
+
// Always return an array (never `undefined`) so the response stays JSON-serializable
|
|
2541
|
+
// for consumers like Next.js Pages Router (getServerSideProps/getStaticProps throw on undefined).
|
|
2542
|
+
errors: response.errors?.length ? response.errors : [],
|
|
2543
|
+
...(styleEditorSchemas?.length && { styleEditorSchemas })
|
|
2574
2544
|
};
|
|
2575
2545
|
}
|
|
2576
2546
|
catch (error) {
|
|
@@ -2578,9 +2548,9 @@ class PageClient extends BaseApiClient {
|
|
|
2578
2548
|
throw error;
|
|
2579
2549
|
}
|
|
2580
2550
|
if (error instanceof types.DotHttpError) {
|
|
2581
|
-
throw new types.DotErrorPage(`Page request failed for URL '${
|
|
2551
|
+
throw new types.DotErrorPage(`Page request failed for URL '${newURL}': ${error.message}`, error.status, 'UNKNOWN', error, { query: completeQuery, variables: requestVariables });
|
|
2582
2552
|
}
|
|
2583
|
-
throw new types.DotErrorPage(`Page request failed for URL '${
|
|
2553
|
+
throw new types.DotErrorPage(`Page request failed for URL '${newURL}': ${error instanceof Error ? error.message : 'Unknown error'}`, 500, 'UNKNOWN', undefined, { query: completeQuery, variables: requestVariables });
|
|
2584
2554
|
}
|
|
2585
2555
|
}
|
|
2586
2556
|
}
|
package/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { consola } from 'consola';
|
|
2
|
-
import { BaseHttpClient, DISTANCE_FUNCTIONS, DotHttpError, DotErrorAISearch, DotErrorContent, DotErrorNavigation, DotErrorPage } from '@dotcms/types';
|
|
2
|
+
import { BaseHttpClient, DISTANCE_FUNCTIONS, DotHttpError, DotErrorAISearch, DotErrorContent, DotErrorNavigation, UVE_MODE, DotErrorPage } from '@dotcms/types';
|
|
3
3
|
import { graphqlToPageEntity } from './internal.esm.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -110,7 +110,7 @@ class FetchHttpClient extends BaseHttpClient {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
/*! *****************************************************************************
|
|
114
114
|
Copyright (c) Microsoft Corporation.
|
|
115
115
|
|
|
116
116
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
@@ -124,7 +124,7 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
124
124
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
125
125
|
PERFORMANCE OF THIS SOFTWARE.
|
|
126
126
|
***************************************************************************** */
|
|
127
|
-
/* global Reflect, Promise
|
|
127
|
+
/* global Reflect, Promise */
|
|
128
128
|
|
|
129
129
|
|
|
130
130
|
function __classPrivateFieldGet(receiver, state, kind, f) {
|
|
@@ -138,12 +138,7 @@ function __classPrivateFieldSet(receiver, state, value, kind, f) {
|
|
|
138
138
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
139
139
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
140
140
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
144
|
-
var e = new Error(message);
|
|
145
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
146
|
-
};
|
|
141
|
+
}
|
|
147
142
|
|
|
148
143
|
/**
|
|
149
144
|
* Utility functions for AI search parameter mapping and processing
|
|
@@ -2158,6 +2153,7 @@ const buildPageQuery = ({ page, fragments, additionalQueries, verbose = false })
|
|
|
2158
2153
|
lockedBy
|
|
2159
2154
|
lockedByName
|
|
2160
2155
|
numberContents
|
|
2156
|
+
styleEditorSchemas
|
|
2161
2157
|
urlContentMap {
|
|
2162
2158
|
_map
|
|
2163
2159
|
}
|
|
@@ -2308,44 +2304,17 @@ function mapContentResponse(responseData, keys) {
|
|
|
2308
2304
|
}, {});
|
|
2309
2305
|
}
|
|
2310
2306
|
/**
|
|
2311
|
-
*
|
|
2312
|
-
*
|
|
2307
|
+
* Returns a shallow copy of the object with every key whose value is `undefined` removed.
|
|
2308
|
+
*
|
|
2309
|
+
* `undefined` is not valid JSON, so keeping such keys breaks consumers that serialize the value
|
|
2310
|
+
* (e.g. Next.js Pages Router `getServerSideProps`/`getStaticProps`). `null` and other falsy values
|
|
2311
|
+
* are preserved since they serialize fine.
|
|
2313
2312
|
*
|
|
2314
|
-
* @
|
|
2313
|
+
* @param {Record<string, unknown>} object - Source object to clean
|
|
2314
|
+
* @returns {Record<string, unknown>} New object without `undefined` values
|
|
2315
2315
|
*/
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
console.warn('[DotCMS PageClient]: fetchStyleEditorSchemas called without a pageId — ' +
|
|
2319
|
-
'make sure "identifier" is included in your GraphQL page fragment.');
|
|
2320
|
-
return [];
|
|
2321
|
-
}
|
|
2322
|
-
try {
|
|
2323
|
-
const url = new URL(config.dotcmsUrl);
|
|
2324
|
-
url.pathname = `/api/v1/page/${encodeURIComponent(pageId)}/contenttype-schema`;
|
|
2325
|
-
const data = await httpClient.request(url.toString(), {
|
|
2326
|
-
...requestOptions,
|
|
2327
|
-
method: 'GET',
|
|
2328
|
-
headers: {
|
|
2329
|
-
Accept: 'application/json',
|
|
2330
|
-
...requestOptions.headers
|
|
2331
|
-
}
|
|
2332
|
-
});
|
|
2333
|
-
const { entity } = data ?? {};
|
|
2334
|
-
if (!Array.isArray(entity)) {
|
|
2335
|
-
return [];
|
|
2336
|
-
}
|
|
2337
|
-
return entity;
|
|
2338
|
-
}
|
|
2339
|
-
catch (error) {
|
|
2340
|
-
if (error instanceof DotHttpError && (error.status === 401 || error.status === 403)) {
|
|
2341
|
-
console.warn(`[DotCMS PageClient]: Style editor schemas request failed with ${error.status} — ` +
|
|
2342
|
-
'make sure your DotCMS client is configured with a valid authToken that has READ access to the page.');
|
|
2343
|
-
}
|
|
2344
|
-
else {
|
|
2345
|
-
console.warn('[DotCMS PageClient]: Skipping style editor schemas:', error);
|
|
2346
|
-
}
|
|
2347
|
-
return [];
|
|
2348
|
-
}
|
|
2316
|
+
function removeUndefinedValues(object) {
|
|
2317
|
+
return Object.fromEntries(Object.entries(object).filter(([, value]) => value !== undefined));
|
|
2349
2318
|
}
|
|
2350
2319
|
/**
|
|
2351
2320
|
* Executes a GraphQL query against the DotCMS API.
|
|
@@ -2466,11 +2435,10 @@ class PageClient extends BaseApiClient {
|
|
|
2466
2435
|
additionalQueries: contentQuery,
|
|
2467
2436
|
verbose
|
|
2468
2437
|
});
|
|
2469
|
-
const
|
|
2470
|
-
const requestVariables = {
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
mode,
|
|
2438
|
+
const newURL = url.startsWith('/') ? url : `/${url}`;
|
|
2439
|
+
const requestVariables = removeUndefinedValues({
|
|
2440
|
+
url: newURL,
|
|
2441
|
+
mode: UVE_MODE[mode], // Translate the UVE_MODE key ('EDIT' | 'PREVIEW' | ...) to the value the backend PageMode enum expects ('EDIT_MODE' | 'PREVIEW_MODE' | ...)
|
|
2474
2442
|
languageId,
|
|
2475
2443
|
personaId,
|
|
2476
2444
|
fireRules,
|
|
@@ -2478,7 +2446,7 @@ class PageClient extends BaseApiClient {
|
|
|
2478
2446
|
siteId,
|
|
2479
2447
|
variantName,
|
|
2480
2448
|
...variables
|
|
2481
|
-
};
|
|
2449
|
+
});
|
|
2482
2450
|
const requestHeaders = this.requestOptions.headers;
|
|
2483
2451
|
const requestBody = JSON.stringify({ query: completeQuery, variables: requestVariables });
|
|
2484
2452
|
try {
|
|
@@ -2494,12 +2462,12 @@ class PageClient extends BaseApiClient {
|
|
|
2494
2462
|
.filter((error) => !error.extensions?.code)
|
|
2495
2463
|
.forEach((error) => {
|
|
2496
2464
|
if (verbose) {
|
|
2497
|
-
logVerboseError(
|
|
2465
|
+
logVerboseError(newURL, error.message, {
|
|
2498
2466
|
variables: requestVariables
|
|
2499
2467
|
});
|
|
2500
2468
|
}
|
|
2501
2469
|
else {
|
|
2502
|
-
consola.error(`[DotCMS GraphQL Error] ${
|
|
2470
|
+
consola.error(`[DotCMS GraphQL Error] ${newURL}: `, error.message);
|
|
2503
2471
|
}
|
|
2504
2472
|
});
|
|
2505
2473
|
}
|
|
@@ -2521,23 +2489,23 @@ class PageClient extends BaseApiClient {
|
|
|
2521
2489
|
if (response.errors?.length && !response.data.page) {
|
|
2522
2490
|
const structuredError = response.errors.find((error) => error.extensions?.code);
|
|
2523
2491
|
if (structuredError) {
|
|
2524
|
-
const code = structuredError.extensions
|
|
2525
|
-
const status = structuredError.extensions
|
|
2492
|
+
const code = structuredError.extensions?.code;
|
|
2493
|
+
const status = structuredError.extensions?.status ??
|
|
2526
2494
|
(code === 'NOT_FOUND' ? 404 : code === 'PERMISSION_DENIED' ? 403 : 400);
|
|
2527
2495
|
const message = code === 'NOT_FOUND'
|
|
2528
|
-
? `Page '${
|
|
2496
|
+
? `Page '${newURL}' was not found`
|
|
2529
2497
|
: code === 'PERMISSION_DENIED'
|
|
2530
|
-
? `Permission denied: you do not have access to page '${
|
|
2531
|
-
: `Page '${
|
|
2498
|
+
? `Permission denied: you do not have access to page '${newURL}'. Verify the page permissions in dotCMS and that the auth token has sufficient access.`
|
|
2499
|
+
: `Page '${newURL}' could not be loaded (${code})`;
|
|
2532
2500
|
if (verbose) {
|
|
2533
|
-
logVerboseError(
|
|
2501
|
+
logVerboseError(newURL, message, {
|
|
2534
2502
|
status,
|
|
2535
2503
|
code,
|
|
2536
2504
|
variables: requestVariables
|
|
2537
2505
|
});
|
|
2538
2506
|
}
|
|
2539
2507
|
else {
|
|
2540
|
-
consola.error(`[DotCMS GraphQL Error] ${
|
|
2508
|
+
consola.error(`[DotCMS GraphQL Error] ${newURL}: `, message);
|
|
2541
2509
|
}
|
|
2542
2510
|
throw new DotErrorPage(message, status, code, undefined, {
|
|
2543
2511
|
query: completeQuery,
|
|
@@ -2549,15 +2517,15 @@ class PageClient extends BaseApiClient {
|
|
|
2549
2517
|
const pageResponse = response.data.page
|
|
2550
2518
|
? graphqlToPageEntity(response.data.page)
|
|
2551
2519
|
: null;
|
|
2520
|
+
const styleEditorSchemas = pageResponse ? pageResponse.page.styleEditorSchemas : [];
|
|
2552
2521
|
if (!pageResponse) {
|
|
2553
|
-
throw new DotErrorPage(`Page '${
|
|
2522
|
+
throw new DotErrorPage(`Page '${newURL}' was not found`, 404, 'NOT_FOUND', new DotHttpError({
|
|
2554
2523
|
status: 404,
|
|
2555
2524
|
statusText: 'Not Found',
|
|
2556
|
-
message: `Page '${
|
|
2525
|
+
message: `Page '${newURL}' was not found`,
|
|
2557
2526
|
data: response.errors
|
|
2558
2527
|
}), { query: completeQuery, variables: requestVariables });
|
|
2559
2528
|
}
|
|
2560
|
-
const styleEditorSchemas = await fetchStyleEditorSchemas(pageResponse.page.identifier, this.config, this.requestOptions, this.httpClient);
|
|
2561
2529
|
// 5. Build response — include any non-fatal errors for consumers to inspect
|
|
2562
2530
|
const contentResponse = mapContentResponse(response.data, Object.keys(content));
|
|
2563
2531
|
return {
|
|
@@ -2567,8 +2535,10 @@ class PageClient extends BaseApiClient {
|
|
|
2567
2535
|
query: completeQuery,
|
|
2568
2536
|
variables: requestVariables
|
|
2569
2537
|
},
|
|
2570
|
-
|
|
2571
|
-
|
|
2538
|
+
// Always return an array (never `undefined`) so the response stays JSON-serializable
|
|
2539
|
+
// for consumers like Next.js Pages Router (getServerSideProps/getStaticProps throw on undefined).
|
|
2540
|
+
errors: response.errors?.length ? response.errors : [],
|
|
2541
|
+
...(styleEditorSchemas?.length && { styleEditorSchemas })
|
|
2572
2542
|
};
|
|
2573
2543
|
}
|
|
2574
2544
|
catch (error) {
|
|
@@ -2576,9 +2546,9 @@ class PageClient extends BaseApiClient {
|
|
|
2576
2546
|
throw error;
|
|
2577
2547
|
}
|
|
2578
2548
|
if (error instanceof DotHttpError) {
|
|
2579
|
-
throw new DotErrorPage(`Page request failed for URL '${
|
|
2549
|
+
throw new DotErrorPage(`Page request failed for URL '${newURL}': ${error.message}`, error.status, 'UNKNOWN', error, { query: completeQuery, variables: requestVariables });
|
|
2580
2550
|
}
|
|
2581
|
-
throw new DotErrorPage(`Page request failed for URL '${
|
|
2551
|
+
throw new DotErrorPage(`Page request failed for URL '${newURL}': ${error instanceof Error ? error.message : 'Unknown error'}`, 500, 'UNKNOWN', undefined, { query: completeQuery, variables: requestVariables });
|
|
2582
2552
|
}
|
|
2583
2553
|
}
|
|
2584
2554
|
}
|
package/internal.cjs.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
3
|
/**
|
|
5
4
|
* Transforms a GraphQL Page response to a Page Entity.
|
|
6
5
|
*
|
|
@@ -19,7 +18,12 @@ const graphqlToPageEntity = (page) => {
|
|
|
19
18
|
}
|
|
20
19
|
const { layout, template, containers, urlContentMap, viewAs, host, vanityUrl, runningExperimentId, numberContents, _map, ...pageAsset } = page;
|
|
21
20
|
const data = (_map || {});
|
|
22
|
-
|
|
21
|
+
// styleEditorSchemas comes back as null from GraphQL outside EDIT_MODE. Separate it from the
|
|
22
|
+
// rest of the page fields so it can be omitted entirely when it has no value. Emitting
|
|
23
|
+
// `undefined` (the previous behaviour) breaks JSON serialization for consumers like Next.js
|
|
24
|
+
// Pages Router (getServerSideProps/getStaticProps), while omitting the key keeps the optional
|
|
25
|
+
// DotCMSPage.styleEditorSchemas type accurate.
|
|
26
|
+
const { styleEditorSchemas, ...typedPageAsset } = pageAsset;
|
|
23
27
|
// Merge all urlContentMap keys into _map, except _map itself
|
|
24
28
|
const mergedUrlContentMap = {
|
|
25
29
|
...(urlContentMap?._map || {}),
|
|
@@ -42,7 +46,9 @@ const graphqlToPageEntity = (page) => {
|
|
|
42
46
|
containers: parseContainers(containers),
|
|
43
47
|
page: {
|
|
44
48
|
...data,
|
|
45
|
-
...typedPageAsset
|
|
49
|
+
...typedPageAsset,
|
|
50
|
+
// Only re-add styleEditorSchemas when it actually has a value (see destructure above).
|
|
51
|
+
...(styleEditorSchemas ? { styleEditorSchemas } : {})
|
|
46
52
|
}
|
|
47
53
|
};
|
|
48
54
|
};
|
package/internal.esm.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
1
|
/**
|
|
3
2
|
* Transforms a GraphQL Page response to a Page Entity.
|
|
4
3
|
*
|
|
@@ -17,7 +16,12 @@ const graphqlToPageEntity = (page) => {
|
|
|
17
16
|
}
|
|
18
17
|
const { layout, template, containers, urlContentMap, viewAs, host, vanityUrl, runningExperimentId, numberContents, _map, ...pageAsset } = page;
|
|
19
18
|
const data = (_map || {});
|
|
20
|
-
|
|
19
|
+
// styleEditorSchemas comes back as null from GraphQL outside EDIT_MODE. Separate it from the
|
|
20
|
+
// rest of the page fields so it can be omitted entirely when it has no value. Emitting
|
|
21
|
+
// `undefined` (the previous behaviour) breaks JSON serialization for consumers like Next.js
|
|
22
|
+
// Pages Router (getServerSideProps/getStaticProps), while omitting the key keeps the optional
|
|
23
|
+
// DotCMSPage.styleEditorSchemas type accurate.
|
|
24
|
+
const { styleEditorSchemas, ...typedPageAsset } = pageAsset;
|
|
21
25
|
// Merge all urlContentMap keys into _map, except _map itself
|
|
22
26
|
const mergedUrlContentMap = {
|
|
23
27
|
...(urlContentMap?._map || {}),
|
|
@@ -40,7 +44,9 @@ const graphqlToPageEntity = (page) => {
|
|
|
40
44
|
containers: parseContainers(containers),
|
|
41
45
|
page: {
|
|
42
46
|
...data,
|
|
43
|
-
...typedPageAsset
|
|
47
|
+
...typedPageAsset,
|
|
48
|
+
// Only re-add styleEditorSchemas when it actually has a value (see destructure above).
|
|
49
|
+
...(styleEditorSchemas ? { styleEditorSchemas } : {})
|
|
44
50
|
}
|
|
45
51
|
};
|
|
46
52
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotcms/client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-next.37",
|
|
4
4
|
"description": "Official JavaScript library for interacting with DotCMS REST APIs.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,9 +12,6 @@
|
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"@dotcms/types": "latest"
|
|
14
14
|
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "nx run sdk-client:build:js; cd ../../../../dotCMS/src/main/webapp/html/js/editor-js; rm -rf src package.json *.esm.d.ts"
|
|
17
|
-
},
|
|
18
15
|
"keywords": [
|
|
19
16
|
"dotCMS",
|
|
20
17
|
"CMS",
|
|
@@ -56,4 +53,4 @@
|
|
|
56
53
|
"module": "./index.esm.js",
|
|
57
54
|
"main": "./index.cjs.js",
|
|
58
55
|
"types": "./index.d.ts"
|
|
59
|
-
}
|
|
56
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { StyleEditorFormSchema } from '@dotcms/types/internal';
|
|
1
|
+
import { DotGraphQLApiResponse, DotHttpClient } from '@dotcms/types';
|
|
3
2
|
/**
|
|
4
3
|
* Builds a GraphQL query for retrieving page content from DotCMS.
|
|
5
4
|
*
|
|
@@ -29,12 +28,16 @@ export declare function buildQuery(queryData: Record<string, string>): string;
|
|
|
29
28
|
*/
|
|
30
29
|
export declare function mapContentResponse(responseData: Record<string, unknown> | undefined, keys: string[]): Record<string, unknown> | undefined;
|
|
31
30
|
/**
|
|
32
|
-
*
|
|
33
|
-
* Requires READ on the page; failures are silently ignored so callers still work without auth.
|
|
31
|
+
* Returns a shallow copy of the object with every key whose value is `undefined` removed.
|
|
34
32
|
*
|
|
35
|
-
*
|
|
33
|
+
* `undefined` is not valid JSON, so keeping such keys breaks consumers that serialize the value
|
|
34
|
+
* (e.g. Next.js Pages Router `getServerSideProps`/`getStaticProps`). `null` and other falsy values
|
|
35
|
+
* are preserved since they serialize fine.
|
|
36
|
+
*
|
|
37
|
+
* @param {Record<string, unknown>} object - Source object to clean
|
|
38
|
+
* @returns {Record<string, unknown>} New object without `undefined` values
|
|
36
39
|
*/
|
|
37
|
-
export declare function
|
|
40
|
+
export declare function removeUndefinedValues(object: Record<string, unknown>): Record<string, unknown>;
|
|
38
41
|
/**
|
|
39
42
|
* Executes a GraphQL query against the DotCMS API.
|
|
40
43
|
*
|