@sitecore-content-sdk/core 0.1.0-beta.13 → 0.1.0-beta.16

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.
Files changed (78) hide show
  1. package/client.d.ts +1 -0
  2. package/client.js +1 -0
  3. package/dist/cjs/client/constants.js +7 -0
  4. package/dist/cjs/{graphql → client}/index.js +5 -5
  5. package/dist/cjs/client/models.js +2 -0
  6. package/dist/cjs/client/sitecore-client.js +265 -0
  7. package/dist/cjs/client/utils.js +30 -0
  8. package/dist/cjs/editing/graphql-editing-service.js +17 -11
  9. package/dist/cjs/graphql-request-client.js +8 -6
  10. package/dist/cjs/i18n/graphql-dictionary-service.js +17 -24
  11. package/dist/cjs/layout/graphql-layout-service.js +14 -26
  12. package/dist/cjs/layout/layout-service.js +2 -1
  13. package/dist/cjs/personalize/graphql-personalize-service.js +2 -0
  14. package/dist/cjs/personalize/index.js +2 -1
  15. package/dist/cjs/site/graphql-error-pages-service.js +9 -6
  16. package/dist/cjs/site/graphql-redirects-service.js +3 -2
  17. package/dist/cjs/site/graphql-siteinfo-service.js +6 -10
  18. package/dist/cjs/site/{graphql-sitemap-service.js → graphql-sitemap-xml-service.js} +3 -2
  19. package/dist/cjs/site/graphql-sitepath-service.js +199 -0
  20. package/dist/cjs/site/index.js +6 -3
  21. package/dist/cjs/sitecore-service-base.js +36 -0
  22. package/dist/esm/client/constants.js +4 -0
  23. package/dist/esm/{graphql → client}/index.js +2 -2
  24. package/dist/esm/client/models.js +1 -0
  25. package/dist/esm/client/sitecore-client.js +261 -0
  26. package/dist/esm/client/utils.js +26 -0
  27. package/dist/esm/editing/graphql-editing-service.js +17 -11
  28. package/dist/esm/editing/index.js +1 -1
  29. package/dist/esm/graphql-request-client.js +8 -6
  30. package/dist/esm/i18n/graphql-dictionary-service.js +15 -22
  31. package/dist/esm/layout/graphql-layout-service.js +14 -26
  32. package/dist/esm/layout/layout-service.js +2 -1
  33. package/dist/esm/personalize/graphql-personalize-service.js +2 -0
  34. package/dist/esm/personalize/index.js +1 -1
  35. package/dist/esm/site/graphql-error-pages-service.js +9 -6
  36. package/dist/esm/site/graphql-redirects-service.js +3 -2
  37. package/dist/esm/site/graphql-siteinfo-service.js +6 -10
  38. package/dist/esm/site/{graphql-sitemap-service.js → graphql-sitemap-xml-service.js} +3 -2
  39. package/dist/esm/site/graphql-sitepath-service.js +191 -0
  40. package/dist/esm/site/index.js +3 -2
  41. package/dist/esm/sitecore-service-base.js +29 -0
  42. package/package.json +2 -2
  43. package/types/client/constants.d.ts +4 -0
  44. package/types/{graphql → client}/index.d.ts +4 -3
  45. package/types/client/models.d.ts +5 -0
  46. package/types/client/sitecore-client.d.ts +147 -0
  47. package/types/client/utils.d.ts +9 -0
  48. package/types/editing/graphql-editing-service.d.ts +19 -13
  49. package/types/editing/index.d.ts +1 -1
  50. package/types/editing/models.d.ts +27 -0
  51. package/types/graphql-request-client.d.ts +4 -13
  52. package/types/i18n/graphql-dictionary-service.d.ts +37 -17
  53. package/types/index.d.ts +1 -1
  54. package/types/layout/graphql-layout-service.d.ts +10 -23
  55. package/types/layout/index.d.ts +1 -2
  56. package/types/layout/layout-service.d.ts +5 -15
  57. package/types/layout/models.d.ts +7 -0
  58. package/types/models.d.ts +46 -0
  59. package/types/personalize/index.d.ts +1 -1
  60. package/types/site/graphql-error-pages-service.d.ts +7 -7
  61. package/types/site/graphql-redirects-service.d.ts +3 -2
  62. package/types/site/graphql-robots-service.d.ts +1 -1
  63. package/types/site/graphql-siteinfo-service.d.ts +2 -3
  64. package/types/site/{graphql-sitemap-service.d.ts → graphql-sitemap-xml-service.d.ts} +3 -2
  65. package/types/site/graphql-sitepath-service.d.ts +135 -0
  66. package/types/site/index.d.ts +3 -2
  67. package/types/sitecore-service-base.d.ts +30 -0
  68. package/dist/cjs/graphql/app-root-query.js +0 -62
  69. package/dist/cjs/graphql/search-service.js +0 -50
  70. package/dist/esm/graphql/app-root-query.js +0 -58
  71. package/dist/esm/graphql/search-service.js +0 -46
  72. package/graphql.d.ts +0 -1
  73. package/graphql.js +0 -1
  74. package/types/graphql/app-root-query.d.ts +0 -32
  75. package/types/graphql/search-service.d.ts +0 -95
  76. /package/dist/cjs/{graphql → client}/graphql-edge-proxy.js +0 -0
  77. /package/dist/esm/{graphql → client}/graphql-edge-proxy.js +0 -0
  78. /package/types/{graphql → client}/graphql-edge-proxy.d.ts +0 -0
