@sitecore-content-sdk/nextjs 0.1.0-beta.9 → 0.1.0-canary.0

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 (99) hide show
  1. package/README.md +5 -4
  2. package/client.d.ts +1 -0
  3. package/client.js +1 -0
  4. package/component-props-loader.d.ts +1 -0
  5. package/component-props-loader.js +3 -0
  6. package/dist/cjs/client/index.js +10 -0
  7. package/dist/cjs/client/sitecore-nextjs-client.js +117 -0
  8. package/dist/cjs/components/RichText.js +16 -4
  9. package/dist/cjs/config/define-cli-config.js +30 -0
  10. package/dist/cjs/config/index.js +3 -1
  11. package/dist/cjs/editing/editing-config-middleware.js +1 -3
  12. package/dist/cjs/editing/editing-render-middleware.js +13 -9
  13. package/dist/cjs/editing/index.js +2 -2
  14. package/dist/cjs/index.js +16 -18
  15. package/dist/cjs/middleware/index.js +3 -1
  16. package/dist/cjs/middleware/middleware.js +23 -12
  17. package/dist/cjs/middleware/multisite-middleware.js +19 -8
  18. package/dist/cjs/middleware/personalize-middleware.js +3 -3
  19. package/dist/cjs/middleware/redirects-middleware.js +28 -13
  20. package/dist/cjs/middleware/sitemap-middleware.js +47 -0
  21. package/dist/cjs/services/component-props-service.js +41 -50
  22. package/dist/cjs/tools/component-props.loader.js +101 -0
  23. package/dist/cjs/tools/index.js +9 -0
  24. package/dist/cjs/tools/templating/byoc-component.js +75 -0
  25. package/dist/cjs/tools/templating/constants.js +7 -0
  26. package/dist/cjs/tools/templating/default-component.js +54 -0
  27. package/dist/cjs/tools/templating/utils.js +18 -0
  28. package/dist/cjs/utils/index.js +2 -1
  29. package/dist/cjs/utils/utils.js +10 -1
  30. package/dist/esm/client/index.js +2 -0
  31. package/dist/esm/client/sitecore-nextjs-client.js +113 -0
  32. package/dist/esm/components/RichText.js +14 -2
  33. package/dist/esm/config/define-cli-config.js +26 -0
  34. package/dist/esm/config/index.js +1 -0
  35. package/dist/esm/editing/editing-config-middleware.js +1 -3
  36. package/dist/esm/editing/editing-render-middleware.js +11 -7
  37. package/dist/esm/editing/index.js +1 -1
  38. package/dist/esm/index.js +3 -5
  39. package/dist/esm/middleware/index.js +1 -0
  40. package/dist/esm/middleware/middleware.js +23 -12
  41. package/dist/esm/middleware/multisite-middleware.js +20 -9
  42. package/dist/esm/middleware/personalize-middleware.js +4 -4
  43. package/dist/esm/middleware/redirects-middleware.js +29 -14
  44. package/dist/esm/middleware/sitemap-middleware.js +43 -0
  45. package/dist/esm/services/component-props-service.js +41 -50
  46. package/dist/esm/tools/component-props.loader.js +65 -0
  47. package/dist/esm/tools/index.js +1 -0
  48. package/dist/esm/tools/templating/byoc-component.js +69 -0
  49. package/dist/esm/tools/templating/constants.js +4 -0
  50. package/dist/esm/tools/templating/default-component.js +48 -0
  51. package/dist/esm/tools/templating/utils.js +12 -0
  52. package/dist/esm/utils/index.js +1 -1
  53. package/dist/esm/utils/utils.js +8 -0
  54. package/package.json +16 -13
  55. package/tools.d.ts +1 -0
  56. package/tools.js +1 -0
  57. package/types/client/index.d.ts +2 -0
  58. package/types/client/sitecore-nextjs-client.d.ts +44 -0
  59. package/types/components/RichText.d.ts +7 -1
  60. package/types/config/define-cli-config.d.ts +8 -0
  61. package/types/config/index.d.ts +1 -0
  62. package/types/editing/editing-config-middleware.d.ts +4 -2
  63. package/types/editing/editing-render-middleware.d.ts +3 -31
  64. package/types/editing/index.d.ts +1 -1
  65. package/types/index.d.ts +6 -8
  66. package/types/middleware/index.d.ts +1 -0
  67. package/types/middleware/middleware.d.ts +13 -7
  68. package/types/middleware/sitemap-middleware.d.ts +12 -0
  69. package/types/services/component-props-service.d.ts +10 -26
  70. package/types/sharedTypes/component-props.d.ts +18 -0
  71. package/types/sharedTypes/sitecore-page-props.d.ts +5 -0
  72. package/types/tools/component-props.loader.d.ts +7 -0
  73. package/types/tools/index.d.ts +1 -0
  74. package/types/tools/templating/byoc-component.d.ts +2 -0
  75. package/types/tools/templating/constants.d.ts +4 -0
  76. package/types/tools/templating/default-component.d.ts +2 -0
  77. package/types/tools/templating/utils.d.ts +6 -0
  78. package/types/utils/index.d.ts +1 -1
  79. package/types/utils/utils.d.ts +2 -0
  80. package/dist/cjs/ComponentBuilder.js +0 -63
  81. package/dist/cjs/graphql/index.js +0 -7
  82. package/dist/cjs/services/base-graphql-sitemap-service.js +0 -206
  83. package/dist/cjs/services/graphql-sitemap-service.js +0 -64
  84. package/dist/cjs/services/mutisite-graphql-sitemap-service.js +0 -81
  85. package/dist/esm/ComponentBuilder.js +0 -59
  86. package/dist/esm/graphql/index.js +0 -1
  87. package/dist/esm/services/base-graphql-sitemap-service.js +0 -201
  88. package/dist/esm/services/graphql-sitemap-service.js +0 -59
  89. package/dist/esm/services/mutisite-graphql-sitemap-service.js +0 -77
  90. package/graphql.d.ts +0 -1
  91. package/graphql.js +0 -1
  92. package/types/ComponentBuilder.d.ts +0 -59
  93. package/types/graphql/index.d.ts +0 -1
  94. package/types/services/base-graphql-sitemap-service.d.ts +0 -148
  95. package/types/services/graphql-sitemap-service.d.ts +0 -51
  96. package/types/services/mutisite-graphql-sitemap-service.d.ts +0 -42
  97. package/types/sharedTypes/module-factory.d.ts +0 -32
  98. /package/dist/cjs/sharedTypes/{module-factory.js → sitecore-page-props.js} +0 -0
  99. /package/dist/esm/sharedTypes/{module-factory.js → sitecore-page-props.js} +0 -0
