@sitecore-jss/sitecore-jss-proxy 22.3.0 → 22.3.1-canary.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/index.js +19 -447
  3. package/dist/cjs/middleware/editing/config.js +27 -0
  4. package/dist/cjs/middleware/editing/index.js +89 -0
  5. package/dist/cjs/middleware/editing/render.js +103 -0
  6. package/dist/cjs/middleware/headless-ssr-proxy/index.js +450 -0
  7. package/dist/cjs/middleware/healthcheck/index.js +16 -0
  8. package/dist/cjs/middleware/index.js +32 -0
  9. package/dist/cjs/personalize/PersonalizeHelper.js +243 -0
  10. package/dist/cjs/personalize/index.js +5 -0
  11. package/dist/cjs/personalize/test-data/personalizeData.js +86 -0
  12. package/dist/cjs/types/personalize.js +2 -0
  13. package/dist/esm/index.js +4 -441
  14. package/dist/esm/middleware/editing/config.js +23 -0
  15. package/dist/esm/middleware/editing/index.js +84 -0
  16. package/dist/esm/middleware/editing/render.js +98 -0
  17. package/dist/esm/middleware/headless-ssr-proxy/index.js +441 -0
  18. package/dist/esm/middleware/healthcheck/index.js +12 -0
  19. package/dist/esm/middleware/index.js +4 -0
  20. package/dist/esm/personalize/PersonalizeHelper.js +236 -0
  21. package/dist/esm/personalize/index.js +1 -0
  22. package/dist/esm/personalize/test-data/personalizeData.js +82 -0
  23. package/dist/esm/types/personalize.js +1 -0
  24. package/package.json +22 -9
  25. package/types/index.d.ts +4 -21
  26. package/types/middleware/editing/config.d.ts +27 -0
  27. package/types/middleware/editing/index.d.ts +32 -0
  28. package/types/middleware/editing/render.d.ts +42 -0
  29. package/types/{ProxyConfig.d.ts → middleware/headless-ssr-proxy/ProxyConfig.d.ts} +2 -5
  30. package/types/middleware/headless-ssr-proxy/index.d.ts +20 -0
  31. package/types/middleware/healthcheck/index.d.ts +6 -0
  32. package/types/middleware/index.d.ts +3 -0
  33. package/types/personalize/PersonalizeHelper.d.ts +43 -0
  34. package/types/personalize/index.d.ts +2 -0
  35. package/types/personalize/test-data/personalizeData.d.ts +59 -0
  36. package/types/types/AppRenderer.d.ts +35 -0
  37. package/types/types/index.d.ts +3 -0
  38. package/types/types/personalize.d.ts +85 -0
  39. package/types/AppRenderer.d.ts +0 -10
  40. package/types/RenderResponse.d.ts +0 -15
  41. /package/dist/cjs/{ProxyConfig.js → middleware/headless-ssr-proxy/ProxyConfig.js} +0 -0
  42. /package/dist/cjs/{AppRenderer.js → types/AppRenderer.js} +0 -0
  43. /package/dist/cjs/{RouteUrlParser.js → types/RouteUrlParser.js} +0 -0
  44. /package/dist/cjs/{RenderResponse.js → types/index.js} +0 -0
  45. /package/dist/esm/{ProxyConfig.js → middleware/headless-ssr-proxy/ProxyConfig.js} +0 -0
  46. /package/dist/esm/{AppRenderer.js → types/AppRenderer.js} +0 -0
  47. /package/dist/esm/{RouteUrlParser.js → types/RouteUrlParser.js} +0 -0
  48. /package/dist/esm/{RenderResponse.js → types/index.js} +0 -0
  49. /package/types/{RouteUrlParser.d.ts → types/RouteUrlParser.d.ts} +0 -0
