@sitecore-jss/sitecore-jss-nextjs 21.1.0-canary.64 → 21.1.0-canary.66

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/dist/cjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useSitecoreContext = exports.withSitecoreContext = exports.SitecoreContextReactContext = exports.SitecoreContext = exports.VisitorIdentification = exports.File = exports.DateField = exports.Text = exports.Image = exports.NextImage = exports.EditingComponentPlaceholder = exports.Placeholder = exports.RichText = exports.Link = exports.getPublicUrl = exports.handleEditorFastRefresh = exports.useComponentProps = exports.ComponentPropsContext = exports.ComponentPropsReactContext = exports.GraphQLErrorPagesService = exports.GraphQLSitemapXmlService = exports.GraphQLSitemapService = exports.DisconnectedSitemapService = exports.ComponentPropsService = exports.GraphQLRequestClient = exports.GraphQLRobotsService = exports.CdpHelper = exports.normalizePersonalizedRewrite = exports.getPersonalizedRewriteData = exports.getPersonalizedRewrite = exports.personalizeLayout = exports.RestDictionaryService = exports.GraphQLDictionaryService = exports.trackingApi = exports.mediaApi = exports.EDITING_COMPONENT_ID = exports.EDITING_COMPONENT_PLACEHOLDER = exports.RenderingType = exports.getFieldValue = exports.getChildPlaceholder = exports.RestLayoutService = exports.GraphQLLayoutService = exports.LayoutServicePageState = exports.resolveUrl = exports.resetEditorChromes = exports.isEditorActive = exports.enableDebug = exports.NativeDataFetcher = exports.AxiosDataFetcher = exports.constants = void 0;
4
- exports.withDatasourceCheck = exports.withPlaceholder = exports.withEditorChromes = void 0;
3
+ exports.File = exports.DateField = exports.Text = exports.Image = exports.NextImage = exports.EditingComponentPlaceholder = exports.Placeholder = exports.RichText = exports.Link = exports.getPublicUrl = exports.handleEditorFastRefresh = exports.useComponentProps = exports.ComponentPropsContext = exports.ComponentPropsReactContext = exports.normalizeSiteRewrite = exports.getSiteRewriteData = exports.getSiteRewrite = exports.GraphQLSiteInfoService = exports.SiteResolver = exports.GraphQLRobotsService = exports.GraphQLErrorPagesService = exports.GraphQLSitemapXmlService = exports.GraphQLSitemapService = exports.DisconnectedSitemapService = exports.ComponentPropsService = exports.GraphQLRequestClient = exports.CdpHelper = exports.normalizePersonalizedRewrite = exports.getPersonalizedRewriteData = exports.getPersonalizedRewrite = exports.personalizeLayout = exports.RestDictionaryService = exports.GraphQLDictionaryService = exports.trackingApi = exports.mediaApi = exports.EDITING_COMPONENT_ID = exports.EDITING_COMPONENT_PLACEHOLDER = exports.RenderingType = exports.getFieldValue = exports.getChildPlaceholder = exports.RestLayoutService = exports.GraphQLLayoutService = exports.LayoutServicePageState = exports.resolveUrl = exports.resetEditorChromes = exports.isEditorActive = exports.enableDebug = exports.NativeDataFetcher = exports.AxiosDataFetcher = exports.constants = void 0;
4
+ exports.withDatasourceCheck = exports.withPlaceholder = exports.withEditorChromes = exports.useSitecoreContext = exports.withSitecoreContext = exports.SitecoreContextReactContext = exports.SitecoreContext = exports.VisitorIdentification = void 0;
5
5
  var sitecore_jss_1 = require("@sitecore-jss/sitecore-jss");
6
6
  Object.defineProperty(exports, "constants", { enumerable: true, get: function () { return sitecore_jss_1.constants; } });
7
7
  Object.defineProperty(exports, "AxiosDataFetcher", { enumerable: true, get: function () { return sitecore_jss_1.AxiosDataFetcher; } });
