@sitecore-jss/sitecore-jss-nextjs 21.7.0-canary.5 → 21.7.0-canary.50

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 (36) hide show
  1. package/context.d.ts +1 -0
  2. package/context.js +1 -0
  3. package/dist/cjs/context/context.js +83 -0
  4. package/dist/cjs/context/index.js +5 -0
  5. package/dist/cjs/editing/editing-render-middleware.js +1 -1
  6. package/dist/cjs/index.js +5 -3
  7. package/dist/cjs/middleware/middleware.js +17 -0
  8. package/dist/cjs/middleware/multisite-middleware.js +1 -6
  9. package/dist/cjs/middleware/personalize-middleware.js +34 -47
  10. package/dist/cjs/middleware/redirects-middleware.js +31 -13
  11. package/dist/cjs/revalidate/index.js +5 -0
  12. package/dist/cjs/revalidate/revalidate-middleware.js +216 -0
  13. package/dist/cjs/utils/utils.js +6 -18
  14. package/dist/esm/context/context.js +79 -0
  15. package/dist/esm/context/index.js +1 -0
  16. package/dist/esm/editing/editing-render-middleware.js +1 -1
  17. package/dist/esm/index.js +3 -2
  18. package/dist/esm/middleware/middleware.js +17 -0
  19. package/dist/esm/middleware/multisite-middleware.js +1 -6
  20. package/dist/esm/middleware/personalize-middleware.js +35 -48
  21. package/dist/esm/middleware/redirects-middleware.js +31 -13
  22. package/dist/esm/revalidate/index.js +1 -0
  23. package/dist/esm/revalidate/revalidate-middleware.js +212 -0
  24. package/dist/esm/utils/utils.js +6 -15
  25. package/package.json +11 -11
  26. package/revalidate.d.ts +1 -0
  27. package/revalidate.js +1 -0
  28. package/types/context/context.d.ts +116 -0
  29. package/types/context/index.d.ts +1 -0
  30. package/types/editing/editing-render-middleware.d.ts +1 -1
  31. package/types/index.d.ts +3 -2
  32. package/types/middleware/middleware.d.ts +8 -0
  33. package/types/middleware/personalize-middleware.d.ts +20 -15
  34. package/types/revalidate/index.d.ts +1 -0
  35. package/types/revalidate/revalidate-middleware.d.ts +115 -0
  36. package/types/utils/utils.d.ts +1 -0
@@ -1,10 +1,6 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.getJssEditingSecret = exports.handleEditorFastRefresh = exports.getPublicUrl = void 0;
7
- const chalk_1 = __importDefault(require("chalk"));
8
4
  const utils_1 = require("@sitecore-jss/sitecore-jss/utils");
9
5
  /**
10
6
  * Get the publicUrl.
@@ -13,27 +9,19 @@ const utils_1 = require("@sitecore-jss/sitecore-jss/utils");
13
9
  * VERCEL_URL is provided by Vercel in case if we are in Preview deployment (deployment based on the custom branch),
14
10
  * preview deployment has unique url, we don't know exact url.
15
11
  * Similarly, DEPLOY_URL is provided by Netlify and would give us the deploy URL
12
+ * In production non-editing environments it is desirable to use relative urls, so in that case set PUBLIC_URL = ''
16
13
  */
17
14
  const getPublicUrl = () => {
18
- if (process.env.NETLIFY && process.env.DEPLOY_URL)
19
- return process.env.DEPLOY_URL;
20
- if (process.env.VERCEL_URL)
21
- return `https://${process.env.VERCEL_URL}`;
22
15
  let url = process.env.PUBLIC_URL;
23
16
  if (url === undefined) {
24
- console.warn(`${chalk_1.default.yellow.bold('Warning:')} An PUBLIC_URL environment variable is not defined. Falling back to http://localhost:3000.`);
17
+ if (process.env.NETLIFY && process.env.DEPLOY_URL)
18
+ return process.env.DEPLOY_URL;
19
+ if (process.env.VERCEL_URL)
20
+ return `https://${process.env.VERCEL_URL}`;
25
21
  url = 'http://localhost:3000';
26
22
  }
27
- else {
28
- try {
29
- new URL(url);
30
- }
31
- catch (error) {
32
- throw new Error(`The PUBLIC_URL environment variable '${url}' is not a valid URL.`);
33
- }
34
- }
35
23
  // Ensure no trailing slash
36
- return url.toString().replace(/\/$/, '');
24
+ return url.replace(/\/$/, '');
37
25
  };