@@ -0,0 +1,236 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { CloudSDK } from '@sitecore-cloudsdk/core/server';
11
+ import { personalize } from '@sitecore-cloudsdk/personalize/server';
12
+ import { debug } from '@sitecore-jss/sitecore-jss';
13
+ import { CdpHelper, DEFAULT_VARIANT, GraphQLPersonalizeService, getGroomedVariantIds, personalizeLayout, } from '@sitecore-jss/sitecore-jss/personalize';
14
+ import querystring from 'querystring';
15
+ export class PersonalizeHelper {
16
+ constructor(config) {
17
+ this.config = config;
18
+ /**
19
+ * Performs personalize on layout data before a page is rendered
20
+ * @param {IncomingMessage} req Incoming request nodejs object
21
+ * @param {OutgoingMessage} res Outgoing response nodejs object
22
+ * @param {LayoutServiceData} layoutData layoutData for the page
23
+ * @returns layout data with personalization applied
24
+ */
25
+ this.personalizeLayoutData = (req, res, layoutData) => __awaiter(this, void 0, void 0, function* () {
26
+ var _a, _b;
27
+ if (!((_a = layoutData.sitecore) === null || _a === void 0 ? void 0 : _a.context)) {
28
+ debug.personalize('skipped (sitecore context is empty)');
29
+ return layoutData;
30
+ }
31
+ if (!((_b = layoutData.sitecore) === null || _b === void 0 ? void 0 : _b.route)) {
32
+ debug.personalize('skipped (layout is empty)');
33
+ return layoutData;
34
+ }
35
+ // current method can run for page requests and for layout service requests.
36
+ // the latter will not have the correct path - so we use path from layoutData instead
37
+ const pathname = layoutData.sitecore.context.itemPath;
38
+ const language = this.getLanguage(layoutData);
39
+ const hostname = this.getHostHeader(req) || this.defaultHostname;
40
+ const startTimestamp = Date.now();
41
+ if (!pathname) {
42
+ debug.personalize('skipped (pathname missing from layoutData)');
43
+ return layoutData;
44
+ }
45
+ debug.personalize('personalize layout start: %o', {
46
+ pathname,
47
+ language,
48
+ hostname,
49
+ headers: this.extractDebugHeaders(req.headers),
50
+ });
51
+ if (this.excludeRoute(pathname)) {
52
+ debug.personalize('skipped (route excluded)');
53
+ return layoutData;
54
+ }
55
+ if (this.config.disabled && this.config.disabled(req, res)) {
56
+ debug.personalize('skipped (personalize is disabled)');
57
+ return layoutData;
58
+ }
59
+ try {
60
+ yield this.initPersonalizeServer(req, res, hostname);
61
+ }
62
+ catch (e) {
63
+ debug.personalize('skipped (CloudSDK initialization failed), error %o', e);
64
+ return layoutData;
65
+ }
66
+ const variantIds = yield this.getVariantIds(req, language, pathname);
67
+ if (!variantIds) {
68
+ return layoutData;
69
+ }
70
+ const personalizeData = getGroomedVariantIds(variantIds);
71
+ // layout will be personalized here
72
+ personalizeLayout(layoutData, personalizeData.variantId, personalizeData.componentVariantIds);
73
+ debug.personalize('personalize layout end in %dms: %o', Date.now() - startTimestamp, {
74
+ headers: this.extractDebugHeaders(req.headers),
75
+ variantIds: variantIds,
76
+ });
77
+ return layoutData;
78
+ });
79
+ this.getVariantIds = (req, language, pathname) => __awaiter(this, void 0, void 0, function* () {
80
+ const timeout = this.config.cdpConfig.timeout;
81
+ // Get personalization info from Experience Edge
82
+ const personalizeInfo = yield this.personalizeService.getPersonalizeInfo(pathname, language, this.config.sitecoreSiteName);
83
+ if (!personalizeInfo) {
84
+ // Likely an invalid route / language
85
+ debug.personalize('skipped (personalize info not found)');
86
+ return [];
87
+ }
88
+ if (personalizeInfo.variantIds.length === 0) {
89
+ debug.personalize('skipped (no personalization configured)');
90
+ return [];
91
+ }
92
+ const params = this.getExperienceParams(req);
93
+ const executions = this.getPersonalizeExecutions(personalizeInfo, language);
94
+ const identifiedVariantIds = [];
95
+ try {
96
+ yield Promise.all(executions.map((execution) => this.personalize({
97
+ friendlyId: execution.friendlyId,
98
+ variantIds: execution.variantIds,
99
+ params,
100
+ language,
101
+ timeout,
102
+ }, req).then((personalization) => {
103
+ const variantId = personalization.variantId;
104
+ if (variantId) {
105
+ if (!execution.variantIds.includes(variantId)) {
106
+ debug.personalize('invalid variant %s', variantId);
107
+ }
108
+ else {
109
+ identifiedVariantIds.push(variantId);
110
+ }
111
+ }
112
+ })));
113
+ }
114
+ catch (e) {
115
+ debug.personalize('skipped, error %o', e);
116
+ }
117
+ if (identifiedVariantIds.length === 0) {
118
+ debug.personalize('skipped (no variant(s) identified)');
119
+ return [];
120
+ }
121
+ return identifiedVariantIds;
122
+ });
123
+ this.personalizeService = new GraphQLPersonalizeService(Object.assign({}, config.edgeConfig));
124
+ this.defaultHostname = config.defaultHostname || 'localhost';
125
+ }
126
+ /**
127
+ * Init CloudSDK personalization on server side
128
+ * @param {IncomingMessage} request incoming nodejs request object
129
+ * @param {OutgoingMessage} response outgoing nodejs response object
130
+ * @param {string} hostname host for cookies. Usually a host header, or a fallback config
131
+ */
132
+ initPersonalizeServer(request, response, hostname) {
133
+ return __awaiter(this, void 0, void 0, function* () {
134
+ yield CloudSDK(request, response, {
135
+ sitecoreEdgeUrl: this.config.cdpConfig.sitecoreEdgeUrl,
136
+ sitecoreEdgeContextId: this.config.cdpConfig.sitecoreEdgeContextId,
137
+ siteName: this.config.sitecoreSiteName,
138
+ cookieDomain: hostname,
139
+ enableServerCookie: true,
140
+ })
141
+ .addPersonalize({ enablePersonalizeCookie: true })
142
+ .initialize();
143
+ });
144
+ }
145
+ getLanguage(layoutData) {
146
+ var _a, _b;
147
+ return ((_b = (_a = layoutData.sitecore) === null || _a === void 0 ? void 0 : _a.context) === null || _b === void 0 ? void 0 : _b.language) || this.config.defaultLanguage || 'en';
148
+ }
149
+ getHostHeader(req) {
150
+ var _a;
151
+ return ((_a = req.headers.host) === null || _a === void 0 ? void 0 : _a.split(':')[0]) || '';
152
+ }
153
+ excludeRoute(pathname) {
154
+ var _a, _b;
155
+ return ((_a = this.config) === null || _a === void 0 ? void 0 : _a.excludeRoute) && ((_b = this.config) === null || _b === void 0 ? void 0 : _b.excludeRoute(pathname));
156
+ }
157
+ extractDebugHeaders(incomingHeaders) {
158
+ const headers = {};
159
+ Object.keys(incomingHeaders).forEach((key) => incomingHeaders[key] && (headers[key] = incomingHeaders[key]));
160
+ return headers;
161
+ }
162
+ personalize(_a, request_1) {
163
+ return __awaiter(this, arguments, void 0, function* ({ params, friendlyId, language, timeout, variantIds, }, request) {
164
+ var _b;
165
+ debug.personalize('executing experience for %s %o', friendlyId, params);
166
+ return (yield personalize(request, {
167
+ channel: this.config.cdpConfig.channel || 'WEB',
168
+ currency: (_b = this.config.cdpConfig.currency) !== null && _b !== void 0 ? _b : 'USD',
169
+ friendlyId,
170
+ params,
171
+ language,
172
+ pageVariantIds: variantIds,
173
+ }, { timeout }));
174
+ });
175
+ }
176
+ getExperienceParams(req) {
177
+ var _a;
178
+ // nodejs req.url does not have a hostname, we parse query string the old fashioned way
179
+ const rawQs = ((_a = req.url) === null || _a === void 0 ? void 0 : _a.split('?')[1]) || '';
180
+ const queryString = querystring.parse(rawQs);
181
+ // also need to account for types (string | string[]) returned by parse()
182
+ const utm = {
183
+ campaign: [queryString.utm_campaign].join('') || undefined,
184
+ content: [queryString.utm_content].join('') || undefined,
185
+ medium: [queryString.utm_medium].join('') || undefined,
186
+ source: [queryString.utm_source].join('') || undefined,
187
+ };
188
+ return {
189
+ // It's expected that the header name "referer" is actually a misspelling of the word "referrer"
190
+ // req.referrer is used during fetching to determine the value of the Referer header of the request being made,
191
+ // used as a fallback
192
+ referrer: req.headers.referer || [''].concat(req.headers.referrer || '').join(''),
193
+ utm: utm,
194
+ };
195
+ }
196
+ getPersonalizeExecutions(personalizeInfo, language) {
197
+ if (personalizeInfo.variantIds.length === 0) {
198
+ return [];
199
+ }
200
+ const results = [];
201
+ return personalizeInfo.variantIds.reduce((results, variantId) => {
202
+ if (variantId.includes('_')) {
203
+ // Component-level personalization in format "<ComponentID>_<VariantID>"
204
+ const componentId = variantId.split('_')[0];
205
+ const friendlyId = CdpHelper.getComponentFriendlyId(personalizeInfo.pageId, componentId, language, this.config.scope);
206
+ const execution = results.find((x) => x.friendlyId === friendlyId);
207
+ if (execution) {
208
+ execution.variantIds.push(variantId);
209
+ }
210
+ else {
211
+ // The default/control variant (format "<ComponentID>_default") is also a valid value returned by the execution
212
+ const defaultVariant = `${componentId}${DEFAULT_VARIANT}`;
213
+ results.push({
214
+ friendlyId,
215
+ variantIds: [defaultVariant, variantId],
216
+ });
217
+ }
218
+ }
219
+ else {
220
+ // Embedded (page-level) personalization in format "<VariantID>"
221
+ const friendlyId = CdpHelper.getPageFriendlyId(personalizeInfo.pageId, language, this.config.scope);
222
+ const execution = results.find((x) => x.friendlyId === friendlyId);
223
+ if (execution) {
224
+ execution.variantIds.push(variantId);
225
+ }
226
+ else {
227
+ results.push({
228
+ friendlyId,
229
+ variantIds: [variantId],
230
+ });
231
+ }
232
+ }
233
+ return results;
234
+ }, results);
235
+ }
236
+ }
@@ -0,0 +1 @@
1
+ export { PersonalizeHelper } from './PersonalizeHelper';
@@ -0,0 +1,82 @@
1
+ export const mountainBikeVariant = {
2
+ uid: '0b6d23d8-c50e-4e79-9eca-317ec43e82b0',
3
+ componentName: 'ContentBlock',
4
+ dataSource: '20679cd4-356b-4452-b507-453beeb0be39',
5
+ fields: {
6
+ content: {
7
+ value: '<p><img src="https://edge-beta.sitecorecloud.io/ser-edge-personalization/media/JssNextWeb/Mountain-Bike.jpg?h=675&amp;w=1200" style="width:1200px;height:675px;" /></p>',
8
+ },
9
+ heading: { value: 'Mountain Bike' },
10
+ },
11
+ };
12
+ export const cityBikeVariant = {
13
+ uid: '0b6d23d8-c50e-4e79-9eca-317ec43e82b0',
14
+ componentName: 'ContentBlock',
15
+ dataSource: '36e02581-2056-4c55-a4d5-f4b700ba1ae2',
16
+ fields: {
17
+ content: {
18
+ value: '<p><img src="https://edge-beta.sitecorecloud.io/ser-edge-personalization/media/JssNextWeb/Mountain-Bike.jpg?h=675&amp;w=1200" style="width:1200px;height:675px;" /></p>',
19
+ },
20
+ heading: { value: 'Mountain Bike' },
21
+ },
22
+ };
23
+ export const component_variant = {
24
+ uid: '0b6d23d8-c50e-4e79-9eca-317ec43e82b0',
25
+ componentName: 'ContentBlock',
26
+ dataSource: '36e02581-2056-4c55-a4d5-f4b700ba1ae2',
27
+ fields: {
28
+ content: {
29
+ value: '<p><img src="https://edge-beta.sitecorecloud.io/ser-edge-personalization/media/JssNextWeb/Hybrid-Bike.jpg?h=675&amp;w=1200" style="width:1200px;height:675px;" /></p>',
30
+ },
31
+ heading: { value: 'Hybrid Bike' },
32
+ },
33
+ };
34
+ const defaultRendering = {
35
+ uid: '0b6d23d8-c50e-4e79-9eca-317ec43e82b0',
36
+ componentName: 'ContentBlock',
37
+ dataSource: 'e020fb58-1be8-4537-aab8-67916452ecf2',
38
+ fields: { content: { value: '' }, heading: { value: 'Default Content' } },
39
+ experiences: {
40
+ 'mountain-bike-audience': mountainBikeVariant,
41
+ 'city-bike-audience': cityBikeVariant,
42
+ 'componentid_variant-id': component_variant,
43
+ },
44
+ };
45
+ export const getPersonalizeLayoutData = (variant, language, path) => {
46
+ const baseLayout = {
47
+ sitecore: {
48
+ context: {
49
+ pageEditing: false,
50
+ site: { name: 'JssNextWeb' },
51
+ visitorIdentificationTimestamp: 1038543,
52
+ language: language || 'en',
53
+ variantId: '',
54
+ itemPath: path || '/styleguide',
55
+ },
56
+ route: {
57
+ name: 'styleguide',
58
+ placeholders: {
59
+ main: {},
60
+ },
61
+ },
62
+ },
63
+ };
64
+ switch (variant) {
65
+ case 'mountain-bike-audience':
66
+ baseLayout.sitecore.route.placeholders.main = [mountainBikeVariant];
67
+ baseLayout.sitecore.context.variantId = 'mountain-bike-audience';
68
+ break;
69
+ case 'city-bike-audience':
70
+ baseLayout.sitecore.route.placeholders.main = [cityBikeVariant];
71
+ baseLayout.sitecore.context.variantId = 'city-bike-audience';
72
+ break;
73
+ case 'componentid_variant-id':
74
+ baseLayout.sitecore.route.placeholders.main = [component_variant];
75
+ baseLayout.sitecore.context.variantId = '_default';
76
+ break;
77
+ default:
78
+ baseLayout.sitecore.route.placeholders.main = [defaultRendering];
79
+ break;
80
+ }
81
+ return baseLayout;
82
+ };
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sitecore-jss/sitecore-jss-proxy",
3
- "version": "22.3.0",
4
- "description": "Proxy middleware for express.js server.",
3
+ "version": "22.3.1-canary.2",
4
+ "description": "Middlewares, utilities to work in a headless mode",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
7
7
  "sideEffects": false,