@@ -0,0 +1,113 @@
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 { SitecoreClient, } from '@sitecore-content-sdk/core/client';
11
+ import { ComponentPropsService } from '../services/component-props-service';
12
+ import { getSiteRewriteData, normalizeSiteRewrite } from '@sitecore-content-sdk/core/site';
13
+ import { getPersonalizedRewriteData, normalizePersonalizedRewrite, } from '@sitecore-content-sdk/core/personalize';
14
+ export class SitecoreNextjsClient extends SitecoreClient {
15
+ constructor(initOptions) {
16
+ super(initOptions);
17
+ this.initOptions = initOptions;
18
+ this.componentPropsService = this.getComponentPropsService();
19
+ }
20
+ /**
21
+ * Resolves site based on the provided path
22
+ * @param {string | string[]} path path to resolve site from
23
+ * @returns resolved site, or default site info if not found
24
+ */
25
+ resolveSiteFromPath(path) {
26
+ const resolvedPath = super.parsePath(path);
27
+ // Get site name (from path rewritten in middleware)
28
+ const siteData = getSiteRewriteData(resolvedPath, this.initOptions.defaultSite);
29
+ // Resolve site by name
30
+ return (this.siteResolver.getByName(siteData.siteName) || {
31
+ name: siteData.siteName,
32
+ hostName: '',
33
+ language: '',
34
+ });
35
+ }
36
+ /**
37
+ * Normalizes a nextjs path that could have been rewritten
38
+ * @param {string | string[]} path nextjs path
39
+ * @returns path string without nextjs prefixes
40
+ */
41
+ parsePath(path) {
42
+ const basePath = super.parsePath(path);
43
+ return normalizeSiteRewrite(normalizePersonalizedRewrite(basePath));
44
+ }
45
+ getPage(path, pageOptions, options) {
46
+ const _super = Object.create(null, {
47
+ parsePath: { get: () => super.parsePath },
48
+ getPage: { get: () => super.getPage }
49
+ });
50
+ return __awaiter(this, void 0, void 0, function* () {
51
+ var _a;
52
+ const resolvedPath = this.parsePath(path);
53
+ // Get variant(s) for personalization (from path), must ensure path is of type string
54
+ const personalizeData = pageOptions.personalize || getPersonalizedRewriteData(_super.parsePath.call(this, path));
55
+ const site = pageOptions.site || ((_a = this.resolveSiteFromPath(path)) === null || _a === void 0 ? void 0 : _a.name);
56
+ const page = yield _super.getPage.call(this, resolvedPath, {
57
+ locale: pageOptions.locale,
58
+ site,
59
+ personalize: personalizeData,
60
+ }, options);
61
+ return page;
62
+ });
63
+ }
64
+ /**
65
+ * Retrieves preview page and layout details
66
+ * @param {PreviewData} previewData - The editing preview data for metadata mode.
67
+ * @param {FetchOptions} [fetchOptions] Additional fetch fetch options to override GraphQL requests (like retries and fetch)
68
+ */
69
+ getPreview(previewData, fetchOptions) {
70
+ const _super = Object.create(null, {
71
+ getPreview: { get: () => super.getPreview }
72
+ });
73
+ return __awaiter(this, void 0, void 0, function* () {
74
+ return _super.getPreview.call(this, previewData, fetchOptions);
75
+ });
76
+ }
77
+ /**
78
+ * Parses components from nextjs component map and layoutData, executes getServerProps/getStaticProps methods
79
+ * and returns resulting props from components
80
+ * @param {LayoutServiceData} layoutData layout data to parse compnents from
81
+ * @param {PreviewData} context Nextjs preview data
82
+ * @param {ComponentMap<NextjsJssComponent>} components component map to get props for
83
+ * @returns {ComponentPropsCollection} component props
84
+ */
85
+ getComponentData(layoutData, context, components) {
86
+ return __awaiter(this, void 0, void 0, function* () {
87
+ let componentProps = {};
88
+ if (!layoutData.sitecore.route)
89
+ return componentProps;
90
+ // Retrieve component props using side-effects defined on components level
91
+ componentProps = yield this.componentPropsService.fetchComponentProps({
92
+ layoutData: layoutData,
93
+ context,
94
+ components,
95
+ });
96
+ const errors = Object.keys(componentProps)
97
+ .map((id) => {
98
+ const component = componentProps[id];
99
+ return component.error
100
+ ? `\nUnable to get component props for ${component.componentName} (${id}): ${component.error}`
101
+ : '';
102
+ })
103
+ .join('');
104
+ if (errors.length) {
105
+ throw new Error(errors);
106
+ }
107
+ return componentProps;
108
+ });
109
+ }
110
+ getComponentPropsService() {
111
+ return new ComponentPropsService();
112
+ }
113
+ }
@@ -13,7 +13,7 @@ import React, { useEffect, useRef } from 'react';
13
13
  import PropTypes from 'prop-types';