package/client.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './types/client/index';
package/client.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./dist/cjs/client/index');
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.languageError = exports.siteNameError = void 0;
4
+ /** @private */
5
+ exports.siteNameError = 'The site name must be a non-empty string';
6
+ /** @private */
7
+ exports.languageError = 'The language must be a non-empty string';
@@ -1,14 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getEdgeProxyFormsUrl = exports.getEdgeProxyContentUrl = exports.DefaultRetryStrategy = exports.SearchQueryService = exports.GraphQLRequestClient = exports.getAppRootId = void 0;
4
- var app_root_query_1 = require("./app-root-query");
5
- Object.defineProperty(exports, "getAppRootId", { enumerable: true, get: function () { return app_root_query_1.getAppRootId; } });
3
+ exports.createGraphQLClientFactory = exports.SitecoreClient = exports.getEdgeProxyFormsUrl = exports.getEdgeProxyContentUrl = exports.DefaultRetryStrategy = exports.GraphQLRequestClient = void 0;
6
4
  var graphql_request_client_1 = require("../graphql-request-client");
7
5
  Object.defineProperty(exports, "GraphQLRequestClient", { enumerable: true, get: function () { return graphql_request_client_1.GraphQLRequestClient; } });
8
- var search_service_1 = require("./search-service");
9
- Object.defineProperty(exports, "SearchQueryService", { enumerable: true, get: function () { return search_service_1.SearchQueryService; } });
10
6
  var retries_1 = require("../retries");
11
7
  Object.defineProperty(exports, "DefaultRetryStrategy", { enumerable: true, get: function () { return retries_1.DefaultRetryStrategy; } });
12
8
  var graphql_edge_proxy_1 = require("./graphql-edge-proxy");
13
9
  Object.defineProperty(exports, "getEdgeProxyContentUrl", { enumerable: true, get: function () { return graphql_edge_proxy_1.getEdgeProxyContentUrl; } });
14
10
  Object.defineProperty(exports, "getEdgeProxyFormsUrl", { enumerable: true, get: function () { return graphql_edge_proxy_1.getEdgeProxyFormsUrl; } });
