@dotcms/client 0.0.1-beta.34 → 0.0.1-beta.36

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/next.cjs.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var consola = require('consola');
3
4
  var transforms = require('./transforms.cjs.js');
4
5
 
5
6
  class NavigationClient {
@@ -79,7 +80,7 @@ const DEFAULT_PAGE_CONTENTLETS_CONTENT = `
79
80
  */
80
81
  const buildPageQuery = ({ page, fragments, additionalQueries }) => {
81
82
  if (!page) {
82
- console.warn('No page query provided. The query will be used by fetching all content with _map. This may mean poor performance in the query. We suggest you provide a detailed query on page attribute.');
83
+ consola.consola.warn("[DotCMS Client]: No page query was found, so we're loading all content using _map. This might slow things down. For better performance, we recommend adding a specific query in the page attribute.");
83
84
  }
84
85
  return `
85
86
  fragment DotCMSPage on DotPage {
@@ -126,9 +127,22 @@ const buildPageQuery = ({ page, fragments, additionalQueries }) => {
126
127
  canEdit
127
128
  canLock
128
129
  canRead
130
+ runningExperimentId
129
131
  urlContentMap {
130
132
  _map
131
133
  }
134
+ host {
135
+ identifier
136
+ hostName
137
+ googleMap
138
+ archived
139
+ contentType
140
+ }
141
+ vanityUrl {
142
+ action
143
+ forwardTo
144
+ uri
145
+ }
132
146
  conLanguage {
133
147
  id
134
148
  language
@@ -136,6 +150,9 @@ const buildPageQuery = ({ page, fragments, additionalQueries }) => {
136
150
  }
137
151
  template {
138
152
  drawed
153
+ anonymous
154
+ theme
155
+ identifier
139
156
  }
140
157
  containers {
141
158
  path
@@ -276,7 +293,6 @@ async function fetchGraphQL({ baseURL, body, headers }) {
276
293
  return await response.json();
277
294
  }
278
295
 
279
- var _PageClient_instances, _PageClient_getPageFromGraphQL;
280
296
  /**
281
297
  * Client for interacting with the DotCMS Page API.
282
298
  * Provides methods to retrieve and manipulate pages.
@@ -304,7 +320,6 @@ class PageClient {
304
320
  * ```
305
321
  */
306
322
  constructor(config, requestOptions) {
307
- _PageClient_instances.add(this);
308
323
  this.requestOptions = requestOptions;
309
324
  this.siteId = config.siteId || '';
310
325
  this.dotcmsUrl = config.dotcmsUrl;
@@ -359,78 +374,66 @@ class PageClient {
359
374
  * });
360
375
  * ```
361
376
  */
362
- get(url, options) {
363
- return transforms.__classPrivateFieldGet(this, _PageClient_instances, "m", _PageClient_getPageFromGraphQL).call(this, url, options);
364
- }
365
- }
366
- _PageClient_instances = new WeakSet(), _PageClient_getPageFromGraphQL =
367
- /**
368
- * Private implementation method that fetches page data using GraphQL.
369
- * This method is used internally by the public get() method.
370
- *
371
- * @private
372
- * @param {string} url - The URL of the page to retrieve
373
- * @param {DotCMSPageRequestParams} [options] - Options including languageId, mode, and GraphQL parameters
374
- * @returns {Promise<DotCMSComposedPageResponse<T>>} A Promise that resolves to the page data
375
- */
376
- async function _PageClient_getPageFromGraphQL(url, options) {
377
- const { languageId = '1', mode = 'LIVE', siteId = this.siteId, fireRules = false, personaId, publishDate, graphql = {} } = options || {};
378
- const { page, content = {}, variables, fragments } = graphql;
379
- const contentQuery = buildQuery(content);
380
- const completeQuery = buildPageQuery({
381
- page,
382
- fragments,
383
- additionalQueries: contentQuery
384
- });
385
- const requestVariables = {
386
- url,
387
- mode,
388
- languageId,
389
- personaId,
390
- fireRules,
391
- publishDate,
392
- siteId,
393
- ...variables
394
- };
395
- const requestHeaders = this.requestOptions.headers;
396
- const requestBody = JSON.stringify({ query: completeQuery, variables: requestVariables });
397
- try {
398
- const { data, errors } = await fetchGraphQL({
399
- baseURL: this.dotcmsUrl,
400
- body: requestBody,
401
- headers: requestHeaders
377
+ async get(url, options) {
378
+ const { languageId = '1', mode = 'LIVE', siteId = this.siteId, fireRules = false, personaId, publishDate, graphql = {} } = options || {};
379
+ const { page, content = {}, variables, fragments } = graphql;
380
+ const contentQuery = buildQuery(content);
381
+ const completeQuery = buildPageQuery({
382
+ page,
383
+ fragments,
384
+ additionalQueries: contentQuery
402
385
  });
403
- if (errors) {
404
- errors.forEach((error) => {
405
- throw new Error(error.message);
386
+ const requestVariables = {
387
+ // The url is expected to have a leading slash to comply on VanityURL Matching, some frameworks like Angular will not add the leading slash
388
+ url: url.startsWith('/') ? url : `/${url}`,
389
+ mode,
390
+ languageId,
391
+ personaId,
392
+ fireRules,
393
+ publishDate,
394
+ siteId,
395
+ ...variables
396
+ };
397
+ const requestHeaders = this.requestOptions.headers;
398
+ const requestBody = JSON.stringify({ query: completeQuery, variables: requestVariables });
399
+ try {
400
+ const { data, errors } = await fetchGraphQL({
401
+ baseURL: this.dotcmsUrl,
402
+ body: requestBody,
403
+ headers: requestHeaders
406
404
  });
407
- }
408
- const pageResponse = transforms.graphqlToPageEntity(data);
409
- if (!pageResponse) {
410
- throw new Error('No page data found');
411
- }
412
- const contentResponse = mapResponseData(data, Object.keys(content));
413
- return {
414
- pageAsset: pageResponse,
415
- content: contentResponse,
416
- graphql: {
417
- query: completeQuery,
418
- variables: requestVariables
405
+ if (errors) {
406
+ errors.forEach((error) => {
407
+ throw new Error(error.message);
408
+ });
419
409
  }
420
- };
421
- }
422
- catch (error) {
423
- const errorMessage = {
424
- error,
425
- message: 'Failed to retrieve page data',
426
- graphql: {
427
- query: completeQuery,
428
- variables: requestVariables
410
+ const pageResponse = transforms.graphqlToPageEntity(data);
411
+ if (!pageResponse) {
412
+ throw new Error('No page data found');
429
413
  }
430
- };
431
- throw errorMessage;
414
+ const contentResponse = mapResponseData(data, Object.keys(content));
415
+ return {
416
+ pageAsset: pageResponse,
417
+ content: contentResponse,
418
+ graphql: {
419
+ query: completeQuery,
420
+ variables: requestVariables
421
+ }
422
+ };
423
+ }
424
+ catch (error) {
425
+ const errorMessage = {
426
+ error,
427
+ message: 'Failed to retrieve page data',
428
+ graphql: {
429
+ query: completeQuery,
430
+ variables: requestVariables
431
+ }
432
+ };
433
+ throw errorMessage;
434
+ }
432
435
  }
433
- };
436
+ }
434
437
 
435
438
  /**
436
439
  * Parses a string into a URL object.
@@ -443,7 +446,7 @@ function parseURL(url) {
443
446
  return new URL(url);
444
447
  }
445
448
  catch {
446
- console.error('Invalid URL:', url);
449
+ consola.consola.error('[DotCMS Client]: Invalid URL:', url);
447
450
  return undefined;
448
451
  }
449
452
  }
package/next.esm.js CHANGED
@@ -1,4 +1,5 @@
1
- import { E as ErrorMessages, _ as __classPrivateFieldGet, g as graphqlToPageEntity, C as Content } from './transforms.esm.js';
1
+ import { consola } from 'consola';
2
+ import { E as ErrorMessages, g as graphqlToPageEntity, C as Content } from './transforms.esm.js';
2
3
 
3
4
  class NavigationClient {
4
5
  constructor(config, requestOptions) {
@@ -77,7 +78,7 @@ const DEFAULT_PAGE_CONTENTLETS_CONTENT = `
77
78
  */
78
79
  const buildPageQuery = ({ page, fragments, additionalQueries }) => {
79
80
  if (!page) {
80
- console.warn('No page query provided. The query will be used by fetching all content with _map. This may mean poor performance in the query. We suggest you provide a detailed query on page attribute.');
81
+ consola.warn("[DotCMS Client]: No page query was found, so we're loading all content using _map. This might slow things down. For better performance, we recommend adding a specific query in the page attribute.");
81
82
  }
82
83
  return `
83
84
  fragment DotCMSPage on DotPage {
@@ -124,9 +125,22 @@ const buildPageQuery = ({ page, fragments, additionalQueries }) => {
124
125
  canEdit
125
126
  canLock
126
127
  canRead
128
+ runningExperimentId
127
129
  urlContentMap {
128
130
  _map
129
131
  }
132
+ host {
133
+ identifier
134
+ hostName
135
+ googleMap
136
+ archived
137
+ contentType
138
+ }
139
+ vanityUrl {
140
+ action
141
+ forwardTo
142
+ uri
143
+ }
130
144
  conLanguage {
131
145
  id
132
146
  language
@@ -134,6 +148,9 @@ const buildPageQuery = ({ page, fragments, additionalQueries }) => {
134
148
  }
135
149
  template {
136
150
  drawed
151
+ anonymous
152
+ theme
153
+ identifier
137
154
  }
138
155
  containers {
139
156
  path
@@ -274,7 +291,6 @@ async function fetchGraphQL({ baseURL, body, headers }) {
274
291
  return await response.json();
275
292
  }
276
293
 
277
- var _PageClient_instances, _PageClient_getPageFromGraphQL;
278
294
  /**
279
295
  * Client for interacting with the DotCMS Page API.
280
296
  * Provides methods to retrieve and manipulate pages.
@@ -302,7 +318,6 @@ class PageClient {
302
318
  * ```
303
319
  */
304
320
  constructor(config, requestOptions) {
305
- _PageClient_instances.add(this);
306
321
  this.requestOptions = requestOptions;
307
322
  this.siteId = config.siteId || '';
308
323
  this.dotcmsUrl = config.dotcmsUrl;
@@ -357,78 +372,66 @@ class PageClient {
357
372
  * });
358
373
  * ```
359
374
  */
360
- get(url, options) {
361
- return __classPrivateFieldGet(this, _PageClient_instances, "m", _PageClient_getPageFromGraphQL).call(this, url, options);
362
- }
363
- }
364
- _PageClient_instances = new WeakSet(), _PageClient_getPageFromGraphQL =
365
- /**
366
- * Private implementation method that fetches page data using GraphQL.
367
- * This method is used internally by the public get() method.
368
- *
369
- * @private
370
- * @param {string} url - The URL of the page to retrieve
371
- * @param {DotCMSPageRequestParams} [options] - Options including languageId, mode, and GraphQL parameters
372
- * @returns {Promise<DotCMSComposedPageResponse<T>>} A Promise that resolves to the page data
373
- */
374
- async function _PageClient_getPageFromGraphQL(url, options) {
375
- const { languageId = '1', mode = 'LIVE', siteId = this.siteId, fireRules = false, personaId, publishDate, graphql = {} } = options || {};
376
- const { page, content = {}, variables, fragments } = graphql;
377
- const contentQuery = buildQuery(content);
378
- const completeQuery = buildPageQuery({
379
- page,
380
- fragments,
381
- additionalQueries: contentQuery
382
- });
383
- const requestVariables = {
384
- url,
385
- mode,
386
- languageId,
387
- personaId,
388
- fireRules,
389
- publishDate,
390
- siteId,
391
- ...variables
392
- };
393
- const requestHeaders = this.requestOptions.headers;
394
- const requestBody = JSON.stringify({ query: completeQuery, variables: requestVariables });
395
- try {
396
- const { data, errors } = await fetchGraphQL({
397
- baseURL: this.dotcmsUrl,
398
- body: requestBody,
399
- headers: requestHeaders
375
+ async get(url, options) {
376
+ const { languageId = '1', mode = 'LIVE', siteId = this.siteId, fireRules = false, personaId, publishDate, graphql = {} } = options || {};
377
+ const { page, content = {}, variables, fragments } = graphql;
378
+ const contentQuery = buildQuery(content);
379
+ const completeQuery = buildPageQuery({
380
+ page,
381
+ fragments,
382
+ additionalQueries: contentQuery
400
383
  });
401
- if (errors) {
402
- errors.forEach((error) => {
403
- throw new Error(error.message);
384
+ const requestVariables = {
385
+ // The url is expected to have a leading slash to comply on VanityURL Matching, some frameworks like Angular will not add the leading slash
386
+ url: url.startsWith('/') ? url : `/${url}`,
387
+ mode,
388
+ languageId,
389
+ personaId,
390
+ fireRules,
391
+ publishDate,
392
+ siteId,
393
+ ...variables
394
+ };
395
+ const requestHeaders = this.requestOptions.headers;
396
+ const requestBody = JSON.stringify({ query: completeQuery, variables: requestVariables });
397
+ try {
398
+ const { data, errors } = await fetchGraphQL({
399
+ baseURL: this.dotcmsUrl,
400
+ body: requestBody,
401
+ headers: requestHeaders
404
402
  });
405
- }
406
- const pageResponse = graphqlToPageEntity(data);
407
- if (!pageResponse) {
408
- throw new Error('No page data found');
409
- }
410
- const contentResponse = mapResponseData(data, Object.keys(content));
411
- return {
412
- pageAsset: pageResponse,
413
- content: contentResponse,
414
- graphql: {
415
- query: completeQuery,
416
- variables: requestVariables
403
+ if (errors) {
404
+ errors.forEach((error) => {
405
+ throw new Error(error.message);
406
+ });
417
407
  }
418
- };
419
- }
420
- catch (error) {
421
- const errorMessage = {
422
- error,
423
- message: 'Failed to retrieve page data',
424
- graphql: {
425
- query: completeQuery,
426
- variables: requestVariables
408
+ const pageResponse = graphqlToPageEntity(data);
409
+ if (!pageResponse) {
410
+ throw new Error('No page data found');
427
411
  }
428
- };
429
- throw errorMessage;
412
+ const contentResponse = mapResponseData(data, Object.keys(content));
413
+ return {
414
+ pageAsset: pageResponse,
415
+ content: contentResponse,
416
+ graphql: {
417
+ query: completeQuery,
418
+ variables: requestVariables
419
+ }
420
+ };
421
+ }
422
+ catch (error) {
423
+ const errorMessage = {
424
+ error,
425
+ message: 'Failed to retrieve page data',
426
+ graphql: {
427
+ query: completeQuery,
428
+ variables: requestVariables
429
+ }
430
+ };
431
+ throw errorMessage;
432
+ }
430
433
  }
431
- };
434
+ }
432
435
 
433
436
  /**
434
437
  * Parses a string into a URL object.
@@ -441,7 +444,7 @@ function parseURL(url) {
441
444
  return new URL(url);
442
445
  }
443
446
  catch {
444
- console.error('Invalid URL:', url);
447
+ consola.error('[DotCMS Client]: Invalid URL:', url);
445
448
  return undefined;
446
449
  }
447
450
  }
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "@dotcms/client",
3
- "version": "0.0.1-beta.34",
3
+ "version": "0.0.1-beta.36",
4
4
  "description": "Official JavaScript library for interacting with DotCMS REST APIs.",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/dotCMS/core.git#main"
8
8
  },
9
+ "dependencies": {
10
+ "consola": "^3.4.2"
11
+ },
9
12
  "devDependencies": {
10
13
  "@dotcms/types": "next"
11
14
  },
@@ -15,7 +15,6 @@ import { CollectionBuilder } from './builders/collection/collection';
15
15
  * .sortBy([{ field: 'title', order: 'asc' }])
16
16
  * .query(q => q.field('author').equals('John Doe'))
17
17
  * .depth(1)
18
- * .fetch();
19
18
  *
20
19
  * console.log(response.contentlets);
21
20
  * ```
@@ -29,7 +28,6 @@ import { CollectionBuilder } from './builders/collection/collection';
29
28
  * .sortBy([{ field: 'title', order: 'asc' }])
30
29
  * .query(q => q.field('author').equals('John Doe'))
31
30
  * .depth(1)
32
- * .fetch()
33
31
  * .then(response => console.log(response.contentlets))
34
32
  * .catch(error => console.error(error));
35
33
  * ```
@@ -45,7 +43,6 @@ import { CollectionBuilder } from './builders/collection/collection';
45
43
  * const posts = await client.content
46
44
  * .getCollection<BlogPost>('Blog')
47
45
  * .limit(10)
48
- * .fetch();
49
46
  *
50
47
  * posts.contentlets.forEach(post => {
51
48
  * console.log(post.title, post.author, post.summary);
@@ -4,7 +4,6 @@ import { DotCMSClientConfig, DotCMSComposedPageResponse, DotCMSExtendedPageRespo
4
4
  * Provides methods to retrieve and manipulate pages.
5
5
  */
6
6
  export declare class PageClient {
7
- #private;
8
7
  /**
9
8
  * Request options including authorization headers.
10
9
  * @private
@@ -1,4 +1,4 @@
1
- import { DotCMSGraphQLPageResponse } from '@dotcms/types';
1
+ import { DotCMSGraphQLPageResponse, DotCMSPageAsset } from '@dotcms/types';
2
2
  /**
3
3
  * Transforms a GraphQL Page response to a Page Entity.
4
4
  *
@@ -10,4 +10,4 @@ import { DotCMSGraphQLPageResponse } from '@dotcms/types';
10
10
  * const pageEntity = graphqlToPageEntity(graphQLPageResponse);
11
11
  * ```
12
12
  */
13
- export declare const graphqlToPageEntity: (graphQLPageResponse: DotCMSGraphQLPageResponse) => any;
13
+ export declare const graphqlToPageEntity: (graphQLPageResponse: DotCMSGraphQLPageResponse) => DotCMSPageAsset | null;
package/transforms.cjs.js CHANGED
@@ -924,7 +924,6 @@ var _Content_requestOptions, _Content_serverUrl;
924
924
  * .sortBy([{ field: 'title', order: 'asc' }])
925
925
  * .query(q => q.field('author').equals('John Doe'))
926
926
  * .depth(1)
927
- * .fetch();
928
927
  *
929
928
  * console.log(response.contentlets);
930
929
  * ```
@@ -938,7 +937,6 @@ var _Content_requestOptions, _Content_serverUrl;
938
937
  * .sortBy([{ field: 'title', order: 'asc' }])
939
938
  * .query(q => q.field('author').equals('John Doe'))
940
939
  * .depth(1)
941
- * .fetch()
942
940
  * .then(response => console.log(response.contentlets))
943
941
  * .catch(error => console.error(error));
944
942
  * ```
@@ -954,7 +952,6 @@ var _Content_requestOptions, _Content_serverUrl;
954
952
  * const posts = await client.content
955
953
  * .getCollection<BlogPost>('Blog')
956
954
  * .limit(10)
957
- * .fetch();
958
955
  *
959
956
  * posts.contentlets.forEach(post => {
960
957
  * console.log(post.title, post.author, post.summary);
@@ -1082,8 +1079,9 @@ const graphqlToPageEntity = (graphQLPageResponse) => {
1082
1079
  if (!page) {
1083
1080
  return null;
1084
1081
  }
1085
- const { layout, template, containers, urlContentMap, viewAs, site, _map, ...pageAsset } = page;
1082
+ const { layout, template, containers, urlContentMap, viewAs, host, vanityUrl, runningExperimentId, _map, ...pageAsset } = page;
1086
1083
  const data = (_map || {});
1084
+ const typedPageAsset = pageAsset;
1087
1085
  // To prevent type errors, we cast the urlContentMap to an object
1088
1086
  const urlContentMapObject = urlContentMap;
1089
1087
  // Extract the _map data from the urlContentMap object
@@ -1092,20 +1090,22 @@ const graphqlToPageEntity = (graphQLPageResponse) => {
1092
1090
  layout,
1093
1091
  template,
1094
1092
  viewAs,
1095
- site,
1093
+ vanityUrl,
1094
+ runningExperimentId,
1095
+ site: host,
1096
+ urlContentMap: urlContentMapData,
1097
+ containers: parseContainers(containers),
1096
1098
  page: {
1097
1099
  ...data,
1098
- ...pageAsset
1099
- },
1100
- containers: parseContainers(containers),
1101
- urlContentMap: urlContentMapData
1102
- }; // NOTE: This is a rabbit hole and we have to fix this, not in this PR tho.
1100
+ ...typedPageAsset
1101
+ }
1102
+ };
1103
1103
  };
1104
1104
  /**
1105
1105
  * Parses the containers from the GraphQL response.
1106
1106
  *
1107
- * @param {Array<Record<string, unknown>>} [containers=[]] - The containers array from the GraphQL response.
1108
- * @returns {Record<string, unknown>} The parsed containers.
1107
+ * @param {DotCMSGraphQLPageContainer[]} [containers=[]] - The containers array from the GraphQL response.
1108
+ * @returns {DotCMSPageAssetContainers} The parsed containers.
1109
1109
  */
1110
1110
  const parseContainers = (containers = []) => {
1111
1111
  return containers.reduce((acc, container) => {
package/transforms.esm.js CHANGED
@@ -922,7 +922,6 @@ var _Content_requestOptions, _Content_serverUrl;
922
922
  * .sortBy([{ field: 'title', order: 'asc' }])
923
923
  * .query(q => q.field('author').equals('John Doe'))
924
924
  * .depth(1)
925
- * .fetch();
926
925
  *
927
926
  * console.log(response.contentlets);
928
927
  * ```
@@ -936,7 +935,6 @@ var _Content_requestOptions, _Content_serverUrl;
936
935
  * .sortBy([{ field: 'title', order: 'asc' }])
937
936
  * .query(q => q.field('author').equals('John Doe'))
938
937
  * .depth(1)
939
- * .fetch()
940
938
  * .then(response => console.log(response.contentlets))
941
939
  * .catch(error => console.error(error));
942
940
  * ```
@@ -952,7 +950,6 @@ var _Content_requestOptions, _Content_serverUrl;
952
950
  * const posts = await client.content
953
951
  * .getCollection<BlogPost>('Blog')
954
952
  * .limit(10)
955
- * .fetch();
956
953
  *
957
954
  * posts.contentlets.forEach(post => {
958
955
  * console.log(post.title, post.author, post.summary);
@@ -1080,8 +1077,9 @@ const graphqlToPageEntity = (graphQLPageResponse) => {
1080
1077
  if (!page) {
1081
1078
  return null;
1082
1079
  }
1083
- const { layout, template, containers, urlContentMap, viewAs, site, _map, ...pageAsset } = page;
1080
+ const { layout, template, containers, urlContentMap, viewAs, host, vanityUrl, runningExperimentId, _map, ...pageAsset } = page;
1084
1081
  const data = (_map || {});
1082
+ const typedPageAsset = pageAsset;
1085
1083
  // To prevent type errors, we cast the urlContentMap to an object
1086
1084
  const urlContentMapObject = urlContentMap;
1087
1085
  // Extract the _map data from the urlContentMap object
@@ -1090,20 +1088,22 @@ const graphqlToPageEntity = (graphQLPageResponse) => {
1090
1088
  layout,
1091
1089
  template,
1092
1090
  viewAs,
1093
- site,
1091
+ vanityUrl,
1092
+ runningExperimentId,
1093
+ site: host,
1094
+ urlContentMap: urlContentMapData,
1095
+ containers: parseContainers(containers),
1094
1096
  page: {
1095
1097
  ...data,
1096
- ...pageAsset
1097
- },
1098
- containers: parseContainers(containers),
1099
- urlContentMap: urlContentMapData
1100
- }; // NOTE: This is a rabbit hole and we have to fix this, not in this PR tho.
1098
+ ...typedPageAsset
1099
+ }
1100
+ };
1101
1101
  };
1102
1102
  /**
1103
1103
  * Parses the containers from the GraphQL response.
1104
1104
  *
1105
- * @param {Array<Record<string, unknown>>} [containers=[]] - The containers array from the GraphQL response.
1106
- * @returns {Record<string, unknown>} The parsed containers.
1105
+ * @param {DotCMSGraphQLPageContainer[]} [containers=[]] - The containers array from the GraphQL response.
1106
+ * @returns {DotCMSPageAssetContainers} The parsed containers.
1107
1107
  */
1108
1108
  const parseContainers = (containers = []) => {
1109
1109
  return containers.reduce((acc, container) => {