14
14
  import { useRouter } from 'next/router';
15
15
  import { RichText as ReactRichText, RichTextPropTypes, } from '@sitecore-content-sdk/react';
16
- const prefetched = {};
16
+ export const prefetched = {};
17
17
  export const RichText = (props) => {
18
18
  const { internalLinksSelector = 'a[href^="/"]', prefetchLinks = true, editable = true } = props, rest = __rest(props, ["internalLinksSelector", "prefetchLinks", "editable"]);
19
19
  const hasText = props.field && props.field.value;
@@ -42,9 +42,21 @@ export const RichText = (props) => {
42
42
  internalLinks.forEach((link) => {
43
43
  if (link.target === '_blank')
44
44
  return;
45
- if (prefetchLinks && !prefetched[link.pathname]) {
45
+ const prefetch = () => {
46
46
  router.prefetch(link.pathname, undefined, { locale: false });
47
47
  prefetched[link.pathname] = true;
48
+ };
49
+ if (!prefetched[link.pathname] && prefetchLinks !== false) {
50
+ if (prefetchLinks === true) {
51
+ prefetch();
52
+ }
53
+ if (prefetchLinks === 'hover') {
54
+ const mouseOverHandler = () => {
55
+ prefetch();
56
+ link.removeEventListener('mouseover', mouseOverHandler);
57
+ };
58
+ link.addEventListener('mouseover', mouseOverHandler, false);
59
+ }
48
60
  }
49
61
  link.addEventListener('click', routeHandler, false);
50
62
  });
@@ -0,0 +1,26 @@
1
+ import { defineCliConfig as defineCliConfigCore, } from '@sitecore-content-sdk/core/config';
2
+ import { byocTemplate } from '../tools/templating/byoc-component';
3
+ import { defaultTemplate } from '../tools/templating/default-component';
4
+ /**
5
+ * Accepts a `SitecoreCliConfigInput` object and returns the Sitecore Content SDK CLI configuration from the specified file,
6
+ * updated with the required default values.
7
+ * @param {SitecoreCliConfigInput} cliConfig the cli configuration provided by the application
8
+ * @returns {SitecoreCliConfig} full sitecore cli configuration to use with cli
9
+ */
10
+ export const defineCliConfig = (cliConfig) => {
11
+ addDefaultScaffoldTemplates(cliConfig);
12
+ return defineCliConfigCore(cliConfig);
13
+ };
14
+ /**
15
+ * Adds default scaffold templates to the CLI configuration.
16
+ * @param {SitecoreCliConfigInput} cliConfig - The CLI configuration object
17
+ */
18
+ function addDefaultScaffoldTemplates(cliConfig) {
19
+ if (!cliConfig.scaffold) {
20
+ cliConfig.scaffold = {};
21
+ }
22
+ if (!cliConfig.scaffold.templates) {
23
+ cliConfig.scaffold.templates = [];
24
+ }
25
+ cliConfig.scaffold.templates.unshift(defaultTemplate, byocTemplate);
26
+ }
@@ -1 +1,2 @@
1
1
  export { defineConfig, } from '@sitecore-content-sdk/core/config';
2
+ export { defineCliConfig } from './define-cli-config';
@@ -38,9 +38,7 @@ export class EditingConfigMiddleware {
38
38
  // CORS headers are set by enforceCors
39
39
  return res.status(204).send(null);
40
40
  }
41
- const components = Array.isArray(this.config.components)
42
- ? this.config.components
43
- : Array.from(this.config.components.keys());
41
+ const components = Array.from(this.config.components.keys());
44
42
  return res.status(200).json({
45
43
  components,
46
44
  packages: this.config.metadata.packages,
@@ -8,19 +8,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { debug } from '@sitecore-content-sdk/core';
11
+ import { QUERY_PARAM_EDITING_SECRET, EDITING_ALLOWED_ORIGINS, PREVIEW_KEY, } from '@sitecore-content-sdk/core/editing';
11
12
  import { LayoutServicePageState } from '@sitecore-content-sdk/core/layout';
12
- import { QUERY_PARAM_EDITING_SECRET, EDITING_ALLOWED_ORIGINS, } from '@sitecore-content-sdk/core/editing';
13
13
  import { getJssEditingSecret } from '../utils/utils';
14
14
  import { RenderMiddlewareBase } from './render-middleware';
15
15
  import { enforceCors, getAllowedOriginsFromEnv } from '@sitecore-content-sdk/core/utils';
16
16
  import { DEFAULT_VARIANT } from '@sitecore-content-sdk/core/personalize';
17
+ import { SITE_KEY } from '@sitecore-content-sdk/core/site';
17
18
  /**
18
- * Type guard for Component Library mode
19
+ * Type guard for Design Library mode
19
20
  * @param {object} data preview data to check
20
21
  * @returns true if the data is EditingPreviewData
21
22
  * @see EditingPreviewData
22
23
  */
23
- export const isComponentLibraryPreviewData = (data) => {
24
+ export const isDesignLibraryPreviewData = (data) => {
24
25
  return (typeof data === 'object' &&
25
26
  data !== null &&
26
27
  'mode' in data &&
@@ -93,15 +94,12 @@ export class EditingRenderMiddleware extends RenderMiddlewareBase {
93
94
  });
94
95
  }
95
96
  if (mode === 'library') {
96
- // dedicated route and layout to SSR component library
97
- query.route = '/component-library/render';
98
97
  res.setPreviewData({
99
98
  itemId: query.sc_itemid,
100
99
  componentUid: query.sc_uid,
101
100
  renderingId: query.sc_renderingId,
102
101
  language: query.sc_lang,
103
102
  site: query.sc_site,
104
- pageState: LayoutServicePageState.Normal,
105
103
  mode: 'library',
106
104
  dataSourceId: query.sc_datasourceId,
107
105
  version: query.sc_version,
@@ -117,7 +115,7 @@ export class EditingRenderMiddleware extends RenderMiddlewareBase {
117
115
  // for sc_variantId we may employ multiple variants (page-layout + component level)
118
116
  variantIds: ((_c = query.sc_variant) === null || _c === void 0 ? void 0 : _c.split(',')) || [DEFAULT_VARIANT],
119
117
  version: query.sc_version,
120
- pageState: query.mode,
118
+ mode: query.mode,
121
119
  layoutKind: query.sc_layoutKind,
122
120
  },
123
121
  // Cache the preview data for 3 seconds to ensure the page is rendered with the correct preview data not the cached one
@@ -145,6 +143,12 @@ export class EditingRenderMiddleware extends RenderMiddlewareBase {
145
143
  }
146
144
  return cookie;
147
145
  });
146
+ // Set Preview mode identifier cookie, if the page is rendered in Sitecore Preview mode
147
+ if (mode === LayoutServicePageState.Preview) {
148
+ const previewSite = `${SITE_KEY}=${query.sc_site}; Path=/; HttpOnly; SameSite=None; Secure`;
149
+ const previewCookie = `${PREVIEW_KEY}=true; Path=/; HttpOnly; SameSite=None; Secure`;
150
+ modifiedCookies.push(previewSite, previewCookie);
151
+ }
148
152
  res.setHeader('Set-Cookie', modifiedCookies);
149
153
  }
150
154
  const route = ((_e = (_d = this.config) === null || _d === void 0 ? void 0 : _d.resolvePageUrl) === null || _e === void 0 ? void 0 : _e.call(_d, query.route)) || query.route;
@@ -1,5 +1,5 @@
1
1
  export { GraphQLEditingService } from '@sitecore-content-sdk/core/editing';
2
- export { EditingRenderMiddleware, isComponentLibraryPreviewData, } from './editing-render-middleware';
2
+ export { EditingRenderMiddleware, isDesignLibraryPreviewData, } from './editing-render-middleware';
3
3
  export { FEAASRenderMiddleware } from './feaas-render-middleware';
4
4
  export { EditingConfigMiddleware, } from './editing-config-middleware';
5
5
  export { RenderingType, EDITING_COMPONENT_PLACEHOLDER, EDITING_COMPONENT_ID, } from '@sitecore-content-sdk/core/layout';
package/dist/esm/index.js CHANGED
@@ -1,14 +1,13 @@
1
1
  export { constants,
2
2
  // generic data access
3
3
  NativeDataFetcher, enableDebug, debug, MemoryCacheClient, } from '@sitecore-content-sdk/core';
4
- export { LayoutServicePageState, GraphQLLayoutService, getChildPlaceholder, getFieldValue, getContentStylesheetLink, EditMode, } from '@sitecore-content-sdk/core/layout';
4
+ export { LayoutServicePageState, GraphQLLayoutService, getChildPlaceholder, getFieldValue, getContentStylesheetLink, EditMode, RenderingType, } from '@sitecore-content-sdk/core/layout';
5
5
  export { RestComponentLayoutService } from '@sitecore-content-sdk/core/editing';
6
6
  export { mediaApi } from '@sitecore-content-sdk/core/media';
7
7
  export { GraphQLDictionaryService, } from '@sitecore-content-sdk/core/i18n';
8
8
  export { personalizeLayout, getPersonalizedRewrite, getPersonalizedRewriteData, getGroomedVariantIds, normalizePersonalizedRewrite, CdpHelper, } from '@sitecore-content-sdk/core/personalize';
9
9
  export { ComponentPropsService } from './services/component-props-service';
10
- export { GraphQLSitemapService, } from './services/graphql-sitemap-service';
11
- export { MultisiteGraphQLSitemapService, } from './services/mutisite-graphql-sitemap-service';
10
+ export { GraphQLSitePathService, } from '@sitecore-content-sdk/core/site';
12
11
  export { GraphQLSitemapXmlService, GraphQLErrorPagesService, GraphQLRobotsService, SiteResolver, GraphQLSiteInfoService, getSiteRewrite, getSiteRewriteData, normalizeSiteRewrite, } from '@sitecore-content-sdk/core/site';
13
12
  export { ComponentPropsReactContext, ComponentPropsContext, useComponentProps, } from './components/ComponentPropsContext';
14
13
  export { Link } from './components/Link';
@@ -19,5 +18,4 @@ import * as FEaaSWrapper from './components/FEaaSWrapper';
19
18
  import * as BYOCWrapper from './components/BYOCWrapper';
20
19
  export { FEaaSWrapper };
21
20
  export { BYOCWrapper };
22
- export { ComponentBuilder } from './ComponentBuilder';
23
- export { Image, Text, DateField, FEaaSComponent, fetchFEaaSComponentServerProps, BYOCComponent, getComponentLibraryStylesheetLinks, File, ComponentLibraryLayout, DefaultEmptyFieldEditingComponentImage, DefaultEmptyFieldEditingComponentText, SitecoreContext, SitecoreContextReactContext, withSitecoreContext, useSitecoreContext, withEditorChromes, withPlaceholder, withDatasourceCheck, withFieldMetadata, withEmptyFieldEditingComponent, EditingScripts, } from '@sitecore-content-sdk/react';
21
+ export { Image, Text, DateField, FEaaSComponent, fetchFEaaSComponentServerProps, BYOCComponent, getDesignLibraryStylesheetLinks, File, DesignLibrary, DefaultEmptyFieldEditingComponentImage, DefaultEmptyFieldEditingComponentText, SitecoreContext, SitecoreContextReactContext, withSitecoreContext, useSitecoreContext, withEditorChromes, withPlaceholder, withDatasourceCheck, withFieldMetadata, withEmptyFieldEditingComponent, EditingScripts, Form, } from '@sitecore-content-sdk/react';
@@ -3,3 +3,4 @@ export { MiddlewareBase, Middleware, defineMiddleware } from './middleware';
3
3
  export { RedirectsMiddleware } from './redirects-middleware';
4
4
  export { PersonalizeMiddleware } from './personalize-middleware';
5
5
  export { MultisiteMiddleware } from './multisite-middleware';
6
+ export { SitemapMiddleware } from './sitemap-middleware';
@@ -7,9 +7,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { SiteResolver } from '@sitecore-content-sdk/core/site';
10
+ import { SITE_KEY, SiteResolver } from '@sitecore-content-sdk/core/site';
11
11
  import { debug } from '@sitecore-content-sdk/core';
12
12
  import { NextResponse } from 'next/server';
13
+ export const REWRITE_HEADER_NAME = 'x-sc-rewrite';
13
14
  /**
14
15
  * Middleware class to be extended by all middleware implementations
15
16
  */
@@ -22,8 +23,6 @@ export class MiddlewareBase extends Middleware {
22
23
  constructor(config) {
23
24
  super();
24
25
  this.config = config;
25
- this.SITE_SYMBOL = 'sc_site';
26
- this.REWRITE_HEADER_NAME = 'x-sc-rewrite';
27
26
  this.siteResolver = new SiteResolver(config.sites);
28
27
  this.defaultHostname = config.defaultHostname || 'localhost';
29
28
  }
@@ -52,7 +51,7 @@ export class MiddlewareBase extends Middleware {
52
51
  return (pathname.startsWith('/api/') || // Ignore Next.js API calls
53
52
  pathname.startsWith('/sitecore/') || // Ignore Sitecore API calls
54
53
  pathname.startsWith('/_next') || // Ignore next service calls
55
- (this.config.disabled && this.config.disabled(req, res)));
54
+ (this.config.skip && this.config.skip(req, res)));
56
55
  }
57
56
  /**
58
57
  * Safely extract all headers for debug logging
@@ -71,7 +70,7 @@ export class MiddlewareBase extends Middleware {
71
70
  * @returns {string} language
72
71
  */
73
72
  getLanguage(req) {
74
- return req.nextUrl.locale || req.nextUrl.defaultLocale || 'en';
73
+ return req.nextUrl.locale || req.nextUrl.defaultLocale || this.config.defaultLanguage || 'en';
75
74
  }
76
75
  /**
77
76
  * Extract 'host' header
@@ -82,18 +81,27 @@ export class MiddlewareBase extends Middleware {
82
81
  return (_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0];
83
82
  }
84
83
  /**
85
- * Get site information.
86
- * Can not be used in **Preview** mode, since site will not be resolved
84
+ * Get site information. If site name is stored in cookie, use it, otherwise resolve by hostname
85
+ * - If site can't be resolved by site name cookie use default site info based on provided parameters
86
+ * - If site can't be resolved by hostname throw an error
87
87
  * @param {NextRequest} req request
88
88
  * @param {NextResponse} [res] response
89
89
  * @returns {SiteInfo} site information
90
90
  */
91
91
  getSite(req, res) {
92
92
  var _a;
93
- const siteNameCookie = (_a = res === null || res === void 0 ? void 0 : res.cookies.get(this.SITE_SYMBOL)) === null || _a === void 0 ? void 0 : _a.value;
94
- if (siteNameCookie)
95
- return this.siteResolver.getByName(siteNameCookie);
93
+ const siteNameCookie = (_a = res === null || res === void 0 ? void 0 : res.cookies.get(SITE_KEY)) === null || _a === void 0 ? void 0 : _a.value;
96
94
  const hostname = this.getHostHeader(req) || this.defaultHostname;
95
+ if (siteNameCookie) {
96
+ // Usually we should be able to resolve site by cookie
97
+ // in case of Sitecore Preview mode, there can be a case that new site was created
98
+ // but it's not present in the sitemap, so we fallback to default site info
99
+ return (this.siteResolver.getByName(siteNameCookie) || {
100
+ name: siteNameCookie,
101
+ language: this.getLanguage(req),
102
+ hostName: '*',
103
+ });
104
+ }
97
105
  return this.siteResolver.getByHost(hostname);
98
106
  }
99
107
  /**
@@ -101,14 +109,17 @@ export class MiddlewareBase extends Middleware {
101
109
  * @param {string} rewritePath the destionation path
102
110
  * @param {NextRequest} req the current request
103
111
  * @param {NextResponse} res the current response
112
+ * @param {boolean} [skipHeader] don't write 'x-sc-rewrite' header
104
113
  */
105
- rewrite(rewritePath, req, res) {
114
+ rewrite(rewritePath, req, res, skipHeader) {
106
115
  // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
107
116
  const rewriteUrl = req.nextUrl.clone();
108
117
  rewriteUrl.pathname = rewritePath;
109
118
  const response = NextResponse.rewrite(rewriteUrl, res);
110
119
  // Share rewrite path with following executed middlewares
111
- response.headers.set(this.REWRITE_HEADER_NAME, rewritePath);
120
+ if (!skipHeader) {
121
+ response.headers.set(REWRITE_HEADER_NAME, rewritePath);
122
+ }
112
123
  return response;
113
124
  }
114
125
  }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
1
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -7,9 +8,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
9
  });
9
10
  };
10
- import { getSiteRewrite } from '@sitecore-content-sdk/core/site';
11
+ import { getSiteRewrite, SITE_KEY } from '@sitecore-content-sdk/core/site';
11
12
  import { debug } from '@sitecore-content-sdk/core';
12
13
  import { MiddlewareBase } from './middleware';
14
+ import { PREVIEW_KEY } from '@sitecore-content-sdk/core/editing';
13
15
  /**
14
16
  * Middleware / handler for multisite support
15
17
  */
@@ -21,7 +23,7 @@ export class MultisiteMiddleware extends MiddlewareBase {
21
23
  super(config);
22
24
  this.config = config;
23
25
  this.handle = (req, res) => __awaiter(this, void 0, void 0, function* () {
24
- var _a;
26
+ var _a, _b, _c;
25
27
  if (!this.config.enabled) {
26
28
  debug.multisite('skipped (multisite middleware is disabled globally)');
27
29
  return res;
@@ -44,12 +46,21 @@ export class MultisiteMiddleware extends MiddlewareBase {
44
46
  debug.multisite('skipped (preview)');
45
47
  return res;
46
48
  }
47
- // Site name can be forced by query string parameter or cookie
48
- const siteName = req.nextUrl.searchParams.get(this.SITE_SYMBOL) ||
49
- (this.config.useCookieResolution &&
50
- this.config.useCookieResolution(req) &&
51
- ((_a = req.cookies.get(this.SITE_SYMBOL)) === null || _a === void 0 ? void 0 : _a.value)) ||
52
- this.siteResolver.getByHost(hostname).name;
49
+ let siteName;
50
+ const isSitecorePreview = (_a = req.cookies.get(PREVIEW_KEY)) === null || _a === void 0 ? void 0 : _a.value;
51
+ if (isSitecorePreview) {
52
+ // This cookie is required to be set in the Sitecore Preview mode
53
+ siteName = (_b = req.cookies.get(SITE_KEY)) === null || _b === void 0 ? void 0 : _b.value;
54
+ }
55
+ else {
56
+ // Site name can be forced by query string parameter or cookie
57
+ siteName =
58
+ req.nextUrl.searchParams.get(SITE_KEY) ||
59
+ (this.config.useCookieResolution &&
60
+ this.config.useCookieResolution(req) &&
61
+ ((_c = req.cookies.get(SITE_KEY)) === null || _c === void 0 ? void 0 : _c.value)) ||
62
+ this.siteResolver.getByHost(hostname).name;
63
+ }
53
64
  // Rewrite to site specific path
54
65
  const rewritePath = getSiteRewrite(pathname, {
55
66
  siteName,
@@ -62,7 +73,7 @@ export class MultisiteMiddleware extends MiddlewareBase {
62
73
  sameSite: 'none',
63
74
  };
64
75
  // Share site name with the following executed middlewares
65
- response.cookies.set(this.SITE_SYMBOL, siteName, defaultCookieAttributes);
76
+ response.cookies.set(SITE_KEY, siteName, defaultCookieAttributes);
66
77
  debug.multisite('multisite middleware end in %dms: %o', Date.now() - startTimestamp, {
67
78
  rewritePath,
68
79
  siteName,
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { GraphQLPersonalizeService, getPersonalizedRewrite, CdpHelper, DEFAULT_VARIANT, } from '@sitecore-content-sdk/core/personalize';
11
11
  import { debug } from '@sitecore-content-sdk/core';
12
- import { MiddlewareBase } from './middleware';
12
+ import { MiddlewareBase, REWRITE_HEADER_NAME } from './middleware';
13
13
  import { CloudSDK } from '@sitecore-cloudsdk/core/server';
14
14
  import { personalize } from '@sitecore-cloudsdk/personalize/server';
15
15
  /**
@@ -32,7 +32,7 @@ export class PersonalizeMiddleware extends MiddlewareBase {
32
32
  const language = this.getLanguage(req);
33
33
  const hostname = this.getHostHeader(req) || this.defaultHostname;
34
34
  const startTimestamp = Date.now();
35
- const timeout = this.config.cdpTimeout;
35
+ const cdpTimeout = this.config.cdpTimeout;
36
36
  debug.personalize('personalize middleware start: %o', {
37
37
  pathname,
38
38
  language,
@@ -84,7 +84,7 @@ export class PersonalizeMiddleware extends MiddlewareBase {
84
84
  variantIds: execution.variantIds,
85
85
  params,
86
86
  language,
87
- timeout,
87
+ timeout: cdpTimeout,
88
88
  }, req).then((personalization) => {
89
89
  const variantId = personalization.variantId;
90
90
  if (variantId) {
@@ -101,7 +101,7 @@ export class PersonalizeMiddleware extends MiddlewareBase {
101
101
  return res;
102
102
  }
103
103
  // Path can be rewritten by previously executed middleware
104
- const basePath = (res === null || res === void 0 ? void 0 : res.headers.get('x-sc-rewrite')) || pathname;
104
+ const basePath = (res === null || res === void 0 ? void 0 : res.headers.get(REWRITE_HEADER_NAME)) || pathname;
105
105
  // Rewrite to persononalized path
106
106
  const rewritePath = getPersonalizedRewrite(basePath, identifiedVariantIds);
107
107
  const response = this.rewrite(rewritePath, req, res);
@@ -12,7 +12,7 @@ import { GraphQLRedirectsService, REDIRECT_TYPE_301, REDIRECT_TYPE_302, REDIRECT
12
12
  import { areURLSearchParamsEqual, escapeNonSpecialQuestionMarks, isRegexOrUrl, mergeURLSearchParams, } from '@sitecore-content-sdk/core/utils';
13
13
  import { NextResponse } from 'next/server';
14
14
  import regexParser from 'regex-parser';
15
- import { MiddlewareBase } from './middleware';
15
+ import { MiddlewareBase, REWRITE_HEADER_NAME } from './middleware';
16
16
  const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
17
17
  const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
18
18
  /**
@@ -66,6 +66,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
66
66
  debug.redirects('skipped (redirect does not exist)');
67
67
  return res;
68
68
  }
69
+ debug.redirects('Matched redirect rule: %o', { existsRedirect });
69
70
  // Find context site language and replace token
70
71
  if (REGEXP_CONTEXT_SITE_LANG.test(existsRedirect.target) &&
71
72
  !(REGEXP_ABSOLUTE_URL.test(existsRedirect.target) &&
@@ -112,7 +113,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
112
113
  return this.createRedirectResponse(url, res, 302, 'Found');
113
114
  }
114
115
  case REDIRECT_TYPE_SERVER_TRANSFER: {
115
- return this.rewrite(url.href, req, res);
116
+ return this.rewrite(url.href, req, res, true);
116
117
  }
117
118
  default:
118
119
  return res;
@@ -147,23 +148,35 @@ export class RedirectsMiddleware extends MiddlewareBase {
147
148
  */
148
149
  getExistsRedirect(req, siteName) {
149
150
  return __awaiter(this, void 0, void 0, function* () {
150
- const { pathname: targetURL, search: targetQS = '', locale } = this.normalizeUrl(req.nextUrl.clone());
151
- const normalizedPath = targetURL.replace(/\/*$/gi, '');
151
+ const { pathname: incomingURL, search: incomingQS = '' } = this.normalizeUrl(req.nextUrl.clone());
152
+ const locale = this.getLanguage(req);
153
+ const normalizedPath = incomingURL.replace(/\/*$/gi, '');
152
154
  const redirects = yield this.redirectsService.fetchRedirects(siteName);
153
155
  const language = this.getLanguage(req);
154
156
  const modifyRedirects = structuredClone(redirects);
155
157
  let matchedQueryString;
158
+ const localePath = `/${locale.toLowerCase()}${normalizedPath}`;
156
159
  return modifyRedirects.length
157
160
  ? modifyRedirects.find((redirect) => {
158
- var _a;
161
+ // process static URL (non-regex) rules
159
162
  if (isRegexOrUrl(redirect.pattern) === 'url') {
160
- const parseUrlPattern = redirect.pattern.endsWith('/')
163
+ const urlArray = redirect.pattern.endsWith('/')
161
164
  ? redirect.pattern.slice(0, -1).split('?')
162
165
  : redirect.pattern.split('?');
163
- return ((parseUrlPattern[0] === normalizedPath ||
164
- parseUrlPattern[0] === `/${locale}${normalizedPath}`) &&
165
- areURLSearchParamsEqual(new URLSearchParams((_a = parseUrlPattern[1]) !== null && _a !== void 0 ? _a : ''), new URLSearchParams(targetQS)));
166
+ const patternQS = urlArray[1];
167
+ let patternPath = urlArray[0];
168
+ // nextjs routes are case-sensitive, but locales should be compared case-insensitively
169
+ const patternParts = patternPath.split('/');
170
+ const maybeLocale = patternParts[1].toLowerCase();
171
+ // case insensitive lookup of locales
172
+ if (new RegExp(this.locales.join('|'), 'i').test(maybeLocale)) {
173
+ patternPath = patternPath.replace(`/${patternParts[1]}`, `/${maybeLocale}`);
174
+ }
175
+ return ((patternPath === localePath || patternPath === normalizedPath) &&
176
+ (!patternQS ||
177
+ areURLSearchParamsEqual(new URLSearchParams(patternQS), new URLSearchParams(incomingQS))));
166
178
  }
179
+ // process regex rules
167
180
  // Modify the redirect pattern to ignore the language prefix in the path
168
181
  // And escapes non-special "?" characters in a string or regex.
169
182
  redirect.pattern = escapeNonSpecialQuestionMarks(redirect.pattern.replace(new RegExp(`^[^]?/${language}/`, 'gi'), ''));
@@ -173,16 +186,17 @@ export class RedirectsMiddleware extends MiddlewareBase {
173
186
  .replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
174
187
  .replace(/^\^|\$$/g, '') // Further cleans up anchors
175
188
  .replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
189
+ // Redirect pattern matches the full incoming URL with query string present
176
190
  matchedQueryString = [
177
- regexParser(redirect.pattern).test(`${normalizedPath}${targetQS}`),
178
- regexParser(redirect.pattern).test(`/${locale}${normalizedPath}${targetQS}`),
191
+ regexParser(redirect.pattern).test(`/${localePath}${incomingQS}`),
192
+ regexParser(redirect.pattern).test(`${normalizedPath}${incomingQS}`),
179
193
  ].some(Boolean)
180
- ? targetQS
194
+ ? incomingQS
181
195
  : undefined;
182
196
  // Save the matched query string (if found) into the redirect object
183
197
  redirect.matchedQueryString = matchedQueryString || '';
184
- return (!!(regexParser(redirect.pattern).test(targetURL) ||
185
- regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${targetURL}`) ||
198
+ return (!!(regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${incomingURL}`) ||
199
+ regexParser(redirect.pattern).test(incomingURL) ||
186
200
  matchedQueryString) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
187
201
  })
188
202
  : undefined;
@@ -245,6 +259,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
245
259
  if (res === null || res === void 0 ? void 0 : res.headers) {
246
260
  redirect.headers.delete('x-middleware-next');
247
261
  redirect.headers.delete('x-middleware-rewrite');
262
+ redirect.headers.delete(REWRITE_HEADER_NAME);
248
263
  }
249
264
  return redirect;
250
265
  }
@@ -0,0 +1,43 @@
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
+ /**
11
+ * Middleware for handling sitemap requests in a Next.js application.
12
+ * Encapsulates all HTTP-related logic for sitemap generation and delivery.
13
+ */
14
+ export class SitemapMiddleware {
15
+ constructor(client) {
16
+ this.client = client;
17
+ }
18
+ getHandler() {
19
+ return this.handler.bind(this);
20
+ }
21
+ handler(req, res) {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ const id = Array.isArray(req.query.id) ? req.query.id[0] : req.query.id;
24
+ const reqHost = req.headers.host || '';
25
+ const reqProtocol = req.headers['x-forwarded-proto'] || 'https';
26
+ const site = this.client.resolveSite(reqHost);
27
+ const options = { reqHost, reqProtocol, id, siteName: site.name };
28
+ try {
29
+ const xmlContent = yield this.client.getSiteMap(options);
30
+ res.setHeader('Content-Type', 'text/xml;charset=utf-8');
31
+ res.send(xmlContent);
32
+ }
33
+ catch (error) {
34
+ if (error instanceof Error && error.message === 'REDIRECT_404') {
35
+ res.redirect('/404');
36
+ }
37
+ else {
38
+ res.status(500).send('Internal Server Error');
39
+ }
40
+ }
41
+ });
42
+ }
43
+ }