@@ -9,13 +9,13 @@
9
9
  "build": "npm run clean && tsc -p tsconfig.json && tsc -p tsconfig-esm.json",
10
10
  "clean": "del-cli dist types",
11
11
  "lint": "eslint \"./src/**/*.ts\"",
12
- "test": "mocha --require ts-node/register \"./src/**/*.test.ts\"",
12
+ "test": "mocha --require ts-node/register \"./src/**/*.test.ts\" --exit",
13
13
  "prepublishOnly": "npm run build",
14
14
  "coverage": "nyc npm test",
15
- "generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --readme none --out ../../ref-docs/sitecore-jss-proxy src/index.ts --githubPages false"
15
+ "generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --outputFileStrategy Members --parametersFormat table --readme none --entryPoints src/personalize/index.ts --out ../../ref-docs/sitecore-jss-proxy src/index.ts --githubPages false"
16
16
  },
17
17
  "engines": {
18
- "node": ">=20"
18
+ "node": ">=22"
19
19
  },
20
20
  "author": {
21
21
  "name": "Sitecore Corporation",
@@ -27,6 +27,9 @@
27
27
  "url": "https://github.com/sitecore/jss/issues"
28
28
  },
29
29
  "dependencies": {
30
+ "@sitecore-cloudsdk/core": "^0.4.1",
31
+ "@sitecore-cloudsdk/personalize": "^0.4.1",
32
+ "@sitecore-jss/sitecore-jss": "^22.3.1-canary.2",
30
33
  "http-proxy-middleware": "^2.0.6",
31
34
  "http-status-codes": "^2.2.0",
32
35
  "set-cookie-parser": "^2.5.1"
@@ -35,18 +38,28 @@
35
38
  "@types/chai": "^4.3.4",
36
39
  "@types/express": "^4.17.17",
37
40
  "@types/mocha": "^10.0.1",
38
- "@types/node": "^20.14.2",
41
+ "@types/node": "^22.9.0",
42
+ "@types/proxyquire": "^1.3.31",
39
43
  "@types/set-cookie-parser": "^2.4.2",
44
+ "@types/sinon": "^17.0.3",
45
+ "@types/supertest": "^6.0.2",
40
46
  "chai": "^4.3.7",
41
47
  "del-cli": "^5.0.0",
42
- "eslint": "^8.33.0",
48
+ "eslint": "^8.56.0",
49
+ "express": "^4.19.2",
43
50
  "mocha": "^10.2.0",
44
51
  "nyc": "^15.1.0",
52
+ "proxyquire": "^2.1.3",
53
+ "sinon": "^17.0.1",
54
+ "supertest": "^7.0.0",
45
55
  "ts-node": "^10.9.1",
46
- "typescript": "~4.9.5"
56
+ "typescript": "~5.6.3"
57
+ },
58
+ "peerDependencies": {
59
+ "express": "^4.19.2"
47
60
  },
