@sitecore-content-sdk/nextjs 0.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/LICENSE.txt +202 -0
  2. package/README.md +10 -0
  3. package/dist/cjs/ComponentBuilder.js +63 -0
  4. package/dist/cjs/components/BYOCWrapper.js +41 -0
  5. package/dist/cjs/components/ComponentPropsContext.js +57 -0
  6. package/dist/cjs/components/FEaaSWrapper.js +43 -0
  7. package/dist/cjs/components/Link.js +87 -0
  8. package/dist/cjs/components/NextImage.js +82 -0
  9. package/dist/cjs/components/Placeholder.js +49 -0
  10. package/dist/cjs/components/RichText.js +95 -0
  11. package/dist/cjs/editing/constants.js +10 -0
  12. package/dist/cjs/editing/editing-config-middleware.js +62 -0
  13. package/dist/cjs/editing/editing-render-middleware.js +182 -0
  14. package/dist/cjs/editing/feaas-render-middleware.js +101 -0
  15. package/dist/cjs/editing/index.js +16 -0
  16. package/dist/cjs/editing/render-middleware.js +43 -0
  17. package/dist/cjs/graphql/index.js +7 -0
  18. package/dist/cjs/index.js +119 -0
  19. package/dist/cjs/middleware/index.js +13 -0
  20. package/dist/cjs/middleware/middleware.js +97 -0
  21. package/dist/cjs/middleware/multisite-middleware.js +93 -0
  22. package/dist/cjs/middleware/personalize-middleware.js +231 -0
  23. package/dist/cjs/middleware/redirects-middleware.js +264 -0
  24. package/dist/cjs/monitoring/healthcheck-middleware.js +30 -0
  25. package/dist/cjs/monitoring/index.js +5 -0
  26. package/dist/cjs/services/base-graphql-sitemap-service.js +206 -0
  27. package/dist/cjs/services/component-props-service.js +167 -0
  28. package/dist/cjs/services/graphql-sitemap-service.js +64 -0
  29. package/dist/cjs/services/mutisite-graphql-sitemap-service.js +81 -0
  30. package/dist/cjs/sharedTypes/component-props.js +2 -0
  31. package/dist/cjs/sharedTypes/module-factory.js +2 -0
  32. package/dist/cjs/site/index.js +5 -0
  33. package/dist/cjs/utils/index.js +11 -0
  34. package/dist/cjs/utils/utils.js +42 -0
  35. package/dist/esm/ComponentBuilder.js +59 -0
  36. package/dist/esm/components/BYOCWrapper.js +36 -0
  37. package/dist/esm/components/ComponentPropsContext.js +19 -0
  38. package/dist/esm/components/FEaaSWrapper.js +38 -0
  39. package/dist/esm/components/Link.js +48 -0
  40. package/dist/esm/components/NextImage.js +76 -0
  41. package/dist/esm/components/Placeholder.js +12 -0
  42. package/dist/esm/components/RichText.js +55 -0
  43. package/dist/esm/editing/constants.js +7 -0
  44. package/dist/esm/editing/editing-config-middleware.js +58 -0
  45. package/dist/esm/editing/editing-render-middleware.js +177 -0
  46. package/dist/esm/editing/feaas-render-middleware.js +97 -0
  47. package/dist/esm/editing/index.js +5 -0
  48. package/dist/esm/editing/render-middleware.js +39 -0
  49. package/dist/esm/graphql/index.js +1 -0
  50. package/dist/esm/index.js +23 -0
  51. package/dist/esm/middleware/index.js +5 -0
  52. package/dist/esm/middleware/middleware.js +93 -0
  53. package/dist/esm/middleware/multisite-middleware.js +89 -0
  54. package/dist/esm/middleware/personalize-middleware.js +227 -0
  55. package/dist/esm/middleware/redirects-middleware.js +257 -0
  56. package/dist/esm/monitoring/healthcheck-middleware.js +26 -0
  57. package/dist/esm/monitoring/index.js +1 -0
  58. package/dist/esm/services/base-graphql-sitemap-service.js +201 -0
  59. package/dist/esm/services/component-props-service.js +160 -0
  60. package/dist/esm/services/graphql-sitemap-service.js +59 -0
  61. package/dist/esm/services/mutisite-graphql-sitemap-service.js +77 -0
  62. package/dist/esm/sharedTypes/component-props.js +1 -0
  63. package/dist/esm/sharedTypes/module-factory.js +1 -0
  64. package/dist/esm/site/index.js +1 -0
  65. package/dist/esm/utils/index.js +3 -0
  66. package/dist/esm/utils/utils.js +37 -0
  67. package/editing.d.ts +1 -0
  68. package/editing.js +1 -0
  69. package/global.d.ts +21 -0
  70. package/graphql.d.ts +1 -0
  71. package/graphql.js +1 -0
  72. package/middleware.d.ts +1 -0
  73. package/middleware.js +1 -0
  74. package/monitoring.d.ts +1 -0
  75. package/monitoring.js +1 -0
  76. package/package.json +92 -0
  77. package/site.d.ts +1 -0
  78. package/site.js +1 -0
  79. package/types/ComponentBuilder.d.ts +59 -0
  80. package/types/components/BYOCWrapper.d.ts +20 -0
  81. package/types/components/ComponentPropsContext.d.ts +18 -0
  82. package/types/components/FEaaSWrapper.d.ts +22 -0
  83. package/types/components/Link.d.ts +10 -0
  84. package/types/components/NextImage.d.ts +6 -0
  85. package/types/components/Placeholder.d.ts +8 -0
  86. package/types/components/RichText.d.ts +32 -0
  87. package/types/editing/constants.d.ts +7 -0
  88. package/types/editing/editing-config-middleware.d.ts +29 -0
  89. package/types/editing/editing-render-middleware.d.ts +79 -0
  90. package/types/editing/feaas-render-middleware.d.ts +32 -0
  91. package/types/editing/index.d.ts +5 -0
  92. package/types/editing/render-middleware.d.ts +24 -0
  93. package/types/graphql/index.d.ts +1 -0
  94. package/types/index.d.ts +24 -0
  95. package/types/middleware/index.d.ts +5 -0
  96. package/types/middleware/middleware.d.ts +82 -0
  97. package/types/middleware/multisite-middleware.d.ts +39 -0
  98. package/types/middleware/personalize-middleware.d.ts +102 -0
  99. package/types/middleware/redirects-middleware.d.ts +57 -0
  100. package/types/monitoring/healthcheck-middleware.d.ts +12 -0
  101. package/types/monitoring/index.d.ts +1 -0
  102. package/types/services/base-graphql-sitemap-service.d.ts +148 -0
  103. package/types/services/component-props-service.d.ts +81 -0
  104. package/types/services/graphql-sitemap-service.d.ts +51 -0
  105. package/types/services/mutisite-graphql-sitemap-service.d.ts +42 -0
  106. package/types/sharedTypes/component-props.d.ts +26 -0
  107. package/types/sharedTypes/module-factory.d.ts +32 -0
  108. package/types/site/index.d.ts +1 -0
  109. package/types/utils/index.d.ts +3 -0
  110. package/types/utils/utils.d.ts +8 -0
  111. package/utils.d.ts +1 -0
  112. package/utils.js +1 -0