@@ -33,8 +33,6 @@ Object.defineProperty(exports, "getPersonalizedRewrite", { enumerable: true, get
33
33
  Object.defineProperty(exports, "getPersonalizedRewriteData", { enumerable: true, get: function () { return personalize_1.getPersonalizedRewriteData; } });
34
34
  Object.defineProperty(exports, "normalizePersonalizedRewrite", { enumerable: true, get: function () { return personalize_1.normalizePersonalizedRewrite; } });
35
35
  Object.defineProperty(exports, "CdpHelper", { enumerable: true, get: function () { return personalize_1.CdpHelper; } });
36
- var site_1 = require("@sitecore-jss/sitecore-jss/site");
37
- Object.defineProperty(exports, "GraphQLRobotsService", { enumerable: true, get: function () { return site_1.GraphQLRobotsService; } });
38
36
  var sitecore_jss_2 = require("@sitecore-jss/sitecore-jss");
39
37
  Object.defineProperty(exports, "GraphQLRequestClient", { enumerable: true, get: function () { return sitecore_jss_2.GraphQLRequestClient; } });
40
38
  var component_props_service_1 = require("./services/component-props-service");
@@ -43,9 +41,15 @@ var disconnected_sitemap_service_1 = require("./services/disconnected-sitemap-se
43
41
  Object.defineProperty(exports, "DisconnectedSitemapService", { enumerable: true, get: function () { return disconnected_sitemap_service_1.DisconnectedSitemapService; } });
44
42
  var graphql_sitemap_service_1 = require("./services/graphql-sitemap-service");
45
43
  Object.defineProperty(exports, "GraphQLSitemapService", { enumerable: true, get: function () { return graphql_sitemap_service_1.GraphQLSitemapService; } });
46
- var site_2 = require("@sitecore-jss/sitecore-jss/site");
47
- Object.defineProperty(exports, "GraphQLSitemapXmlService", { enumerable: true, get: function () { return site_2.GraphQLSitemapXmlService; } });
48
- Object.defineProperty(exports, "GraphQLErrorPagesService", { enumerable: true, get: function () { return site_2.GraphQLErrorPagesService; } });
44
+ var site_1 = require("@sitecore-jss/sitecore-jss/site");
45
+ Object.defineProperty(exports, "GraphQLSitemapXmlService", { enumerable: true, get: function () { return site_1.GraphQLSitemapXmlService; } });
46
+ Object.defineProperty(exports, "GraphQLErrorPagesService", { enumerable: true, get: function () { return site_1.GraphQLErrorPagesService; } });
47
+ Object.defineProperty(exports, "GraphQLRobotsService", { enumerable: true, get: function () { return site_1.GraphQLRobotsService; } });
48
+ Object.defineProperty(exports, "SiteResolver", { enumerable: true, get: function () { return site_1.SiteResolver; } });
49
+ Object.defineProperty(exports, "GraphQLSiteInfoService", { enumerable: true, get: function () { return site_1.GraphQLSiteInfoService; } });
50
+ Object.defineProperty(exports, "getSiteRewrite", { enumerable: true, get: function () { return site_1.getSiteRewrite; } });
51
+ Object.defineProperty(exports, "getSiteRewriteData", { enumerable: true, get: function () { return site_1.getSiteRewriteData; } });
52
+ Object.defineProperty(exports, "normalizeSiteRewrite", { enumerable: true, get: function () { return site_1.normalizeSiteRewrite; } });
49
53
  var ComponentPropsContext_1 = require("./components/ComponentPropsContext");
50
54
  Object.defineProperty(exports, "ComponentPropsReactContext", { enumerable: true, get: function () { return ComponentPropsContext_1.ComponentPropsReactContext; } });
51
55
  Object.defineProperty(exports, "ComponentPropsContext", { enumerable: true, get: function () { return ComponentPropsContext_1.ComponentPropsContext; } });
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PersonalizeMiddleware = exports.RedirectsMiddleware = void 0;
3
+ exports.SiteResolver = exports.MultisiteMiddleware = exports.PersonalizeMiddleware = exports.RedirectsMiddleware = void 0;
4
4
  var redirects_middleware_1 = require("./redirects-middleware");
5
5
  Object.defineProperty(exports, "RedirectsMiddleware", { enumerable: true, get: function () { return redirects_middleware_1.RedirectsMiddleware; } });
6
6
  var personalize_middleware_1 = require("./personalize-middleware");
7
7
  Object.defineProperty(exports, "PersonalizeMiddleware", { enumerable: true, get: function () { return personalize_middleware_1.PersonalizeMiddleware; } });
8
+ var multisite_middleware_1 = require("./multisite-middleware");
9
+ Object.defineProperty(exports, "MultisiteMiddleware", { enumerable: true, get: function () { return multisite_middleware_1.MultisiteMiddleware; } });
10
+ var site_1 = require("@sitecore-jss/sitecore-jss/site");
11
+ Object.defineProperty(exports, "SiteResolver", { enumerable: true, get: function () { return site_1.SiteResolver; } });
@@ -0,0 +1,102 @@
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-jss/sitecore-jss/site");
15
+ const sitecore_jss_1 = require("@sitecore-jss/sitecore-jss");
16
+ /**
17
+ * Middleware / handler for multisite support
18
+ */
19
+ class MultisiteMiddleware {
20
+ /**
21
+ * @param {MultisiteMiddlewareConfig} [config] Multisite middleware config
22
+ */
23
+ constructor(config) {
24
+ this.config = config;
25
+ this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
26
+ var _a;
27
+ const pathname = req.nextUrl.pathname;
28
+ const hostHeader = (_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0];
29
+ const hostname = hostHeader || this.defaultHostname;
30
+ sitecore_jss_1.debug.multisite('multisite middleware start: %o', {
31
+ pathname,
32
+ hostname,
33
+ });
34
+ if (!hostHeader) {
35
+ sitecore_jss_1.debug.multisite(`host header is missing, default ${hostname} is used`);
36
+ }
37
+ // Response will be provided if other middleware is run before us
38
+ let response = res || server_1.NextResponse.next();
39
+ if (this.excludeRoute(pathname) ||
40
+ (this.config.excludeRoute && this.config.excludeRoute(pathname))) {
41
+ sitecore_jss_1.debug.multisite('skipped (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('sc_site') ||
46
+ req.cookies.get('sc_site') ||
47
+ this.config.getSite(hostname).name;
48
+ // Rewrite to site specific path
49
+ const rewritePath = site_1.getSiteRewrite(pathname, {
50
+ siteName,
51
+ });
52
+ // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
53
+ const rewriteUrl = req.nextUrl.clone();
54
+ rewriteUrl.pathname = rewritePath;
55
+ response = server_1.NextResponse.rewrite(rewriteUrl);
56
+ // Share site name with the following executed middlewares
57
+ response.cookies.set('sc_site', siteName);
58
+ // Share rewrite path with following executed middlewares
59
+ response.headers.set('x-sc-rewrite', rewritePath);
60
+ sitecore_jss_1.debug.multisite('multisite middleware end: %o', {
61
+ rewritePath,
62
+ siteName,
63
+ headers: this.extractDebugHeaders(response.headers),
64
+ cookies: response.cookies,
65
+ });
66
+ return response;
67
+ });
68
+ this.defaultHostname = config.defaultHostname || 'localhost';
69
+ }
70
+ /**
71
+ * Gets the Next.js middleware handler with error handling
72
+ * @returns middleware handler
73
+ */
74
+ getHandler() {
75
+ return (req, res) => __awaiter(this, void 0, void 0, function* () {
76
+ try {
77
+ return yield this.handler(req, res);
78
+ }
79
+ catch (error) {
80
+ console.log('Multisite middleware failed:');
81
+ console.log(error);
82
+ return res || server_1.NextResponse.next();
83
+ }
84
+ });
85
+ }
86
+ excludeRoute(pathname) {
87
+ if (pathname.includes('.') || // Ignore files
88
+ pathname.startsWith('/api/') || // Ignore Next.js API calls
89
+ pathname.startsWith('/sitecore/') || // Ignore Sitecore API calls
90
+ pathname.startsWith('/_next') // Ignore next service calls
91
+ ) {
92
+ return true;
93
+ }
94
+ return false;
95
+ }
96
+ extractDebugHeaders(incomingHeaders) {
97
+ const headers = {};
98
+ incomingHeaders.forEach((value, key) => (headers[key] = value));
99
+ return headers;
100
+ }
101
+ }
102
+ exports.MultisiteMiddleware = MultisiteMiddleware;
@@ -23,13 +23,20 @@ class PersonalizeMiddleware {
23
23
  constructor(config) {
24
24
  this.config = config;
25
25
  this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
26
+ var _a;
27
+ const hostHeader = (_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0];
28
+ const hostname = hostHeader || this.defaultHostname;
26
29
  const pathname = req.nextUrl.pathname;
27
30
  const language = req.nextUrl.locale || req.nextUrl.defaultLocale || 'en';
31
+ const siteName = (res === null || res === void 0 ? void 0 : res.cookies.get('sc_site')) || this.config.getSite(hostname).name;
28
32
  let browserId = this.getBrowserId(req);
29
33
  sitecore_jss_1.debug.personalize('personalize middleware start: %o', {
30
34
  pathname,
31
35
  language,
32
36
  });
37
+ if (!hostHeader) {
38
+ sitecore_jss_1.debug.personalize(`host header is missing, default ${hostname} is used`);
39
+ }
33
40
  // Response will be provided if other middleware is run before us (e.g. redirects)
34
41
  let response = res || server_1.NextResponse.next();
35
42
  if (this.config.disabled && this.config.disabled(req, response)) {
@@ -44,7 +51,7 @@ class PersonalizeMiddleware {
44
51
  return response;
45
52
  }
46
53
  // Get personalization info from Experience Edge
47
- const personalizeInfo = yield this.personalizeService.getPersonalizeInfo(pathname, language);
54
+ const personalizeInfo = yield this.personalizeService.getPersonalizeInfo(pathname, language, siteName);
48
55
  if (!personalizeInfo) {
49
56
  // Likely an invalid route / language
50
57
  sitecore_jss_1.debug.personalize('skipped (personalize info not found)');
@@ -74,8 +81,10 @@ class PersonalizeMiddleware {
74
81
  sitecore_jss_1.debug.personalize('skipped (invalid variant)');
75
82
  return response;
76
83
  }
84
+ // Path can be rewritten by previously executed middleware
85
+ const basePath = (res === null || res === void 0 ? void 0 : res.headers.get('x-sc-rewrite')) || pathname;
77
86
  // Rewrite to persononalized path
78
- const rewritePath = personalize_1.getPersonalizedRewrite(pathname, { variantId });
87
+ const rewritePath = personalize_1.getPersonalizedRewrite(basePath, { variantId });
79
88
  // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
80
89
  const rewriteUrl = req.nextUrl.clone();
81
90
  rewriteUrl.pathname = rewritePath;
@@ -83,8 +92,12 @@ class PersonalizeMiddleware {
83
92
  // Disable preflight caching to force revalidation on client-side navigation (personalization may be influenced)
84
93
  // See https://github.com/vercel/next.js/issues/32727
85
94
  response.headers.set('x-middleware-cache', 'no-cache');
95
+ // Share rewrite path with following executed middlewares
96
+ response.headers.set('x-sc-rewrite', rewritePath);
86
97
  // Set browserId cookie on the response
87
98
  this.setBrowserId(response, browserId);
99
+ // Share site name with the following executed middlewares
100
+ response.cookies.set('sc_site', siteName);
88
101
  sitecore_jss_1.debug.personalize('personalize middleware end: %o', {
89
102
  rewritePath,
90
103
  browserId,
@@ -104,6 +117,7 @@ class PersonalizeMiddleware {
104
117
  });
105
118
  return (url, data) => fetcher.fetch(url, data);
106
119
  } }));
120
+ this.defaultHostname = config.defaultHostname || 'localhost';
107
121
  }
108
122
  /**
109
123
  * Gets the Next.js middleware handler with error handling
@@ -26,51 +26,60 @@ class RedirectsMiddleware {
26
26
  */
27
27
  constructor(config) {
28
28
  this.config = config;
29
- this.handler = (req) => __awaiter(this, void 0, void 0, function* () {
30
- if ((this.config.disabled && this.config.disabled(req, server_1.NextResponse.next())) ||
31
- this.excludeRoute(req.nextUrl.pathname) ||
32
- (this.config.excludeRoute && this.config.excludeRoute(req.nextUrl.pathname))) {
33
- return server_1.NextResponse.next();
34
- }
35
- // Find the redirect from result of RedirectService
36
- const existsRedirect = yield this.getExistsRedirect(req);
37
- if (!existsRedirect) {
38
- return server_1.NextResponse.next();
39
- }
40
- const url = req.nextUrl.clone();
41
- const absoluteUrlRegex = new RegExp('^(?:[a-z]+:)?//', 'i');
42
- if (absoluteUrlRegex.test(existsRedirect.target)) {
43
- url.href = existsRedirect.target;
44
- url.locale = req.nextUrl.locale;
45
- }
46
- else {
47
- url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
48
- const urlFirstPart = existsRedirect.target.split('/')[1];
49
- if (this.locales.includes(urlFirstPart)) {
50
- url.locale = urlFirstPart;
51
- url.pathname = existsRedirect.target.replace(`/${urlFirstPart}`, '');
29
+ this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
30
+ const hostname = this.getHostname(req);
31
+ const siteName = (res === null || res === void 0 ? void 0 : res.cookies.get('sc_site')) || this.config.getSite(hostname).name;
32
+ const createResponse = () => __awaiter(this, void 0, void 0, function* () {
33
+ if ((this.config.disabled && this.config.disabled(req, server_1.NextResponse.next())) ||
34
+ this.excludeRoute(req.nextUrl.pathname) ||
35
+ (this.config.excludeRoute && this.config.excludeRoute(req.nextUrl.pathname))) {
36
+ return server_1.NextResponse.next();
37
+ }
38
+ // Find the redirect from result of RedirectService
39
+ const existsRedirect = yield this.getExistsRedirect(req, siteName);
40
+ if (!existsRedirect) {
41
+ return server_1.NextResponse.next();
42
+ }
43
+ const url = req.nextUrl.clone();
44
+ const absoluteUrlRegex = new RegExp('^(?:[a-z]+:)?//', 'i');
45
+ if (absoluteUrlRegex.test(existsRedirect.target)) {
46
+ url.href = existsRedirect.target;
47
+ url.locale = req.nextUrl.locale;
52
48
  }
53
49
  else {
54
- url.pathname = existsRedirect.target;
50
+ url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
51
+ const urlFirstPart = existsRedirect.target.split('/')[1];
52
+ if (this.locales.includes(urlFirstPart)) {
53
+ url.locale = urlFirstPart;
54
+ url.pathname = existsRedirect.target.replace(`/${urlFirstPart}`, '');
55
+ }
56
+ else {
57
+ url.pathname = existsRedirect.target;
58
+ }
55
59
  }
56
- }
57
- const redirectUrl = decodeURIComponent(url.href);
58
- /** return Response redirect with http code of redirect type **/
59
- switch (existsRedirect.redirectType) {
60
- case site_1.REDIRECT_TYPE_301:
61
- return server_1.NextResponse.redirect(redirectUrl, 301);
62
- case site_1.REDIRECT_TYPE_302:
63
- return server_1.NextResponse.redirect(redirectUrl, 302);
64
- case site_1.REDIRECT_TYPE_SERVER_TRANSFER:
65
- return server_1.NextResponse.rewrite(redirectUrl);
66
- default:
67
- return server_1.NextResponse.next();
68
- }
60
+ const redirectUrl = decodeURIComponent(url.href);
61
+ /** return Response redirect with http code of redirect type **/
62
+ switch (existsRedirect.redirectType) {
63
+ case site_1.REDIRECT_TYPE_301:
64
+ return server_1.NextResponse.redirect(redirectUrl, 301);
65
+ case site_1.REDIRECT_TYPE_302:
66
+ return server_1.NextResponse.redirect(redirectUrl, 302);
67
+ case site_1.REDIRECT_TYPE_SERVER_TRANSFER:
68
+ return server_1.NextResponse.rewrite(redirectUrl);
69
+ default:
70
+ return server_1.NextResponse.next();
71
+ }
72
+ });
73
+ const response = yield createResponse();
74
+ // Share site name with the following executed middlewares
75
+ response.cookies.set('sc_site', siteName);
76
+ return response;
69
77
  });
70
78
  // NOTE: we provide native fetch for compatibility on Next.js Edge Runtime
71
79
  // (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
72
80
  this.redirectsService = new site_1.GraphQLRedirectsService(Object.assign(Object.assign({}, config), { fetch: fetch }));
73
81
  this.locales = config.locales;
82
+ this.defaultHostname = config.defaultHostname || 'localhost';
74
83
  }
75
84
  /**
76
85
  * Gets the Next.js API route handler
@@ -89,15 +98,21 @@ class RedirectsMiddleware {
89
98
  }
90
99
  return false;
91
100
  }
101
+ getHostname(req) {
102
+ var _a;
103
+ const hostHeader = (_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0];
104
+ return hostHeader || this.defaultHostname;
105
+ }
92
106
  /**
93
107
  * Method returns RedirectInfo when matches
94
- * @param {NextRequest} req
108
+ * @param {NextRequest} req request
109
+ * @param {string} siteName site name
95
110
  * @returns Promise<RedirectInfo | undefined>
96
111
  * @private
97
112
  */
98
- getExistsRedirect(req) {
113
+ getExistsRedirect(req, siteName) {
99
114
  return __awaiter(this, void 0, void 0, function* () {
100
- const redirects = yield this.redirectsService.fetchRedirects();
115
+ const redirects = yield this.redirectsService.fetchRedirects(siteName);
101
116
  return redirects.length
102
117
  ? redirects.find((redirect) => {
103
118
  return ((regex_parser_1.default(redirect.pattern.toLowerCase()).test(req.nextUrl.pathname.toLowerCase()) ||
@@ -9,12 +9,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.GraphQLSitemapService = exports.getSiteEmptyError = exports.languageError = void 0;
12
+ exports.GraphQLSitemapService = exports.getSiteEmptyError = exports.sitesError = exports.languageError = void 0;
13
13
  const graphql_1 = require("@sitecore-jss/sitecore-jss/graphql");
14
14
  const sitecore_jss_1 = require("@sitecore-jss/sitecore-jss");
15
15
  const personalize_1 = require("@sitecore-jss/sitecore-jss/personalize");
16
+ const site_1 = require("@sitecore-jss/sitecore-jss/site");
16
17
  /** @private */
17
18
  exports.languageError = 'The list of languages cannot be empty';
19
+ exports.sitesError = 'The list of sites cannot be empty';
18
20
  /**
19
21
  * @param {string} siteName to inject into error text
20
22
  * @private
@@ -53,7 +55,7 @@ query ${usesPersonalize ? 'PersonalizeSitemapQuery' : 'DefaultSitemapQuery'}(
53
55
  hasNext
54
56
  }
55
57
  results {
56
- path: routePath
58
+ path: routePath
57
59
  ${usesPersonalize
58
60
  ? `
59
61
  route {
@@ -66,7 +68,7 @@ query ${usesPersonalize ? 'PersonalizeSitemapQuery' : 'DefaultSitemapQuery'}(
66
68
  }
67
69
  }
68
70
  }
69
- }
71
+ }
70
72
  `;
71
73
  /**
72
74
  * Service that fetches the list of site pages using Sitecore's GraphQL API.
@@ -132,43 +134,64 @@ class GraphQLSitemapService {
132
134
  */
133
135
  fetchSitemap(languages, formatStaticPath) {
134
136
  return __awaiter(this, void 0, void 0, function* () {
137
+ const paths = new Array();
135
138
  if (!languages.length) {
136
139
  throw new RangeError(exports.languageError);
137
140
  }
138
- // Fetch paths using all locales
139
- const paths = yield Promise.all(languages.map((language) => {
140
- if (language === '') {
141
- throw new RangeError(languageEmptyError);
142
- }
143
- sitecore_jss_1.debug.sitemap('fetching sitemap data for %s', language);
144
- return this.fetchLanguageSitePaths(language).then((results) => this.transformLanguageSitePaths(results, formatStaticPath, language));
145
- }));
146
- // merge promises results into single result
141
+ // Get all sites
142
+ const sites = this.options.sites;
143
+ if (!sites || !sites.length) {
144
+ throw new RangeError(exports.sitesError);
145
+ }
146
+ // Fetch paths for each site
147
+ for (let i = 0; i < sites.length; i++) {
148
+ const siteName = sites[i];
149
+ const multiSiteName = sites.length > 1 ? siteName : undefined;
150
+ // Fetch paths using all locales
151
+ yield Promise.all(languages.map((language) => __awaiter(this, void 0, void 0, function* () {
152
+ if (language === '') {
153
+ throw new RangeError(languageEmptyError);
154
+ }
155
+ sitecore_jss_1.debug.sitemap('fetching sitemap data for %s %s', language, siteName);
156
+ const results = yield this.fetchLanguageSitePaths(language, siteName);
157
+ const transformedPaths = yield this.transformLanguageSitePaths(results, formatStaticPath, language, multiSiteName);
158
+ paths.push(...transformedPaths);
159
+ })));
160
+ }
147
161
  return [].concat(...paths);
148
162
  });
149
163
  }
150
- transformLanguageSitePaths(sitePaths, formatStaticPath, language) {
164
+ transformLanguageSitePaths(sitePaths, formatStaticPath, language, multiSiteName) {
151
165
  return __awaiter(this, void 0, void 0, function* () {
152
166
  const formatPath = (path) => formatStaticPath(path.replace(/^\/|\/$/g, '').split('/'), language);
153
167
  const aggregatedPaths = [];
154
168
  sitePaths.forEach((item) => {
155
- var _a, _b, _c, _d;
169
+ var _a, _b, _c, _d, _e, _f;
156
170
  if (!item)
157
171
  return;
158
- aggregatedPaths.push(formatPath(item.path));
172
+ if (!multiSiteName) {
173
+ aggregatedPaths.push(formatPath(item.path));
174
+ }
175
+ else {
176
+ aggregatedPaths.push(formatPath(site_1.getSiteRewrite(item.path, { siteName: multiSiteName })));
177
+ }
159
178
  // check for type safety's sake - personalize may be empty depending on query type
160
179
  if ((_b = (_a = item.route) === null || _a === void 0 ? void 0 : _a.personalization) === null || _b === void 0 ? void 0 : _b.variantIds.length) {
161
- aggregatedPaths.push(...(_d = (_c = item.route) === null || _c === void 0 ? void 0 : _c.personalization) === null || _d === void 0 ? void 0 : _d.variantIds.map((varId) => formatPath(personalize_1.getPersonalizedRewrite(item.path, { variantId: varId }))));
180
+ multiSiteName
181
+ ? aggregatedPaths.push(...(_d = (_c = item.route) === null || _c === void 0 ? void 0 : _c.personalization) === null || _d === void 0 ? void 0 : _d.variantIds.map((varId) => formatPath(personalize_1.getPersonalizedRewrite(site_1.getSiteRewrite(item.path, { siteName: multiSiteName }), {
182
+ variantId: varId,
183
+ }))))
184
+ : aggregatedPaths.push(...(_f = (_e = item.route) === null || _e === void 0 ? void 0 : _e.personalization) === null || _f === void 0 ? void 0 : _f.variantIds.map((varId) => formatPath(personalize_1.getPersonalizedRewrite(item.path, { variantId: varId }))));
162
185
  }
163
186
  });
164
187
  return aggregatedPaths;
165
188
  });
166
189
  }
167
- fetchLanguageSitePaths(language) {
190
+ fetchLanguageSitePaths(language, siteName) {
168
191
  var _a, _b, _c, _d;
169
192
  return __awaiter(this, void 0, void 0, function* () {
170
193
  const args = {
171
- siteName: this.options.siteName,
194
+ siteName: siteName,
172
195
  language: language,
173
196
  pageSize: this.options.pageSize,
174
197
  includedPaths: this.options.includedPaths,
@@ -180,7 +203,7 @@ class GraphQLSitemapService {
180
203
  while (hasNext) {
181
204
  const fetchResponse = yield this.graphQLClient.request(this.query, Object.assign(Object.assign({}, args), { after }));
182
205
  if (!((_a = fetchResponse === null || fetchResponse === void 0 ? void 0 : fetchResponse.site) === null || _a === void 0 ? void 0 : _a.siteInfo)) {
183
- throw new RangeError(getSiteEmptyError(this.options.siteName));
206
+ throw new RangeError(getSiteEmptyError(siteName));
184
207
  }
185
208
  else {
186
209
  results = results.concat((_b = fetchResponse.site.siteInfo.routes) === null || _b === void 0 ? void 0 : _b.results);
package/dist/esm/index.js CHANGED
@@ -5,12 +5,11 @@ export { mediaApi } from '@sitecore-jss/sitecore-jss/media';
5
5
  export { trackingApi, } from '@sitecore-jss/sitecore-jss/tracking';
6
6
  export { GraphQLDictionaryService, RestDictionaryService, } from '@sitecore-jss/sitecore-jss/i18n';
7
7
  export { personalizeLayout, getPersonalizedRewrite, getPersonalizedRewriteData, normalizePersonalizedRewrite, CdpHelper, } from '@sitecore-jss/sitecore-jss/personalize';
8
- export { GraphQLRobotsService, } from '@sitecore-jss/sitecore-jss/site';
9
8
  export { GraphQLRequestClient } from '@sitecore-jss/sitecore-jss';
10
9
  export { ComponentPropsService } from './services/component-props-service';
11
10
  export { DisconnectedSitemapService } from './services/disconnected-sitemap-service';
12
11
  export { GraphQLSitemapService, } from './services/graphql-sitemap-service';
13
- export { GraphQLSitemapXmlService, GraphQLErrorPagesService, } from '@sitecore-jss/sitecore-jss/site';
12
+ export { GraphQLSitemapXmlService, GraphQLErrorPagesService, GraphQLRobotsService, SiteResolver, GraphQLSiteInfoService, getSiteRewrite, getSiteRewriteData, normalizeSiteRewrite, } from '@sitecore-jss/sitecore-jss/site';
14
13
  export { ComponentPropsReactContext, ComponentPropsContext, useComponentProps, } from './components/ComponentPropsContext';
15
14
  export { handleEditorFastRefresh, getPublicUrl } from './utils';
16
15
  export { Link } from './components/Link';
@@ -1,2 +1,4 @@
1
1
  export { RedirectsMiddleware } from './redirects-middleware';
2
2
  export { PersonalizeMiddleware } from './personalize-middleware';
3
+ export { MultisiteMiddleware } from './multisite-middleware';
4
+ export { SiteResolver } from '@sitecore-jss/sitecore-jss/site';
@@ -0,0 +1,98 @@
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 { NextResponse } from 'next/server';
11
+ import { getSiteRewrite } from '@sitecore-jss/sitecore-jss/site';
12
+ import { debug } from '@sitecore-jss/sitecore-jss';
13
+ /**
14
+ * Middleware / handler for multisite support
15
+ */
16
+ export class MultisiteMiddleware {
17
+ /**
18
+ * @param {MultisiteMiddlewareConfig} [config] Multisite middleware config
19
+ */
20
+ constructor(config) {
21
+ this.config = config;
22
+ this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
23
+ var _a;
24
+ const pathname = req.nextUrl.pathname;
25
+ const hostHeader = (_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0];
26
+ const hostname = hostHeader || this.defaultHostname;
27
+ debug.multisite('multisite middleware start: %o', {
28
+ pathname,
29
+ hostname,
30
+ });
31
+ if (!hostHeader) {
32
+ debug.multisite(`host header is missing, default ${hostname} is used`);
33
+ }
34
+ // Response will be provided if other middleware is run before us
35
+ let response = res || NextResponse.next();
36
+ if (this.excludeRoute(pathname) ||
37
+ (this.config.excludeRoute && this.config.excludeRoute(pathname))) {
38
+ debug.multisite('skipped (route excluded)');
39
+ return response;
40
+ }
41
+ // Site name can be forced by query string parameter or cookie
42
+ const siteName = req.nextUrl.searchParams.get('sc_site') ||
43
+ req.cookies.get('sc_site') ||
44
+ this.config.getSite(hostname).name;
45
+ // Rewrite to site specific path
46
+ const rewritePath = getSiteRewrite(pathname, {
47
+ siteName,
48
+ });
49
+ // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
50
+ const rewriteUrl = req.nextUrl.clone();
51
+ rewriteUrl.pathname = rewritePath;
52
+ response = NextResponse.rewrite(rewriteUrl);
53
+ // Share site name with the following executed middlewares
54
+ response.cookies.set('sc_site', siteName);
55
+ // Share rewrite path with following executed middlewares
56
+ response.headers.set('x-sc-rewrite', rewritePath);
57
+ debug.multisite('multisite middleware end: %o', {
58
+ rewritePath,
59
+ siteName,
60
+ headers: this.extractDebugHeaders(response.headers),
61
+ cookies: response.cookies,
62
+ });
63
+ return response;
64
+ });
65
+ this.defaultHostname = config.defaultHostname || 'localhost';
66
+ }
67
+ /**
68
+ * Gets the Next.js middleware handler with error handling
69
+ * @returns middleware handler
70
+ */
71
+ getHandler() {
72
+ return (req, res) => __awaiter(this, void 0, void 0, function* () {
73
+ try {
74
+ return yield this.handler(req, res);
75
+ }
76
+ catch (error) {
77
+ console.log('Multisite middleware failed:');
78
+ console.log(error);
79
+ return res || NextResponse.next();
80
+ }
81
+ });
82
+ }
83
+ excludeRoute(pathname) {
84
+ if (pathname.includes('.') || // Ignore files
85
+ pathname.startsWith('/api/') || // Ignore Next.js API calls
86
+ pathname.startsWith('/sitecore/') || // Ignore Sitecore API calls
87
+ pathname.startsWith('/_next') // Ignore next service calls
88
+ ) {
89
+ return true;
90
+ }
91
+ return false;
92
+ }
93
+ extractDebugHeaders(incomingHeaders) {
94
+ const headers = {};
95
+ incomingHeaders.forEach((value, key) => (headers[key] = value));
96
+ return headers;
97
+ }
98
+ }
@@ -20,13 +20,20 @@ export class PersonalizeMiddleware {
20
20
  constructor(config) {
21
21
  this.config = config;
22
22
  this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
23
+ var _a;
24
+ const hostHeader = (_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0];
25
+ const hostname = hostHeader || this.defaultHostname;
23
26
  const pathname = req.nextUrl.pathname;
24
27
  const language = req.nextUrl.locale || req.nextUrl.defaultLocale || 'en';
28
+ const siteName = (res === null || res === void 0 ? void 0 : res.cookies.get('sc_site')) || this.config.getSite(hostname).name;
25
29
  let browserId = this.getBrowserId(req);
26
30
  debug.personalize('personalize middleware start: %o', {
27
31
  pathname,
28
32
  language,
29
33
  });
34
+ if (!hostHeader) {
35
+ debug.personalize(`host header is missing, default ${hostname} is used`);
36
+ }
30
37
  // Response will be provided if other middleware is run before us (e.g. redirects)
31
38
  let response = res || NextResponse.next();
32
39
  if (this.config.disabled && this.config.disabled(req, response)) {
@@ -41,7 +48,7 @@ export class PersonalizeMiddleware {
41
48
  return response;
42
49
  }
43
50
  // Get personalization info from Experience Edge
44
- const personalizeInfo = yield this.personalizeService.getPersonalizeInfo(pathname, language);
51
+ const personalizeInfo = yield this.personalizeService.getPersonalizeInfo(pathname, language, siteName);
45
52
  if (!personalizeInfo) {
46
53
  // Likely an invalid route / language
47
54
  debug.personalize('skipped (personalize info not found)');
@@ -71,8 +78,10 @@ export class PersonalizeMiddleware {
71
78
  debug.personalize('skipped (invalid variant)');
72
79
  return response;
73
80
  }
81
+ // Path can be rewritten by previously executed middleware
82
+ const basePath = (res === null || res === void 0 ? void 0 : res.headers.get('x-sc-rewrite')) || pathname;
74
83
  // Rewrite to persononalized path
75
- const rewritePath = getPersonalizedRewrite(pathname, { variantId });
84
+ const rewritePath = getPersonalizedRewrite(basePath, { variantId });
76
85
  // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
77
86
  const rewriteUrl = req.nextUrl.clone();
78
87
  rewriteUrl.pathname = rewritePath;
@@ -80,8 +89,12 @@ export class PersonalizeMiddleware {
80
89
  // Disable preflight caching to force revalidation on client-side navigation (personalization may be influenced)
81
90
  // See https://github.com/vercel/next.js/issues/32727
82
91
  response.headers.set('x-middleware-cache', 'no-cache');
92
+ // Share rewrite path with following executed middlewares
93
+ response.headers.set('x-sc-rewrite', rewritePath);
83
94
  // Set browserId cookie on the response
84
95
  this.setBrowserId(response, browserId);
96
+ // Share site name with the following executed middlewares
97
+ response.cookies.set('sc_site', siteName);
85
98
  debug.personalize('personalize middleware end: %o', {
86
99
  rewritePath,
87
100
  browserId,
@@ -101,6 +114,7 @@ export class PersonalizeMiddleware {
101
114
  });
102
115
  return (url, data) => fetcher.fetch(url, data);
103
116
  } }));
117
+ this.defaultHostname = config.defaultHostname || 'localhost';
104
118
  }
105
119
  /**
106
120
  * Gets the Next.js middleware handler with error handling
@@ -20,51 +20,60 @@ export class RedirectsMiddleware {
20
20
  */
21
21
  constructor(config) {
22
22
  this.config = config;
23
- this.handler = (req) => __awaiter(this, void 0, void 0, function* () {
24
- if ((this.config.disabled && this.config.disabled(req, NextResponse.next())) ||
25
- this.excludeRoute(req.nextUrl.pathname) ||
26
- (this.config.excludeRoute && this.config.excludeRoute(req.nextUrl.pathname))) {
27
- return NextResponse.next();
28
- }
29
- // Find the redirect from result of RedirectService
30
- const existsRedirect = yield this.getExistsRedirect(req);
31
- if (!existsRedirect) {
32
- return NextResponse.next();
33
- }
34
- const url = req.nextUrl.clone();
35
- const absoluteUrlRegex = new RegExp('^(?:[a-z]+:)?//', 'i');
36
- if (absoluteUrlRegex.test(existsRedirect.target)) {
37
- url.href = existsRedirect.target;
38
- url.locale = req.nextUrl.locale;
39
- }
40
- else {
41
- url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
42
- const urlFirstPart = existsRedirect.target.split('/')[1];
43
- if (this.locales.includes(urlFirstPart)) {
44
- url.locale = urlFirstPart;
45
- url.pathname = existsRedirect.target.replace(`/${urlFirstPart}`, '');
23
+ this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
24
+ const hostname = this.getHostname(req);
25
+ const siteName = (res === null || res === void 0 ? void 0 : res.cookies.get('sc_site')) || this.config.getSite(hostname).name;
26
+ const createResponse = () => __awaiter(this, void 0, void 0, function* () {
27
+ if ((this.config.disabled && this.config.disabled(req, NextResponse.next())) ||
28
+ this.excludeRoute(req.nextUrl.pathname) ||
29
+ (this.config.excludeRoute && this.config.excludeRoute(req.nextUrl.pathname))) {
30
+ return NextResponse.next();
31
+ }
32
+ // Find the redirect from result of RedirectService
33
+ const existsRedirect = yield this.getExistsRedirect(req, siteName);
34
+ if (!existsRedirect) {
35
+ return NextResponse.next();
36
+ }
37
+ const url = req.nextUrl.clone();
38
+ const absoluteUrlRegex = new RegExp('^(?:[a-z]+:)?//', 'i');
39
+ if (absoluteUrlRegex.test(existsRedirect.target)) {
40
+ url.href = existsRedirect.target;
41
+ url.locale = req.nextUrl.locale;
46
42
  }
47
43
  else {
48
- url.pathname = existsRedirect.target;
44
+ url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
45
+ const urlFirstPart = existsRedirect.target.split('/')[1];
46
+ if (this.locales.includes(urlFirstPart)) {
47
+ url.locale = urlFirstPart;
48
+ url.pathname = existsRedirect.target.replace(`/${urlFirstPart}`, '');
49
+ }
50
+ else {
51
+ url.pathname = existsRedirect.target;
52
+ }
49
53
  }
50
- }
51
- const redirectUrl = decodeURIComponent(url.href);
52
- /** return Response redirect with http code of redirect type **/
53
- switch (existsRedirect.redirectType) {
54
- case REDIRECT_TYPE_301:
55
- return NextResponse.redirect(redirectUrl, 301);
56
- case REDIRECT_TYPE_302:
57
- return NextResponse.redirect(redirectUrl, 302);
58
- case REDIRECT_TYPE_SERVER_TRANSFER:
59
- return NextResponse.rewrite(redirectUrl);
60
- default:
61
- return NextResponse.next();
62
- }
54
+ const redirectUrl = decodeURIComponent(url.href);
55
+ /** return Response redirect with http code of redirect type **/
56
+ switch (existsRedirect.redirectType) {
57
+ case REDIRECT_TYPE_301:
58
+ return NextResponse.redirect(redirectUrl, 301);
59
+ case REDIRECT_TYPE_302:
60
+ return NextResponse.redirect(redirectUrl, 302);
61
+ case REDIRECT_TYPE_SERVER_TRANSFER:
62
+ return NextResponse.rewrite(redirectUrl);
63
+ default:
64
+ return NextResponse.next();
65
+ }
66
+ });
67
+ const response = yield createResponse();
68
+ // Share site name with the following executed middlewares
69
+ response.cookies.set('sc_site', siteName);
70
+ return response;
63
71
  });
64
72
  // NOTE: we provide native fetch for compatibility on Next.js Edge Runtime
65
73
  // (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
66
74
  this.redirectsService = new GraphQLRedirectsService(Object.assign(Object.assign({}, config), { fetch: fetch }));
67
75
  this.locales = config.locales;
76
+ this.defaultHostname = config.defaultHostname || 'localhost';
68
77
  }
69
78
  /**
70
79
  * Gets the Next.js API route handler
@@ -83,15 +92,21 @@ export class RedirectsMiddleware {
83
92
  }
84
93
  return false;
85
94
  }
95
+ getHostname(req) {
96
+ var _a;
97
+ const hostHeader = (_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0];
98
+ return hostHeader || this.defaultHostname;
99
+ }
86
100
  /**
87
101
  * Method returns RedirectInfo when matches
88
- * @param {NextRequest} req
102
+ * @param {NextRequest} req request
103
+ * @param {string} siteName site name
89
104
  * @returns Promise<RedirectInfo | undefined>
90
105
  * @private
91
106
  */
92
- getExistsRedirect(req) {
107
+ getExistsRedirect(req, siteName) {
93
108
  return __awaiter(this, void 0, void 0, function* () {
94
- const redirects = yield this.redirectsService.fetchRedirects();
109
+ const redirects = yield this.redirectsService.fetchRedirects(siteName);
95
110
  return redirects.length
96
111
  ? redirects.find((redirect) => {
97
112
  return ((regexParser(redirect.pattern.toLowerCase()).test(req.nextUrl.pathname.toLowerCase()) ||
@@ -10,8 +10,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { GraphQLRequestClient } from '@sitecore-jss/sitecore-jss/graphql';
11
11
  import { debug } from '@sitecore-jss/sitecore-jss';
12
12
  import { getPersonalizedRewrite } from '@sitecore-jss/sitecore-jss/personalize';
13
+ import { getSiteRewrite } from '@sitecore-jss/sitecore-jss/site';
13
14
  /** @private */
14
15
  export const languageError = 'The list of languages cannot be empty';
16
+ export const sitesError = 'The list of sites cannot be empty';
15
17
  /**
16
18
  * @param {string} siteName to inject into error text
17
19
  * @private
@@ -49,7 +51,7 @@ query ${usesPersonalize ? 'PersonalizeSitemapQuery' : 'DefaultSitemapQuery'}(
49
51
  hasNext
50
52
  }
51
53
  results {
52
- path: routePath
54
+ path: routePath
53
55
  ${usesPersonalize
54
56
  ? `
55
57
  route {
@@ -62,7 +64,7 @@ query ${usesPersonalize ? 'PersonalizeSitemapQuery' : 'DefaultSitemapQuery'}(
62
64
  }
63
65
  }
64
66
  }
65
- }
67
+ }
66
68
  `;
67
69
  /**
68
70
  * Service that fetches the list of site pages using Sitecore's GraphQL API.
@@ -128,43 +130,64 @@ export class GraphQLSitemapService {
128
130
  */
129
131
  fetchSitemap(languages, formatStaticPath) {
130
132
  return __awaiter(this, void 0, void 0, function* () {
133
+ const paths = new Array();
131
134
  if (!languages.length) {
132
135
  throw new RangeError(languageError);
133
136
  }
134
- // Fetch paths using all locales
135
- const paths = yield Promise.all(languages.map((language) => {
136
- if (language === '') {
137
- throw new RangeError(languageEmptyError);
138
- }
139
- debug.sitemap('fetching sitemap data for %s', language);
140
- return this.fetchLanguageSitePaths(language).then((results) => this.transformLanguageSitePaths(results, formatStaticPath, language));
141
- }));
142
- // merge promises results into single result
137
+ // Get all sites
138
+ const sites = this.options.sites;
139
+ if (!sites || !sites.length) {
140
+ throw new RangeError(sitesError);
141
+ }
142
+ // Fetch paths for each site
143
+ for (let i = 0; i < sites.length; i++) {
144
+ const siteName = sites[i];
145
+ const multiSiteName = sites.length > 1 ? siteName : undefined;
146
+ // Fetch paths using all locales
147
+ yield Promise.all(languages.map((language) => __awaiter(this, void 0, void 0, function* () {
148
+ if (language === '') {
149
+ throw new RangeError(languageEmptyError);
150
+ }
151
+ debug.sitemap('fetching sitemap data for %s %s', language, siteName);
152
+ const results = yield this.fetchLanguageSitePaths(language, siteName);
153
+ const transformedPaths = yield this.transformLanguageSitePaths(results, formatStaticPath, language, multiSiteName);
154
+ paths.push(...transformedPaths);
155
+ })));
156
+ }
143
157
  return [].concat(...paths);
144
158
  });
145
159
  }
146
- transformLanguageSitePaths(sitePaths, formatStaticPath, language) {
160
+ transformLanguageSitePaths(sitePaths, formatStaticPath, language, multiSiteName) {
147
161
  return __awaiter(this, void 0, void 0, function* () {
148
162
  const formatPath = (path) => formatStaticPath(path.replace(/^\/|\/$/g, '').split('/'), language);
149
163
  const aggregatedPaths = [];
150
164
  sitePaths.forEach((item) => {
151
- var _a, _b, _c, _d;
165
+ var _a, _b, _c, _d, _e, _f;
152
166
  if (!item)
153
167
  return;
154
- aggregatedPaths.push(formatPath(item.path));
168
+ if (!multiSiteName) {
169
+ aggregatedPaths.push(formatPath(item.path));
170
+ }
171
+ else {
172
+ aggregatedPaths.push(formatPath(getSiteRewrite(item.path, { siteName: multiSiteName })));
173
+ }
155
174
  // check for type safety's sake - personalize may be empty depending on query type
156
175
  if ((_b = (_a = item.route) === null || _a === void 0 ? void 0 : _a.personalization) === null || _b === void 0 ? void 0 : _b.variantIds.length) {
157
- aggregatedPaths.push(...(_d = (_c = item.route) === null || _c === void 0 ? void 0 : _c.personalization) === null || _d === void 0 ? void 0 : _d.variantIds.map((varId) => formatPath(getPersonalizedRewrite(item.path, { variantId: varId }))));
176
+ multiSiteName
177
+ ? aggregatedPaths.push(...(_d = (_c = item.route) === null || _c === void 0 ? void 0 : _c.personalization) === null || _d === void 0 ? void 0 : _d.variantIds.map((varId) => formatPath(getPersonalizedRewrite(getSiteRewrite(item.path, { siteName: multiSiteName }), {
178
+ variantId: varId,
179
+ }))))
180
+ : aggregatedPaths.push(...(_f = (_e = item.route) === null || _e === void 0 ? void 0 : _e.personalization) === null || _f === void 0 ? void 0 : _f.variantIds.map((varId) => formatPath(getPersonalizedRewrite(item.path, { variantId: varId }))));
158
181
  }
159
182
  });
160
183
  return aggregatedPaths;
161
184
  });
162
185
  }
163
- fetchLanguageSitePaths(language) {
186
+ fetchLanguageSitePaths(language, siteName) {
164
187
  var _a, _b, _c, _d;
165
188
  return __awaiter(this, void 0, void 0, function* () {
166
189
  const args = {
167
- siteName: this.options.siteName,
190
+ siteName: siteName,
168
191
  language: language,
169
192
  pageSize: this.options.pageSize,
170
193
  includedPaths: this.options.includedPaths,
@@ -176,7 +199,7 @@ export class GraphQLSitemapService {
176
199
  while (hasNext) {
177
200
  const fetchResponse = yield this.graphQLClient.request(this.query, Object.assign(Object.assign({}, args), { after }));
178
201
  if (!((_a = fetchResponse === null || fetchResponse === void 0 ? void 0 : fetchResponse.site) === null || _a === void 0 ? void 0 : _a.siteInfo)) {
179
- throw new RangeError(getSiteEmptyError(this.options.siteName));
202
+ throw new RangeError(getSiteEmptyError(siteName));
180
203
  }
181
204
  else {
182
205
  results = results.concat((_b = fetchResponse.site.siteInfo.routes) === null || _b === void 0 ? void 0 : _b.results);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sitecore-jss/sitecore-jss-nextjs",
3
- "version": "21.1.0-canary.64",
3
+ "version": "21.1.0-canary.66",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "sideEffects": false,
@@ -70,9 +70,9 @@
70
70
  "react-dom": "^18.2.0"
71
71
  },
72
72
  "dependencies": {
73
- "@sitecore-jss/sitecore-jss": "^21.1.0-canary.64",
74
- "@sitecore-jss/sitecore-jss-dev-tools": "^21.1.0-canary.64",
75
- "@sitecore-jss/sitecore-jss-react": "^21.1.0-canary.64",
73
+ "@sitecore-jss/sitecore-jss": "^21.1.0-canary.66",
74
+ "@sitecore-jss/sitecore-jss-dev-tools": "^21.1.0-canary.66",
75
+ "@sitecore-jss/sitecore-jss-react": "^21.1.0-canary.66",
76
76
  "node-html-parser": "^6.0.0",
77
77
  "prop-types": "^15.8.1",
78
78
  "regex-parser": "^2.2.11",
@@ -80,7 +80,7 @@
80
80
  },
81
81
  "description": "",
82
82
  "types": "types/index.d.ts",
83
- "gitHead": "b0aa7675eacfa5b9a08a82258833da29e01e010c",
83
+ "gitHead": "11af0c7e9a04340df6c0829735ac9fb0b8a771da",
84
84
  "files": [
85
85
  "dist",
86
86
  "types",
package/types/index.d.ts CHANGED
@@ -5,14 +5,13 @@ export { mediaApi } from '@sitecore-jss/sitecore-jss/media';
5
5
  export { trackingApi, TrackingRequestOptions, CampaignInstance, GoalInstance, OutcomeInstance, EventInstance, PageViewInstance, } from '@sitecore-jss/sitecore-jss/tracking';
6
6
  export { DictionaryPhrases, DictionaryService, GraphQLDictionaryService, GraphQLDictionaryServiceConfig, RestDictionaryService, RestDictionaryServiceConfig, } from '@sitecore-jss/sitecore-jss/i18n';
7
7
  export { personalizeLayout, getPersonalizedRewrite, getPersonalizedRewriteData, normalizePersonalizedRewrite, CdpHelper, } from '@sitecore-jss/sitecore-jss/personalize';
8
- export { RobotsQueryResult, GraphQLRobotsService, GraphQLRobotsServiceConfig, } from '@sitecore-jss/sitecore-jss/site';
9
8
  export { GraphQLRequestClient } from '@sitecore-jss/sitecore-jss';
10
9
  export { ComponentPropsCollection, GetStaticComponentProps, GetServerSideComponentProps, } from './sharedTypes/component-props';
11
10
  export { ComponentModule } from './sharedTypes/component-module';
12
11
  export { ComponentPropsService } from './services/component-props-service';
13
12
  export { DisconnectedSitemapService } from './services/disconnected-sitemap-service';
14
13
  export { GraphQLSitemapService, GraphQLSitemapServiceConfig, } from './services/graphql-sitemap-service';
15
- export { GraphQLSitemapXmlService, GraphQLSitemapXmlServiceConfig, GraphQLErrorPagesService, GraphQLErrorPagesServiceConfig, ErrorPages, } from '@sitecore-jss/sitecore-jss/site';
14
+ export { GraphQLSitemapXmlService, GraphQLSitemapXmlServiceConfig, GraphQLErrorPagesService, GraphQLErrorPagesServiceConfig, RobotsQueryResult, GraphQLRobotsService, GraphQLRobotsServiceConfig, ErrorPages, SiteInfo, SiteResolver, GraphQLSiteInfoService, GraphQLSiteInfoServiceConfig, getSiteRewrite, getSiteRewriteData, normalizeSiteRewrite, } from '@sitecore-jss/sitecore-jss/site';
16
15
  export { StaticPath } from './services/graphql-sitemap-service';
17
16
  export { ComponentPropsReactContext, ComponentPropsContextProps, ComponentPropsContext, useComponentProps, } from './components/ComponentPropsContext';
18
17
  export { handleEditorFastRefresh, getPublicUrl } from './utils';
@@ -1,2 +1,4 @@
1
1
  export { RedirectsMiddleware, RedirectsMiddlewareConfig } from './redirects-middleware';
2
2
  export { PersonalizeMiddleware, PersonalizeMiddlewareConfig } from './personalize-middleware';
3
+ export { MultisiteMiddleware, MultisiteMiddlewareConfig } from './multisite-middleware';
4
+ export { SiteResolver, SiteInfo } from '@sitecore-jss/sitecore-jss/site';
@@ -0,0 +1,42 @@
1
+ import { NextResponse, NextRequest } from 'next/server';
2
+ import { SiteInfo } from '@sitecore-jss/sitecore-jss/site';
3
+ export declare type MultisiteMiddlewareConfig = {
4
+ /**
5
+ * Function used to determine if route should be excluded during execution.
6
+ * By default, files (pathname.includes('.')), Next.js API routes (pathname.startsWith('/api/')), and Sitecore API routes (pathname.startsWith('/sitecore/')) are ignored.
7
+ * This is an important performance consideration since Next.js Edge middleware runs on every request.
8
+ * @param {string} pathname The pathname
9
+ * @returns {boolean} Whether to exclude the route
10
+ */
11
+ excludeRoute?: (pathname: string) => boolean;
12
+ /**
13
+ * function used to resolve site for given hostname
14
+ */
15
+ getSite: (hostname: string) => SiteInfo;
16
+ /**
17
+ * Fallback hostname in case `host` header is not present
18
+ * @default localhost
19
+ */
20
+ defaultHostname?: string;
21
+ };
22
+ /**
23
+ * Middleware / handler for multisite support
24
+ */
25
+ export declare class MultisiteMiddleware {
26
+ protected config: MultisiteMiddlewareConfig;
27
+ private defaultHostname;
28
+ /**
29
+ * @param {MultisiteMiddlewareConfig} [config] Multisite middleware config
30
+ */
31
+ constructor(config: MultisiteMiddlewareConfig);
32
+ /**
33
+ * Gets the Next.js middleware handler with error handling
34
+ * @returns middleware handler
35
+ */
36
+ getHandler(): (req: NextRequest, res?: NextResponse) => Promise<NextResponse>;
37
+ protected excludeRoute(pathname: string): boolean;
38
+ protected extractDebugHeaders(incomingHeaders: Headers): {
39
+ [key: string]: string;
40
+ };
41
+ private handler;
42
+ }
@@ -1,5 +1,6 @@
1
1
  import { NextResponse, NextRequest } from 'next/server';
2
2
  import { GraphQLPersonalizeServiceConfig, CdpServiceConfig, ExperienceParams } from '@sitecore-jss/sitecore-jss/personalize';
3
+ import { SiteInfo } from '@sitecore-jss/sitecore-jss/site';
3
4
  export declare type PersonalizeMiddlewareConfig = {
4
5
  /**
5
6
  * Function used to determine if route should be excluded from personalization.
@@ -29,6 +30,15 @@ export declare type PersonalizeMiddlewareConfig = {
29
30
  * @returns {boolean} false by default
30
31
  */
31
32
  disabled?: (req?: NextRequest, res?: NextResponse) => boolean;
33
+ /**
34
+ * function used to resolve site for given hostname
35
+ */
36
+ getSite: (hostname: string) => SiteInfo;
37
+ /**
38
+ * fallback hostname in case `host` header is not present
39
+ * @default localhost
40
+ */
41
+ defaultHostname?: string;
32
42
  };
33
43
  /**
34
44
  * Middleware / handler to support Sitecore Personalize
@@ -37,6 +47,7 @@ export declare class PersonalizeMiddleware {
37
47
  protected config: PersonalizeMiddlewareConfig;
38
48
  private personalizeService;
39
49
  private cdpService;
50
+ private defaultHostname;
40
51
  /**
41
52
  * @param {PersonalizeMiddlewareConfig} [config] Personalize middleware config
42
53
  */
@@ -1,5 +1,5 @@
1
1
  import { NextResponse, NextRequest } from 'next/server';
2
- import { GraphQLRedirectsServiceConfig } from '@sitecore-jss/sitecore-jss/site';
2
+ import { GraphQLRedirectsServiceConfig, SiteInfo } from '@sitecore-jss/sitecore-jss/site';
3
3
  /**
4
4
  * extended RedirectsMiddlewareConfig config type for RedirectsMiddleware
5
5
  */
@@ -20,6 +20,15 @@ export declare type RedirectsMiddlewareConfig = Omit<GraphQLRedirectsServiceConf
20
20
  * @returns {boolean} false by default
21
21
  */
22
22
  disabled?: (req?: NextRequest, res?: NextResponse) => boolean;
23
+ /**
24
+ * function used to resolve site for given hostname
25
+ */
26
+ getSite: (hostname: string) => SiteInfo;
27
+ /**
28
+ * fallback hostname in case `host` header is not present
29
+ * @default localhost
30
+ */
31
+ defaultHostname?: string;
23
32
  };
24
33
  /**
25
34
  * Middleware / handler fetches all redirects from Sitecore instance by grapqhl service
@@ -29,6 +38,7 @@ export declare class RedirectsMiddleware {
29
38
  protected config: RedirectsMiddlewareConfig;
30
39
  private redirectsService;
31
40
  private locales;
41
+ private defaultHostname;
32
42
  /**
33
43
  * @param {RedirectsMiddlewareConfig} [config] redirects middleware config
34
44
  */
@@ -37,12 +47,14 @@ export declare class RedirectsMiddleware {
37
47
  * Gets the Next.js API route handler
38
48
  * @returns route handler
39
49
  */
40
- getHandler(): (req: NextRequest) => Promise<NextResponse>;
50
+ getHandler(): (req: NextRequest, res?: NextResponse) => Promise<NextResponse>;
41
51
  protected excludeRoute(pathname: string): boolean;
52
+ protected getHostname(req: NextRequest): string;
42
53
  private handler;
43
54
  /**
44
55
  * Method returns RedirectInfo when matches
45
- * @param {NextRequest} req
56
+ * @param {NextRequest} req request
57
+ * @param {string} siteName site name
46
58
  * @returns Promise<RedirectInfo | undefined>
47
59
  * @private
48
60
  */
@@ -1,6 +1,7 @@
1
1
  import { GraphQLClient, PageInfo } from '@sitecore-jss/sitecore-jss/graphql';
2
2
  /** @private */
3
3
  export declare const languageError = "The list of languages cannot be empty";
4
+ export declare const sitesError = "The list of sites cannot be empty";
4
5
  /**
5
6
  * @param {string} siteName to inject into error text
6
7
  * @private
@@ -64,7 +65,7 @@ export declare type RouteListQueryResult = {
64
65
  /**
65
66
  * Configuration options for @see GraphQLSitemapService instances
66
67
  */
67
- export interface GraphQLSitemapServiceConfig extends Omit<SiteRouteQueryVariables, 'language'> {
68
+ export interface GraphQLSitemapServiceConfig extends Omit<SiteRouteQueryVariables, 'language' | 'siteName'> {
68
69
  /**
69
70
  * Your Graphql endpoint
70
71
  */
@@ -73,6 +74,10 @@ export interface GraphQLSitemapServiceConfig extends Omit<SiteRouteQueryVariable
73
74
  * The API key to use for authentication.
74
75
  */
75
76
  apiKey: string;
77
+ /**
78
+ * Names of the configured sites
79
+ */
80
+ sites: string[];
76
81
  /**
77
82
  * A flag for whether to include personalized routes in service output - only works on XM Cloud
78
83
  * turned off by default
@@ -129,8 +134,8 @@ export declare class GraphQLSitemapService {
129
134
  * @throws {RangeError} if the any of the languages is an empty string.
130
135
  */
131
136
  protected fetchSitemap(languages: string[], formatStaticPath: (path: string[], language: string) => StaticPath): Promise<StaticPath[]>;
132
- protected transformLanguageSitePaths(sitePaths: RouteListQueryResult[], formatStaticPath: (path: string[], language: string) => StaticPath, language: string): Promise<StaticPath[]>;
133
- protected fetchLanguageSitePaths(language: string): Promise<RouteListQueryResult[]>;
137
+ protected transformLanguageSitePaths(sitePaths: RouteListQueryResult[], formatStaticPath: (path: string[], language: string) => StaticPath, language: string, multiSiteName?: string): Promise<StaticPath[]>;
138
+ protected fetchLanguageSitePaths(language: string, siteName: string): Promise<RouteListQueryResult[]>;
134
139
  /**
135
140
  * Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default
136
141
  * library for fetching graphql data (@see GraphQLRequestClient). Override this method if you