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