@sitecore-content-sdk/nextjs 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.
- package/LICENSE.txt +202 -0
- package/README.md +10 -0
- package/dist/cjs/ComponentBuilder.js +63 -0
- package/dist/cjs/components/BYOCWrapper.js +41 -0
- package/dist/cjs/components/ComponentPropsContext.js +57 -0
- package/dist/cjs/components/FEaaSWrapper.js +43 -0
- package/dist/cjs/components/Link.js +87 -0
- package/dist/cjs/components/NextImage.js +82 -0
- package/dist/cjs/components/Placeholder.js +49 -0
- package/dist/cjs/components/RichText.js +95 -0
- package/dist/cjs/editing/constants.js +10 -0
- package/dist/cjs/editing/editing-config-middleware.js +62 -0
- package/dist/cjs/editing/editing-render-middleware.js +182 -0
- package/dist/cjs/editing/feaas-render-middleware.js +101 -0
- package/dist/cjs/editing/index.js +16 -0
- package/dist/cjs/editing/render-middleware.js +43 -0
- package/dist/cjs/graphql/index.js +7 -0
- package/dist/cjs/index.js +119 -0
- package/dist/cjs/middleware/index.js +13 -0
- package/dist/cjs/middleware/middleware.js +97 -0
- package/dist/cjs/middleware/multisite-middleware.js +93 -0
- package/dist/cjs/middleware/personalize-middleware.js +231 -0
- package/dist/cjs/middleware/redirects-middleware.js +264 -0
- package/dist/cjs/monitoring/healthcheck-middleware.js +30 -0
- package/dist/cjs/monitoring/index.js +5 -0
- package/dist/cjs/services/base-graphql-sitemap-service.js +206 -0
- package/dist/cjs/services/component-props-service.js +167 -0
- package/dist/cjs/services/graphql-sitemap-service.js +64 -0
- package/dist/cjs/services/mutisite-graphql-sitemap-service.js +81 -0
- package/dist/cjs/sharedTypes/component-props.js +2 -0
- package/dist/cjs/sharedTypes/module-factory.js +2 -0
- package/dist/cjs/site/index.js +5 -0
- package/dist/cjs/utils/index.js +11 -0
- package/dist/cjs/utils/utils.js +42 -0
- package/dist/esm/ComponentBuilder.js +59 -0
- package/dist/esm/components/BYOCWrapper.js +36 -0
- package/dist/esm/components/ComponentPropsContext.js +19 -0
- package/dist/esm/components/FEaaSWrapper.js +38 -0
- package/dist/esm/components/Link.js +48 -0
- package/dist/esm/components/NextImage.js +76 -0
- package/dist/esm/components/Placeholder.js +12 -0
- package/dist/esm/components/RichText.js +55 -0
- package/dist/esm/editing/constants.js +7 -0
- package/dist/esm/editing/editing-config-middleware.js +58 -0
- package/dist/esm/editing/editing-render-middleware.js +177 -0
- package/dist/esm/editing/feaas-render-middleware.js +97 -0
- package/dist/esm/editing/index.js +5 -0
- package/dist/esm/editing/render-middleware.js +39 -0
- package/dist/esm/graphql/index.js +1 -0
- package/dist/esm/index.js +23 -0
- package/dist/esm/middleware/index.js +5 -0
- package/dist/esm/middleware/middleware.js +93 -0
- package/dist/esm/middleware/multisite-middleware.js +89 -0
- package/dist/esm/middleware/personalize-middleware.js +227 -0
- package/dist/esm/middleware/redirects-middleware.js +257 -0
- package/dist/esm/monitoring/healthcheck-middleware.js +26 -0
- package/dist/esm/monitoring/index.js +1 -0
- package/dist/esm/services/base-graphql-sitemap-service.js +201 -0
- package/dist/esm/services/component-props-service.js +160 -0
- package/dist/esm/services/graphql-sitemap-service.js +59 -0
- package/dist/esm/services/mutisite-graphql-sitemap-service.js +77 -0
- package/dist/esm/sharedTypes/component-props.js +1 -0
- package/dist/esm/sharedTypes/module-factory.js +1 -0
- package/dist/esm/site/index.js +1 -0
- package/dist/esm/utils/index.js +3 -0
- package/dist/esm/utils/utils.js +37 -0
- package/editing.d.ts +1 -0
- package/editing.js +1 -0
- package/global.d.ts +21 -0
- package/graphql.d.ts +1 -0
- package/graphql.js +1 -0
- package/middleware.d.ts +1 -0
- package/middleware.js +1 -0
- package/monitoring.d.ts +1 -0
- package/monitoring.js +1 -0
- package/package.json +92 -0
- package/site.d.ts +1 -0
- package/site.js +1 -0
- package/types/ComponentBuilder.d.ts +59 -0
- package/types/components/BYOCWrapper.d.ts +20 -0
- package/types/components/ComponentPropsContext.d.ts +18 -0
- package/types/components/FEaaSWrapper.d.ts +22 -0
- package/types/components/Link.d.ts +10 -0
- package/types/components/NextImage.d.ts +6 -0
- package/types/components/Placeholder.d.ts +8 -0
- package/types/components/RichText.d.ts +32 -0
- package/types/editing/constants.d.ts +7 -0
- package/types/editing/editing-config-middleware.d.ts +29 -0
- package/types/editing/editing-render-middleware.d.ts +79 -0
- package/types/editing/feaas-render-middleware.d.ts +32 -0
- package/types/editing/index.d.ts +5 -0
- package/types/editing/render-middleware.d.ts +24 -0
- package/types/graphql/index.d.ts +1 -0
- package/types/index.d.ts +24 -0
- package/types/middleware/index.d.ts +5 -0
- package/types/middleware/middleware.d.ts +82 -0
- package/types/middleware/multisite-middleware.d.ts +39 -0
- package/types/middleware/personalize-middleware.d.ts +102 -0
- package/types/middleware/redirects-middleware.d.ts +57 -0
- package/types/monitoring/healthcheck-middleware.d.ts +12 -0
- package/types/monitoring/index.d.ts +1 -0
- package/types/services/base-graphql-sitemap-service.d.ts +148 -0
- package/types/services/component-props-service.d.ts +81 -0
- package/types/services/graphql-sitemap-service.d.ts +51 -0
- package/types/services/mutisite-graphql-sitemap-service.d.ts +42 -0
- package/types/sharedTypes/component-props.d.ts +26 -0
- package/types/sharedTypes/module-factory.d.ts +32 -0
- package/types/site/index.d.ts +1 -0
- package/types/utils/index.d.ts +3 -0
- package/types/utils/utils.d.ts +8 -0
- package/utils.d.ts +1 -0
- package/utils.js +1 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MultisiteMiddleware = exports.PersonalizeMiddleware = exports.RedirectsMiddleware = exports.MiddlewareBase = exports.debug = void 0;
|
|
4
|
+
var core_1 = require("@sitecore-content-sdk/core");
|
|
5
|
+
Object.defineProperty(exports, "debug", { enumerable: true, get: function () { return core_1.debug; } });
|
|
6
|
+
var middleware_1 = require("./middleware");
|
|
7
|
+
Object.defineProperty(exports, "MiddlewareBase", { enumerable: true, get: function () { return middleware_1.MiddlewareBase; } });
|
|
8
|
+
var redirects_middleware_1 = require("./redirects-middleware");
|
|
9
|
+
Object.defineProperty(exports, "RedirectsMiddleware", { enumerable: true, get: function () { return redirects_middleware_1.RedirectsMiddleware; } });
|
|
10
|
+
var personalize_middleware_1 = require("./personalize-middleware");
|
|
11
|
+
Object.defineProperty(exports, "PersonalizeMiddleware", { enumerable: true, get: function () { return personalize_middleware_1.PersonalizeMiddleware; } });
|
|
12
|
+
var multisite_middleware_1 = require("./multisite-middleware");
|
|
13
|
+
Object.defineProperty(exports, "MultisiteMiddleware", { enumerable: true, get: function () { return multisite_middleware_1.MultisiteMiddleware; } });
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MiddlewareBase = void 0;
|
|
4
|
+
const server_1 = require("next/server");
|
|
5
|
+
class MiddlewareBase {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
this.SITE_SYMBOL = 'sc_site';
|
|
9
|
+
this.REWRITE_HEADER_NAME = 'x-sc-rewrite';
|
|
10
|
+
this.defaultHostname = config.defaultHostname || 'localhost';
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Determines if mode is preview
|
|
14
|
+
* @param {NextRequest} req request
|
|
15
|
+
* @returns {boolean} is preview
|
|
16
|
+
*/
|
|
17
|
+
isPreview(req) {
|
|
18
|
+
var _a, _b;
|
|
19
|
+
return !!(((_a = req.cookies.get('__prerender_bypass')) === null || _a === void 0 ? void 0 : _a.value) || ((_b = req.cookies.get('__next_preview_data')) === null || _b === void 0 ? void 0 : _b.value));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Determines if the request is a Next.js (next/link) prefetch request
|
|
23
|
+
* @param {NextRequest} req request
|
|
24
|
+
* @returns {boolean} is prefetch
|
|
25
|
+
*/
|
|
26
|
+
isPrefetch(req) {
|
|
27
|
+
return (
|
|
28
|
+
// eslint-disable-next-line prettier/prettier
|
|
29
|
+
req.headers.get('purpose') === 'prefetch' || req.headers.get('Next-Router-Prefetch') === '1' // Pages Router // App Router
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
excludeRoute(pathname) {
|
|
33
|
+
var _a, _b;
|
|
34
|
+
return (pathname.startsWith('/api/') || // Ignore Next.js API calls
|
|
35
|
+
pathname.startsWith('/sitecore/') || // Ignore Sitecore API calls
|
|
36
|
+
pathname.startsWith('/_next') || // Ignore next service calls
|
|
37
|
+
(((_a = this.config) === null || _a === void 0 ? void 0 : _a.excludeRoute) && ((_b = this.config) === null || _b === void 0 ? void 0 : _b.excludeRoute(pathname))));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Safely extract all headers for debug logging
|
|
41
|
+
* Necessary to avoid middleware issue https://github.com/vercel/next.js/issues/39765
|
|
42
|
+
* @param {Headers} incomingHeaders Incoming headers
|
|
43
|
+
* @returns Object with headers as key/value pairs
|
|
44
|
+
*/
|
|
45
|
+
extractDebugHeaders(incomingHeaders) {
|
|
46
|
+
const headers = {};
|
|
47
|
+
incomingHeaders.forEach((value, key) => (headers[key] = value));
|
|
48
|
+
return headers;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Provides used language
|
|
52
|
+
* @param {NextRequest} req request
|
|
53
|
+
* @returns {string} language
|
|
54
|
+
*/
|
|
55
|
+
getLanguage(req) {
|
|
56
|
+
return req.nextUrl.locale || req.nextUrl.defaultLocale || 'en';
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Extract 'host' header
|
|
60
|
+
* @param {NextRequest} req request
|
|
61
|
+
*/
|
|
62
|
+
getHostHeader(req) {
|
|
63
|
+
var _a;
|
|
64
|
+
return (_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get site information.
|
|
68
|
+
* Can not be used in **Preview** mode, since site will not be resolved
|
|
69
|
+
* @param {NextRequest} req request
|
|
70
|
+
* @param {NextResponse} [res] response
|
|
71
|
+
* @returns {SiteInfo} site information
|
|
72
|
+
*/
|
|
73
|
+
getSite(req, res) {
|
|
74
|
+
var _a;
|
|
75
|
+
const siteNameCookie = (_a = res === null || res === void 0 ? void 0 : res.cookies.get(this.SITE_SYMBOL)) === null || _a === void 0 ? void 0 : _a.value;
|
|
76
|
+
if (siteNameCookie)
|
|
77
|
+
return this.config.siteResolver.getByName(siteNameCookie);
|
|
78
|
+
const hostname = this.getHostHeader(req) || this.defaultHostname;
|
|
79
|
+
return this.config.siteResolver.getByHost(hostname);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Create a rewrite response
|
|
83
|
+
* @param {string} rewritePath the destionation path
|
|
84
|
+
* @param {NextRequest} req the current request
|
|
85
|
+
* @param {NextResponse} res the current response
|
|
86
|
+
*/
|
|
87
|
+
rewrite(rewritePath, req, res) {
|
|
88
|
+
// Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
|
|
89
|
+
const rewriteUrl = req.nextUrl.clone();
|
|
90
|
+
rewriteUrl.pathname = rewritePath;
|
|
91
|
+
const response = server_1.NextResponse.rewrite(rewriteUrl, res);
|
|
92
|
+
// Share rewrite path with following executed middlewares
|
|
93
|
+
response.headers.set(this.REWRITE_HEADER_NAME, rewritePath);
|
|
94
|
+
return response;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.MiddlewareBase = MiddlewareBase;
|
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MultisiteMiddleware = void 0;
|
|
13
|
+
const server_1 = require("next/server");
|
|
14
|
+
const site_1 = require("@sitecore-content-sdk/core/site");
|
|
15
|
+
const core_1 = require("@sitecore-content-sdk/core");
|
|
16
|
+
const middleware_1 = require("./middleware");
|
|
17
|
+
/**
|
|
18
|
+
* Middleware / handler for multisite support
|
|
19
|
+
*/
|
|
20
|
+
class MultisiteMiddleware extends middleware_1.MiddlewareBase {
|
|
21
|
+
/**
|
|
22
|
+
* @param {MultisiteMiddlewareConfig} [config] Multisite middleware config
|
|
23
|
+
*/
|
|
24
|
+
constructor(config) {
|
|
25
|
+
super(config);
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
var _a;
|
|
29
|
+
const pathname = req.nextUrl.pathname;
|
|
30
|
+
const language = this.getLanguage(req);
|
|
31
|
+
const hostname = this.getHostHeader(req) || this.defaultHostname;
|
|
32
|
+
const startTimestamp = Date.now();
|
|
33
|
+
core_1.debug.multisite('multisite middleware start: %o', {
|
|
34
|
+
pathname,
|
|
35
|
+
language,
|
|
36
|
+
hostname,
|
|
37
|
+
});
|
|
38
|
+
// Response will be provided if other middleware is run before us
|
|
39
|
+
let response = res || server_1.NextResponse.next();
|
|
40
|
+
if (this.isPreview(req) || this.excludeRoute(pathname)) {
|
|
41
|
+
core_1.debug.multisite('skipped (%s)', this.isPreview(req) ? 'preview' : 'route excluded');
|
|
42
|
+
return response;
|
|
43
|
+
}
|
|
44
|
+
// Site name can be forced by query string parameter or cookie
|
|
45
|
+
const siteName = req.nextUrl.searchParams.get(this.SITE_SYMBOL) ||
|
|
46
|
+
(this.config.useCookieResolution &&
|
|
47
|
+
this.config.useCookieResolution(req) &&
|
|
48
|
+
((_a = req.cookies.get(this.SITE_SYMBOL)) === null || _a === void 0 ? void 0 : _a.value)) ||
|
|
49
|
+
this.config.siteResolver.getByHost(hostname).name;
|
|
50
|
+
// Rewrite to site specific path
|
|
51
|
+
const rewritePath = (0, site_1.getSiteRewrite)(pathname, {
|
|
52
|
+
siteName,
|
|
53
|
+
});
|
|
54
|
+
response = this.rewrite(rewritePath, req, response);
|
|
55
|
+
// default site cookie attributes
|
|
56
|
+
const defaultCookieAttributes = {
|
|
57
|
+
secure: true,
|
|
58
|
+
httpOnly: true,
|
|
59
|
+
sameSite: 'none',
|
|
60
|
+
};
|
|
61
|
+
// Share site name with the following executed middlewares
|
|
62
|
+
response.cookies.set(this.SITE_SYMBOL, siteName, defaultCookieAttributes);
|
|
63
|
+
core_1.debug.multisite('multisite middleware end in %dms: %o', Date.now() - startTimestamp, {
|
|
64
|
+
rewritePath,
|
|
65
|
+
siteName,
|
|
66
|
+
headers: this.extractDebugHeaders(response.headers),
|
|
67
|
+
cookies: response.cookies,
|
|
68
|
+
});
|
|
69
|
+
return response;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Gets the Next.js middleware handler with error handling
|
|
74
|
+
* @returns middleware handler
|
|
75
|
+
*/
|
|
76
|
+
getHandler() {
|
|
77
|
+
return (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
try {
|
|
79
|
+
return yield this.handler(req, res);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.log('Multisite middleware failed:');
|
|
83
|
+
console.log(error);
|
|
84
|
+
return res || server_1.NextResponse.next();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
excludeRoute(pathname) {
|
|
89
|
+
// ignore files
|
|
90
|
+
return pathname.includes('.') || super.excludeRoute(pathname);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.MultisiteMiddleware = MultisiteMiddleware;
|
|
@@ -0,0 +1,231 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.PersonalizeMiddleware = void 0;
|
|
13
|
+
const server_1 = require("next/server");
|
|
14
|
+
const personalize_1 = require("@sitecore-content-sdk/core/personalize");
|
|
15
|
+
const core_1 = require("@sitecore-content-sdk/core");
|
|
16
|
+
const middleware_1 = require("./middleware");
|
|
17
|
+
const server_2 = require("@sitecore-cloudsdk/core/server");
|
|
18
|
+
const server_3 = require("@sitecore-cloudsdk/personalize/server");
|
|
19
|
+
/**
|
|
20
|
+
* Middleware / handler to support Sitecore Personalize
|
|
21
|
+
*/
|
|
22
|
+
class PersonalizeMiddleware extends middleware_1.MiddlewareBase {
|
|
23
|
+
/**
|
|
24
|
+
* @param {PersonalizeMiddlewareConfig} [config] Personalize middleware config
|
|
25
|
+
*/
|
|
26
|
+
constructor(config) {
|
|
27
|
+
super(config);
|
|
28
|
+
this.config = config;
|
|
29
|
+
this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
const pathname = req.nextUrl.pathname;
|
|
31
|
+
const language = this.getLanguage(req);
|
|
32
|
+
const hostname = this.getHostHeader(req) || this.defaultHostname;
|
|
33
|
+
const startTimestamp = Date.now();
|
|
34
|
+
const timeout = this.config.cdpConfig.timeout;
|
|
35
|
+
// Response will be provided if other middleware is run before us (e.g. redirects)
|
|
36
|
+
let response = res || server_1.NextResponse.next();
|
|
37
|
+
core_1.debug.personalize('personalize middleware start: %o', {
|
|
38
|
+
pathname,
|
|
39
|
+
language,
|
|
40
|
+
hostname,
|
|
41
|
+
headers: this.extractDebugHeaders(req.headers),
|
|
42
|
+
});
|
|
43
|
+
if (this.config.disabled && this.config.disabled(req, response)) {
|
|
44
|
+
core_1.debug.personalize('skipped (personalize middleware is disabled)');
|
|
45
|
+
return response;
|
|
46
|
+
}
|
|
47
|
+
if (response.redirected || // Don't attempt to personalize a redirect
|
|
48
|
+
this.isPreview(req) || // No need to personalize for preview (layout data is already prepared for preview)
|
|
49
|
+
this.excludeRoute(pathname)) {
|
|
50
|
+
core_1.debug.personalize('skipped (%s)', response.redirected ? 'redirected' : this.isPreview(req) ? 'preview' : 'route excluded');
|
|
51
|
+
return response;
|
|
52
|
+
}
|
|
53
|
+
const site = this.getSite(req, response);
|
|
54
|
+
// Get personalization info from Experience Edge
|
|
55
|
+
const personalizeInfo = yield this.personalizeService.getPersonalizeInfo(pathname, language, site.name);
|
|
56
|
+
if (!personalizeInfo) {
|
|
57
|
+
// Likely an invalid route / language
|
|
58
|
+
core_1.debug.personalize('skipped (personalize info not found)');
|
|
59
|
+
return response;
|
|
60
|
+
}
|
|
61
|
+
if (personalizeInfo.variantIds.length === 0) {
|
|
62
|
+
core_1.debug.personalize('skipped (no personalization configured)');
|
|
63
|
+
return response;
|
|
64
|
+
}
|
|
65
|
+
if (this.isPrefetch(req)) {
|
|
66
|
+
core_1.debug.personalize('skipped (prefetch)');
|
|
67
|
+
// Personalized, but this is a prefetch request.
|
|
68
|
+
// In this case, don't execute a personalize request; otherwise, the metrics for component A/B experiments would be inaccurate.
|
|
69
|
+
// Disable preflight caching to force revalidation on client-side navigation (personalization WILL be influenced).
|
|
70
|
+
// Note the reason we don't move this any earlier in the middleware is that we would then be sacrificing performance for non-personalized pages.
|
|
71
|
+
response.headers.set('x-middleware-cache', 'no-cache');
|
|
72
|
+
return response;
|
|
73
|
+
}
|
|
74
|
+
yield this.initPersonalizeServer({
|
|
75
|
+
hostname,
|
|
76
|
+
siteName: site.name,
|
|
77
|
+
request: req,
|
|
78
|
+
response,
|
|
79
|
+
});
|
|
80
|
+
const params = this.getExperienceParams(req);
|
|
81
|
+
const executions = this.getPersonalizeExecutions(personalizeInfo, language);
|
|
82
|
+
const identifiedVariantIds = [];
|
|
83
|
+
yield Promise.all(executions.map((execution) => this.personalize({
|
|
84
|
+
friendlyId: execution.friendlyId,
|
|
85
|
+
variantIds: execution.variantIds,
|
|
86
|
+
params,
|
|
87
|
+
language,
|
|
88
|
+
timeout,
|
|
89
|
+
}, req).then((personalization) => {
|
|
90
|
+
const variantId = personalization.variantId;
|
|
91
|
+
if (variantId) {
|
|
92
|
+
if (!execution.variantIds.includes(variantId)) {
|
|
93
|
+
core_1.debug.personalize('invalid variant %s', variantId);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
identifiedVariantIds.push(variantId);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
})));
|
|
100
|
+
if (identifiedVariantIds.length === 0) {
|
|
101
|
+
core_1.debug.personalize('skipped (no variant(s) identified)');
|
|
102
|
+
return response;
|
|
103
|
+
}
|
|
104
|
+
// Path can be rewritten by previously executed middleware
|
|
105
|
+
const basePath = (res === null || res === void 0 ? void 0 : res.headers.get('x-sc-rewrite')) || pathname;
|
|
106
|
+
// Rewrite to persononalized path
|
|
107
|
+
const rewritePath = (0, personalize_1.getPersonalizedRewrite)(basePath, identifiedVariantIds);
|
|
108
|
+
response = this.rewrite(rewritePath, req, response);
|
|
109
|
+
// Disable preflight caching to force revalidation on client-side navigation (personalization MAY be influenced).
|
|
110
|
+
// See https://github.com/vercel/next.js/pull/32767
|
|
111
|
+
response.headers.set('x-middleware-cache', 'no-cache');
|
|
112
|
+
core_1.debug.personalize('personalize middleware end in %dms: %o', Date.now() - startTimestamp, {
|
|
113
|
+
rewritePath,
|
|
114
|
+
headers: this.extractDebugHeaders(response.headers),
|
|
115
|
+
});
|
|
116
|
+
return response;
|
|
117
|
+
});
|
|
118
|
+
// NOTE: we provide native fetch for compatibility on Next.js Edge Runtime
|
|
119
|
+
// (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
|
|
120
|
+
this.personalizeService = new personalize_1.GraphQLPersonalizeService(Object.assign(Object.assign({}, config.edgeConfig), { fetch: fetch }));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Gets the Next.js middleware handler with error handling
|
|
124
|
+
* @returns middleware handler
|
|
125
|
+
*/
|
|
126
|
+
getHandler() {
|
|
127
|
+
return (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
128
|
+
try {
|
|
129
|
+
return yield this.handler(req, res);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
console.log('Personalize middleware failed:');
|
|
133
|
+
console.log(error);
|
|
134
|
+
return res || server_1.NextResponse.next();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
initPersonalizeServer(_a) {
|
|
139
|
+
return __awaiter(this, arguments, void 0, function* ({ hostname, siteName, request, response, }) {
|
|
140
|
+
yield (0, server_2.CloudSDK)(request, response, {
|
|
141
|
+
sitecoreEdgeUrl: this.config.cdpConfig.sitecoreEdgeUrl,
|
|
142
|
+
sitecoreEdgeContextId: this.config.cdpConfig.sitecoreEdgeContextId,
|
|
143
|
+
siteName,
|
|
144
|
+
cookieDomain: hostname,
|
|
145
|
+
enableServerCookie: true,
|
|
146
|
+
})
|
|
147
|
+
.addPersonalize({ enablePersonalizeCookie: true })
|
|
148
|
+
.initialize();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
personalize(_a, request_1) {
|
|
152
|
+
return __awaiter(this, arguments, void 0, function* ({ params, friendlyId, language, timeout, variantIds, }, request) {
|
|
153
|
+
var _b;
|
|
154
|
+
core_1.debug.personalize('executing experience for %s %o', friendlyId, params);
|
|
155
|
+
return (yield (0, server_3.personalize)(request, {
|
|
156
|
+
channel: this.config.cdpConfig.channel || 'WEB',
|
|
157
|
+
currency: (_b = this.config.cdpConfig.currency) !== null && _b !== void 0 ? _b : 'USD',
|
|
158
|
+
friendlyId,
|
|
159
|
+
params,
|
|
160
|
+
language,
|
|
161
|
+
pageVariantIds: variantIds,
|
|
162
|
+
}, { timeout }));
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
getExperienceParams(req) {
|
|
166
|
+
const utm = {
|
|
167
|
+
campaign: req.nextUrl.searchParams.get('utm_campaign') || undefined,
|
|
168
|
+
content: req.nextUrl.searchParams.get('utm_content') || undefined,
|
|
169
|
+
medium: req.nextUrl.searchParams.get('utm_medium') || undefined,
|
|
170
|
+
source: req.nextUrl.searchParams.get('utm_source') || undefined,
|
|
171
|
+
};
|
|
172
|
+
return {
|
|
173
|
+
// It's expected that the header name "referer" is actually a misspelling of the word "referrer"
|
|
174
|
+
// req.referrer is used during fetching to determine the value of the Referer header of the request being made,
|
|
175
|
+
// used as a fallback
|
|
176
|
+
referrer: req.headers.get('referer') || req.referrer,
|
|
177
|
+
utm: utm,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
excludeRoute(pathname) {
|
|
181
|
+
// ignore files
|
|
182
|
+
return pathname.includes('.') || super.excludeRoute(pathname);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Aggregates personalize executions based on the provided route personalize information and language
|
|
186
|
+
* @param {PersonalizeInfo} personalizeInfo the route personalize information
|
|
187
|
+
* @param {string} language the language
|
|
188
|
+
* @returns An array of personalize executions
|
|
189
|
+
*/
|
|
190
|
+
getPersonalizeExecutions(personalizeInfo, language) {
|
|
191
|
+
if (personalizeInfo.variantIds.length === 0) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
const results = [];
|
|
195
|
+
return personalizeInfo.variantIds.reduce((results, variantId) => {
|
|
196
|
+
if (variantId.includes('_')) {
|
|
197
|
+
// Component-level personalization in format "<ComponentID>_<VariantID>"
|
|
198
|
+
const componentId = variantId.split('_')[0];
|
|
199
|
+
const friendlyId = personalize_1.CdpHelper.getComponentFriendlyId(personalizeInfo.pageId, componentId, language, this.config.scope || this.config.edgeConfig.scope);
|
|
200
|
+
const execution = results.find((x) => x.friendlyId === friendlyId);
|
|
201
|
+
if (execution) {
|
|
202
|
+
execution.variantIds.push(variantId);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// The default/control variant (format "<ComponentID>_default") is also a valid value returned by the execution
|
|
206
|
+
const defaultVariant = `${componentId}${personalize_1.DEFAULT_VARIANT}`;
|
|
207
|
+
results.push({
|
|
208
|
+
friendlyId,
|
|
209
|
+
variantIds: [defaultVariant, variantId],
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
// Embedded (page-level) personalization in format "<VariantID>"
|
|
215
|
+
const friendlyId = personalize_1.CdpHelper.getPageFriendlyId(personalizeInfo.pageId, language, this.config.scope || this.config.edgeConfig.scope);
|
|
216
|
+
const execution = results.find((x) => x.friendlyId === friendlyId);
|
|
217
|
+
if (execution) {
|
|
218
|
+
execution.variantIds.push(variantId);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
results.push({
|
|
222
|
+
friendlyId,
|
|
223
|
+
variantIds: [variantId],
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return results;
|
|
228
|
+
}, results);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
exports.PersonalizeMiddleware = PersonalizeMiddleware;
|