@@ -0,0 +1,177 @@
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 { debug } from '@sitecore-content-sdk/core';
11
+ import { LayoutServicePageState } from '@sitecore-content-sdk/core/layout';
12
+ import { QUERY_PARAM_EDITING_SECRET, EDITING_ALLOWED_ORIGINS, } from '@sitecore-content-sdk/core/editing';
13
+ import { getJssEditingSecret } from '../utils/utils';
14
+ import { RenderMiddlewareBase } from './render-middleware';
15
+ import { enforceCors, getAllowedOriginsFromEnv } from '@sitecore-content-sdk/core/utils';
16
+ import { DEFAULT_VARIANT } from '@sitecore-content-sdk/core/personalize';
17
+ /**
18
+ * Type guard for Component Library mode
19
+ * @param {object} data preview data to check
20
+ * @returns true if the data is EditingPreviewData
21
+ * @see EditingPreviewData
22
+ */
23
+ export const isComponentLibraryPreviewData = (data) => {
24
+ return (typeof data === 'object' &&
25
+ data !== null &&
26
+ 'mode' in data &&
27
+ data.mode === 'library');
28
+ };
29
+ /**
30
+ * Middleware / handler for use in the editing render Next.js API route (e.g. '/api/editing/render')
31
+ * which is required for Sitecore editing support.
32
+ */
33
+ export class EditingRenderMiddleware extends RenderMiddlewareBase {
34
+ /**
35
+ * @param {EditingRenderMiddlewareConfig} [config] Editing render middleware config
36
+ */
37
+ constructor(config) {
38
+ super();
39
+ this.config = config;
40
+ this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
41
+ var _a, _b, _c, _d, _e;
42
+ const { query, body, method, headers } = req;
43
+ debug.editing('editing render middleware start: %o', {
44
+ method,
45
+ query,
46
+ headers,
47
+ body,
48
+ });
49
+ if (!enforceCors(req, res, EDITING_ALLOWED_ORIGINS)) {
50
+ debug.editing('invalid origin host - set allowed origins in JSS_ALLOWED_ORIGINS environment variable');
51
+ return res.status(401).json({
52
+ html: `<html><body>Requests from origin ${(_a = req.headers) === null || _a === void 0 ? void 0 : _a.origin} not allowed</body></html>`,
53
+ });
54
+ }
55
+ // Validate secret
56
+ const secret = (_b = query[QUERY_PARAM_EDITING_SECRET]) !== null && _b !== void 0 ? _b : body === null || body === void 0 ? void 0 : body.jssEditingSecret;
57
+ if (secret !== getJssEditingSecret()) {
58
+ debug.editing('invalid editing secret - sent "%s" expected "%s"', secret, getJssEditingSecret());
59
+ return res.status(401).json({
60
+ html: '<html><body>Missing or invalid secret</body></html>',
61
+ });
62
+ }
63
+ if (req.method === 'OPTIONS') {
64
+ debug.editing('preflight request');
65
+ // CORS headers are set by enforceCors
66
+ return res.status(204).send(null);
67
+ }
68
+ if (req.method !== 'GET') {
69
+ debug.editing('invalid method - sent %s expected GET', req.method);
70
+ res.setHeader('Allow', 'GET');
71
+ return res.status(405).json({
72
+ html: `<html><body>Invalid request method '${req.method}'</body></html>`,
73
+ });
74
+ }
75
+ const startTimestamp = Date.now();
76
+ const mode = query.mode;
77
+ const defaultRequiredParams = ['sc_site', 'sc_itemid', 'sc_lang', 'route', 'mode'];
78
+ const componentRequiredParams = [
79
+ 'sc_site',
80
+ 'sc_itemid',
81
+ 'sc_renderingId',
82
+ 'sc_uid',
83
+ 'sc_lang',
84
+ 'mode',
85
+ ];
86
+ const requiredQueryParams = mode === 'library' ? componentRequiredParams : defaultRequiredParams;
87
+ const missingQueryParams = requiredQueryParams.filter((param) => !query[param]);
88
+ // Validate query parameters
89
+ if (missingQueryParams.length) {
90
+ debug.editing('missing required query parameters: %o', missingQueryParams);
91
+ return res.status(400).json({
92
+ html: `<html><body>Missing required query parameters: ${missingQueryParams.join(', ')}</body></html>`,
93
+ });
94
+ }
95
+ if (mode === 'library') {
96
+ // dedicated route and layout to SSR component library
97
+ query.route = '/component-library/render';
98
+ res.setPreviewData({
99
+ itemId: query.sc_itemid,
100
+ componentUid: query.sc_uid,
101
+ renderingId: query.sc_renderingId,
102
+ language: query.sc_lang,
103
+ site: query.sc_site,
104
+ pageState: LayoutServicePageState.Normal,
105
+ mode: 'library',
106
+ dataSourceId: query.sc_datasourceId,
107
+ version: query.sc_version,
108
+ }, {
109
+ maxAge: 3,
110
+ });
111
+ }
112
+ else {
113
+ res.setPreviewData({
114
+ site: query.sc_site,
115
+ itemId: query.sc_itemid,
116
+ language: query.sc_lang,
117
+ // for sc_variantId we may employ multiple variants (page-layout + component level)
118
+ variantIds: ((_c = query.sc_variant) === null || _c === void 0 ? void 0 : _c.split(',')) || [DEFAULT_VARIANT],
119
+ version: query.sc_version,
120
+ pageState: query.mode,
121
+ layoutKind: query.sc_layoutKind,
122
+ },
123
+ // Cache the preview data for 3 seconds to ensure the page is rendered with the correct preview data not the cached one
124
+ {
125
+ maxAge: 3,
126
+ });
127
+ }
128
+ // Cookies with the SameSite=Lax policy set by Next.js setPreviewData function causes CORS issue
129
+ // when Next.js preview mode is activated, resulting the page to render in normal mode instead.
130
+ // By replacing it with "SameSite=None; Secure", we ensure cookies are correctly sent with
131
+ // cross-origin requests, allowing the page to be editable. This change should be reverted
132
+ // once vercel addresses this open issue: https://github.com/vercel/next.js/issues/49927
133
+ const setCookieHeader = res.getHeader('Set-Cookie');
134
+ if (setCookieHeader && Array.isArray(setCookieHeader)) {
135
+ const modifiedCookies = setCookieHeader.map((cookie) => {
136
+ const cookieIdentifiers = {
137
+ __prerender_bypass: /^__prerender_bypass=/,
138
+ __next_preview_data: /^__next_preview_data=/,
139
+ };
140
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
141
+ for (const [_, regex] of Object.entries(cookieIdentifiers)) {
142
+ if (cookie.match(regex)) {
143
+ return cookie.replace(/SameSite=Lax/, 'SameSite=None; Secure');
144
+ }
145
+ }
146
+ return cookie;
147
+ });
148
+ res.setHeader('Set-Cookie', modifiedCookies);
149
+ }
150
+ 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;
151
+ debug.editing('editing render middleware end in %dms: redirect %o', Date.now() - startTimestamp, {
152
+ status: 307,
153
+ route,
154
+ });
155
+ // Restrict the page to be rendered only within the allowed origins
156
+ res.setHeader('Content-Security-Policy', this.getSCPHeader());
157
+ res.redirect(route);
158
+ });
159
+ }
160
+ /**
161
+ * Gets the Next.js API route handler
162
+ * @returns route handler
163
+ */
164
+ getHandler() {
165
+ return this.handler;
166
+ }
167
+ /**
168
+ * Gets the Content-Security-Policy header value
169
+ * @returns Content-Security-Policy header value
170
+ */
171
+ getSCPHeader() {
172
+ return `frame-ancestors 'self' ${[
173
+ ...getAllowedOriginsFromEnv(),
174
+ ...EDITING_ALLOWED_ORIGINS,
175
+ ].join(' ')}`;
176
+ }
177
+ }
@@ -0,0 +1,97 @@
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 { debug } from '@sitecore-content-sdk/core';
11
+ import { EDITING_ALLOWED_ORIGINS, QUERY_PARAM_EDITING_SECRET, } from '@sitecore-content-sdk/core/editing';
12
+ import { getJssEditingSecret } from '../utils/utils';
13
+ import { RenderMiddlewareBase } from './render-middleware';
14
+ import { enforceCors } from '@sitecore-content-sdk/core/utils';
15
+ /**
16
+ * Middleware / handler for use in the feaas render Next.js API route (e.g. '/api/editing/feaas/render')
17
+ * which is required for Sitecore editing support.
18
+ */
19
+ export class FEAASRenderMiddleware extends RenderMiddlewareBase {
20
+ /**
21
+ * @param {EditingRenderMiddlewareConfig} [config] Editing render middleware config
22
+ */
23
+ constructor(config) {
24
+ var _a;
25
+ super();
26
+ this.config = config;
27
+ this.defaultPageUrl = '/feaas/render';
28
+ this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
29
+ var _a;
30
+ const { method, query, headers } = req;
31
+ const startTimestamp = Date.now();
32
+ debug.editing('feaas render middleware start: %o', {
33
+ method,
34
+ query,
35
+ headers,
36
+ });
37
+ if (!enforceCors(req, res, EDITING_ALLOWED_ORIGINS)) {
38
+ debug.editing('invalid origin host - set allowed origins in JSS_ALLOWED_ORIGINS environment variable');
39
+ return res
40
+ .status(401)
41
+ .send(`<html><body>Requests from origin ${(_a = req.headers) === null || _a === void 0 ? void 0 : _a.origin} are not allowed</body></html>`);
42
+ }
43
+ if (!method || !['GET', 'OPTIONS'].includes(method)) {
44
+ debug.editing('invalid method - sent %s expected GET,OPTIONS', method);
45
+ res.setHeader('Allow', 'GET, OPTIONS');
46
+ return res.status(405).send(`<html><body>Invalid request method '${method}'</body></html>`);
47
+ }
48
+ // Validate secret
49
+ const secret = query[QUERY_PARAM_EDITING_SECRET];
50
+ if (secret !== getJssEditingSecret()) {
51
+ debug.editing('invalid editing secret - sent "%s" expected "%s"', secret, getJssEditingSecret());
52
+ return res.status(401).send('<html><body>Missing or invalid secret</body></html>');
53
+ }
54
+ // Handle preflight request
55
+ if (method === 'OPTIONS') {
56
+ debug.editing('preflight request');
57
+ // CORS headers are set by enforceCors
58
+ return res.status(204).send(null);
59
+ }
60
+ try {
61
+ // Get query string parameters to propagate on subsequent requests (e.g. for deployment protection bypass)
62
+ const params = this.getQueryParamsForPropagation(query);
63
+ // Enable Next.js Preview Mode
64
+ res.setPreviewData({});
65
+ const queryParams = new URLSearchParams();
66
+ for (const key in params) {
67
+ if ({}.hasOwnProperty.call(params, key)) {
68
+ queryParams.append(key, params[key]);
69
+ }
70
+ }
71
+ // Pass "feaasSrc" in case a FEAASComponent is being requested
72
+ if (query.feaasSrc) {
73
+ queryParams.append('feaasSrc', query.feaasSrc);
74
+ }
75
+ const redirectUrl = this.pageUrl + (queryParams.toString() ? `?${queryParams.toString()}` : '');
76
+ debug.editing('redirecting to page route %s', redirectUrl);
77
+ debug.editing('feaas render middleware end in %dms', Date.now() - startTimestamp);
78
+ res.redirect(redirectUrl);
79
+ }
80
+ catch (err) {
81
+ const error = err;
82
+ console.info(
83
+ // eslint-disable-next-line quotes
84
+ "Hint: for non-standard server or Next.js route configurations, you may need to override the 'pageUrl' available on the 'FEAASRenderMiddleware' config.");
85
+ res.status(500).send(`<html><body>${error}</body></html>`);
86
+ }
87
+ });
88
+ this.pageUrl = (_a = config === null || config === void 0 ? void 0 : config.pageUrl) !== null && _a !== void 0 ? _a : this.defaultPageUrl;
89
+ }
90
+ /**
91
+ * Gets the Next.js API route handler
92
+ * @returns route handler
93
+ */
94
+ getHandler() {
95
+ return this.handler;
96
+ }
97
+ }
@@ -0,0 +1,5 @@
1
+ export { GraphQLEditingService } from '@sitecore-content-sdk/core/editing';
2
+ export { EditingRenderMiddleware, isComponentLibraryPreviewData, } from './editing-render-middleware';
3
+ export { FEAASRenderMiddleware } from './feaas-render-middleware';
4
+ export { EditingConfigMiddleware, } from './editing-config-middleware';
5
+ export { RenderingType, EDITING_COMPONENT_PLACEHOLDER, EDITING_COMPONENT_ID, } from '@sitecore-content-sdk/core/layout';
@@ -0,0 +1,39 @@
1
+ import { QUERY_PARAM_VERCEL_PROTECTION_BYPASS, QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE, EDITING_PASS_THROUGH_HEADERS, } from './constants';
2
+ /**
3
+ * Base class for middleware that handles pages and components rendering in Sitecore Editors.
4
+ */
5
+ export class RenderMiddlewareBase {
6
+ constructor() {
7
+ /**
8
+ * Gets query parameters that should be passed along to subsequent requests (e.g. for deployment protection bypass)
9
+ * @param {object} query Object of query parameters from incoming URL
10
+ * @returns Object of approved query parameters
11
+ */
12
+ this.getQueryParamsForPropagation = (query) => {
13
+ const params = {};
14
+ if (query[QUERY_PARAM_VERCEL_PROTECTION_BYPASS]) {
15
+ params[QUERY_PARAM_VERCEL_PROTECTION_BYPASS] = query[QUERY_PARAM_VERCEL_PROTECTION_BYPASS];
16
+ }
17
+ if (query[QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE]) {
18
+ params[QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE] = query[QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE];
19
+ }
20
+ return params;
21
+ };
22
+ /**
23
+ * Get headers that should be passed along to subsequent requests
24
+ * @param {IncomingHttpHeaders} headers Incoming HTTP Headers
25
+ * @returns Object of approved headers
26
+ */
27
+ this.getHeadersForPropagation = (headers) => {
28
+ // Filter and normalize headers
29
+ const filteredHeaders = EDITING_PASS_THROUGH_HEADERS.reduce((acc, header) => {
30
+ const value = headers[header];
31
+ if (value) {
32
+ acc[header] = Array.isArray(value) ? value.join(', ') : value;
33
+ }
34
+ return acc;
35
+ }, {});
36
+ return filteredHeaders;
37
+ };
38
+ }
39
+ }
@@ -0,0 +1 @@
1
+ export { DefaultRetryStrategy, GraphQLRequestClient, getEdgeProxyContentUrl, } from '@sitecore-content-sdk/core/graphql';
@@ -0,0 +1,23 @@
1
+ export { constants,
2
+ // generic data access
3
+ NativeDataFetcher, enableDebug, debug, MemoryCacheClient, } from '@sitecore-content-sdk/core';
4
+ export { LayoutServicePageState, GraphQLLayoutService, getChildPlaceholder, getFieldValue, getContentStylesheetLink, EditMode, } from '@sitecore-content-sdk/core/layout';
5
+ export { RestComponentLayoutService } from '@sitecore-content-sdk/core/editing';
6
+ export { mediaApi } from '@sitecore-content-sdk/core/media';
7
+ export { GraphQLDictionaryService, } from '@sitecore-content-sdk/core/i18n';
8
+ export { personalizeLayout, getPersonalizedRewrite, getPersonalizedRewriteData, getGroomedVariantIds, normalizePersonalizedRewrite, CdpHelper, } from '@sitecore-content-sdk/core/personalize';
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';
12
+ export { GraphQLSitemapXmlService, GraphQLErrorPagesService, GraphQLRobotsService, SiteResolver, GraphQLSiteInfoService, getSiteRewrite, getSiteRewriteData, normalizeSiteRewrite, } from '@sitecore-content-sdk/core/site';
13
+ export { ComponentPropsReactContext, ComponentPropsContext, useComponentProps, } from './components/ComponentPropsContext';
14
+ export { Link } from './components/Link';
15
+ export { RichText } from './components/RichText';
16
+ export { Placeholder } from './components/Placeholder';
17
+ export { NextImage } from './components/NextImage';
18
+ import * as FEaaSWrapper from './components/FEaaSWrapper';
19
+ import * as BYOCWrapper from './components/BYOCWrapper';
20
+ export { FEaaSWrapper };
21
+ 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';
@@ -0,0 +1,5 @@
1
+ export { debug } from '@sitecore-content-sdk/core';
2
+ export { MiddlewareBase } from './middleware';
3
+ export { RedirectsMiddleware } from './redirects-middleware';
4
+ export { PersonalizeMiddleware } from './personalize-middleware';
5
+ export { MultisiteMiddleware } from './multisite-middleware';
@@ -0,0 +1,93 @@
1
+ import { NextResponse } from 'next/server';
2
+ export class MiddlewareBase {
3
+ constructor(config) {
4
+ this.config = config;
5
+ this.SITE_SYMBOL = 'sc_site';
6
+ this.REWRITE_HEADER_NAME = 'x-sc-rewrite';
7
+ this.defaultHostname = config.defaultHostname || 'localhost';
8
+ }
9
+ /**
10
+ * Determines if mode is preview
11
+ * @param {NextRequest} req request
12
+ * @returns {boolean} is preview
13
+ */
14
+ isPreview(req) {
15
+ var _a, _b;
16
+ return !!(((_a = req.cookies.get('__prerender_bypass')) === null || _a === void 0 ? void 0 : _a.value) || ((_b = req.cookies.get('__next_preview_data')) === null || _b === void 0 ? void 0 : _b.value));
17
+ }
18
+ /**
19
+ * Determines if the request is a Next.js (next/link) prefetch request
20
+ * @param {NextRequest} req request
21
+ * @returns {boolean} is prefetch
22
+ */
23
+ isPrefetch(req) {
24
+ return (
25
+ // eslint-disable-next-line prettier/prettier
26
+ req.headers.get('purpose') === 'prefetch' || req.headers.get('Next-Router-Prefetch') === '1' // Pages Router // App Router
27
+ );
28
+ }
29
+ excludeRoute(pathname) {
30
+ var _a, _b;
31
+ return (pathname.startsWith('/api/') || // Ignore Next.js API calls
32
+ pathname.startsWith('/sitecore/') || // Ignore Sitecore API calls
33
+ pathname.startsWith('/_next') || // Ignore next service calls
34
+ (((_a = this.config) === null || _a === void 0 ? void 0 : _a.excludeRoute) && ((_b = this.config) === null || _b === void 0 ? void 0 : _b.excludeRoute(pathname))));
35
+ }
36
+ /**
37
+ * Safely extract all headers for debug logging
38
+ * Necessary to avoid middleware issue https://github.com/vercel/next.js/issues/39765
39
+ * @param {Headers} incomingHeaders Incoming headers
40
+ * @returns Object with headers as key/value pairs
41
+ */
42
+ extractDebugHeaders(incomingHeaders) {
43
+ const headers = {};
44
+ incomingHeaders.forEach((value, key) => (headers[key] = value));
45
+ return headers;
46
+ }
47
+ /**
48
+ * Provides used language
49
+ * @param {NextRequest} req request
50
+ * @returns {string} language
51
+ */
52
+ getLanguage(req) {
53
+ return req.nextUrl.locale || req.nextUrl.defaultLocale || 'en';
54
+ }
55
+ /**
56
+ * Extract 'host' header
57
+ * @param {NextRequest} req request
58
+ */
59
+ getHostHeader(req) {
60
+ var _a;
61
+ return (_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0];
62
+ }
63
+ /**
64
+ * Get site information.
65
+ * Can not be used in **Preview** mode, since site will not be resolved
66
+ * @param {NextRequest} req request
67
+ * @param {NextResponse} [res] response
68
+ * @returns {SiteInfo} site information
69
+ */
70
+ getSite(req, res) {
71
+ var _a;
72
+ const siteNameCookie = (_a = res === null || res === void 0 ? void 0 : res.cookies.get(this.SITE_SYMBOL)) === null || _a === void 0 ? void 0 : _a.value;
73
+ if (siteNameCookie)
74
+ return this.config.siteResolver.getByName(siteNameCookie);
75
+ const hostname = this.getHostHeader(req) || this.defaultHostname;
76
+ return this.config.siteResolver.getByHost(hostname);
77
+ }
78
+ /**
79
+ * Create a rewrite response
80
+ * @param {string} rewritePath the destionation path
81
+ * @param {NextRequest} req the current request
82
+ * @param {NextResponse} res the current response
83
+ */
84
+ rewrite(rewritePath, req, res) {
85
+ // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
86
+ const rewriteUrl = req.nextUrl.clone();
87
+ rewriteUrl.pathname = rewritePath;
88
+ const response = NextResponse.rewrite(rewriteUrl, res);
89
+ // Share rewrite path with following executed middlewares
90
+ response.headers.set(this.REWRITE_HEADER_NAME, rewritePath);
91
+ return response;
92
+ }
93
+ }
@@ -0,0 +1,89 @@
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-content-sdk/core/site';
12
+ import { debug } from '@sitecore-content-sdk/core';
13
+ import { MiddlewareBase } from './middleware';
14
+ /**
15
+ * Middleware / handler for multisite support
16
+ */
17
+ export class MultisiteMiddleware extends MiddlewareBase {
18
+ /**
19
+ * @param {MultisiteMiddlewareConfig} [config] Multisite middleware config
20
+ */
21
+ constructor(config) {
22
+ super(config);
23
+ this.config = config;
24
+ this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
25
+ var _a;
26
+ const pathname = req.nextUrl.pathname;
27
+ const language = this.getLanguage(req);
28
+ const hostname = this.getHostHeader(req) || this.defaultHostname;
29
+ const startTimestamp = Date.now();
30
+ debug.multisite('multisite middleware start: %o', {
31
+ pathname,
32
+ language,
33
+ hostname,
34
+ });
35
+ // Response will be provided if other middleware is run before us
36
+ let response = res || NextResponse.next();
37
+ if (this.isPreview(req) || this.excludeRoute(pathname)) {
38
+ debug.multisite('skipped (%s)', this.isPreview(req) ? 'preview' : '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(this.SITE_SYMBOL) ||
43
+ (this.config.useCookieResolution &&
44
+ this.config.useCookieResolution(req) &&
45
+ ((_a = req.cookies.get(this.SITE_SYMBOL)) === null || _a === void 0 ? void 0 : _a.value)) ||
46
+ this.config.siteResolver.getByHost(hostname).name;
47
+ // Rewrite to site specific path
48
+ const rewritePath = getSiteRewrite(pathname, {
49
+ siteName,
50
+ });
51
+ response = this.rewrite(rewritePath, req, response);
52
+ // default site cookie attributes
53
+ const defaultCookieAttributes = {
54
+ secure: true,
55
+ httpOnly: true,
56
+ sameSite: 'none',
57
+ };
58
+ // Share site name with the following executed middlewares
59
+ response.cookies.set(this.SITE_SYMBOL, siteName, defaultCookieAttributes);
60
+ debug.multisite('multisite middleware end in %dms: %o', Date.now() - startTimestamp, {
61
+ rewritePath,
62
+ siteName,
63
+ headers: this.extractDebugHeaders(response.headers),
64
+ cookies: response.cookies,
65
+ });
66
+ return response;
67
+ });
68
+ }
69
+ /**
70
+ * Gets the Next.js middleware handler with error handling
71
+ * @returns middleware handler
72
+ */
73
+ getHandler() {
74
+ return (req, res) => __awaiter(this, void 0, void 0, function* () {
75
+ try {
76
+ return yield this.handler(req, res);
77
+ }
78
+ catch (error) {
79
+ console.log('Multisite middleware failed:');
80
+ console.log(error);
81
+ return res || NextResponse.next();
82
+ }
83
+ });
84
+ }
85
+ excludeRoute(pathname) {
86
+ // ignore files
87
+ return pathname.includes('.') || super.excludeRoute(pathname);
88
+ }
89
+ }