@dotcms/client 1.2.1 → 1.2.2

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.esm.js CHANGED
@@ -524,6 +524,279 @@ const CONTENT_TYPE_MAIN_FIELDS = [
524
524
  */
525
525
  const CONTENT_API_URL = '/api/content/_search';
526
526
 
527
+ var _BaseBuilder_page, _BaseBuilder_limit, _BaseBuilder_depth, _BaseBuilder_render, _BaseBuilder_sortBy;
528
+ /**
529
+ * Abstract base class for content builders that provides common functionality
530
+ * for querying content from the DotCMS Content API.
531
+ *
532
+ * This class extracts shared behavior between different builder implementations,
533
+ * including pagination, sorting, rendering, and response formatting.
534
+ *
535
+ * @export
536
+ * @abstract
537
+ * @class BaseBuilder
538
+ * @template T - The type of the content items (defaults to unknown)
539
+ */
540
+ class BaseBuilder extends BaseApiClient {
541
+ /**
542
+ * Creates an instance of BaseBuilder.
543
+ *
544
+ * @param {object} params - Constructor parameters
545
+ * @param {DotRequestOptions} params.requestOptions - Options for the client request
546
+ * @param {DotCMSClientConfig} params.config - The client configuration
547
+ * @param {DotHttpClient} params.httpClient - HTTP client for making requests
548
+ * @memberof BaseBuilder
549
+ */
550
+ constructor(params) {
551
+ super(params.config, params.requestOptions, params.httpClient);
552
+ /**
553
+ * Current page number for pagination.
554
+ * @private
555
+ */
556
+ _BaseBuilder_page.set(this, 1);
557
+ /**
558
+ * Maximum number of items per page.
559
+ * @private
560
+ */
561
+ _BaseBuilder_limit.set(this, 10);
562
+ /**
563
+ * Depth of related content to include in results.
564
+ * Valid values: 0-3
565
+ * @private
566
+ */
567
+ _BaseBuilder_depth.set(this, 0);
568
+ /**
569
+ * Whether to server-side render widgets in content.
570
+ * @private
571
+ */
572
+ _BaseBuilder_render.set(this, false);
573
+ /**
574
+ * Sorting configuration for results.
575
+ * @private
576
+ */
577
+ _BaseBuilder_sortBy.set(this, void 0);
578
+ }
579
+ /**
580
+ * Returns the offset for pagination based on current page and limit.
581
+ *
582
+ * @readonly
583
+ * @protected
584
+ * @memberof BaseBuilder
585
+ */
586
+ get offset() {
587
+ return __classPrivateFieldGet(this, _BaseBuilder_limit, "f") * (__classPrivateFieldGet(this, _BaseBuilder_page, "f") - 1);
588
+ }
589
+ /**
590
+ * Returns the sort query in the format: field order, field order, ...
591
+ *
592
+ * @readonly
593
+ * @protected
594
+ * @memberof BaseBuilder
595
+ */
596
+ get sort() {
597
+ return __classPrivateFieldGet(this, _BaseBuilder_sortBy, "f")?.map((sort) => `${sort.field} ${sort.order}`).join(',');
598
+ }
599
+ /**
600
+ * Returns the full URL for the content API.
601
+ *
602
+ * @readonly
603
+ * @protected
604
+ * @memberof BaseBuilder
605
+ */
606
+ get url() {
607
+ return `${this.config.dotcmsUrl}${CONTENT_API_URL}`;
608
+ }
609
+ /**
610
+ * Sets the maximum amount of content to fetch.
611
+ *
612
+ * @example
613
+ * ```typescript
614
+ * builder.limit(20);
615
+ * ```
616
+ *
617
+ * @param {number} limit - The maximum amount of content to fetch
618
+ * @return {this} The builder instance for method chaining
619
+ * @memberof BaseBuilder
620
+ */
621
+ limit(limit) {
622
+ __classPrivateFieldSet(this, _BaseBuilder_limit, limit, "f");
623
+ return this;
624
+ }
625
+ /**
626
+ * Sets the page number to fetch.
627
+ *
628
+ * @example
629
+ * ```typescript
630
+ * builder.page(2);
631
+ * ```
632
+ *
633
+ * @param {number} page - The page number to fetch
634
+ * @return {this} The builder instance for method chaining
635
+ * @memberof BaseBuilder
636
+ */
637
+ page(page) {
638
+ __classPrivateFieldSet(this, _BaseBuilder_page, page, "f");
639
+ return this;
640
+ }
641
+ /**
642
+ * Sorts the content by the specified fields and orders.
643
+ *
644
+ * @example
645
+ * ```typescript
646
+ * const sortBy = [
647
+ * { field: 'title', order: 'asc' },
648
+ * { field: 'modDate', order: 'desc' }
649
+ * ];
650
+ * builder.sortBy(sortBy);
651
+ * ```
652
+ *
653
+ * @param {SortBy[]} sortBy - Array of constraints to sort the content by
654
+ * @return {this} The builder instance for method chaining
655
+ * @memberof BaseBuilder
656
+ */
657
+ sortBy(sortBy) {
658
+ __classPrivateFieldSet(this, _BaseBuilder_sortBy, sortBy, "f");
659
+ return this;
660
+ }
661
+ /**
662
+ * Setting this to true will server-side render (using velocity) any widgets
663
+ * that are returned by the content query.
664
+ *
665
+ * More information here: {@link https://www.dotcms.com/docs/latest/content-api-retrieval-and-querying#ParamsOptional}
666
+ *
667
+ * @example
668
+ * ```typescript
669
+ * builder.render();
670
+ * ```
671
+ *
672
+ * @return {this} The builder instance for method chaining
673
+ * @memberof BaseBuilder
674
+ */
675
+ render() {
676
+ __classPrivateFieldSet(this, _BaseBuilder_render, true, "f");
677
+ return this;
678
+ }
679
+ /**
680
+ * Sets the depth of the relationships of the content.
681
+ * Specifies the depth of related content to return in the results.
682
+ *
683
+ * More information here: {@link https://www.dotcms.com/docs/latest/content-api-retrieval-and-querying#ParamsOptional}
684
+ *
685
+ * @example
686
+ * ```typescript
687
+ * builder.depth(2);
688
+ * ```
689
+ *
690
+ * @param {number} depth - The depth of the relationships of the content (0-3)
691
+ * @return {this} The builder instance for method chaining
692
+ * @throws {Error} When depth is not between 0 and 3
693
+ * @memberof BaseBuilder
694
+ */
695
+ depth(depth) {
696
+ if (depth < 0 || depth > 3) {
697
+ throw new Error('Depth value must be between 0 and 3');
698
+ }
699
+ __classPrivateFieldSet(this, _BaseBuilder_depth, depth, "f");
700
+ return this;
701
+ }
702
+ /**
703
+ * Executes the fetch and returns a promise that resolves to the content or rejects with an error.
704
+ *
705
+ * @example
706
+ * ```typescript
707
+ * builder
708
+ * .limit(10)
709
+ * .then((response) => console.log(response))
710
+ * .catch((error) => console.error(error));
711
+ * ```
712
+ *
713
+ * @example Using async/await
714
+ * ```typescript
715
+ * const response = await builder.limit(10);
716
+ * ```
717
+ *
718
+ * @param {OnFullfilled} [onfulfilled] - A callback that is called when the fetch is successful
719
+ * @param {OnRejected} [onrejected] - A callback that is called when the fetch fails
720
+ * @return {Promise<GetCollectionResponse<T> | DotErrorContent>} A promise that resolves to the content or rejects with an error
721
+ * @memberof BaseBuilder
722
+ */
723
+ then(onfulfilled, onrejected) {
724
+ return this.fetch().then((data) => {
725
+ const formattedResponse = this.formatResponse(data);
726
+ if (typeof onfulfilled === 'function') {
727
+ const result = onfulfilled(formattedResponse);
728
+ // Ensure we always return a value, fallback to formattedResponse if callback returns undefined
729
+ return result ?? formattedResponse;
730
+ }
731
+ return formattedResponse;
732
+ }, (error) => {
733
+ // Wrap error in DotErrorContent
734
+ const contentError = this.wrapError(error);
735
+ if (typeof onrejected === 'function') {
736
+ const result = onrejected(contentError);
737
+ // Ensure we always return a value, fallback to original error if callback returns undefined
738
+ return result ?? contentError;
739
+ }
740
+ // Throw the wrapped error to trigger .catch()
741
+ throw contentError;
742
+ });
743
+ }
744
+ /**
745
+ * Formats the response to the desired format.
746
+ *
747
+ * @protected
748
+ * @param {GetCollectionRawResponse<T>} data - The raw response data
749
+ * @return {GetCollectionResponse<T>} The formatted response
750
+ * @memberof BaseBuilder
751
+ */
752
+ formatResponse(data) {
753
+ const contentlets = data.entity.jsonObjectView.contentlets;
754
+ const total = data.entity.resultsSize;
755
+ const mappedResponse = {
756
+ contentlets,
757
+ total,
758
+ page: __classPrivateFieldGet(this, _BaseBuilder_page, "f"),
759
+ size: contentlets.length
760
+ };
761
+ return __classPrivateFieldGet(this, _BaseBuilder_sortBy, "f")
762
+ ? {
763
+ ...mappedResponse,
764
+ sortedBy: __classPrivateFieldGet(this, _BaseBuilder_sortBy, "f")
765
+ }
766
+ : mappedResponse;
767
+ }
768
+ /**
769
+ * Calls the content API to fetch the content.
770
+ *
771
+ * @protected
772
+ * @return {Promise<GetCollectionRawResponse<T>>} The fetch response data
773
+ * @throws {DotHttpError} When the HTTP request fails
774
+ * @memberof BaseBuilder
775
+ */
776
+ fetch() {
777
+ const finalQuery = this.buildFinalQuery();
778
+ const languageId = this.getLanguageId();
779
+ return this.httpClient.request(this.url, {
780
+ ...this.requestOptions,
781
+ method: 'POST',
782
+ headers: {
783
+ ...this.requestOptions.headers,
784
+ 'Content-Type': 'application/json'
785
+ },
786
+ body: JSON.stringify({
787
+ query: finalQuery,
788
+ render: __classPrivateFieldGet(this, _BaseBuilder_render, "f"),
789
+ sort: this.sort,
790
+ limit: __classPrivateFieldGet(this, _BaseBuilder_limit, "f"),
791
+ offset: this.offset,
792
+ depth: __classPrivateFieldGet(this, _BaseBuilder_depth, "f"),
793
+ ...(languageId !== undefined ? { languageId } : {})
794
+ })
795
+ });
796
+ }
797
+ }
798
+ _BaseBuilder_page = new WeakMap(), _BaseBuilder_limit = new WeakMap(), _BaseBuilder_depth = new WeakMap(), _BaseBuilder_render = new WeakMap(), _BaseBuilder_sortBy = new WeakMap();
799
+
527
800
  /**
528
801
  * @description
529
802
  * Sanitizes the query for the given content type.
@@ -1106,7 +1379,7 @@ class QueryBuilder {
1106
1379
  }
1107
1380
  _QueryBuilder_query = new WeakMap();
1108
1381
 
1109
- var _CollectionBuilder_page, _CollectionBuilder_limit, _CollectionBuilder_depth, _CollectionBuilder_render, _CollectionBuilder_sortBy, _CollectionBuilder_contentType, _CollectionBuilder_defaultQuery, _CollectionBuilder_query, _CollectionBuilder_rawQuery, _CollectionBuilder_languageId, _CollectionBuilder_draft;
1382
+ var _CollectionBuilder_contentType, _CollectionBuilder_defaultQuery, _CollectionBuilder_query, _CollectionBuilder_rawQuery, _CollectionBuilder_languageId, _CollectionBuilder_draft;
1110
1383
  /**
1111
1384
  * Creates a Builder to filter and fetch content from the content API for a specific content type.
1112
1385
  *
@@ -1114,62 +1387,28 @@ var _CollectionBuilder_page, _CollectionBuilder_limit, _CollectionBuilder_depth,
1114
1387
  * @class CollectionBuilder
1115
1388
  * @template T Represents the type of the content type to fetch. Defaults to unknown.
1116
1389
  */
1117
- class CollectionBuilder extends BaseApiClient {
1390
+ class CollectionBuilder extends BaseBuilder {
1118
1391
  /**
1119
1392
  * Creates an instance of CollectionBuilder.
1120
- * @param {ClientOptions} requestOptions Options for the client request.
1121
- * @param {DotCMSClientConfig} config The client configuration.
1122
- * @param {string} contentType The content type to fetch.
1123
- * @param {DotHttpClient} httpClient HTTP client for making requests.
1393
+ * @param {object} params - Constructor parameters
1394
+ * @param {DotRequestOptions} params.requestOptions - Options for the client request
1395
+ * @param {DotCMSClientConfig} params.config - The client configuration
1396
+ * @param {string} params.contentType - The content type to fetch
1397
+ * @param {DotHttpClient} params.httpClient - HTTP client for making requests
1124
1398
  * @memberof CollectionBuilder
1125
1399
  */
1126
- constructor(requestOptions, config, contentType, httpClient) {
1127
- super(config, requestOptions, httpClient);
1128
- _CollectionBuilder_page.set(this, 1);
1129
- _CollectionBuilder_limit.set(this, 10);
1130
- _CollectionBuilder_depth.set(this, 0);
1131
- _CollectionBuilder_render.set(this, false);
1132
- _CollectionBuilder_sortBy.set(this, void 0);
1400
+ constructor(params) {
1401
+ super(params);
1133
1402
  _CollectionBuilder_contentType.set(this, void 0);
1134
1403
  _CollectionBuilder_defaultQuery.set(this, void 0);
1135
1404
  _CollectionBuilder_query.set(this, void 0);
1136
1405
  _CollectionBuilder_rawQuery.set(this, void 0);
1137
1406
  _CollectionBuilder_languageId.set(this, 1);
1138
1407
  _CollectionBuilder_draft.set(this, false);
1139
- __classPrivateFieldSet(this, _CollectionBuilder_contentType, contentType, "f");
1408
+ __classPrivateFieldSet(this, _CollectionBuilder_contentType, params.contentType, "f");
1140
1409
  // Build the default query with the contentType field
1141
1410
  __classPrivateFieldSet(this, _CollectionBuilder_defaultQuery, new QueryBuilder().field('contentType').equals(__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")), "f");
1142
1411
  }
1143
- /**
1144
- * Returns the sort query in the format: field order, field order, ...
1145
- *
1146
- * @readonly
1147
- * @private
1148
- * @memberof CollectionBuilder
1149
- */
1150
- get sort() {
1151
- return __classPrivateFieldGet(this, _CollectionBuilder_sortBy, "f")?.map((sort) => `${sort.field} ${sort.order}`).join(',');
1152
- }
1153
- /**
1154
- * Returns the offset for pagination.
1155
- *
1156
- * @readonly
1157
- * @private
1158
- * @memberof CollectionBuilder
1159
- */
1160
- get offset() {
1161
- return __classPrivateFieldGet(this, _CollectionBuilder_limit, "f") * (__classPrivateFieldGet(this, _CollectionBuilder_page, "f") - 1);
1162
- }
1163
- /**
1164
- * Returns the full URL for the content API.
1165
- *
1166
- * @readonly
1167
- * @private
1168
- * @memberof CollectionBuilder
1169
- */
1170
- get url() {
1171
- return `${this.config.dotcmsUrl}${CONTENT_API_URL}`;
1172
- }
1173
1412
  /**
1174
1413
  * Returns the current query built.
1175
1414
  *
@@ -1198,59 +1437,6 @@ class CollectionBuilder extends BaseApiClient {
1198
1437
  __classPrivateFieldSet(this, _CollectionBuilder_languageId, languageId, "f");
1199
1438
  return this;
1200
1439
  }
1201
- /**
1202
- * Setting this to true will server side render (using velocity) any widgets that are returned by the content query.
1203
- *
1204
- * More information here: {@link https://www.dotcms.com/docs/latest/content-api-retrieval-and-querying#ParamsOptional}
1205
- *
1206
- * @return {CollectionBuilder} A CollectionBuilder instance.
1207
- * @memberof CollectionBuilder
1208
- */
1209
- render() {
1210
- __classPrivateFieldSet(this, _CollectionBuilder_render, true, "f");
1211
- return this;
1212
- }
1213
- /**
1214
- * Sorts the content by the specified fields and orders.
1215
- *
1216
- * @example
1217
- * ```typescript
1218
- * const client = new DotCMSClient(config);
1219
- * const collectionBuilder = client.content.getCollection("Blog");
1220
- * const sortBy = [{ field: 'title', order: 'asc' }, { field: 'modDate', order: 'desc' }];
1221
- * collectionBuilder("Blog").sortBy(sortBy);
1222
- * ```
1223
- *
1224
- * @param {SortBy[]} sortBy Array of constraints to sort the content by.
1225
- * @return {CollectionBuilder} A CollectionBuilder instance.
1226
- * @memberof CollectionBuilder
1227
- */
1228
- sortBy(sortBy) {
1229
- __classPrivateFieldSet(this, _CollectionBuilder_sortBy, sortBy, "f");
1230
- return this;
1231
- }
1232
- /**
1233
- * Sets the maximum amount of content to fetch.
1234
- *
1235
- * @param {number} limit The maximum amount of content to fetch.
1236
- * @return {CollectionBuilder} A CollectionBuilder instance.
1237
- * @memberof CollectionBuilder
1238
- */
1239
- limit(limit) {
1240
- __classPrivateFieldSet(this, _CollectionBuilder_limit, limit, "f");
1241
- return this;
1242
- }
1243
- /**
1244
- * Sets the page number to fetch.
1245
- *
1246
- * @param {number} page The page number to fetch.
1247
- * @return {CollectionBuilder} A CollectionBuilder instance.
1248
- * @memberof CollectionBuilder
1249
- */
1250
- page(page) {
1251
- __classPrivateFieldSet(this, _CollectionBuilder_page, page, "f");
1252
- return this;
1253
- }
1254
1440
  query(arg) {
1255
1441
  if (typeof arg === 'string') {
1256
1442
  __classPrivateFieldSet(this, _CollectionBuilder_rawQuery, arg, "f");
@@ -1312,132 +1498,29 @@ class CollectionBuilder extends BaseApiClient {
1312
1498
  return this;
1313
1499
  }
1314
1500
  /**
1315
- * Sets the depth of the relationships of the content.
1316
- * Specifies the depth of related content to return in the results.
1501
+ * Wraps an error in a DotErrorContent instance with helpful context.
1317
1502
  *
1318
- * More information here: {@link https://www.dotcms.com/docs/latest/content-api-retrieval-and-querying#ParamsOptional}
1319
- *
1320
- * @example
1321
- * ```ts
1322
- * const client = new DotCMSClient(config);
1323
- * const collectionBuilder = client.content.getCollection("Blog");
1324
- * collectionBuilder
1325
- * .depth(1)
1326
- * .then((response) => // Your code here })
1327
- * .catch((error) => // Your code here })
1328
- * ```
1329
- *
1330
- * @param {number} depth The depth of the relationships of the content.
1331
- * @return {CollectionBuilder} A CollectionBuilder instance.
1503
+ * @protected
1504
+ * @param {unknown} error - The error to wrap
1505
+ * @return {DotErrorContent} The wrapped error
1332
1506
  * @memberof CollectionBuilder
1333
1507
  */
1334
- depth(depth) {
1335
- if (depth < 0 || depth > 3) {
1336
- throw new Error('Depth value must be between 0 and 3');
1508
+ wrapError(error) {
1509
+ if (error instanceof DotHttpError) {
1510
+ return new DotErrorContent(`Content API failed for '${__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")}' (fetch): ${error.message}`, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"), 'fetch', error, this.buildFinalQuery());
1337
1511
  }
1338
- __classPrivateFieldSet(this, _CollectionBuilder_depth, depth, "f");
1339
- return this;
1340
- }
1341
- /**
1342
- * Executes the fetch and returns a promise that resolves to the content or rejects with an error.
1343
- *
1344
- * @example
1345
- * ```ts
1346
- * const client = new DotCMSClient(config);
1347
- * const collectionBuilder = client.content.getCollection("Blog");
1348
- * collectionBuilder
1349
- * .limit(10)
1350
- * .then((response) => // Your code here })
1351
- * .catch((error) => // Your code here })
1352
- * ```
1353
- *
1354
- * @param {OnFullfilled} [onfulfilled] A callback that is called when the fetch is successful.
1355
- * @param {OnRejected} [onrejected] A callback that is called when the fetch fails.
1356
- * @return {Promise<GetCollectionResponse<T> | DotErrorContent>} A promise that resolves to the content or rejects with an error.
1357
- * @memberof CollectionBuilder
1358
- */
1359
- then(onfulfilled, onrejected) {
1360
- return this.fetch().then((data) => {
1361
- const formattedResponse = this.formatResponse(data);
1362
- if (typeof onfulfilled === 'function') {
1363
- const result = onfulfilled(formattedResponse);
1364
- // Ensure we always return a value, fallback to formattedResponse if callback returns undefined
1365
- return result ?? formattedResponse;
1366
- }
1367
- return formattedResponse;
1368
- }, (error) => {
1369
- // Wrap error in DotCMSContentError
1370
- let contentError;
1371
- if (error instanceof DotHttpError) {
1372
- contentError = new DotErrorContent(`Content API failed for '${__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")}' (fetch): ${error.message}`, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"), 'fetch', error, this.getFinalQuery());
1373
- }
1374
- else {
1375
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1376
- contentError = new DotErrorContent(`Content API failed for '${__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")}' (fetch): ${errorMessage}`, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"), 'fetch', undefined, this.getFinalQuery());
1377
- }
1378
- if (typeof onrejected === 'function') {
1379
- const result = onrejected(contentError);
1380
- // Ensure we always return a value, fallback to original error if callback returns undefined
1381
- return result ?? contentError;
1382
- }
1383
- // Throw the wrapped error to trigger .catch()
1384
- throw contentError;
1385
- });
1386
- }
1387
- /**
1388
- * Formats the response to the desired format.
1389
- *
1390
- * @private
1391
- * @param {GetCollectionRawResponse<T>} data The raw response data.
1392
- * @return {GetCollectionResponse<T>} The formatted response.
1393
- * @memberof CollectionBuilder
1394
- */
1395
- formatResponse(data) {
1396
- const contentlets = data.entity.jsonObjectView.contentlets;
1397
- const total = data.entity.resultsSize;
1398
- const mappedResponse = {
1399
- contentlets,
1400
- total,
1401
- page: __classPrivateFieldGet(this, _CollectionBuilder_page, "f"),
1402
- size: contentlets.length
1403
- };
1404
- return __classPrivateFieldGet(this, _CollectionBuilder_sortBy, "f")
1405
- ? {
1406
- ...mappedResponse,
1407
- sortedBy: __classPrivateFieldGet(this, _CollectionBuilder_sortBy, "f")
1408
- }
1409
- : mappedResponse;
1512
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1513
+ return new DotErrorContent(`Content API failed for '${__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")}' (fetch): ${errorMessage}`, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"), 'fetch', undefined, this.buildFinalQuery());
1410
1514
  }
1411
1515
  /**
1412
- * Calls the content API to fetch the content.
1516
+ * Gets the language ID for the query.
1413
1517
  *
1414
- * @private
1415
- * @return {Promise<GetCollectionRawResponse<T>>} The fetch response data.
1416
- * @throws {DotHttpError} When the HTTP request fails.
1518
+ * @protected
1519
+ * @return {number | string} The language ID
1417
1520
  * @memberof CollectionBuilder
1418
1521
  */
1419
- fetch() {
1420
- const finalQuery = this.getFinalQuery();
1421
- const sanitizedQuery = sanitizeQueryForContentType(finalQuery, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"));
1422
- const query = __classPrivateFieldGet(this, _CollectionBuilder_rawQuery, "f") ? `${sanitizedQuery} ${__classPrivateFieldGet(this, _CollectionBuilder_rawQuery, "f")}` : sanitizedQuery;
1423
- return this.httpClient.request(this.url, {
1424
- ...this.requestOptions,
1425
- method: 'POST',
1426
- headers: {
1427
- ...this.requestOptions.headers,
1428
- 'Content-Type': 'application/json'
1429
- },
1430
- body: JSON.stringify({
1431
- query,
1432
- render: __classPrivateFieldGet(this, _CollectionBuilder_render, "f"),
1433
- sort: this.sort,
1434
- limit: __classPrivateFieldGet(this, _CollectionBuilder_limit, "f"),
1435
- offset: this.offset,
1436
- depth: __classPrivateFieldGet(this, _CollectionBuilder_depth, "f")
1437
- //userId: This exist but we currently don't use it
1438
- //allCategoriesInfo: This exist but we currently don't use it
1439
- })
1440
- });
1522
+ getLanguageId() {
1523
+ return __classPrivateFieldGet(this, _CollectionBuilder_languageId, "f");
1441
1524
  }
1442
1525
  /**
1443
1526
  * Builds the final Lucene query string by combining the base query with required system constraints.
@@ -1445,14 +1528,15 @@ class CollectionBuilder extends BaseApiClient {
1445
1528
  * This method constructs the complete query by:
1446
1529
  * 1. Adding language ID filter to ensure content matches the specified language
1447
1530
  * 2. Adding live/draft status filter based on the draft flag
1448
- * 3. Optionally adding site ID constraint if conditions are met
1531
+ * 3. Applying content type specific query sanitization
1532
+ * 4. Optionally adding site ID constraint if conditions are met
1449
1533
  *
1450
1534
  * Site ID constraint is added only when:
1451
1535
  * - Query doesn't already contain a positive site constraint (+conhost)
1452
1536
  * - Query doesn't explicitly exclude the current site ID (-conhost:currentSiteId)
1453
1537
  * - Site ID is configured in the system
1454
1538
  *
1455
- * @private
1539
+ * @protected
1456
1540
  * @returns {string} The complete Lucene query string ready for the Content API
1457
1541
  * @memberof CollectionBuilder
1458
1542
  *
@@ -1474,8 +1558,11 @@ class CollectionBuilder extends BaseApiClient {
1474
1558
  * // Query: "+contentType:Blog -conhost:456"
1475
1559
  * // Returns: "+contentType:Blog -conhost:456 +languageId:1 +live:true +conhost:123" (site ID still added)
1476
1560
  */
1477
- getFinalQuery() {
1478
- // Build base query with language and live/draft constraints
1561
+ buildFinalQuery() {
1562
+ // Build base query with language and live/draft constraints.
1563
+ // NOTE: languageId is intentionally sent in BOTH places:
1564
+ // - in the Lucene query (backend requires this)
1565
+ // - as a top-level request body field (backend also requires this)
1479
1566
  let baseQuery = this.currentQuery.field('languageId').equals(__classPrivateFieldGet(this, _CollectionBuilder_languageId, "f").toString());
1480
1567
  if (__classPrivateFieldGet(this, _CollectionBuilder_draft, "f")) {
1481
1568
  baseQuery = baseQuery.raw('+(live:false AND working:true AND deleted:false)');
@@ -1487,14 +1574,122 @@ class CollectionBuilder extends BaseApiClient {
1487
1574
  // Check if site ID constraint should be added using utility function
1488
1575
  const shouldAddSiteId = shouldAddSiteIdConstraint(builtQuery, this.siteId);
1489
1576
  // Add site ID constraint if needed
1577
+ let finalQuery;
1490
1578
  if (shouldAddSiteId) {
1491
- const queryWithSiteId = `${builtQuery} +conhost:${this.siteId}`;
1492
- return sanitizeQuery(queryWithSiteId);
1579
+ finalQuery = `${builtQuery} +conhost:${this.siteId}`;
1493
1580
  }
1494
- return builtQuery;
1581
+ else {
1582
+ finalQuery = builtQuery;
1583
+ }
1584
+ // Apply content-type specific sanitization (adds contentType. prefix to fields)
1585
+ const sanitizedQuery = sanitizeQueryForContentType(finalQuery, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"));
1586
+ // Append raw query if provided (raw query is NOT sanitized for content type)
1587
+ const query = __classPrivateFieldGet(this, _CollectionBuilder_rawQuery, "f") ? `${sanitizedQuery} ${__classPrivateFieldGet(this, _CollectionBuilder_rawQuery, "f")}` : sanitizedQuery;
1588
+ return sanitizeQuery(query);
1495
1589
  }
1496
1590
  }
1497
- _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();
1591
+ _CollectionBuilder_contentType = new WeakMap(), _CollectionBuilder_defaultQuery = new WeakMap(), _CollectionBuilder_query = new WeakMap(), _CollectionBuilder_rawQuery = new WeakMap(), _CollectionBuilder_languageId = new WeakMap(), _CollectionBuilder_draft = new WeakMap();
1592
+
1593
+ var _RawQueryBuilder_rawQuery, _RawQueryBuilder_languageId;
1594
+ /**
1595
+ * Builder for executing raw Lucene queries against the DotCMS Content API.
1596
+ *
1597
+ * This builder provides direct access to Lucene query syntax while still
1598
+ * offering common functionality like pagination, sorting, and language filtering.
1599
+ *
1600
+ * @export
1601
+ * @class RawQueryBuilder
1602
+ * @template T - The type of the content items (defaults to unknown)
1603
+ */
1604
+ class RawQueryBuilder extends BaseBuilder {
1605
+ /**
1606
+ * Creates an instance of RawQueryBuilder.
1607
+ *
1608
+ * @param {object} params - Constructor parameters
1609
+ * @param {DotRequestOptions} params.requestOptions - Options for the client request
1610
+ * @param {DotCMSClientConfig} params.config - The client configuration
1611
+ * @param {string} params.rawQuery - The raw Lucene query string
1612
+ * @param {DotHttpClient} params.httpClient - HTTP client for making requests
1613
+ * @memberof RawQueryBuilder
1614
+ */
1615
+ constructor(params) {
1616
+ super(params);
1617
+ /**
1618
+ * The raw Lucene query string.
1619
+ * @private
1620
+ */
1621
+ _RawQueryBuilder_rawQuery.set(this, void 0);
1622
+ /**
1623
+ * Optional language ID for the request body.
1624
+ * When provided, it will be sent as `languageId` in the request payload.
1625
+ *
1626
+ * NOTE: This does NOT modify the raw query string. If you need `+languageId:<id>` inside
1627
+ * the Lucene query, include it in the raw query yourself.
1628
+ *
1629
+ * @private
1630
+ */
1631
+ _RawQueryBuilder_languageId.set(this, void 0);
1632
+ __classPrivateFieldSet(this, _RawQueryBuilder_rawQuery, params.rawQuery, "f");
1633
+ }
1634
+ /**
1635
+ * Filters the content by the specified language ID.
1636
+ *
1637
+ * This sets the `languageId` request body field (it does not alter the raw Lucene query string).
1638
+ *
1639
+ * @example
1640
+ * ```typescript
1641
+ * const response = await client.content
1642
+ * .query('+contentType:Blog +title:Hello')
1643
+ * .language(1)
1644
+ * .limit(10);
1645
+ * ```
1646
+ *
1647
+ * @param {number | string} languageId The language ID to filter the content by.
1648
+ * @return {RawQueryBuilder} A RawQueryBuilder instance.
1649
+ * @memberof RawQueryBuilder
1650
+ */
1651
+ language(languageId) {
1652
+ __classPrivateFieldSet(this, _RawQueryBuilder_languageId, languageId, "f");
1653
+ return this;
1654
+ }
1655
+ /**
1656
+ * Wraps an error in a DotErrorContent instance with helpful context.
1657
+ *
1658
+ * @protected
1659
+ * @param {unknown} error - The error to wrap
1660
+ * @return {DotErrorContent} The wrapped error
1661
+ * @memberof RawQueryBuilder
1662
+ */
1663
+ wrapError(error) {
1664
+ if (error instanceof DotHttpError) {
1665
+ return new DotErrorContent(`Content API failed for raw query (fetch): ${error.message}`, 'raw-query', 'fetch', error, this.buildFinalQuery());
1666
+ }
1667
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1668
+ return new DotErrorContent(`Content API failed for raw query (fetch): ${errorMessage}`, 'raw-query', 'fetch', undefined, this.buildFinalQuery());
1669
+ }
1670
+ /**
1671
+ * @protected
1672
+ * @return {number | string | undefined} Optional languageId to send in the request body.
1673
+ * @memberof RawQueryBuilder
1674
+ */
1675
+ getLanguageId() {
1676
+ return __classPrivateFieldGet(this, _RawQueryBuilder_languageId, "f");
1677
+ }
1678
+ /**
1679
+ * Builds the final Lucene query string.
1680
+ *
1681
+ * Raw query is used as provided (only minimal sanitization is applied).
1682
+ * No implicit constraints are added.
1683
+ *
1684
+ * @protected
1685
+ * @return {string} The final Lucene query string
1686
+ * @memberof RawQueryBuilder
1687
+ */
1688
+ buildFinalQuery() {
1689
+ return sanitizeQuery(__classPrivateFieldGet(this, _RawQueryBuilder_rawQuery, "f"));
1690
+ }
1691
+ }
1692
+ _RawQueryBuilder_rawQuery = new WeakMap(), _RawQueryBuilder_languageId = new WeakMap();
1498
1693
 
1499
1694
  /**
1500
1695
  * Creates a builder to filter and fetch a collection of content items.
@@ -1621,7 +1816,102 @@ class Content extends BaseApiClient {
1621
1816
  *
1622
1817
  */
1623
1818
  getCollection(contentType) {
1624
- return new CollectionBuilder(this.requestOptions, this.config, contentType, this.httpClient);
1819
+ return new CollectionBuilder({
1820
+ requestOptions: this.requestOptions,
1821
+ config: this.config,
1822
+ contentType,
1823
+ httpClient: this.httpClient
1824
+ });
1825
+ }
1826
+ /**
1827
+ * Executes a raw Lucene query with optional pagination, sorting, and rendering options.
1828
+ *
1829
+ * This method provides direct access to Lucene query syntax, giving you full control
1830
+ * over query construction while still benefiting from common features like pagination,
1831
+ * sorting, and error handling.
1832
+ *
1833
+ * **Important Notes:**
1834
+ * - The raw query is used as-is (minimal sanitization only)
1835
+ * - NO automatic field prefixing (unlike `getCollection()`)
1836
+ * - NO implicit/system constraints are added (language, live/draft, site, variant, etc.)
1837
+ * - If you need constraints, include them in the raw query string (e.g. `+contentType:Blog +languageId:1 +live:true`)
1838
+ *
1839
+ * @template T - The type of the content items (defaults to unknown)
1840
+ * @param {string} rawQuery - Raw Lucene query string
1841
+ * @return {RawQueryBuilder<T>} A RawQueryBuilder instance for chaining options
1842
+ * @memberof Content
1843
+ *
1844
+ * @example Simple query with pagination
1845
+ * ```typescript
1846
+ * const response = await client.content
1847
+ * .query('+contentType:Blog +title:"Hello World"')
1848
+ * .limit(10)
1849
+ * .page(1);
1850
+ *
1851
+ * console.log(response.contentlets);
1852
+ * ```
1853
+ *
1854
+ * @example Complex query with all available options
1855
+ * ```typescript
1856
+ * const response = await client.content
1857
+ * .query('+(contentType:Blog OR contentType:News) +tags:"technology" +languageId:1 +(live:false AND working:true AND deleted:false) +variant:legends-forceSensitive')
1858
+ * .limit(20)
1859
+ * .page(2)
1860
+ * .sortBy([{ field: 'modDate', order: 'desc' }])
1861
+ * .render()
1862
+ * .depth(2);
1863
+ *
1864
+ * console.log(`Found ${response.total} items`);
1865
+ * response.contentlets.forEach(item => console.log(item.title));
1866
+ * ```
1867
+ *
1868
+ * @example Using TypeScript generics for type safety
1869
+ * ```typescript
1870
+ * interface BlogPost {
1871
+ * title: string;
1872
+ * author: string;
1873
+ * publishDate: string;
1874
+ * }
1875
+ *
1876
+ * const response = await client.content
1877
+ * .query<BlogPost>('+contentType:Blog +author:"John Doe"')
1878
+ * .limit(10);
1879
+ *
1880
+ * // TypeScript knows the type of contentlets
1881
+ * response.contentlets.forEach(post => {
1882
+ * console.log(post.title, post.author);
1883
+ * });
1884
+ * ```
1885
+ *
1886
+ * @example Error handling with helpful messages
1887
+ * ```typescript
1888
+ * try {
1889
+ * const response = await client.content
1890
+ * .query('+contentType:Blog +publishDate:[2024-01-01 TO 2024-12-31]');
1891
+ * } catch (error) {
1892
+ * if (error instanceof DotErrorContent) {
1893
+ * console.error('Query failed:', error.message);
1894
+ * console.error('Failed query:', error.query);
1895
+ * }
1896
+ * }
1897
+ * ```
1898
+ *
1899
+ * @example Using Promise chain instead of async/await
1900
+ * ```typescript
1901
+ * client.content
1902
+ * .query('+contentType:Blog')
1903
+ * .limit(10)
1904
+ * .then(response => console.log(response.contentlets))
1905
+ * .catch(error => console.error(error));
1906
+ * ```
1907
+ */
1908
+ query(rawQuery) {
1909
+ return new RawQueryBuilder({
1910
+ requestOptions: this.requestOptions,
1911
+ config: this.config,
1912
+ rawQuery,
1913
+ httpClient: this.httpClient
1914
+ });
1625
1915
  }
1626
1916
  }
1627
1917