@sitecore-content-sdk/nextjs 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +202 -0
- package/README.md +10 -0
- package/dist/cjs/ComponentBuilder.js +63 -0
- package/dist/cjs/components/BYOCWrapper.js +41 -0
- package/dist/cjs/components/ComponentPropsContext.js +57 -0
- package/dist/cjs/components/FEaaSWrapper.js +43 -0
- package/dist/cjs/components/Link.js +87 -0
- package/dist/cjs/components/NextImage.js +82 -0
- package/dist/cjs/components/Placeholder.js +49 -0
- package/dist/cjs/components/RichText.js +95 -0
- package/dist/cjs/editing/constants.js +10 -0
- package/dist/cjs/editing/editing-config-middleware.js +62 -0
- package/dist/cjs/editing/editing-render-middleware.js +182 -0
- package/dist/cjs/editing/feaas-render-middleware.js +101 -0
- package/dist/cjs/editing/index.js +16 -0
- package/dist/cjs/editing/render-middleware.js +43 -0
- package/dist/cjs/graphql/index.js +7 -0
- package/dist/cjs/index.js +119 -0
- package/dist/cjs/middleware/index.js +13 -0
- package/dist/cjs/middleware/middleware.js +97 -0
- package/dist/cjs/middleware/multisite-middleware.js +93 -0
- package/dist/cjs/middleware/personalize-middleware.js +231 -0
- package/dist/cjs/middleware/redirects-middleware.js +264 -0
- package/dist/cjs/monitoring/healthcheck-middleware.js +30 -0
- package/dist/cjs/monitoring/index.js +5 -0
- package/dist/cjs/services/base-graphql-sitemap-service.js +206 -0
- package/dist/cjs/services/component-props-service.js +167 -0
- package/dist/cjs/services/graphql-sitemap-service.js +64 -0
- package/dist/cjs/services/mutisite-graphql-sitemap-service.js +81 -0
- package/dist/cjs/sharedTypes/component-props.js +2 -0
- package/dist/cjs/sharedTypes/module-factory.js +2 -0
- package/dist/cjs/site/index.js +5 -0
- package/dist/cjs/utils/index.js +11 -0
- package/dist/cjs/utils/utils.js +42 -0
- package/dist/esm/ComponentBuilder.js +59 -0
- package/dist/esm/components/BYOCWrapper.js +36 -0
- package/dist/esm/components/ComponentPropsContext.js +19 -0
- package/dist/esm/components/FEaaSWrapper.js +38 -0
- package/dist/esm/components/Link.js +48 -0
- package/dist/esm/components/NextImage.js +76 -0
- package/dist/esm/components/Placeholder.js +12 -0
- package/dist/esm/components/RichText.js +55 -0
- package/dist/esm/editing/constants.js +7 -0
- package/dist/esm/editing/editing-config-middleware.js +58 -0
- package/dist/esm/editing/editing-render-middleware.js +177 -0
- package/dist/esm/editing/feaas-render-middleware.js +97 -0
- package/dist/esm/editing/index.js +5 -0
- package/dist/esm/editing/render-middleware.js +39 -0
- package/dist/esm/graphql/index.js +1 -0
- package/dist/esm/index.js +23 -0
- package/dist/esm/middleware/index.js +5 -0
- package/dist/esm/middleware/middleware.js +93 -0
- package/dist/esm/middleware/multisite-middleware.js +89 -0
- package/dist/esm/middleware/personalize-middleware.js +227 -0
- package/dist/esm/middleware/redirects-middleware.js +257 -0
- package/dist/esm/monitoring/healthcheck-middleware.js +26 -0
- package/dist/esm/monitoring/index.js +1 -0
- package/dist/esm/services/base-graphql-sitemap-service.js +201 -0
- package/dist/esm/services/component-props-service.js +160 -0
- package/dist/esm/services/graphql-sitemap-service.js +59 -0
- package/dist/esm/services/mutisite-graphql-sitemap-service.js +77 -0
- package/dist/esm/sharedTypes/component-props.js +1 -0
- package/dist/esm/sharedTypes/module-factory.js +1 -0
- package/dist/esm/site/index.js +1 -0
- package/dist/esm/utils/index.js +3 -0
- package/dist/esm/utils/utils.js +37 -0
- package/editing.d.ts +1 -0
- package/editing.js +1 -0
- package/global.d.ts +21 -0
- package/graphql.d.ts +1 -0
- package/graphql.js +1 -0
- package/middleware.d.ts +1 -0
- package/middleware.js +1 -0
- package/monitoring.d.ts +1 -0
- package/monitoring.js +1 -0
- package/package.json +92 -0
- package/site.d.ts +1 -0
- package/site.js +1 -0
- package/types/ComponentBuilder.d.ts +59 -0
- package/types/components/BYOCWrapper.d.ts +20 -0
- package/types/components/ComponentPropsContext.d.ts +18 -0
- package/types/components/FEaaSWrapper.d.ts +22 -0
- package/types/components/Link.d.ts +10 -0
- package/types/components/NextImage.d.ts +6 -0
- package/types/components/Placeholder.d.ts +8 -0
- package/types/components/RichText.d.ts +32 -0
- package/types/editing/constants.d.ts +7 -0
- package/types/editing/editing-config-middleware.d.ts +29 -0
- package/types/editing/editing-render-middleware.d.ts +79 -0
- package/types/editing/feaas-render-middleware.d.ts +32 -0
- package/types/editing/index.d.ts +5 -0
- package/types/editing/render-middleware.d.ts +24 -0
- package/types/graphql/index.d.ts +1 -0
- package/types/index.d.ts +24 -0
- package/types/middleware/index.d.ts +5 -0
- package/types/middleware/middleware.d.ts +82 -0
- package/types/middleware/multisite-middleware.d.ts +39 -0
- package/types/middleware/personalize-middleware.d.ts +102 -0
- package/types/middleware/redirects-middleware.d.ts +57 -0
- package/types/monitoring/healthcheck-middleware.d.ts +12 -0
- package/types/monitoring/index.d.ts +1 -0
- package/types/services/base-graphql-sitemap-service.d.ts +148 -0
- package/types/services/component-props-service.d.ts +81 -0
- package/types/services/graphql-sitemap-service.d.ts +51 -0
- package/types/services/mutisite-graphql-sitemap-service.d.ts +42 -0
- package/types/sharedTypes/component-props.d.ts +26 -0
- package/types/sharedTypes/module-factory.d.ts +32 -0
- package/types/site/index.d.ts +1 -0
- package/types/utils/index.d.ts +3 -0
- package/types/utils/utils.d.ts +8 -0
- package/utils.d.ts +1 -0
- package/utils.js +1 -0
|
@@ -0,0 +1,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
|
+
}
|