@sitecore-content-sdk/core 0.1.0-beta.1

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 (160) hide show
  1. package/LICENSE.txt +202 -0
  2. package/README.md +11 -0
  3. package/dist/cjs/cache-client.js +54 -0
  4. package/dist/cjs/constants.js +13 -0
  5. package/dist/cjs/data-fetcher.js +33 -0
  6. package/dist/cjs/debug.js +43 -0
  7. package/dist/cjs/editing/component-library.js +104 -0
  8. package/dist/cjs/editing/graphql-editing-service.js +186 -0
  9. package/dist/cjs/editing/index.js +23 -0
  10. package/dist/cjs/editing/models.js +23 -0
  11. package/dist/cjs/editing/rest-component-layout-service.js +76 -0
  12. package/dist/cjs/editing/utils.js +86 -0
  13. package/dist/cjs/graphql/app-root-query.js +73 -0
  14. package/dist/cjs/graphql/graphql-edge-proxy.js +21 -0
  15. package/dist/cjs/graphql/index.js +13 -0
  16. package/dist/cjs/graphql/search-service.js +61 -0
  17. package/dist/cjs/graphql-request-client.js +152 -0
  18. package/dist/cjs/i18n/dictionary-service.js +45 -0
  19. package/dist/cjs/i18n/graphql-dictionary-service.js +136 -0
  20. package/dist/cjs/i18n/index.js +7 -0
  21. package/dist/cjs/index.js +55 -0
  22. package/dist/cjs/layout/content-styles.js +70 -0
  23. package/dist/cjs/layout/graphql-layout-service.js +86 -0
  24. package/dist/cjs/layout/index.js +24 -0
  25. package/dist/cjs/layout/layout-service.js +9 -0
  26. package/dist/cjs/layout/models.js +35 -0
  27. package/dist/cjs/layout/themes.js +74 -0
  28. package/dist/cjs/layout/utils.js +110 -0
  29. package/dist/cjs/media/index.js +38 -0
  30. package/dist/cjs/media/media-api.js +96 -0
  31. package/dist/cjs/models.js +2 -0
  32. package/dist/cjs/native-fetcher.js +200 -0
  33. package/dist/cjs/personalize/graphql-personalize-service.js +114 -0
  34. package/dist/cjs/personalize/index.js +14 -0
  35. package/dist/cjs/personalize/layout-personalizer.js +97 -0
  36. package/dist/cjs/personalize/utils.js +136 -0
  37. package/dist/cjs/site/graphql-error-pages-service.js +89 -0
  38. package/dist/cjs/site/graphql-redirects-service.js +105 -0
  39. package/dist/cjs/site/graphql-robots-service.js +83 -0
  40. package/dist/cjs/site/graphql-siteinfo-service.js +107 -0
  41. package/dist/cjs/site/graphql-sitemap-service.js +93 -0
  42. package/dist/cjs/site/index.js +22 -0
  43. package/dist/cjs/site/site-resolver.js +79 -0
  44. package/dist/cjs/site/utils.js +43 -0
  45. package/dist/cjs/utils/env.js +26 -0
  46. package/dist/cjs/utils/index.js +20 -0
  47. package/dist/cjs/utils/is-server.js +10 -0
  48. package/dist/cjs/utils/timeout-promise.js +31 -0
  49. package/dist/cjs/utils/utils.js +222 -0
  50. package/dist/esm/cache-client.js +50 -0
  51. package/dist/esm/constants.js +10 -0
  52. package/dist/esm/data-fetcher.js +28 -0
  53. package/dist/esm/debug.js +36 -0
  54. package/dist/esm/editing/component-library.js +98 -0
  55. package/dist/esm/editing/graphql-editing-service.js +179 -0
  56. package/dist/esm/editing/index.js +5 -0
  57. package/dist/esm/editing/models.js +20 -0
  58. package/dist/esm/editing/rest-component-layout-service.js +72 -0
  59. package/dist/esm/editing/utils.js +76 -0
  60. package/dist/esm/graphql/app-root-query.js +69 -0
  61. package/dist/esm/graphql/graphql-edge-proxy.js +16 -0
  62. package/dist/esm/graphql/index.js +4 -0
  63. package/dist/esm/graphql/search-service.js +57 -0
  64. package/dist/esm/graphql-request-client.js +144 -0
  65. package/dist/esm/i18n/dictionary-service.js +41 -0
  66. package/dist/esm/i18n/graphql-dictionary-service.js +129 -0
  67. package/dist/esm/i18n/index.js +2 -0
  68. package/dist/esm/index.js +9 -0
  69. package/dist/esm/layout/content-styles.js +62 -0
  70. package/dist/esm/layout/graphql-layout-service.js +79 -0
  71. package/dist/esm/layout/index.js +6 -0
  72. package/dist/esm/layout/layout-service.js +5 -0
  73. package/dist/esm/layout/models.js +32 -0
  74. package/dist/esm/layout/themes.js +69 -0
  75. package/dist/esm/layout/utils.js +102 -0
  76. package/dist/esm/media/index.js +2 -0
  77. package/dist/esm/media/media-api.js +86 -0
  78. package/dist/esm/models.js +1 -0
  79. package/dist/esm/native-fetcher.js +193 -0
  80. package/dist/esm/personalize/graphql-personalize-service.js +107 -0
  81. package/dist/esm/personalize/index.js +3 -0
  82. package/dist/esm/personalize/layout-personalizer.js +92 -0
  83. package/dist/esm/personalize/utils.js +128 -0
  84. package/dist/esm/site/graphql-error-pages-service.js +82 -0
  85. package/dist/esm/site/graphql-redirects-service.js +98 -0
  86. package/dist/esm/site/graphql-robots-service.js +76 -0
  87. package/dist/esm/site/graphql-siteinfo-service.js +100 -0
  88. package/dist/esm/site/graphql-sitemap-service.js +86 -0
  89. package/dist/esm/site/index.js +7 -0
  90. package/dist/esm/site/site-resolver.js +75 -0
  91. package/dist/esm/site/utils.js +37 -0
  92. package/dist/esm/utils/env.js +22 -0
  93. package/dist/esm/utils/index.js +3 -0
  94. package/dist/esm/utils/is-server.js +8 -0
  95. package/dist/esm/utils/timeout-promise.js +28 -0
  96. package/dist/esm/utils/utils.js +207 -0
  97. package/editing.d.ts +1 -0
  98. package/editing.js +1 -0
  99. package/graphql.d.ts +1 -0
  100. package/graphql.js +1 -0
  101. package/i18n.d.ts +1 -0
  102. package/i18n.js +1 -0
  103. package/layout.d.ts +1 -0
  104. package/layout.js +1 -0
  105. package/media.d.ts +1 -0
  106. package/media.js +1 -0
  107. package/package.json +76 -0
  108. package/personalize.d.ts +1 -0
  109. package/personalize.js +1 -0
  110. package/site.d.ts +1 -0
  111. package/site.js +1 -0
  112. package/types/cache-client.d.ts +64 -0
  113. package/types/constants.d.ts +7 -0
  114. package/types/data-fetcher.d.ts +34 -0
  115. package/types/debug.d.ts +26 -0
  116. package/types/editing/component-library.d.ts +48 -0
  117. package/types/editing/graphql-editing-service.d.ts +90 -0
  118. package/types/editing/index.d.ts +6 -0
  119. package/types/editing/models.d.ts +52 -0
  120. package/types/editing/rest-component-layout-service.d.ts +100 -0
  121. package/types/editing/utils.d.ts +70 -0
  122. package/types/graphql/app-root-query.d.ts +32 -0
  123. package/types/graphql/graphql-edge-proxy.d.ts +15 -0
  124. package/types/graphql/index.d.ts +4 -0
  125. package/types/graphql/search-service.d.ts +95 -0
  126. package/types/graphql-request-client.d.ts +159 -0
  127. package/types/i18n/dictionary-service.d.ts +56 -0
  128. package/types/i18n/graphql-dictionary-service.d.ts +94 -0
  129. package/types/i18n/index.d.ts +2 -0
  130. package/types/index.d.ts +8 -0
  131. package/types/layout/content-styles.d.ts +18 -0
  132. package/types/layout/graphql-layout-service.d.ts +58 -0
  133. package/types/layout/index.d.ts +6 -0
  134. package/types/layout/layout-service.d.ts +19 -0
  135. package/types/layout/models.d.ts +145 -0
  136. package/types/layout/themes.d.ts +11 -0
  137. package/types/layout/utils.d.ts +40 -0
  138. package/types/media/index.d.ts +2 -0
  139. package/types/media/media-api.d.ts +55 -0
  140. package/types/models.d.ts +6 -0
  141. package/types/native-fetcher.d.ts +121 -0
  142. package/types/personalize/graphql-personalize-service.d.ts +80 -0
  143. package/types/personalize/index.d.ts +3 -0
  144. package/types/personalize/layout-personalizer.d.ts +27 -0
  145. package/types/personalize/utils.d.ts +69 -0
  146. package/types/site/graphql-error-pages-service.d.ts +57 -0
  147. package/types/site/graphql-redirects-service.d.ts +68 -0
  148. package/types/site/graphql-robots-service.d.ts +49 -0
  149. package/types/site/graphql-siteinfo-service.d.ts +70 -0
  150. package/types/site/graphql-sitemap-service.d.ts +55 -0
  151. package/types/site/index.d.ts +7 -0
  152. package/types/site/site-resolver.d.ts +27 -0
  153. package/types/site/utils.d.ts +24 -0
  154. package/types/utils/env.d.ts +7 -0
  155. package/types/utils/index.d.ts +3 -0
  156. package/types/utils/is-server.d.ts +6 -0
  157. package/types/utils/timeout-promise.d.ts +17 -0
  158. package/types/utils/utils.d.ts +71 -0
  159. package/utils.d.ts +1 -0
  160. package/utils.js +1 -0
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getSrcSet = exports.updateImageUrl = exports.replaceMediaUrlPrefix = exports.getRequiredParams = void 0;
7
+ const url_parse_1 = __importDefault(require("url-parse"));
8
+ // finds the Sitecore media URL prefix
9
+ const mediaUrlPrefixRegex = /\/([-~]{1})\/media\//i;
10
+ /**
11
+ * Get required query string params which should be merged with user params
12
+ * @param {object} qs layout service parsed query string
13
+ * @returns {object} requiredParams
14
+ */
15
+ const getRequiredParams = (qs) => {
16
+ const { rev, db, la, vs, ts } = qs;
17
+ return { rev, db, la, vs, ts };
18
+ };
19
+ exports.getRequiredParams = getRequiredParams;
20
+ /**
21
+ * Replace `/~/media` or `/-/media` with `/~/jssmedia` or `/-/jssmedia`, respectively.
22
+ * Can use `mediaUrlPrefix` in order to use a custom prefix.
23
+ * @param {string} url The URL to replace the media URL prefix in
24
+ * @param {RegExp} [mediaUrlPrefix] The regex to match the media URL prefix
25
+ * @returns {string} The URL with the media URL prefix replaced
26
+ */
27
+ const replaceMediaUrlPrefix = (url, mediaUrlPrefix = mediaUrlPrefixRegex) => {
28
+ const parsed = (0, url_parse_1.default)(url, {}, true);
29
+ const match = mediaUrlPrefix.exec(parsed.pathname);
30
+ if (match && match.length > 1) {
31
+ // regex will provide us with /-/ or /~/ type
32
+ parsed.set('pathname', parsed.pathname.replace(mediaUrlPrefix, `/${match[1]}/jssmedia/`));
33
+ }
34
+ return parsed.toString();
35
+ };
36
+ exports.replaceMediaUrlPrefix = replaceMediaUrlPrefix;
37
+ /**
38
+ * Prepares a Sitecore media URL with `params` for use by the JSS media handler.
39
+ * This is done by replacing `/~/media` or `/-/media` with `/~/jssmedia` or `/-/jssmedia`, respectively.
40
+ * Provided `params` are used as the querystring parameters for the media URL.
41
+ * Can use `mediaUrlPrefix` in order to use a custom prefix.
42
+ * If no `params` are sent, the original media URL is returned.
43
+ * @param {string} url The URL to prepare
44
+ * @param {object} [params] The querystring parameters to use
45
+ * @param {RegExp} [mediaUrlPrefix] The regex to match the media URL prefix
46
+ * @returns {string} The prepared URL
47
+ */
48
+ const updateImageUrl = (url, params, mediaUrlPrefix = mediaUrlPrefixRegex) => {
49
+ if (!params || Object.keys(params).length === 0) {
50
+ // if params aren't supplied, no need to run it through JSS media handler
51
+ return url;
52
+ }
53
+ // polyfill node `global` in browser to workaround https://github.com/unshiftio/url-parse/issues/150
54
+ if (typeof window !== 'undefined' && !window.global) {
55
+ window.global = {};
56
+ }
57
+ const parsed = (0, url_parse_1.default)((0, exports.replaceMediaUrlPrefix)(url, mediaUrlPrefix), {}, true);
58
+ const requiredParams = (0, exports.getRequiredParams)(parsed.query);
59
+ const query = Object.assign({}, params);
60
+ Object.entries(requiredParams).forEach(([key, param]) => {
61
+ if (param) {
62
+ query[key] = param;
63
+ }
64
+ });
65
+ parsed.set('query', query);
66
+ return parsed.toString();
67
+ };
68
+ exports.updateImageUrl = updateImageUrl;
69
+ /**
70
+ * Receives an array of `srcSet` parameters that are iterated and used as parameters to generate
71
+ * a corresponding set of updated Sitecore media URLs via @see updateImageUrl. The result is a comma-delimited
72
+ * list of media URLs with respective dimension parameters.
73
+ * @example
74
+ * // returns '/ipsum.jpg?h=1000&w=1000 1000w, /ipsum.jpg?mh=250&mw=250 250w'
75
+ * getSrcSet('/ipsum.jpg', [{ h: 1000, w: 1000 }, { mh: 250, mw: 250 } ])
76
+ * More information about `srcSet`: {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img}
77
+ * @param {string} url The URL to prepare
78
+ * @param {Array} srcSet The array of parameters to use
79
+ * @param {object} [imageParams] The querystring parameters to use
80
+ * @param {RegExp} [mediaUrlPrefix] The regex to match the media URL prefix
81
+ * @returns {string} The prepared URL
82
+ */
83
+ const getSrcSet = (url, srcSet, imageParams, mediaUrlPrefix) => {
84
+ return srcSet
85
+ .map((params) => {
86
+ const newParams = Object.assign(Object.assign({}, imageParams), params);
87
+ const imageWidth = newParams.w || newParams.mw;
88
+ if (!imageWidth) {
89
+ return null;
90
+ }
91
+ return `${(0, exports.updateImageUrl)(url, newParams, mediaUrlPrefix)} ${imageWidth}w`;
92
+ })
93
+ .filter((value) => value)
94
+ .join(', ');
95
+ };
96
+ exports.getSrcSet = getSrcSet;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.NativeDataFetcher = void 0;
27
+ const debug_1 = __importDefault(require("./debug"));
28
+ const timeout_promise_1 = __importDefault(require("./utils/timeout-promise"));
29
+ class NativeDataFetcher {
30
+ constructor(config = {}) {
31
+ this.config = config;
32
+ }
33
+ /**
34
+ * Implements a data fetcher.
35
+ * @param {string} url The URL to request (may include query string)
36
+ * @param {RequestInit} [options] Optional fetch options
37
+ * @returns {Promise<NativeDataFetcherResponse<T>>} response
38
+ */
39
+ fetch(url_1) {
40
+ return __awaiter(this, arguments, void 0, function* (url, options = {}) {
41
+ var _a;
42
+ const _b = this.config, { debugger: debugOverride, fetch: fetchOverride } = _b, init = __rest(_b, ["debugger", "fetch"]);
43
+ const startTimestamp = Date.now();
44
+ const fetchImpl = fetchOverride || fetch;
45
+ const debug = debugOverride || debug_1.default.http;
46
+ const requestInit = this.getRequestInit(Object.assign(Object.assign({}, init), options));
47
+ const fetchWithOptionalTimeout = [fetchImpl(url, requestInit)];
48
+ if (init.timeout) {
49
+ this.abortTimeout = new timeout_promise_1.default(init.timeout);
50
+ fetchWithOptionalTimeout.push(this.abortTimeout.start);
51
+ }
52
+ debug('Request initiated: %o', Object.assign({ url, headers: this.extractDebugHeaders(requestInit.headers) }, requestInit));
53
+ try {
54
+ const response = yield Promise.race(fetchWithOptionalTimeout).then((res) => {
55
+ var _a;
56
+ (_a = this.abortTimeout) === null || _a === void 0 ? void 0 : _a.clear();
57
+ return res;
58
+ });
59
+ const respData = yield this.parseResponse(response, debug);
60
+ if (!response.ok) {
61
+ const error = this.createError(response, respData);
62
+ debug('Response error: %o', error.response);
63
+ throw error;
64
+ }
65
+ debug('Response in %dms: %o', Date.now() - startTimestamp, {
66
+ status: response.status,
67
+ statusText: response.statusText,
68
+ headers: this.extractDebugHeaders(response.headers),
69
+ url: response.url,
70
+ data: respData,
71
+ });
72
+ return Object.assign(Object.assign({}, response), { data: respData });
73
+ }
74
+ catch (error) {
75
+ (_a = this.abortTimeout) === null || _a === void 0 ? void 0 : _a.clear();
76
+ debug('Request failed: %o', error);
77
+ throw error;
78
+ }
79
+ });
80
+ }
81
+ /**
82
+ * Perform a GET request
83
+ * @param {string} url The URL to request (may include query string)
84
+ * @param {RequestInit} [options] Fetch options
85
+ * @returns {Promise<NativeDataFetcherResponse<T>>} response
86
+ */
87
+ get(url_1) {
88
+ return __awaiter(this, arguments, void 0, function* (url, options = {}) {
89
+ return this.fetch(url, Object.assign({ method: 'GET' }, options));
90
+ });
91
+ }
92
+ /**
93
+ * Perform a POST request
94
+ * @param {string} url The URL to request (may include query string)
95
+ * @param {unknown} body The data to send with the request
96
+ * @param {RequestInit} [options] Fetch options
97
+ * @returns {Promise<NativeDataFetcherResponse<T>>} response
98
+ */
99
+ post(url_1, body_1) {
100
+ return __awaiter(this, arguments, void 0, function* (url, body, options = {}) {
101
+ return this.fetch(url, Object.assign({ method: 'POST', body: JSON.stringify(body) }, options));
102
+ });
103
+ }
104
+ /**
105
+ * Perform a DELETE request
106
+ * @param {string} url The URL to request (may include query string)
107
+ * @param {RequestInit} [options] Fetch options
108
+ * @returns {Promise<NativeDataFetcherResponse<T>>} response
109
+ */
110
+ delete(url_1) {
111
+ return __awaiter(this, arguments, void 0, function* (url, options = {}) {
112
+ return this.fetch(url, Object.assign({ method: 'DELETE' }, options));
113
+ });
114
+ }
115
+ /**
116
+ * Perform a PUT request
117
+ * @param {string} url The URL to request (may include query string)
118
+ * @param {unknown} body The data to send with the request
119
+ * @param {RequestInit} [options] Fetch options
120
+ * @returns {Promise<NativeDataFetcherResponse<T>>} response
121
+ */
122
+ put(url_1, body_1) {
123
+ return __awaiter(this, arguments, void 0, function* (url, body, options = {}) {
124
+ return this.fetch(url, Object.assign({ method: 'PUT', body: JSON.stringify(body) }, options));
125
+ });
126
+ }
127
+ /**
128
+ * Perform a HEAD request
129
+ * @param {string} url The URL to request (may include query string)
130
+ * @param {RequestInit} [options] Fetch options
131
+ * @returns {Promise<NativeDataFetcherResponse<T>>} response
132
+ */
133
+ head(url, options = {}) {
134
+ return this.fetch(url, Object.assign({ method: 'HEAD' }, options));
135
+ }
136
+ /**
137
+ * Determines settings for the request
138
+ * @param {RequestInit} init Custom settings for request
139
+ * @returns {RequestInit} The final request settings
140
+ */
141
+ getRequestInit(init = {}) {
142
+ const headers = new Headers(init.headers);
143
+ if (!init.method) {
144
+ init.method = init.body ? 'POST' : 'GET';
145
+ }
146
+ headers.set('Content-Type', 'application/json');
147
+ init.headers = headers;
148
+ return init;
149
+ }
150
+ /**
151
+ * Safely extract all headers for debug logging
152
+ * @param {HeadersInit} incomingHeaders Incoming headers
153
+ * @returns Object with headers as key/value pairs
154
+ */
155
+ extractDebugHeaders(incomingHeaders = {}) {
156
+ const headers = {};
157
+ if (typeof (incomingHeaders === null || incomingHeaders === void 0 ? void 0 : incomingHeaders.forEach) !== 'string' && incomingHeaders.forEach) {
158
+ incomingHeaders === null || incomingHeaders === void 0 ? void 0 : incomingHeaders.forEach((value, key) => {
159
+ headers[key] = value;
160
+ });
161
+ }
162
+ return headers;
163
+ }
164
+ /**
165
+ * Parses the response data.
166
+ * @param {Response} response - The fetch response object.
167
+ * @param {Function} debug - The debug logger function.
168
+ * @returns {Promise<unknown>} - The parsed response data.
169
+ */
170
+ parseResponse(response, debug) {
171
+ return __awaiter(this, void 0, void 0, function* () {
172
+ const contentType = response.headers.get('Content-Type') || '';
173
+ try {
174
+ if (contentType.includes('application/json')) {
175
+ return yield response.json();
176
+ }
177
+ return yield response.text();
178
+ }
179
+ catch (error) {
180
+ debug('Response parsing error: %o', error);
181
+ return undefined;
182
+ }
183
+ });
184
+ }
185
+ /**
186
+ * Creates a custom error for fetch failures.
187
+ * @param {Response} response - The fetch response object.
188
+ * @param {unknown} data - The parsed response data.
189
+ * @returns {NativeDataFetcherError} - The constructed error object.
190
+ */
191
+ createError(response, data) {
192
+ return Object.assign(Object.assign({}, new Error(`HTTP ${response.status} ${response.statusText}`)), { response: {
193
+ status: response.status,
194
+ statusText: response.statusText,
195
+ headers: this.extractDebugHeaders(response.headers),
196
+ data,
197
+ } });
198
+ }
199
+ }
200
+ exports.NativeDataFetcher = NativeDataFetcher;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.GraphQLPersonalizeService = void 0;
16
+ const debug_1 = __importDefault(require("../debug"));
17
+ const utils_1 = require("../utils");
18
+ const cache_client_1 = require("../cache-client");
19
+ class GraphQLPersonalizeService {
20
+ /**
21
+ * Fetch personalize data using the Sitecore GraphQL endpoint.
22
+ * @param {GraphQLPersonalizeServiceConfig} config
23
+ */
24
+ constructor(config) {
25
+ this.config = config;
26
+ this.config.timeout = config.timeout || 400;
27
+ this.graphQLClient = this.getGraphQLClient();
28
+ this.cache = this.getCacheClient();
29
+ }
30
+ get query() {
31
+ return /* GraphQL */ `
32
+ query($siteName: String!, $language: String!, $itemPath: String!) {
33
+ layout(site: $siteName, routePath: $itemPath, language: $language) {
34
+ item {
35
+ id
36
+ version
37
+ personalization {
38
+ variantIds
39
+ }
40
+ }
41
+ }
42
+ }
43
+ `;
44
+ }
45
+ /**
46
+ * Get personalize information for a route
47
+ * @param {string} itemPath page route
48
+ * @param {string} language language
49
+ * @param {string} siteName site name
50
+ * @returns {Promise<PersonalizeInfo | undefined>} the personalize information or undefined (if itemPath / language not found)
51
+ */
52
+ getPersonalizeInfo(itemPath, language, siteName) {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ var _a;
55
+ debug_1.default.personalize('fetching personalize info for %s %s %s', siteName, itemPath, language);
56
+ const cacheKey = this.getCacheKey(itemPath, language, siteName);
57
+ let data = this.cache.getCacheValue(cacheKey);
58
+ if (!data) {
59
+ try {
60
+ data = yield this.graphQLClient.request(this.query, {
61
+ siteName,
62
+ itemPath,
63
+ language,
64
+ });
65
+ this.cache.setCacheValue(cacheKey, data);
66
+ }
67
+ catch (error) {
68
+ if ((0, utils_1.isTimeoutError)(error)) {
69
+ return undefined;
70
+ }
71
+ throw error;
72
+ }
73
+ }
74
+ return ((_a = data === null || data === void 0 ? void 0 : data.layout) === null || _a === void 0 ? void 0 : _a.item)
75
+ ? {
76
+ pageId: data.layout.item.id,
77
+ variantIds: data.layout.item.personalization.variantIds,
78
+ }
79
+ : undefined;
80
+ });
81
+ }
82
+ /**
83
+ * Gets cache client implementation
84
+ * Override this method if custom cache needs to be used
85
+ * @returns CacheClient instance
86
+ */
87
+ getCacheClient() {
88
+ var _a, _b;
89
+ return new cache_client_1.MemoryCacheClient({
90
+ cacheEnabled: (_a = this.config.cacheEnabled) !== null && _a !== void 0 ? _a : true,
91
+ cacheTimeout: (_b = this.config.cacheTimeout) !== null && _b !== void 0 ? _b : 10,
92
+ });
93
+ }
94
+ getCacheKey(itemPath, language, siteName) {
95
+ return `${siteName}-${itemPath}-${language}`;
96
+ }
97
+ /**
98
+ * Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default
99
+ * library for fetching graphql data (@see GraphQLRequestClient). Override this method if you
100
+ * want to use something else.
101
+ * @returns {GraphQLClient} implementation
102
+ */
103
+ getGraphQLClient() {
104
+ if (!this.config.clientFactory) {
105
+ throw new Error('clientFactory needs to be provided when initializing GraphQL client.');
106
+ }
107
+ return this.config.clientFactory({
108
+ debugger: debug_1.default.personalize,
109
+ fetch: this.config.fetch,
110
+ timeout: this.config.timeout,
111
+ });
112
+ }
113
+ }
114
+ exports.GraphQLPersonalizeService = GraphQLPersonalizeService;
@@ -0,0 +1,14 @@
1
+ "use strict";
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;
4
+ var layout_personalizer_1 = require("./layout-personalizer");
5
+ Object.defineProperty(exports, "personalizeLayout", { enumerable: true, get: function () { return layout_personalizer_1.personalizeLayout; } });
6
+ var graphql_personalize_service_1 = require("./graphql-personalize-service");
7
+ Object.defineProperty(exports, "GraphQLPersonalizeService", { enumerable: true, get: function () { return graphql_personalize_service_1.GraphQLPersonalizeService; } });
8
+ var utils_1 = require("./utils");
9
+ Object.defineProperty(exports, "getPersonalizedRewrite", { enumerable: true, get: function () { return utils_1.getPersonalizedRewrite; } });
10
+ Object.defineProperty(exports, "getPersonalizedRewriteData", { enumerable: true, get: function () { return utils_1.getPersonalizedRewriteData; } });
11
+ Object.defineProperty(exports, "getGroomedVariantIds", { enumerable: true, get: function () { return utils_1.getGroomedVariantIds; } });
12
+ Object.defineProperty(exports, "normalizePersonalizedRewrite", { enumerable: true, get: function () { return utils_1.normalizePersonalizedRewrite; } });
13
+ Object.defineProperty(exports, "CdpHelper", { enumerable: true, get: function () { return utils_1.CdpHelper; } });
14
+ Object.defineProperty(exports, "DEFAULT_VARIANT", { enumerable: true, get: function () { return utils_1.DEFAULT_VARIANT; } });
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.personalizeLayout = personalizeLayout;
4
+ exports.personalizePlaceholder = personalizePlaceholder;
5
+ exports.personalizeComponent = personalizeComponent;
6
+ const constants_1 = require("../constants");
7
+ const transformToHiddenRenderingVariant = (component) => (Object.assign(Object.assign({}, component), { componentName: constants_1.HIDDEN_RENDERING_NAME, experiences: {} }));
8
+ /**
9
+ * Apply personalization to layout data. This will recursively go through all placeholders/components, check experiences nodes and replace default with object from specific experience.
10
+ * @param {LayoutServiceData} layout Layout data
11
+ * @param {string} variantId variant id
12
+ * @param {string[]} [componentVariantIds] component variant ids
13
+ */
14
+ function personalizeLayout(layout, variantId, componentVariantIds) {
15
+ var _a;
16
+ // Add (page-level) variantId to Sitecore context so that it is accessible here
17
+ layout.sitecore.context.variantId = variantId;
18
+ const placeholders = ((_a = layout.sitecore.route) === null || _a === void 0 ? void 0 : _a.placeholders) || {};
19
+ if (Object.keys(placeholders).length === 0) {
20
+ return undefined;
21
+ }
22
+ const isEditing = layout.sitecore.context.pageEditing;
23
+ if (placeholders) {
24
+ Object.keys(placeholders).forEach((placeholder) => {
25
+ placeholders[placeholder] = personalizePlaceholder(placeholders[placeholder], [variantId, ...(componentVariantIds || [])], isEditing);
26
+ });
27
+ }
28
+ return placeholders;
29
+ }
30
+ /**
31
+ * @param {Array} components components within placeholder
32
+ * @param {string[]} variantIds variant ids
33
+ * @param {boolean} isEditing indicates if page is rendered in metadata edit mode
34
+ * @returns {ComponentRendering[]} components with personalization applied
35
+ */
36
+ function personalizePlaceholder(components, variantIds, isEditing) {
37
+ return components
38
+ .map((component) => {
39
+ const rendering = component;
40
+ if (rendering.experiences !== undefined) {
41
+ return personalizeComponent(rendering, variantIds, isEditing);
42
+ }
43
+ else if (rendering.placeholders) {
44
+ const placeholders = rendering.placeholders;
45
+ Object.keys(placeholders).forEach((placeholder) => {
46
+ placeholders[placeholder] = personalizePlaceholder(placeholders[placeholder], variantIds, isEditing);
47
+ });
48
+ }
49
+ return component;
50
+ })
51
+ .filter(Boolean);
52
+ }
53
+ /**
54
+ * @param {ComponentRenderingWithExperiences} component component with experiences
55
+ * @param {string[]} variantIds variant ids
56
+ * @param {boolean} isEditing indicates if page is rendered in metadata edit mode
57
+ * @returns {ComponentRendering | null} component with personalization applied or null if hidden
58
+ */
59
+ function personalizeComponent(component, variantIds, isEditing) {
60
+ // Check if we have a page/component experience matching any of the variants (there should be at most 1)
61
+ const match = Object.keys(component.experiences).find((variantId) => variantIds.includes(variantId));
62
+ const variant = match && component.experiences[match];
63
+ // variant and componentName can be undefined or null
64
+ if (!variant && !component.componentName) {
65
+ // DEFAULT IS HIDDEN
66
+ if (isEditing) {
67
+ component = transformToHiddenRenderingVariant(component);
68
+ }
69
+ else {
70
+ return null;
71
+ }
72
+ }
73
+ else if (variant && variant.componentName === null && variant.dataSource === null) {
74
+ // VARIANT IS HIDDEN
75
+ if (isEditing) {
76
+ component = transformToHiddenRenderingVariant(component);
77
+ }
78
+ else {
79
+ return null;
80
+ }
81
+ }
82
+ else if (variant) {
83
+ component = variant;
84
+ }
85
+ // remove unused experiences from layout data
86
+ if (component.experiences) {
87
+ component.experiences = {};
88
+ }
89
+ if (!component.placeholders)
90
+ return component;
91
+ Object.keys(component === null || component === void 0 ? void 0 : component.placeholders).forEach((placeholder) => {
92
+ if (component.placeholders) {
93
+ component.placeholders[placeholder] = personalizePlaceholder(component.placeholders[placeholder], variantIds);
94
+ }
95
+ });
96
+ return component;
97
+ }
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CdpHelper = exports.VARIANT_PREFIX = exports.DEFAULT_VARIANT = void 0;
4
+ exports.getPersonalizedRewrite = getPersonalizedRewrite;
5
+ exports.getPersonalizedRewriteData = getPersonalizedRewriteData;
6
+ exports.getGroomedVariantIds = getGroomedVariantIds;
7
+ exports.normalizePersonalizedRewrite = normalizePersonalizedRewrite;
8
+ exports.DEFAULT_VARIANT = '_default';
9
+ exports.VARIANT_PREFIX = '_variantId_';
10
+ /**
11
+ * Get a personalized rewrite path for given pathname
12
+ * @param {string} pathname the pathname
13
+ * @param {string[]} variantIds the variantIds to include in the rewrite
14
+ * @returns {string} the rewrite path
15
+ */
16
+ function getPersonalizedRewrite(pathname, variantIds) {
17
+ const path = pathname.startsWith('/') ? pathname : '/' + pathname;
18
+ return `${variantIds.map((variantId) => `/${exports.VARIANT_PREFIX}${variantId}`).join('')}${path}`;
19
+ }
20
+ /**
21
+ * Get personalize data from the rewrite path
22
+ * @param {string} pathname the pathname
23
+ * @returns {PersonalizedRewriteData} the personalize data from the rewrite
24
+ */
25
+ function getPersonalizedRewriteData(pathname) {
26
+ const segments = pathname.split('/');
27
+ const variantIds = [];
28
+ segments.forEach((segment) => {
29
+ const result = segment.match(`${exports.VARIANT_PREFIX}(.*$)`);
30
+ if (result) {
31
+ variantIds.push(result[1]);
32
+ }
33
+ });
34
+ return getGroomedVariantIds(variantIds);
35
+ }
36
+ /**
37
+ * Parses a list of variantIds and divides into layout and component variants
38
+ * @param {string[]} variantIds the list of variant IDs for a page
39
+ * @returns {PersonalizedRewriteData} object with variant IDs sorted
40
+ */
41
+ function getGroomedVariantIds(variantIds) {
42
+ const data = {
43
+ variantId: exports.DEFAULT_VARIANT,
44
+ componentVariantIds: [],
45
+ };
46
+ variantIds.forEach((variantId) => {
47
+ var _a;
48
+ if (variantId.includes('_')) {
49
+ // Component-level personalization in format "<ComponentID>_<VariantID>"
50
+ // There can be multiple
51
+ (_a = data.componentVariantIds) === null || _a === void 0 ? void 0 : _a.push(variantId);
52
+ }
53
+ else {
54
+ // Embedded (page-level) personalization in format "<VariantID>"
55
+ // There should be only one
56
+ data.variantId = variantId;
57
+ }
58
+ });
59
+ return data;
60
+ }
61
+ /**
62
+ * Normalize a personalized rewrite path (remove personalize data)
63
+ * @param {string} pathname the pathname
64
+ * @returns {string} the pathname with personalize data removed
65
+ */
66
+ function normalizePersonalizedRewrite(pathname) {
67
+ if (!pathname.includes(exports.VARIANT_PREFIX)) {
68
+ return pathname;
69
+ }
70
+ let segments = pathname.split('/');
71
+ segments = segments.filter((segment) => !segment.includes(exports.VARIANT_PREFIX));
72
+ const result = segments.join('/');
73
+ // return root path if all segments were personalize data
74
+ return result ? result : '/';
75
+ }
76
+ /**
77
+ * Static utility class for Sitecore CDP
78
+ */
79
+ class CdpHelper {
80
+ /**
81
+ * Gets the page variant id for CDP in the required format
82
+ * @param {string} pageId the page id
83
+ * @param {string} language the language
84
+ * @param {string} variantId the variant id
85
+ * @param {string} [scope] the scope value
86
+ * @returns {string} the formatted page variant id
87
+ */
88
+ static getPageVariantId(pageId, language, variantId, scope) {
89
+ const formattedPageId = pageId.replace(/[{}-]/g, '');
90
+ const formattedLanguage = language.replace('-', '_');
91
+ const scopeId = scope ? `${this.normalizeScope(scope)}_` : '';
92
+ let formattedVariantId = variantId;
93
+ if (!variantId || variantId === exports.DEFAULT_VARIANT) {
94
+ formattedVariantId = 'default';
95
+ }
96
+ return `${scopeId}${formattedPageId}_${formattedLanguage}_${formattedVariantId}`.toLowerCase();
97
+ }
98
+ /**
99
+ * Gets the friendly id for (page-level) Embedded Personalization in the required format `embedded_[<scope>_]<id>_<lang>`
100
+ * @param {string} pageId the page id
101
+ * @param {string} language the language
102
+ * @param {string} [scope] the scope value
103
+ * @returns {string} the friendly id
104
+ */
105
+ static getPageFriendlyId(pageId, language, scope) {
106
+ const formattedPageId = pageId.replace(/[{}-]/g, '');
107
+ const formattedLanguage = language.replace('-', '_');
108
+ const scopeId = scope ? `${this.normalizeScope(scope)}_` : '';
109
+ return `embedded_${scopeId}${formattedPageId}_${formattedLanguage}`.toLowerCase();
110
+ }
111
+ /**
112
+ * Gets the friendly id for Component A/B Testing in the required format `component_[<scope>_]<pageId>_<componentId>_<language>*`
113
+ * @param {string} pageId the page id
114
+ * @param {string} componentId the component id
115
+ * @param {string} language the language
116
+ * @param {string} [scope] the scope value
117
+ * @returns {string} the friendly id
118
+ */
119
+ static getComponentFriendlyId(pageId, componentId, language, scope) {
120
+ const formattedPageId = pageId.replace(/[{}-]/g, '');
121
+ const formattedComponentId = componentId.replace(/[{}-]/g, '');
122
+ const formattedLanguage = language.replace('-', '_');
123
+ const scopeId = scope ? `${this.normalizeScope(scope)}_` : '';
124
+ return `component_${scopeId}${formattedPageId}_${formattedComponentId}_${formattedLanguage}*`.toLowerCase();
125
+ }
126
+ /**
127
+ * Normalizes the scope from the given string value
128
+ * Removes all non-alphanumeric characters
129
+ * @param {string} [scope] the scope value
130
+ * @returns {string} normalized scope value
131
+ */
132
+ static normalizeScope(scope) {
133
+ return (scope === null || scope === void 0 ? void 0 : scope.replace(/[^a-zA-Z0-9]+/g, '')) || '';
134
+ }
135
+ }
136
+ exports.CdpHelper = CdpHelper;