11
+ var sitecore_client_1 = require("./sitecore-client");
12
+ Object.defineProperty(exports, "SitecoreClient", { enumerable: true, get: function () { return sitecore_client_1.SitecoreClient; } });
13
+ var utils_1 = require("./utils");
14
+ Object.defineProperty(exports, "createGraphQLClientFactory", { enumerable: true, get: function () { return utils_1.createGraphQLClientFactory; } });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SitecoreClient = void 0;
4
+ const editing_1 = require("../editing");
5
+ const i18n_1 = require("../i18n");
6
+ const layout_1 = require("../layout");
7
+ const utils_1 = require("../personalize/utils");
8
+ const layout_personalizer_1 = require("../personalize/layout-personalizer");
9
+ const site_1 = require("../site");
10
+ const utils_2 = require("./utils");
11
+ /**
12
+ * This is a generic content client that can be used by any framework.
13
+ * Use it to retrieve pages, preview data, dictionary and other data
14
+ */
15
+ class SitecoreClient {
16
+ /**
17
+ * Init SitecoreClient
18
+ * @param {SitecoreClientInit} initOptions initOptions for the client, containing site and Sitecore connection details
19
+ */
20
+ constructor(initOptions) {
21
+ this.initOptions = initOptions;
22
+ this.clientFactory = this.getClientFactory();
23
+ this.siteResolver = this.getSiteResolver();
24
+ const baseServiceOptions = this.getBaseServiceOptions();
25
+ this.layoutService = this.getLayoutService(baseServiceOptions);
26
+ this.dictionaryService = this.getDictionaryService(baseServiceOptions);
27
+ this.editingService = this.getEditingService();
28
+ this.errorPagesService = this.getErrorPagesService();
29
+ this.componentService = this.getComponentService();
30
+ this.sitePathService = this.getSitePathService();
31
+ this.siteResolver = new site_1.SiteResolver(initOptions.sites);
32
+ this.editingService = new editing_1.GraphQLEditingService({ clientFactory: this.clientFactory });
33
+ }
34
+ /**
35
+ * Resolve site by hostname
36
+ * @param {string} hostname site hostname
37
+ * @returns {SiteInfo} site details matching the hostname
38
+ */
39
+ resolveSite(hostname) {
40
+ const site = this.siteResolver.getByHost(hostname);
41
+ return site;
42
+ }
43
+ /**
44
+ * Normalize path regardless of type
45
+ * @param {string | string[]} path string or string array path
46
+ * @returns {string} string path
47
+ */
48
+ parsePath(path) {
49
+ // join array path, while clearing extra slashes and ensure first slash
50
+ return typeof path === 'string'
51
+ ? path.startsWith('/')
52
+ ? path
53
+ : `/${path}`
54
+ : `/${path
55
+ .filter((part) => part !== '/')
56
+ .map((part) => part.replace(/^\/+/, '').replace(/\/+$/, ''))
57
+ .join('/')}`;
58
+ }
59
+ /**
60
+ * Get page details for a route, with layout and other details
61
+ * @param {string} path route path
62
+ * @param {PageOptions} [pageOptions] site, language and personalization variant details for route
63
+ * @param {FetchOptions} [fetchOptions] Additional fetch fetch options to override GraphQL requests (like retries and fetch)
64
+ * @returns {Page | null} page details
65
+ */
66
+ async getPage(path, pageOptions, fetchOptions) {
67
+ var _a, _b, _c;
68
+ const computedPath = this.parsePath(path);
69
+ const locale = (_a = pageOptions === null || pageOptions === void 0 ? void 0 : pageOptions.locale) !== null && _a !== void 0 ? _a : this.initOptions.defaultLanguage;
70
+ const site = (_b = pageOptions === null || pageOptions === void 0 ? void 0 : pageOptions.site) !== null && _b !== void 0 ? _b : this.initOptions.defaultSite;
71
+ // Fetch layout data, passing on req/res for SSR
72
+ const layout = await this.layoutService.fetchLayoutData(computedPath, {
73
+ locale,
74
+ site,
75
+ }, fetchOptions);
76
+ if (!layout.sitecore.route) {
77
+ return null;
78
+ }
79
+ else {
80
+ const siteInfo = this.siteResolver.getByName(site);
81
+ // Initialize links to be inserted on the page
82
+ if ((_c = pageOptions === null || pageOptions === void 0 ? void 0 : pageOptions.personalize) === null || _c === void 0 ? void 0 : _c.variantId) {
83
+ // Modify layoutData to use specific variant(s) instead of default
84
+ // This will also set the variantId on the Sitecore context so that it is accessible here
85
+ (0, layout_personalizer_1.personalizeLayout)(layout, pageOptions.personalize.variantId, pageOptions.personalize.componentVariantIds);
86
+ }
87
+ return {
88
+ layout,
89
+ site: siteInfo,
90
+ locale,
91
+ };
92
+ }
93
+ }
94
+ /**
95
+ * Retrieves the head `<link>` elements for Sitecore styles and themes.
96
+ * @param {LayoutServiceData} layoutData - The layout data containing styles and themes.
97
+ * @param {object} [options] - Optional configuration for enabling styles and themes.
98
+ * @param {boolean} [options.enableStyles] - Whether to include content styles.
99
+ * @param {boolean} [options.enableThemes] - Whether to include theme styles.
100
+ * @returns {HTMLLink[]} An array of `<link>` elements for stylesheets.
101
+ */
102
+ getHeadLinks(layoutData, options = {}) {
103
+ const { enableStyles = true, enableThemes = true } = options;
104
+ const { contextId, edgeUrl } = this.initOptions.api.edge;
105
+ const headLinks = [];
106
+ if (enableStyles) {
107
+ const contentStyles = (0, layout_1.getContentStylesheetLink)(layoutData, contextId, edgeUrl);
108
+ if (contentStyles)
109
+ headLinks.push(contentStyles);
110
+ }
111
+ if (enableThemes) {
112
+ headLinks.push(...(0, layout_1.getComponentLibraryStylesheetLinks)(layoutData, contextId, edgeUrl));
113
+ }
114
+ return headLinks;
115
+ }
116
+ /**
117
+ * Retrieves dictionary phrases for a given site and locale.
118
+ * @param {PageOptions} pageOptions - Route options containing language and site name to load dictionary for
119
+ * @param {FetchOptions} [fetchOptions] Additional fetch fetch options to override GraphQL requests (like retries and fetch)
120
+ * @returns {DictionaryPhrases} A promise that resolves to the dictionary phrases.
121
+ */
122
+ async getDictionary(pageOptions, fetchOptions) {
123
+ const locale = (pageOptions === null || pageOptions === void 0 ? void 0 : pageOptions.locale) || this.initOptions.defaultLanguage;
124
+ const site = (pageOptions === null || pageOptions === void 0 ? void 0 : pageOptions.site) || this.initOptions.defaultSite;
125
+ return await this.dictionaryService.fetchDictionaryData(locale, site, fetchOptions);
126
+ }
127
+ /**
128
+ * Retrieves error pages for a given site and locale.
129
+ * @param {RouteOptions} routeOptions - Route options containing language and site name to load error pages
130
+ * @param {FetchOptions} [fetchOptions] Additional fetch fetch options to override GraphQL requests (like retries and fetch)
131
+ * @returns {ErrorPages | null} A promise that resolves to the error pages or null if not found.
132
+ */
133
+ async getErrorPages(routeOptions, fetchOptions) {
134
+ const locale = (routeOptions === null || routeOptions === void 0 ? void 0 : routeOptions.locale) || this.initOptions.defaultLanguage;
135
+ const site = (routeOptions === null || routeOptions === void 0 ? void 0 : routeOptions.site) || this.initOptions.defaultSite;
136
+ return await this.errorPagesService.fetchErrorPages(site, locale, fetchOptions);
137
+ }
138
+ /**
139
+ * Retrieves preview page and layout details
140
+ * @param {EditingPreviewData | undefined} previewData - The editing preview data for metadata mode.
141
+ * @param {FetchOptions} [fetchOptions] Additional fetch fetch options to override GraphQL requests (like retries and fetch)
142
+ * @returns {Page} preview page details
143
+ */
144
+ async getPreview(previewData, fetchOptions) {
145
+ if (!previewData) {
146
+ console.error('Preview data missing');
147
+ return null;
148
+ }
149
+ // If we're in Pages preview (editing) mode, prefetch the editing data
150
+ const { site, itemId, language, version, variantIds, layoutKind, } = previewData;
151
+ const data = await this.editingService.fetchEditingData({
152
+ siteName: site,
153
+ itemId,
154
+ language,
155
+ version,
156
+ layoutKind,
157
+ }, fetchOptions);
158
+ if (!data) {
159
+ throw new Error(`Unable to fetch editing data for preview ${JSON.stringify(previewData)}`);
160
+ }
161
+ const page = {
162
+ locale: language,
163
+ layout: data.layoutData,
164
+ headLinks: this.getHeadLinks(data.layoutData),
165
+ dictionary: data.dictionary,
166
+ site: data.layoutData.sitecore.context.site,
167
+ };
168
+ const personalizeData = (0, utils_1.getGroomedVariantIds)(variantIds);
169
+ (0, layout_personalizer_1.personalizeLayout)(page.layout, personalizeData.variantId, personalizeData.componentVariantIds);
170
+ return page;
171
+ }
172
+ /**
173
+ * Get component library page details for Component Library mode of your app
174
+ * @param {ComponentLibraryRenderPreviewData} componentLibData preview data set in 'library' mode of the app
175
+ * @param {FetchOptions} [fetchOptions] Additional fetch fetch options to override GraphQL requests (like retries and fetch)
176
+ * @returns {Page} preview page for Component Library
177
+ */
178
+ async getComponentLibraryData(componentLibData, fetchOptions) {
179
+ if (!this.initOptions.api.local) {
180
+ throw new Error('Component Library requires Sitecore apiHost and apiKey to be provided');
181
+ }
182
+ const { itemId, componentUid, site, language, renderingId, dataSourceId, version, } = componentLibData;
183
+ const componentData = await this.componentService.fetchComponentData({
184
+ siteName: site,
185
+ itemId,
186
+ language,
187
+ componentUid,
188
+ renderingId,
189
+ dataSourceId,
190
+ version,
191
+ });
192
+ const dictionaryData = await this.editingService.fetchDictionaryData({
193
+ siteName: site,
194
+ language,
195
+ }, fetchOptions);
196
+ if (!componentData) {
197
+ throw new Error(`Unable to fetch editing data for preview ${JSON.stringify(componentLibData)}`);
198
+ }
199
+ const page = {
200
+ locale: componentLibData.language,
201
+ layout: componentData,
202
+ headLinks: this.getHeadLinks(componentData),
203
+ dictionary: dictionaryData,
204
+ };
205
+ return page;
206
+ }
207
+ /**
208
+ * Retrieves the static paths for pages based on the given languages.
209
+ * @param {string[]} [languages] - An optional array of language codes to generate paths for.
210
+ * @param {FetchOptions} [fetchOptions] - Additional fetch options.
211
+ * @returns {Promise<StaticPath[]>} A promise that resolves to an array of static paths.
212
+ */
213
+ async getPagePaths(languages, fetchOptions) {
214
+ return this.sitePathService.fetchSiteRoutes(languages || [], fetchOptions);
215
+ }
216
+ /**
217
+ * Factory methods for creating dependencies
218
+ * Subclasses can override these to provide custom implementations.
219
+ */
220
+ getBaseServiceOptions() {
221
+ return {
222
+ defaultSite: this.initOptions.defaultSite,
223
+ clientFactory: this.clientFactory,
224
+ retries: this.initOptions.retries,
225
+ };
226
+ }
227
+ getClientFactory() {
228
+ const graphQLOptions = {
229
+ api: this.initOptions.api,
230
+ retries: this.initOptions.retries.count,
231
+ retryStrategy: this.initOptions.retries.retryStrategy,
232
+ };
233
+ return (0, utils_2.createGraphQLClientFactory)(graphQLOptions);
234
+ }
235
+ getSiteResolver() {
236
+ return new site_1.SiteResolver(this.initOptions.sites);
237
+ }
238
+ getLayoutService(baseOptions) {
239
+ return new layout_1.GraphQLLayoutService(Object.assign(Object.assign({}, baseOptions), { formatLayoutQuery: this.initOptions.layout.formatLayoutQuery }));
240
+ }
241
+ getDictionaryService(baseOptions) {
242
+ return new i18n_1.GraphQLDictionaryService(Object.assign(Object.assign({}, baseOptions), { cacheEnabled: this.initOptions.dictionary.caching.enabled, cacheTimeout: this.initOptions.dictionary.caching.timeout }));
243
+ }
244
+ getEditingService() {
245
+ return new editing_1.GraphQLEditingService({ clientFactory: this.clientFactory });
246
+ }
247
+ getErrorPagesService() {
248
+ return new site_1.GraphQLErrorPagesService(Object.assign(Object.assign({}, this.initOptions), { language: this.initOptions.defaultLanguage, clientFactory: this.clientFactory }));
249
+ }
250
+ getComponentService() {
251
+ var _a, _b;
252
+ return new editing_1.RestComponentLayoutService({
253
+ apiHost: (_a = this.initOptions.api.local) === null || _a === void 0 ? void 0 : _a.apiHost,
254
+ apiKey: (_b = this.initOptions.api.local) === null || _b === void 0 ? void 0 : _b.apiKey,
255
+ siteName: this.initOptions.defaultSite,
256
+ });
257
+ }
258
+ getSitePathService() {
259
+ return new site_1.GraphQLSitePathService({
260
+ clientFactory: this.clientFactory,
261
+ sites: this.siteResolver.sites,
262
+ });
263
+ }
264
+ }
265
+ exports.SitecoreClient = SitecoreClient;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createGraphQLClientFactory = void 0;
4
+ const graphql_request_client_1 = require("../graphql-request-client");
5
+ const graphql_edge_proxy_1 = require("./graphql-edge-proxy");
6
+ /**
7
+ * Creates a new GraphQLRequestClientFactory instance
8
+ * @param {FetchOptions} options jss config
9
+ * @returns GraphQLRequestClientFactory instance
10
+ */
11
+ const createGraphQLClientFactory = (options) => {
12
+ var _a, _b, _c, _d, _e, _f;
13
+ let clientConfig;
14
+ if ((_b = (_a = options.api) === null || _a === void 0 ? void 0 : _a.edge) === null || _b === void 0 ? void 0 : _b.contextId) {
15
+ clientConfig = {
16
+ endpoint: (0, graphql_edge_proxy_1.getEdgeProxyContentUrl)(options.api.edge.contextId, options.api.edge.edgeUrl),
17
+ };
18
+ }
19
+ else if (((_d = (_c = options.api) === null || _c === void 0 ? void 0 : _c.local) === null || _d === void 0 ? void 0 : _d.apiKey) && ((_f = (_e = options.api) === null || _e === void 0 ? void 0 : _e.local) === null || _f === void 0 ? void 0 : _f.apiHost)) {
20
+ clientConfig = {
21
+ endpoint: `${options.api.local.apiHost}${options.api.local.path}`,
22
+ apiKey: options.api.local.apiKey,
23
+ };
24
+ }
25
+ else {
26
+ throw new Error('Please configure and use either your sitecoreEdgeContextId, or your graphQLEndpoint and sitecoreApiKey.');
27
+ }
28
+ return graphql_request_client_1.GraphQLRequestClient.createClientFactory(Object.assign(Object.assign({}, clientConfig), options));
29
+ };
30
+ exports.createGraphQLClientFactory = createGraphQLClientFactory;
@@ -89,9 +89,10 @@ class GraphQLEditingService {
89
89
  * @param {string} variables.language - The language to fetch layout data for.
90
90
  * @param {string} [variables.version] - The version of the item (optional).
91
91
  * @param {LayoutKind} [variables.layoutKind] - The final or shared layout variant.
92
+ * @param {FetchOptions} [fetchOptions] Options to override graphQL client details like retries and fetch implementation
92
93
  * @returns {Promise} The layout data and dictionary phrases.
93
94
  */
94
- async fetchEditingData({ siteName, itemId, language, version, layoutKind = models_1.LayoutKind.Final, }) {
95
+ async fetchEditingData({ siteName, itemId, language, version, layoutKind = models_1.LayoutKind.Final }, fetchOptions) {
95
96
  var _a, _b, _c;
96
97
  debug_1.default.editing('fetching editing data for %s %s %s %s', siteName, itemId, language, version, layoutKind);
97
98
  if (!siteName) {
@@ -100,7 +101,7 @@ class GraphQLEditingService {
100
101
  if (!language) {
101
102
  throw new RangeError('The language must be a non-empty string');
102
103
  }
103
- let dictionaryResults = [];
104
+ let initDictionary = [];
104
105
  let hasNext = true;
105
106
  let after = '';
106
107
  const editingData = await this.graphQLClient.request(exports.query, {
@@ -108,20 +109,24 @@ class GraphQLEditingService {
108
109
  itemId,
109
110
  version,
110
111
  language,
111
- }, {
112
- headers: {
112
+ }, Object.assign(Object.assign({}, fetchOptions), { headers: {
113
113
  sc_layoutKind: layoutKind,
114
- },
115
- });
114
+ } }));
116
115
  if ((_b = (_a = editingData === null || editingData === void 0 ? void 0 : editingData.site) === null || _a === void 0 ? void 0 : _a.siteInfo) === null || _b === void 0 ? void 0 : _b.dictionary) {
117
- dictionaryResults = editingData.site.siteInfo.dictionary.results;
116
+ initDictionary = editingData.site.siteInfo.dictionary.results;
118
117
  hasNext = editingData.site.siteInfo.dictionary.pageInfo.hasNext;
119
118
  after = editingData.site.siteInfo.dictionary.pageInfo.endCursor;
120
119
  }
121
120
  else {
122
121
  hasNext = false;
123
122
  }
124
- const dictionary = await this.fetchDictionaryData({ siteName, language }, dictionaryResults, hasNext, after);
123
+ const dictionary = await this.fetchDictionaryData({
124
+ siteName,
125
+ language,
126
+ initDictionary,
127
+ hasNext,
128
+ after,
129
+ }, fetchOptions);
125
130
  return {
126
131
  layoutData: ((_c = editingData === null || editingData === void 0 ? void 0 : editingData.item) === null || _c === void 0 ? void 0 : _c.rendered) || {
127
132
  sitecore: {
@@ -132,16 +137,17 @@ class GraphQLEditingService {
132
137
  dictionary,
133
138
  };
134
139
  }
135
- async fetchDictionaryData({ siteName, language, }, initDictionary = [], hasNext = true, after) {
140
+ async fetchDictionaryData({ siteName, language, initDictionary, hasNext, after, }, fetchOptions) {
136
141
  var _a, _b;
137
- let dictionaryResults = initDictionary;
142
+ hasNext = hasNext !== undefined ? hasNext : true;
143
+ let dictionaryResults = initDictionary || [];
138
144
  const dictionary = {};
139
145
  while (hasNext) {
140
146
  const data = await this.graphQLClient.request(exports.dictionaryQuery, {
141
147
  siteName,
142
148
  language,
143
149
  after,
144
- });
150
+ }, fetchOptions);
145
151
  if ((_b = (_a = data === null || data === void 0 ? void 0 : data.site) === null || _a === void 0 ? void 0 : _a.siteInfo) === null || _b === void 0 ? void 0 : _b.dictionary) {
146
152
  dictionaryResults = dictionaryResults.concat(data.site.siteInfo.dictionary.results);
147
153
  hasNext = data.site.siteInfo.dictionary.pageInfo.hasNext;
@@ -55,15 +55,17 @@ class GraphQLRequestClient {
55
55
  /**
56
56
  * Execute graphql request
57
57
  * @param {string | DocumentNode} query graphql query
58
- * @param {object} [variables] graphql variables
59
58
  * @param {RequestOptions} [options] Options for configuring a GraphQL request.
60
59
  */
61
60
  async request(query, variables, options) {
62
61
  let attempt = 1;
63
62
  const retryer = async () => {
63
+ const retries = (options === null || options === void 0 ? void 0 : options.retries) || this.retries;
64
+ const retryStrategy = (options === null || options === void 0 ? void 0 : options.retryStrategy) || this.retryStrategy;
65
+ const debug = (options === null || options === void 0 ? void 0 : options.debugger) || this.debug;
64
66
  // Note we don't have access to raw request/response with graphql-request
65
67
  // but we should log whatever we have.
66
- this.debug('request: %o', {
68
+ debug('request: %o', {
67
69
  url: this.endpoint,
68
70
  headers: Object.assign(Object.assign({}, this.headers), options === null || options === void 0 ? void 0 : options.headers),
69
71
  query,
@@ -84,12 +86,12 @@ class GraphQLRequestClient {
84
86
  }, async (error) => {
85
87
  var _a, _b;
86
88
  (_a = this.abortTimeout) === null || _a === void 0 ? void 0 : _a.clear();
87
- this.debug('response error: %o', error.response || error.message || error);
89
+ debug('response error: %o', error.response || error.message || error);
88
90
  const status = ((_b = error.response) === null || _b === void 0 ? void 0 : _b.status) || error.code;
89
- const shouldRetry = this.retryStrategy.shouldRetry(error, attempt, this.retries);
91
+ const shouldRetry = retryStrategy.shouldRetry(error, attempt, retries);
90
92
  if (shouldRetry) {
91
- const delayMs = this.retryStrategy.getDelay(error, attempt);
92
- this.debug('Error: %s. Retrying in %dms (attempt %d).', status, delayMs, attempt);
93
+ const delayMs = retryStrategy.getDelay(error, attempt);
94
+ debug('Error: %s. Retrying in %dms (attempt %d).', status, delayMs, attempt);
93
95
  attempt++;
94
96
  return new Promise((resolve) => setTimeout(resolve, delayMs)).then(retryer);
95
97
  }
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.GraphQLDictionaryService = exports.queryError = void 0;
7
7
  const cache_client_1 = require("../cache-client");
8
- const app_root_query_1 = require("../graphql/app-root-query");
8
+ const constants_1 = require("../client/constants");
9
9
  const debug_1 = __importDefault(require("../debug"));
10
10
  /** @private */
11
11
  exports.queryError = 'Valid value for rootItemId not provided and failed to auto-resolve app root item.';
@@ -51,46 +51,37 @@ class GraphQLDictionaryService {
51
51
  /**
52
52
  * Fetches dictionary data for internalization. Uses search query by default
53
53
  * @param {string} language the language to fetch
54
+ * @param {string} site site name to fetch data for.
55
+ * @param {FetchOptions} [fetchOptions] Options to override graphQL client details like retries and fetch implementation
54
56
  * @returns {Promise<DictionaryPhrases>} dictionary phrases
55
57
  * @throws {Error} if the app root was not found for the specified site and language.
56
58
  */
57
- async fetchDictionaryData(language) {
58
- const cacheKey = this.options.siteName + language;
59
+ async fetchDictionaryData(language, site, fetchOptions) {
60
+ var _a, _b;
61
+ const cacheKey = site + language;
59
62
  const cachedValue = this.getCacheValue(cacheKey);
60
63
  if (cachedValue) {
61
- debug_1.default.dictionary('using cached dictionary data for %s %s', language, this.options.siteName);
64
+ debug_1.default.dictionary('using cached dictionary data for %s %s', language, site);
62
65
  return cachedValue;
63
66
  }
64
- const phrases = await this.fetchWithSiteQuery(language);
65
- this.setCacheValue(cacheKey, phrases);
66
- return phrases;
67
- }
68
- /**
69
- * Fetches dictionary data with site query
70
- * This is the default behavior for XMCloud deployments. Uses `siteQuery` to retrieve data.
71
- * @param {string} language the language to fetch
72
- * @returns {Promise<DictionaryPhrases>} dictionary phrases
73
- */
74
- async fetchWithSiteQuery(language) {
75
- var _a, _b;
76
67
  const phrases = {};
77
- debug_1.default.dictionary('fetching dictionary data for %s %s', language, this.options.siteName);
68
+ debug_1.default.dictionary('fetching dictionary data for %s %s', language, site);
78
69
  let results = [];
79
70
  let hasNext = true;
80
71
  let after = '';
81
- if (!this.options.siteName) {
82
- throw new RangeError(app_root_query_1.siteNameError);
72
+ if (!site) {
73
+ throw new RangeError(constants_1.siteNameError);
83
74
  }
84
75
  if (!language) {
85
- throw new RangeError(app_root_query_1.languageError);
76
+ throw new RangeError(constants_1.languageError);
86
77
  }
87
78
  while (hasNext) {
88
79
  const fetchResponse = await this.graphQLClient.request(siteQuery, {
89
- siteName: this.options.siteName,
80
+ siteName: site,
90
81
  language,
91
82
  pageSize: this.options.pageSize,
92
83
  after,
93
- });
84
+ }, fetchOptions);
94
85
  if ((_b = (_a = fetchResponse === null || fetchResponse === void 0 ? void 0 : fetchResponse.site) === null || _a === void 0 ? void 0 : _a.siteInfo) === null || _b === void 0 ? void 0 : _b.dictionary) {
95
86
  results = results.concat(fetchResponse.site.siteInfo.dictionary.results);
96
87
  after = fetchResponse.site.siteInfo.dictionary.pageInfo.endCursor;
@@ -101,6 +92,7 @@ class GraphQLDictionaryService {
101
92
  }
102
93
  }
103
94
  results.forEach((item) => (phrases[item.key] = item.value));
95
+ this.setCacheValue(cacheKey, phrases);
104
96
  return phrases;
105
97
  }
106
98
  /**
@@ -137,13 +129,14 @@ class GraphQLDictionaryService {
137
129
  * @returns {GraphQLClient} implementation
138
130
  */
139
131
  getGraphQLClient() {
132
+ var _a, _b;
140
133
  if (!this.options.clientFactory) {
141
134
  throw new Error('clientFactory needs to be provided when initializing GraphQL client.');
142
135
  }
143
136
  return this.options.clientFactory({
144
137
  debugger: debug_1.default.dictionary,
145
- retries: this.options.retries,
146
- retryStrategy: this.options.retryStrategy,
138
+ retries: (_a = this.options.retries) === null || _a === void 0 ? void 0 : _a.count,
139
+ retryStrategy: (_b = this.options.retries) === null || _b === void 0 ? void 0 : _b.retryStrategy,
147
140
  });
148
141
  }
149
142
  }
@@ -4,8 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.GraphQLLayoutService = exports.GRAPHQL_LAYOUT_QUERY_NAME = void 0;
7
- const layout_service_1 = require("./layout-service");
8
7
  const debug_1 = __importDefault(require("../debug"));
8
+ const layout_service_1 = require("./layout-service");
9
9
  exports.GRAPHQL_LAYOUT_QUERY_NAME = 'JssLayoutQuery';
10
10
  /**
11
11
  * Service that fetch layout data using Sitecore's GraphQL API.
@@ -18,51 +18,39 @@ class GraphQLLayoutService extends layout_service_1.LayoutServiceBase {
18
18
  * @param {GraphQLLayoutServiceConfig} serviceConfig configuration
19
19
  */
20
20
  constructor(serviceConfig) {
21
- super();
21
+ super(serviceConfig);
22
22
  this.serviceConfig = serviceConfig;
23
- this.graphQLClient = this.getGraphQLClient();
24
23
  }
25
24
  /**
26
25
  * Fetch layout data for an item.
27
26
  * @param {string} itemPath item path to fetch layout data for.
28
- * @param {string} [language] the language to fetch layout data for.
27
+ * @param {RouteOptions} [routeOptions] Request options like language and site to retrieve data for
28
+ * @param {FetchOptions} [fetchOptions] Options to override graphQL client details like retries and fetch implementation
29
29
  * @returns {Promise<LayoutServiceData>} layout service data
30
30
  */
31
- async fetchLayoutData(itemPath, language) {
31
+ async fetchLayoutData(itemPath, routeOptions, fetchOptions) {
32
32
  var _a, _b;
33
- const query = this.getLayoutQuery(itemPath, language);
34
- debug_1.default.layout('fetching layout data for %s %s %s', itemPath, language, this.serviceConfig.siteName);
35
- const data = await this.graphQLClient.request(query);
33
+ const site = routeOptions.site;
34
+ const query = this.getLayoutQuery(itemPath, site, routeOptions === null || routeOptions === void 0 ? void 0 : routeOptions.locale);
35
+ debug_1.default.layout('fetching layout data for %s %s %s', itemPath, routeOptions === null || routeOptions === void 0 ? void 0 : routeOptions.locale, site);
36
+ const data = await this.graphQLClient.request(query, {}, fetchOptions);
36
37
  // If `rendered` is empty -> not found
37
38
  return (((_b = (_a = data === null || data === void 0 ? void 0 : data.layout) === null || _a === void 0 ? void 0 : _a.item) === null || _b === void 0 ? void 0 : _b.rendered) || {
38
- sitecore: { context: { pageEditing: false, language }, route: null },
39
- });
40
- }
41
- /**
42
- * Gets a GraphQL client that can make requests to the API.
43
- * @returns {GraphQLClient} implementation
44
- */
45
- getGraphQLClient() {
46
- if (!this.serviceConfig.clientFactory) {
47
- throw new Error('clientFactory needs to be provided when initializing GraphQL client.');
48
- }
49
- return this.serviceConfig.clientFactory({
50
- debugger: debug_1.default.layout,
51
- retries: this.serviceConfig.retries,
52
- retryStrategy: this.serviceConfig.retryStrategy,
39
+ sitecore: { context: { pageEditing: false, language: routeOptions === null || routeOptions === void 0 ? void 0 : routeOptions.locale }, route: null },
53
40
  });
54
41
  }
55
42
  /**
56
43
  * Returns GraphQL Layout query
57
44
  * @param {string} itemPath page route
45
+ * @param {string} [site] site name
58
46
  * @param {string} [language] language
59
47
  * @returns {string} GraphQL query
60
48
  */
61
- getLayoutQuery(itemPath, language) {
49
+ getLayoutQuery(itemPath, site, language) {
62
50
  const languageVariable = language ? `, language:"${language}"` : '';
63
51
  const layoutQuery = this.serviceConfig.formatLayoutQuery
64
- ? this.serviceConfig.formatLayoutQuery(this.serviceConfig.siteName, itemPath, language)
65
- : `layout(site:"${this.serviceConfig.siteName}", routePath:"${itemPath}"${languageVariable})`;
52
+ ? this.serviceConfig.formatLayoutQuery(site, itemPath, language)
53
+ : `layout(site:"${site}", routePath:"${itemPath}"${languageVariable})`;
66
54
  return `query ${exports.GRAPHQL_LAYOUT_QUERY_NAME} {
67
55
  ${layoutQuery}{
68
56
  item {
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LayoutServiceBase = void 0;
4
+ const sitecore_service_base_1 = require("../sitecore-service-base");
4
5
  /**
5
6
  * Base abstraction to implement custom layout service
6
7
  */
7
- class LayoutServiceBase {
8
+ class LayoutServiceBase extends sitecore_service_base_1.SitecoreServiceBase {
8
9
  }
9
10
  exports.LayoutServiceBase = LayoutServiceBase;
@@ -42,6 +42,8 @@ class GraphQLPersonalizeService {
42
42
  */
43
43
  async getPersonalizeInfo(itemPath, language, siteName) {
44
44
  var _a;
45
+ // while other graphql services can use fetchOptions, personalize is more sensitive
46
+ // we don't allow retries in it since we need to be fast
45
47
  debug_1.default.personalize('fetching personalize info for %s %s %s', siteName, itemPath, language);
46
48
  const cacheKey = this.getCacheKey(itemPath, language, siteName);
47
49
  let data = this.cache.getCacheValue(cacheKey);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_VARIANT = exports.CdpHelper = exports.normalizePersonalizedRewrite = exports.getGroomedVariantIds = exports.getPersonalizedRewriteData = exports.getPersonalizedRewrite = exports.GraphQLPersonalizeService = exports.personalizeLayout = void 0;
3
+ exports.VARIANT_PREFIX = exports.DEFAULT_VARIANT = exports.CdpHelper = exports.normalizePersonalizedRewrite = exports.getGroomedVariantIds = exports.getPersonalizedRewriteData = exports.getPersonalizedRewrite = exports.GraphQLPersonalizeService = exports.personalizeLayout = void 0;
4
4
  var layout_personalizer_1 = require("./layout-personalizer");
5
5
  Object.defineProperty(exports, "personalizeLayout", { enumerable: true, get: function () { return layout_personalizer_1.personalizeLayout; } });
6
6
  var graphql_personalize_service_1 = require("./graphql-personalize-service");
@@ -12,3 +12,4 @@ Object.defineProperty(exports, "getGroomedVariantIds", { enumerable: true, get:
12
12
  Object.defineProperty(exports, "normalizePersonalizedRewrite", { enumerable: true, get: function () { return utils_1.normalizePersonalizedRewrite; } });
13
13
  Object.defineProperty(exports, "CdpHelper", { enumerable: true, get: function () { return utils_1.CdpHelper; } });
14
14
  Object.defineProperty(exports, "DEFAULT_VARIANT", { enumerable: true, get: function () { return utils_1.DEFAULT_VARIANT; } });
15
+ Object.defineProperty(exports, "VARIANT_PREFIX", { enumerable: true, get: function () { return utils_1.VARIANT_PREFIX; } });