48
61
  "types": "types/index.d.ts",
49
- "gitHead": "81b0239517966b6d0debdf6da3f12a98228c4d0c",
62
+ "gitHead": "5b35bbbe22b6ea21a1829950e371d54422ebf8fd",
50
63
  "files": [
51
64
  "dist",
52
65
  "types"
package/types/index.d.ts CHANGED
@@ -1,21 +1,4 @@
1
- /// <reference types="qs" />
2
- import { IncomingMessage } from 'http';
3
- import { Request, RequestHandler } from 'express';
4
- import { AppRenderer } from './AppRenderer';
5
- import { ProxyConfig, ServerBundle } from './ProxyConfig';
6
- import { RouteUrlParser } from './RouteUrlParser';
7
- export declare const removeEmptyAnalyticsCookie: (proxyResponse: IncomingMessage) => void;
8
- /**
9
- * @param {string} reqPath
10
- * @param {Request} req
11
- * @param {ProxyConfig} config
12
- * @param {RouteUrlParser} parseRouteUrl
13
- */
14
- export declare function rewriteRequestPath(reqPath: string, req: Request, config: ProxyConfig, parseRouteUrl?: RouteUrlParser): string;
15
- /**
16
- * @param {AppRenderer} renderer
17
- * @param {ProxyConfig} config
18
- * @param {RouteUrlParser} parseRouteUrl
19
- */
20
- export default function scProxy(renderer: AppRenderer, config: ProxyConfig, parseRouteUrl: RouteUrlParser): RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>[];
21
- export { ProxyConfig, ServerBundle };
1
+ export * from './middleware';
2
+ export * from './types';
3
+ export * from './personalize';
4
+ export { GRAPHQL_LAYOUT_QUERY_NAME } from '@sitecore-jss/sitecore-jss/layout';
@@ -0,0 +1,27 @@
1
+ import { Request, Response } from 'express';
2
+ import { Metadata } from '@sitecore-jss/sitecore-jss/utils';
3
+ /**
4
+ * Configuration for the editing config endpoint
5
+ */
6
+ export type EditingConfigEndpointOptions = {
7
+ /**
8
+ * Custom path for the endpoint. Default is `<routerPath>/config`
9
+ * @example
10
+ * { path: '/foo/config' } -> <routerPath>/foo/config
11
+ */
12
+ path?: string;
13
+ /**
14
+ * Components registered in the application
15
+ */
16
+ components: string[] | Map<string, unknown>;
17
+ /**
18
+ * Application metadata
19
+ */
20
+ metadata: Metadata;
21
+ };
22
+ /**
23
+ * Middleware to handle editing config requests
24
+ * @param {EditingConfigEndpointOptions} config Configuration for the endpoint
25
+ * @returns {RequestHandler} Middleware function
26
+ */
27
+ export declare const editingConfigMiddleware: (config: EditingConfigEndpointOptions) => (_req: Request, res: Response) => void;
@@ -0,0 +1,32 @@
1
+ import { NextFunction, Request, Response } from 'express';
2
+ import { EditingConfigEndpointOptions } from './config';
3
+ import { EditingRenderEndpointOptions } from './render';
4
+ /**
5
+ * Configuration for the editing router
6
+ */
7
+ export type EditingRouterConfig = {
8
+ /**
9
+ * Configuration for the /config endpoint
10
+ */
11
+ config: EditingConfigEndpointOptions;
12
+ /**
13
+ * Configuration for the /render endpoint
14
+ */
15
+ render: EditingRenderEndpointOptions;
16
+ };
17
+ /**
18
+ * Middleware to handle editing requests
19
+ * @param {Request} req Request
20
+ * @param {Response} res Response
21
+ * @param {NextFunction} next Next function
22
+ */
23
+ export declare const editingMiddleware: (req: Request, res: Response, next: NextFunction) => Promise<unknown>;
24
+ /**
25
+ * Creates a router for editing requests.
26
+ * Supports the following routes:
27
+ * - <routerPath>/render (GET) - renders a route
28
+ * - <routerPath>/config (GET) - returns the current application configuration
29
+ * @param {EditingRouterConfig} options Editing router configuration
30
+ * @returns {Router} Editing router
31
+ */
32
+ export declare const editingRouter: (options: EditingRouterConfig) => import("express-serve-static-core").Router;
@@ -0,0 +1,42 @@
1
+ import { GraphQLRequestClientFactory } from '@sitecore-jss/sitecore-jss';
2
+ import { AppRenderer } from '../../types/AppRenderer';
3
+ import { Request, Response } from 'express';
4
+ import { RenderMetadataQueryParams } from '@sitecore-jss/sitecore-jss/editing';
5
+ import { PersonalizeHelper } from '../../personalize';
6
+ /**
7
+ * Configuration for the editing render endpoint
8
+ */
9
+ export type EditingRenderEndpointOptions = {
10
+ /**
11
+ * Custom path for the endpoint. Default is `<routerPath>/render`
12
+ * @example
13
+ * { path: '/foo/render' } -> <routerPath>/foo/render
14
+ */
15
+ path?: string;
16
+ /**
17
+ * GraphQl Request Client Factory provided by the server bundle
18
+ */
19
+ clientFactory: GraphQLRequestClientFactory;
20
+ /**
21
+ * The appRenderer will produce the requested route's html
22
+ */
23
+ renderView: AppRenderer;
24
+ /**
25
+ * Personalize helper instance passed from proxy app
26
+ */
27
+ personalizeHelper?: PersonalizeHelper;
28
+ };
29
+ type MetadataRequest = Request & {
30
+ query: RenderMetadataQueryParams;
31
+ };
32
+ /**
33
+ * Middleware to handle editing render requests
34
+ * @param {EditingRenderEndpointOptions} config for the endpoint
35
+ */
36
+ export declare const editingRenderMiddleware: (config: EditingRenderEndpointOptions) => (req: MetadataRequest, res: Response) => Promise<void>;
37
+ /**
38
+ * Gets the Content-Security-Policy header value
39
+ * @returns {string} Content-Security-Policy header value
40
+ */
41
+ export declare const getSCPHeader: () => string;
42
+ export {};
@@ -1,11 +1,8 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
1
  import { IncomingMessage, ServerResponse, Agent } from 'http';
4
2
  import { Agent as HttpsAgent } from 'https';
5
3
  import { Options } from 'http-proxy-middleware';
6
- import { AppRenderer } from './AppRenderer';
7
- import { RenderResponse } from './RenderResponse';
8
- import { RouteUrlParser } from './RouteUrlParser';
4
+ import { AppRenderer, RenderResponse } from '../../types/AppRenderer';
5
+ import { RouteUrlParser } from '../../types/RouteUrlParser';
9
6
  /** A reply from the Sitecore Layout Service */
10
7
  export interface LayoutServiceData {
11
8
  sitecore: {
@@ -0,0 +1,20 @@
1
+ import { IncomingMessage } from 'http';
2
+ import { Request, RequestHandler } from 'express';
3
+ import { AppRenderer } from '../../types/AppRenderer';
4
+ import { RouteUrlParser } from '../../types/RouteUrlParser';
5
+ import { ProxyConfig, ServerBundle } from './ProxyConfig';
6
+ export declare const removeEmptyAnalyticsCookie: (proxyResponse: IncomingMessage) => void;
7
+ /**
8
+ * @param {string} reqPath
9
+ * @param {Request} req
10
+ * @param {ProxyConfig} config
11
+ * @param {RouteUrlParser} parseRouteUrl
12
+ */
13
+ export declare function rewriteRequestPath(reqPath: string, req: Request, config: ProxyConfig, parseRouteUrl?: RouteUrlParser): string;
14
+ /**
15
+ * @param {AppRenderer} renderer
16
+ * @param {ProxyConfig} config
17
+ * @param {RouteUrlParser} parseRouteUrl
18
+ */
19
+ export declare function middleware(renderer: AppRenderer, config: ProxyConfig, parseRouteUrl: RouteUrlParser): RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>[];
20
+ export { ProxyConfig, ServerBundle };
@@ -0,0 +1,6 @@
1
+ import { Router } from 'express';
2
+ /**
3
+ * Creates a router for health check requests.
4
+ * @returns {Router} Editing router
5
+ */
6
+ export declare const healthCheck: () => Router;
@@ -0,0 +1,3 @@
1
+ export * as headlessProxy from './headless-ssr-proxy';
2
+ export { editingRouter } from './editing';
3
+ export { healthCheck } from './healthcheck';
@@ -0,0 +1,43 @@
1
+ import { LayoutServiceData } from '@sitecore-jss/sitecore-jss/layout';
2
+ import { PersonalizeInfo } from '@sitecore-jss/sitecore-jss/personalize';
3
+ import { IncomingHttpHeaders, IncomingMessage, OutgoingMessage } from 'http';
4
+ import { ExperienceParams, PersonalizeConfig, PersonalizeExecution } from '../types/personalize';
5
+ export declare class PersonalizeHelper {
6
+ protected config: PersonalizeConfig;
7
+ private personalizeService;
8
+ private defaultHostname;
9
+ constructor(config: PersonalizeConfig);
10
+ /**
11
+ * Performs personalize on layout data before a page is rendered
12
+ * @param {IncomingMessage} req Incoming request nodejs object
13
+ * @param {OutgoingMessage} res Outgoing response nodejs object
14
+ * @param {LayoutServiceData} layoutData layoutData for the page
15
+ * @returns layout data with personalization applied
16
+ */
17
+ personalizeLayoutData: (req: IncomingMessage, res: OutgoingMessage, layoutData: LayoutServiceData) => Promise<LayoutServiceData>;
18
+ /**
19
+ * Init CloudSDK personalization on server side
20
+ * @param {IncomingMessage} request incoming nodejs request object
21
+ * @param {OutgoingMessage} response outgoing nodejs response object
22
+ * @param {string} hostname host for cookies. Usually a host header, or a fallback config
23
+ */
24
+ protected initPersonalizeServer(request: IncomingMessage, response: OutgoingMessage, hostname: string): Promise<void>;
25
+ protected getVariantIds: (req: IncomingMessage, language: string, pathname: string) => Promise<string[]>;
26
+ protected getLanguage(layoutData: LayoutServiceData): string;
27
+ protected getHostHeader(req: IncomingMessage): string;
28
+ protected excludeRoute(pathname: string): boolean | undefined;
29
+ protected extractDebugHeaders(incomingHeaders: IncomingHttpHeaders): {
30
+ [key: string]: string | string[] | undefined;
31
+ };
32
+ protected personalize({ params, friendlyId, language, timeout, variantIds, }: {
33
+ params: ExperienceParams;
34
+ friendlyId: string;
35
+ language: string;
36
+ timeout?: number;
37
+ variantIds?: string[];
38
+ }, request: IncomingMessage): Promise<{
39
+ variantId: string;
40
+ }>;
41
+ protected getExperienceParams(req: IncomingMessage): ExperienceParams;
42
+ protected getPersonalizeExecutions(personalizeInfo: PersonalizeInfo, language: string): PersonalizeExecution[];
43
+ }
@@ -0,0 +1,2 @@
1
+ export { PersonalizeHelper } from './PersonalizeHelper';
2
+ export { PersonalizeConfig } from '../types/personalize';