38
26
  exports.getPublicUrl = getPublicUrl;
39
27
  /**
@@ -0,0 +1,79 @@
1
+ import { LayoutServicePageState } from '@sitecore-jss/sitecore-jss-react';
2
+ /**
3
+ * Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs).
4
+ */
5
+ export class Context {
6
+ constructor(props) {
7
+ this.props = props;
8
+ /**
9
+ * Indicates whether the Context and SDK(s) have been initialized
10
+ */
11
+ this.isInitialized = false;
12
+ /**
13
+ * Software Development Kits (SDKs) to be initialized
14
+ */
15
+ this.sdks = {};
16
+ /**
17
+ * Promises for the SDKs
18
+ */
19
+ this.sdkPromises = {};
20
+ this.sdkErrors = {};
21
+ /**
22
+ * Retrieves the Software Development Kit (SDK) instance, ensuring it is initialized before returning
23
+ *
24
+ * @param {string} name SDK name
25
+ * @returns initialized SDK
26
+ */
27
+ this.getSDK = (name) => {
28
+ if (!this.sdkPromises[name]) {
29
+ return Promise.reject(`Unknown SDK '${String(name)}'`);
30
+ }
31
+ else {
32
+ return this.sdkPromises[name].then((result) => {
33
+ return ((this.sdkErrors[name] && Promise.reject(this.sdkErrors[name])) || Promise.resolve(result));
34
+ });
35
+ }
36
+ };
37
+ this.sitecoreEdgeUrl = props.sitecoreEdgeUrl;
38
+ this.sitecoreEdgeContextId = props.sitecoreEdgeContextId;
39
+ this.siteName = props.siteName;
40
+ this.pageState = LayoutServicePageState.Normal;
41
+ }
42
+ init(props = {}) {
43
+ // Context and SDKs are initialized only once
44
+ if (this.isInitialized)
45
+ return;
46
+ this.isInitialized = true;
47
+ if (props.siteName) {
48
+ this.siteName = props.siteName;
49
+ }
50
+ if (props.pageState) {
51
+ this.pageState = props.pageState;
52
+ }
53
+ // iterate over the SDKs and initialize them
54
+ for (const sdkName of Object.keys(this.props.sdks)) {
55
+ this.initSDK(sdkName);
56
+ }
57
+ }
58
+ /**
59
+ * Initializes the Software Development Kit (SDK)
60
+ *
61
+ * @param {T} name SDK name
62
+ * @returns {void}
63
+ */
64
+ initSDK(name) {
65
+ this.sdkPromises[name] = new Promise((resolve) => {
66
+ this.props.sdks[name]
67
+ .init(this)
68
+ .then(() => {
69
+ this.sdks[name] = this.props.sdks[name].sdk;
70
+ resolve(this.sdks[name]);
71
+ })
72
+ .catch((e) => {
73
+ // if init rejects, we mark SDK as failed - so getSDK call would reject with a reason
74
+ this.sdkErrors[name] = e;
75
+ resolve(undefined);
76
+ });
77
+ });
78
+ }
79
+ }
@@ -0,0 +1 @@
1
+ export { Context } from './context';
@@ -26,7 +26,7 @@ export class EditingRenderMiddleware {
26
26
  var _a, _b, _c, _d;
27
27
  /**
28
28
  * Gets query parameters that should be passed along to subsequent requests
29
- * @param query Object of query parameters from incoming URL
29
+ * @param {Object} query Object of query parameters from incoming URL
30
30
  * @returns Object of approved query parameters
31
31
  */
32
32
  this.getQueryParamsForPropagation = (query) => {
package/dist/esm/index.js CHANGED
@@ -20,11 +20,11 @@ const { GraphQLRequestClientDep: GraphQLRequestClient } = {
20
20
  export { GraphQLRequestClient };
21
21
  export { handleEditorFastRefresh, getPublicUrl };
22
22
  export { isEditorActive, resetEditorChromes, resolveUrl, tryParseEnvValue };
23
- export { LayoutServicePageState, GraphQLLayoutService, RestLayoutService, getChildPlaceholder, getFieldValue, RenderingType, EDITING_COMPONENT_PLACEHOLDER, EDITING_COMPONENT_ID, } from '@sitecore-jss/sitecore-jss/layout';
23
+ export { LayoutServicePageState, GraphQLLayoutService, RestLayoutService, getChildPlaceholder, getFieldValue, RenderingType, EDITING_COMPONENT_PLACEHOLDER, EDITING_COMPONENT_ID, getContentStylesheetLink, } from '@sitecore-jss/sitecore-jss/layout';
24
24
  export { mediaApi } from '@sitecore-jss/sitecore-jss/media';
25
25
  export { trackingApi, } from '@sitecore-jss/sitecore-jss/tracking';
26
26
  export { GraphQLDictionaryService, RestDictionaryService, } from '@sitecore-jss/sitecore-jss/i18n';
27
- export { personalizeLayout, getPersonalizedRewrite, getPersonalizedRewriteData, normalizePersonalizedRewrite, CdpHelper, PosResolver, } from '@sitecore-jss/sitecore-jss/personalize';
27
+ export { personalizeLayout, getPersonalizedRewrite, getPersonalizedRewriteData, normalizePersonalizedRewrite, CdpHelper, } from '@sitecore-jss/sitecore-jss/personalize';
28
28
  export { ComponentPropsService } from './services/component-props-service';
29
29
  export { DisconnectedSitemapService } from './services/disconnected-sitemap-service';
30
30
  export { GraphQLSitemapService, } from './services/graphql-sitemap-service';
@@ -41,4 +41,5 @@ import * as BYOCWrapper from './components/BYOCWrapper';
41
41
  export { FEaaSWrapper };
42
42
  export { BYOCWrapper };
43
43
  export { ComponentBuilder } from './ComponentBuilder';
44
+ export { Context } from './context';
44
45
  export { Image, Text, DateField, EditFrame, FEaaSComponent, fetchFEaaSComponentServerProps, BYOCComponent, getFEAASLibraryStylesheetLinks, File, VisitorIdentification, SitecoreContext, SitecoreContextReactContext, withSitecoreContext, useSitecoreContext, withEditorChromes, withPlaceholder, withDatasourceCheck, } from '@sitecore-jss/sitecore-jss-react';
@@ -1,7 +1,9 @@
1
+ import { NextResponse } from 'next/server';
1
2
  export class MiddlewareBase {
2
3
  constructor(config) {
3
4
  this.config = config;
4
5
  this.SITE_SYMBOL = 'sc_site';
6
+ this.REWRITE_HEADER_NAME = 'x-sc-rewrite';
5
7
  this.defaultHostname = config.defaultHostname || 'localhost';
6
8
  }
7
9
  /**
@@ -62,4 +64,19 @@ export class MiddlewareBase {
62
64
  const hostname = this.getHostHeader(req) || this.defaultHostname;
63
65
  return this.config.siteResolver.getByHost(hostname);
64
66
  }
67
+ /**
68
+ * Create a rewrite response
69
+ * @param {string} rewritePath the destionation path
70
+ * @param {NextRequest} req the current request
71
+ * @param {NextResponse} res the current response
72
+ */
73
+ rewrite(rewritePath, req, res) {
74
+ // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
75
+ const rewriteUrl = req.nextUrl.clone();
76
+ rewriteUrl.pathname = rewritePath;
77
+ const response = NextResponse.rewrite(rewriteUrl, res);
78
+ // Share rewrite path with following executed middlewares
79
+ response.headers.set(this.REWRITE_HEADER_NAME, rewritePath);
80
+ return response;
81
+ }
65
82
  }
@@ -48,14 +48,9 @@ export class MultisiteMiddleware extends MiddlewareBase {
48
48
  const rewritePath = getSiteRewrite(pathname, {
49
49
  siteName,
50
50
  });
51
- // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
52
- const rewriteUrl = req.nextUrl.clone();
53
- rewriteUrl.pathname = rewritePath;
54
- response = NextResponse.rewrite(rewriteUrl);
51
+ response = this.rewrite(rewritePath, req, response);
55
52
  // Share site name with the following executed middlewares
56
53
  response.cookies.set(this.SITE_SYMBOL, siteName);
57
- // Share rewrite path with following executed middlewares
58
- response.headers.set('x-sc-rewrite', rewritePath);
59
54
  debug.multisite('multisite middleware end in %dms: %o', Date.now() - startTimestamp, {
60
55
  rewritePath,
61
56
  siteName,
@@ -8,10 +8,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { NextResponse } from 'next/server';
11
- import { GraphQLPersonalizeService, getPersonalizedRewrite, PosResolver, } from '@sitecore-jss/sitecore-jss/personalize';
11
+ import { GraphQLPersonalizeService, getPersonalizedRewrite, } from '@sitecore-jss/sitecore-jss/personalize';
12
12
  import { debug } from '@sitecore-jss/sitecore-jss';
13
13
  import { MiddlewareBase } from './middleware';
14
- import { initServer } from '@sitecore/engage';
14
+ import { init, personalize } from '@sitecore-cloudsdk/personalize/server';
15
15
  /**
16
16
  * Middleware / handler to support Sitecore Personalize
17
17
  */
@@ -23,7 +23,6 @@ export class PersonalizeMiddleware extends MiddlewareBase {
23
23
  super(config);
24
24
  this.config = config;
25
25
  this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
26
- var _a;
27
26
  const pathname = req.nextUrl.pathname;
28
27
  const language = this.getLanguage(req);
29
28
  const hostname = this.getHostHeader(req) || this.defaultHostname;
@@ -59,30 +58,20 @@ export class PersonalizeMiddleware extends MiddlewareBase {
59
58
  debug.personalize('skipped (no personalization configured)');
60
59
  return response;
61
60
  }
62
- const engageServer = this.initializeEngageServer(hostname, site, language);
63
- // creates the browser ID cookie on the server side
64
- // and includes the cookie in the response header
65
- try {
66
- yield engageServer.handleCookie(req, response, timeout);
67
- }
68
- catch (error) {
69
- debug.personalize('skipped (browser id generation failed)');
70
- throw error;
71
- }
61
+ yield this.initPersonalizeServer({
62
+ hostname,
63
+ siteName: site.name,
64
+ request: req,
65
+ response,
66
+ });
72
67
  const params = this.getExperienceParams(req);
73
- debug.personalize('executing experience for %s %s %o', personalizeInfo.contentId, params);
74
- const personalizationData = {
75
- channel: this.config.cdpConfig.channel || 'WEB',
76
- currency: (_a = this.config.cdpConfig.currency) !== null && _a !== void 0 ? _a : 'USA',
77
- friendlyId: personalizeInfo.contentId,
78
- params,
79
- language,
80
- };
68
+ debug.personalize('executing experience for %s %o', personalizeInfo.contentId, params);
81
69
  let variantId;
82
- // Execute targeted experience in CDP
70
+ // Execute targeted experience in Personalize SDK
83
71
  // eslint-disable-next-line no-useless-catch
84
72
  try {
85
- variantId = (yield engageServer.personalize(personalizationData, req, timeout)).variantId;
73
+ const personalization = yield this.personalize({ personalizeInfo, params, language, timeout }, req);
74
+ variantId = personalization.variantId;
86
75
  }
87
76
  catch (error) {
88
77
  throw error;
@@ -99,24 +88,10 @@ export class PersonalizeMiddleware extends MiddlewareBase {
99
88
  const basePath = (res === null || res === void 0 ? void 0 : res.headers.get('x-sc-rewrite')) || pathname;
100
89
  // Rewrite to persononalized path
101
90
  const rewritePath = getPersonalizedRewrite(basePath, { variantId });
102
- // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
103
- const rewriteUrl = req.nextUrl.clone();
104
- // Preserve cookies from previous response
105
- const cookies = response.cookies.getAll();
106
- rewriteUrl.pathname = rewritePath;
107
- response = NextResponse.rewrite(rewriteUrl, response);
91
+ response = this.rewrite(rewritePath, req, response);
108
92
  // Disable preflight caching to force revalidation on client-side navigation (personalization may be influenced)
109
93
  // See https://github.com/vercel/next.js/issues/32727
110
94
  response.headers.set('x-middleware-cache', 'no-cache');
111
- // Share rewrite path with following executed middleware
112
- response.headers.set('x-sc-rewrite', rewritePath);
113
- // Share site name with the following executed middlewares
114
- response.cookies.set(this.SITE_SYMBOL, site.name);
115
- // Restore cookies from previous response since
116
- // browserId cookie gets omitted after rewrite
117
- cookies.forEach((cookie) => {
118
- response.cookies.set(cookie);
119
- });
120
95
  debug.personalize('personalize middleware end in %dms: %o', Date.now() - startTimestamp, {
121
96
  rewritePath,
122
97
  headers: this.extractDebugHeaders(response.headers),
@@ -143,17 +118,29 @@ export class PersonalizeMiddleware extends MiddlewareBase {
143
118
  }
144
119
  });
145
120
  }
146
- initializeEngageServer(hostName, site, language) {
147
- const engageServer = initServer({
148
- clientKey: this.config.cdpConfig.clientKey,
149
- targetURL: this.config.cdpConfig.endpoint,
150
- pointOfSale: this.config.getPointOfSale
151
- ? this.config.getPointOfSale(site, language)
152
- : PosResolver.resolve(site, language),
153
- cookieDomain: hostName,
154
- forceServerCookieMode: true,
121
+ initPersonalizeServer({ hostname, siteName, request, response, }) {
122
+ return __awaiter(this, void 0, void 0, function* () {
123
+ yield init({
124
+ sitecoreEdgeUrl: this.config.cdpConfig.sitecoreEdgeUrl,
125
+ sitecoreEdgeContextId: this.config.cdpConfig.sitecoreEdgeContextId,
126
+ siteName,
127
+ cookieDomain: hostname,
128
+ enableServerCookie: true,
129
+ }, request, response);
130
+ });
131
+ }
132
+ personalize({ params, personalizeInfo, language, timeout, }, request) {
133
+ var _a;
134
+ return __awaiter(this, void 0, void 0, function* () {
135
+ const personalizationData = {
136
+ channel: this.config.cdpConfig.channel || 'WEB',
137
+ currency: (_a = this.config.cdpConfig.currency) !== null && _a !== void 0 ? _a : 'USD',
138
+ friendlyId: personalizeInfo.contentId,
139
+ params,
140
+ language,
141
+ };
142
+ return (yield personalize(personalizationData, request, timeout));
155
143
  });
156
- return engageServer;
157
144
  }
158
145
  getExperienceParams(req) {
159
146
  const utm = {
@@ -36,7 +36,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
36
36
  hostname,
37
37
  });
38
38
  const createResponse = () => __awaiter(this, void 0, void 0, function* () {
39
- if (this.config.disabled && this.config.disabled(req, NextResponse.next())) {
39
+ if (this.config.disabled && this.config.disabled(req, res || NextResponse.next())) {
40
40
  debug.redirects('skipped (redirects middleware is disabled)');
41
41
  return res || NextResponse.next();
42
42
  }
@@ -62,33 +62,47 @@ export class RedirectsMiddleware extends MiddlewareBase {
62
62
  url.locale = req.nextUrl.locale;
63
63
  }
64
64
  else {
65
+ const source = `${url.pathname}${url.search}`;
65
66
  url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
66
67
  const urlFirstPart = existsRedirect.target.split('/')[1];
67
68
  if (this.locales.includes(urlFirstPart)) {
68
69
  url.locale = urlFirstPart;
69
70
  existsRedirect.target = existsRedirect.target.replace(`/${urlFirstPart}`, '');
70
71
  }
71
- url.pathname = url.pathname
72
+ const target = source
72
73
  .replace(regexParser(existsRedirect.pattern), existsRedirect.target)
73
- .replace(/^\/\//, '/');
74
+ .replace(/^\/\//, '/')
75
+ .split('?');
76
+ url.pathname = target[0];
77
+ if (target[1]) {
78
+ const newParams = new URLSearchParams(target[1]);
79
+ for (const [key, val] of newParams.entries()) {
80
+ url.searchParams.append(key, val);
81
+ }
82
+ }
74
83
  }
75
84
  const redirectUrl = decodeURIComponent(url.href);
76
85
  /** return Response redirect with http code of redirect type **/
77
86
  switch (existsRedirect.redirectType) {
78
87
  case REDIRECT_TYPE_301:
79
- return NextResponse.redirect(redirectUrl, 301);
88
+ return NextResponse.redirect(redirectUrl, {
89
+ status: 301,
90
+ statusText: 'Moved Permanently',
91
+ headers: res === null || res === void 0 ? void 0 : res.headers,
92
+ });
80
93
  case REDIRECT_TYPE_302:
81
- return NextResponse.redirect(redirectUrl, 302);
94
+ return NextResponse.redirect(redirectUrl, {
95
+ status: 302,
96
+ statusText: 'Found',
97
+ headers: res === null || res === void 0 ? void 0 : res.headers,
98
+ });
82
99
  case REDIRECT_TYPE_SERVER_TRANSFER:
83
- return NextResponse.rewrite(redirectUrl);
100
+ return NextResponse.rewrite(redirectUrl, res);
84
101
  default:
85
- return NextResponse.next();
102
+ return res || NextResponse.next();
86
103
  }
87
104
  });
88
105
  const response = yield createResponse();
89
- // Share site name with the following executed middlewares
90
- // Don't need to set when middleware is disabled
91
- site && response.cookies.set(this.SITE_SYMBOL, site.name);
92
106
  debug.redirects('redirects middleware end in %dms: %o', Date.now() - startTimestamp, {
93
107
  redirected: response.redirected,
94
108
  status: response.status,
@@ -130,13 +144,17 @@ export class RedirectsMiddleware extends MiddlewareBase {
130
144
  const redirects = yield this.redirectsService.fetchRedirects(siteName);
131
145
  const tragetURL = req.nextUrl.pathname;
132
146
  const targetQS = req.nextUrl.search || '';
133
- return redirects.length
134
- ? redirects.find((redirect) => {
147
+ const language = this.getLanguage(req);
148
+ const modifyRedirects = structuredClone(redirects);
149
+ return modifyRedirects.length
150
+ ? modifyRedirects.find((redirect) => {
151
+ redirect.pattern = redirect.pattern.replace(RegExp(`^[^]?/${language}/`, 'gi'), '');
135
152
  redirect.pattern = `/^\/${redirect.pattern
136
153
  .replace(/^\/|\/$/g, '')
137
154
  .replace(/^\^\/|\/\$$/g, '')
138
155
  .replace(/^\^|\$$/g, '')
139
- .replace(/\$\/gi$/g, '')}$/gi`;
156
+ .replace(/(?<!\\)\?/g, '\\?')
157
+ .replace(/\$\/gi$/g, '')}[\/]?$/gi`;
140
158
  return ((regexParser(redirect.pattern).test(tragetURL) ||
141
159
  regexParser(redirect.pattern).test(`${tragetURL}${targetQS}`) ||
142
160
  regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${tragetURL}`) ||
@@ -0,0 +1 @@
1
+ export { RevalidateMiddleware } from './revalidate-middleware';
@@ -0,0 +1,212 @@
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 { I18NConfig } from 'next/dist/server/config-shared';
11
+ import { GraphQLPersonalizeService, getPersonalizedRewrite, } from '@sitecore-jss/sitecore-jss/personalize';
12
+ import { getSiteRewrite } from '@sitecore-jss/sitecore-jss/site';
13
+ import { debug } from '@sitecore-jss/sitecore-jss';
14
+ var EntityDefinition;
15
+ (function (EntityDefinition) {
16
+ EntityDefinition["LayoutData"] = "LayoutData";
17
+ EntityDefinition["Item"] = "Item";
18
+ })(EntityDefinition || (EntityDefinition = {}));
19
+ /**
20
+ * Middleware / handler for on-demand ISR (e.g. '/api/revalidate').
21
+ */
22
+ export class RevalidateMiddleware {
23
+ constructor(config) {
24
+ this.config = config;
25
+ this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
26
+ // filter out updated paths and language from request.body
27
+ const filteredUpdates = this.getFilteredUpdates(req);
28
+ if (this.isEmpty(filteredUpdates)) {
29
+ // nothing to revalidate
30
+ return res.status(204).json({ message: 'No updates to revalidate' });
31
+ }
32
+ // extract only paths from filtered updates object
33
+ const paths = this.extractPaths(filteredUpdates);
34
+ const pathsToRevalidate = [];
35
+ // when personalization is configured and when both multiSite and personalization are configured
36
+ if (this.config.personalize) {
37
+ const personalizeInfo = yield this.getPersonalizedResults(filteredUpdates);
38
+ if (this.config.multiSite) {
39
+ this.handleMultiSitePersonalization(personalizeInfo, pathsToRevalidate, this.getPathName, this.getSiteName);
40
+ }
41
+ else {
42
+ this.handleNonMultiSitePersonalization(personalizeInfo, pathsToRevalidate, this.getPathName);
43
+ }
44
+ }
45
+ // when only multiSite is configured
46
+ if (this.config.multiSite && !this.config.personalize) {
47
+ const multiSitePaths = paths.map((path) => getSiteRewrite(this.getPathName(path), { siteName: this.getSiteName(path) }));
48
+ pathsToRevalidate.push(...multiSitePaths);
49
+ }
50
+ // when both multiSite and personalization are not configured
51
+ if (!this.config.multiSite && !this.config.personalize) {
52
+ const defaultPaths = paths.map((path) => this.getPathName(path));
53
+ pathsToRevalidate.push(...defaultPaths);
54
+ }
55
+ // when other locales are configured besides defaultLocale
56
+ if (!this.isEmpty(filteredUpdates)) {
57
+ const filteredLanguage = [...new Set(filteredUpdates.map(({ language }) => language))].join(',');
58
+ if (this.config.localePrefix) {
59
+ const language = this.config.localePrefix(filteredLanguage);
60
+ if (language) {
61
+ yield Promise.all(pathsToRevalidate.map((path) => res.revalidate(`/${language}` + path)));
62
+ }
63
+ }
64
+ }
65
+ yield Promise.all(pathsToRevalidate.map((path) => res.revalidate(path)));
66
+ debug.revalidate(`revalidated paths: ${pathsToRevalidate.join(', ')}`);
67
+ });
68
+ this.personalizeService = new GraphQLPersonalizeService({
69
+ clientFactory: config.clientFactory,
70
+ });
71
+ }
72
+ /**
73
+ * Generates a Next.js API route handler that executes a revalidation process.
74
+ * @returns The route handler function for handling Next.js API requests.
75
+ */
76
+ getHandler() {
77
+ return (req, res) => __awaiter(this, void 0, void 0, function* () {
78
+ try {
79
+ yield this.handler(req, res);
80
+ return res.status(200).json({ revalidated: true });
81
+ }
82
+ catch (error) {
83
+ console.log('Error Revalidating:');
84
+ console.log(error);
85
+ return res.status(500).json({ revalidated: false });
86
+ }
87
+ });
88
+ }
89
+ /**
90
+ * Gets personalized results for the updated paths
91
+ * @param {UpdatedPaths[]} filteredUpdates Updated paths
92
+ */
93
+ getPersonalizedResults(filteredUpdates) {
94
+ return __awaiter(this, void 0, void 0, function* () {
95
+ const personalizedResults = [];
96
+ const nonPersonalizedResults = [];
97
+ yield Promise.all(filteredUpdates.map((update) => __awaiter(this, void 0, void 0, function* () {
98
+ const siteName = this.getSiteName(update.path);
99
+ const pathName = this.getPathName(update.path);
100
+ const personalizeInfo = yield this.personalizeService.getPersonalizeInfo(pathName, update.language, siteName);
101
+ if (personalizeInfo && personalizeInfo.variantIds.length > 0) {
102
+ personalizeInfo.variantIds.forEach((variantId) => {
103
+ personalizedResults.push({
104
+ path: update.path,
105
+ variantId,
106
+ });
107
+ });
108
+ }
109
+ else {
110
+ // Collect paths without personalized info
111
+ nonPersonalizedResults.push({
112
+ path: update.path,
113
+ });
114
+ }
115
+ })));
116
+ return {
117
+ personalized: personalizedResults,
118
+ nonPersonalized: nonPersonalizedResults,
119
+ };
120
+ });
121
+ }
122
+ isEmpty(data) {
123
+ return data.length === 0;
124
+ }
125
+ /**
126
+ * Extracts the paths from the updated paths
127
+ * @param {UpdatedPaths[]} filteredUpdates Updated paths
128
+ * @returns {string[]} paths
129
+ */
130
+ extractPaths(filteredUpdates) {
131
+ return filteredUpdates.map((update) => update.path);
132
+ }
133
+ /**
134
+ * Gets the site name from the path name
135
+ * @param {string} pathname Path name
136
+ * @returns {string} site name
137
+ */
138
+ getSiteName(pathname) {
139
+ let siteName = '';
140
+ const path = pathname.endsWith('/') ? pathname : pathname + '/';
141
+ const result = path.match('(.*?)\\/');
142
+ if (result && result[1] !== '') {
143
+ siteName = result[1];
144
+ }
145
+ return siteName;
146
+ }
147
+ /**
148
+ * Gets the path name from the full path
149
+ * @param {string} fullPath Full path
150
+ * @returns {string} path name
151
+ */
152
+ getPathName(fullPath) {
153
+ const pathParts = fullPath.split('/').filter((part) => part !== '');
154
+ if (pathParts.length >= 2) {
155
+ const siteName = `/${pathParts[0]}/`;
156
+ const path = `/${pathParts.slice(1).join('/')}`;
157
+ return path.startsWith(siteName) ? path.slice(siteName.length) : path;
158
+ }
159
+ return '/';
160
+ }
161
+ extractSiteName(path) {
162
+ const siteName = path.split('/')[0];
163
+ return siteName;
164
+ }
165
+ /**
166
+ * Filters out the updated paths and language from the request body
167
+ * @param {NextApiRequest} req Next.js API request
168
+ * @returns {UpdatedPaths[]} updated paths
169
+ */
170
+ getFilteredUpdates(req) {
171
+ var _a, _b;
172
+ if (!((_a = req.body) === null || _a === void 0 ? void 0 : _a.updates) || this.isEmpty(req.body.updates)) {
173
+ return [];
174
+ }
175
+ return (_b = req.body) === null || _b === void 0 ? void 0 : _b.updates.filter((update) => update.entity_definition === EntityDefinition.LayoutData && update.entity_culture).map((update) => {
176
+ if (update.identifier === 'website/') {
177
+ return null;
178
+ }
179
+ return {
180
+ path: update.identifier,
181
+ language: update.entity_culture,
182
+ };
183
+ }).filter(Boolean);
184
+ }
185
+ handleMultiSitePersonalization(personalizeInfo, pathsToRevalidate, getPathName, getSiteName) {
186
+ if (personalizeInfo.personalized.length > 0) {
187
+ const personalizedRewrite = personalizeInfo.personalized.map((info) => {
188
+ return getPersonalizedRewrite(getSiteRewrite(getPathName(info.path), { siteName: getSiteName(info.path) }), {
189
+ variantId: info.variantId,
190
+ });
191
+ });
192
+ pathsToRevalidate.push(...personalizedRewrite);
193
+ }
194
+ if (personalizeInfo.nonPersonalized.length > 0) {
195
+ const nonPersonalizedRewrite = personalizeInfo.nonPersonalized.map((info) => {
196
+ return getSiteRewrite(getPathName(info.path), {
197
+ siteName: getSiteName(info.path),
198
+ });
199
+ });
200
+ pathsToRevalidate.push(...nonPersonalizedRewrite);
201
+ }
202
+ }
203
+ handleNonMultiSitePersonalization(personalizeInfo, pathsToRevalidate, getPathName) {
204
+ const nonMultiSitePersonalizedRewrite = personalizeInfo.personalized.map((info) => {
205
+ return getPersonalizedRewrite(getPathName(info.path), { variantId: info.variantId });
206
+ });
207
+ const nonMultiSiteNonPersonalizedRewrite = personalizeInfo.nonPersonalized.map((info) => {
208
+ return this.getPathName(info.path);
209
+ });
210
+ pathsToRevalidate.push(...nonMultiSitePersonalizedRewrite, ...nonMultiSiteNonPersonalizedRewrite);
211
+ }
212
+ }