@sitecore-jss/sitecore-jss-nextjs 22.1.0-canary.3 → 22.1.0-canary.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/Link.js +2 -5
- package/dist/cjs/components/NextImage.js +3 -7
- package/dist/cjs/components/RichText.js +2 -6
- package/dist/cjs/editing/constants.js +5 -1
- package/dist/cjs/editing/editing-config-middleware.js +8 -0
- package/dist/cjs/editing/editing-data-middleware.js +6 -0
- package/dist/cjs/editing/editing-render-middleware.js +205 -94
- package/dist/cjs/editing/feaas-render-middleware.js +8 -0
- package/dist/cjs/editing/index.js +4 -1
- package/dist/cjs/index.js +5 -2
- package/dist/cjs/utils/index.js +4 -3
- package/dist/cjs/utils/utils.js +3 -3
- package/dist/esm/components/Link.js +2 -5
- package/dist/esm/components/NextImage.js +4 -7
- package/dist/esm/components/RichText.js +2 -6
- package/dist/esm/editing/constants.js +4 -0
- package/dist/esm/editing/editing-config-middleware.js +9 -1
- package/dist/esm/editing/editing-data-middleware.js +7 -1
- package/dist/esm/editing/editing-render-middleware.js +203 -94
- package/dist/esm/editing/feaas-render-middleware.js +9 -1
- package/dist/esm/editing/index.js +2 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/utils/index.js +2 -1
- package/dist/esm/utils/utils.js +1 -1
- package/package.json +7 -7
- package/types/ComponentBuilder.d.ts +3 -5
- package/types/components/Placeholder.d.ts +7 -2
- package/types/components/RichText.d.ts +3 -4
- package/types/editing/constants.d.ts +4 -0
- package/types/editing/editing-config-middleware.d.ts +7 -0
- package/types/editing/editing-data-service.d.ts +1 -0
- package/types/editing/editing-render-middleware.d.ts +111 -23
- package/types/editing/index.d.ts +2 -1
- package/types/index.d.ts +2 -2
- package/types/utils/index.d.ts +2 -1
|
@@ -14,7 +14,7 @@ import PropTypes from 'prop-types';
|
|
|
14
14
|
import NextLink from 'next/link';
|
|
15
15
|
import { Link as ReactLink, LinkPropTypes, } from '@sitecore-jss/sitecore-jss-react';
|
|
16
16
|
export const Link = forwardRef((props, ref) => {
|
|
17
|
-
const { field, editable, children, internalLinkMatcher = /^\//g, showLinkTextWithChildrenPresent } = props, htmlLinkProps = __rest(props, ["field", "editable", "children", "internalLinkMatcher", "showLinkTextWithChildrenPresent"]);
|
|
17
|
+
const { field, editable = true, children, internalLinkMatcher = /^\//g, showLinkTextWithChildrenPresent } = props, htmlLinkProps = __rest(props, ["field", "editable", "children", "internalLinkMatcher", "showLinkTextWithChildrenPresent"]);
|
|
18
18
|
if (!field ||
|
|
19
19
|
(!field.editable && !field.value && !field.href)) {
|
|
20
20
|
return null;
|
|
@@ -23,7 +23,7 @@ export const Link = forwardRef((props, ref) => {
|
|
|
23
23
|
? field
|
|
24
24
|
: field.value);
|
|
25
25
|
const { href, querystring, anchor } = value;
|
|
26
|
-
const isEditing = editable && field.editable;
|
|
26
|
+
const isEditing = editable && (field.editable || field.metadata);
|
|
27
27
|
if (href && !isEditing) {
|
|
28
28
|
const text = showLinkTextWithChildrenPresent || !children ? value.text || value.href : null;
|
|
29
29
|
// determine if a link is a route or not.
|
|
@@ -38,8 +38,5 @@ export const Link = forwardRef((props, ref) => {
|
|
|
38
38
|
delete reactLinkProps.internalLinkMatcher;
|
|
39
39
|
return React.createElement(ReactLink, Object.assign({}, reactLinkProps, { ref: ref }));
|
|
40
40
|
});
|
|
41
|
-
Link.defaultProps = {
|
|
42
|
-
editable: true,
|
|
43
|
-
};
|
|
44
41
|
Link.displayName = 'NextLink';
|
|
45
42
|
Link.propTypes = Object.assign({ internalLinkMatcher: PropTypes.instanceOf(RegExp) }, LinkPropTypes);
|
|
@@ -12,10 +12,10 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { mediaApi } from '@sitecore-jss/sitecore-jss/media';
|
|
13
13
|
import PropTypes from 'prop-types';
|
|
14
14
|
import React from 'react';
|
|
15
|
-
import { getEEMarkup, } from '@sitecore-jss/sitecore-jss-react';
|
|
15
|
+
import { getEEMarkup, withFieldMetadata, } from '@sitecore-jss/sitecore-jss-react';
|
|
16
16
|
import Image from 'next/image';
|
|
17
|
-
export const NextImage = (_a) => {
|
|
18
|
-
var { editable, imageParams, field, mediaUrlPrefix, fill, priority } = _a, otherProps = __rest(_a, ["editable", "imageParams", "field", "mediaUrlPrefix", "fill", "priority"]);
|
|
17
|
+
export const NextImage = withFieldMetadata((_a) => {
|
|
18
|
+
var { editable = true, imageParams, field, mediaUrlPrefix, fill, priority } = _a, otherProps = __rest(_a, ["editable", "imageParams", "field", "mediaUrlPrefix", "fill", "priority"]);
|
|
19
19
|
// next handles src and we use a custom loader,
|
|
20
20
|
// throw error if these are present
|
|
21
21
|
if (otherProps.src) {
|
|
@@ -53,7 +53,7 @@ export const NextImage = (_a) => {
|
|
|
53
53
|
return React.createElement(Image, Object.assign({ alt: "" }, imageProps));
|
|
54
54
|
}
|
|
55
55
|
return null; // we can't handle the truth
|
|
56
|
-
};
|
|
56
|
+
});
|
|
57
57
|
NextImage.propTypes = {
|
|
58
58
|
field: PropTypes.oneOfType([
|
|
59
59
|
PropTypes.shape({
|
|
@@ -68,7 +68,4 @@ NextImage.propTypes = {
|
|
|
68
68
|
mediaUrlPrefix: PropTypes.instanceOf(RegExp),
|
|
69
69
|
imageParams: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.number.isRequired, PropTypes.string.isRequired]).isRequired),
|
|
70
70
|
};
|
|
71
|
-
NextImage.defaultProps = {
|
|
72
|
-
editable: true,
|
|
73
|
-
};
|
|
74
71
|
NextImage.displayName = 'NextImage';
|
|
@@ -15,9 +15,9 @@ import { useRouter } from 'next/router';
|
|
|
15
15
|
import { RichText as ReactRichText, RichTextPropTypes, } from '@sitecore-jss/sitecore-jss-react';
|
|
16
16
|
const prefetched = {};
|
|
17
17
|
export const RichText = (props) => {
|
|
18
|
-
const { internalLinksSelector = 'a[href^="/"]', prefetchLinks = true } = props, rest = __rest(props, ["internalLinksSelector", "prefetchLinks"]);
|
|
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;
|
|
20
|
-
const isEditing =
|
|
20
|
+
const isEditing = editable && props.field && props.field.editable;
|
|
21
21
|
const router = useRouter();
|
|
22
22
|
const richTextRef = useRef(null);
|
|
23
23
|
useEffect(() => {
|
|
@@ -52,8 +52,4 @@ export const RichText = (props) => {
|
|
|
52
52
|
return React.createElement(ReactRichText, Object.assign({ ref: richTextRef }, rest));
|
|
53
53
|
};
|
|
54
54
|
RichText.propTypes = Object.assign({ internalLinksSelector: PropTypes.string }, RichTextPropTypes);
|
|
55
|
-
RichText.defaultProps = {
|
|
56
|
-
tag: 'div',
|
|
57
|
-
editable: true,
|
|
58
|
-
};
|
|
59
55
|
RichText.displayName = 'NextRichText';
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export const QUERY_PARAM_EDITING_SECRET = 'secret';
|
|
2
2
|
export const QUERY_PARAM_PROTECTION_BYPASS_SITECORE = 'x-sitecore-protection-bypass';
|
|
3
3
|
export const QUERY_PARAM_PROTECTION_BYPASS_VERCEL = 'x-vercel-protection-bypass';
|
|
4
|
+
/**
|
|
5
|
+
* Default allowed origins for editing requests. This is used to enforce CORS, CSP headers.
|
|
6
|
+
*/
|
|
7
|
+
export const EDITING_ALLOWED_ORIGINS = ['https://pages*.cloud', 'https://pages.sitecorecloud.io'];
|
|
@@ -7,9 +7,11 @@ 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 { QUERY_PARAM_EDITING_SECRET } from './constants';
|
|
10
|
+
import { EDITING_ALLOWED_ORIGINS, QUERY_PARAM_EDITING_SECRET } from './constants';
|
|
11
11
|
import { getJssEditingSecret } from '../utils/utils';
|
|
12
12
|
import { debug } from '@sitecore-jss/sitecore-jss';
|
|
13
|
+
import { EditMode } from '@sitecore-jss/sitecore-jss/layout';
|
|
14
|
+
import { enforceCors } from '@sitecore-jss/sitecore-jss/utils';
|
|
13
15
|
/**
|
|
14
16
|
* Middleware / handler used in the editing config API route in xmcloud add on (e.g. '/api/editing/config')
|
|
15
17
|
* provides configuration information to determine feature compatibility on Pages side.
|
|
@@ -22,6 +24,10 @@ export class EditingConfigMiddleware {
|
|
|
22
24
|
this.config = config;
|
|
23
25
|
this.handler = (_req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
24
26
|
const secret = _req.query[QUERY_PARAM_EDITING_SECRET];
|
|
27
|
+
if (!enforceCors(_req, res, EDITING_ALLOWED_ORIGINS)) {
|
|
28
|
+
debug.editing('invalid origin host - set allowed origins in JSS_ALLOWED_ORIGINS environment variable');
|
|
29
|
+
return res.status(401).json({ message: 'Invalid origin' });
|
|
30
|
+
}
|
|
25
31
|
if (secret !== getJssEditingSecret()) {
|
|
26
32
|
debug.editing('invalid editing secret - sent "%s" expected "%s"', secret, getJssEditingSecret());
|
|
27
33
|
return res.status(401).json({ message: 'Missing or invalid editing secret' });
|
|
@@ -29,9 +35,11 @@ export class EditingConfigMiddleware {
|
|
|
29
35
|
const components = Array.isArray(this.config.components)
|
|
30
36
|
? this.config.components
|
|
31
37
|
: Array.from(this.config.components.keys());
|
|
38
|
+
const editMode = this.config.pagesEditMode || EditMode.Metadata;
|
|
32
39
|
return res.status(200).json({
|
|
33
40
|
components,
|
|
34
41
|
packages: this.config.metadata.packages,
|
|
42
|
+
editMode,
|
|
35
43
|
});
|
|
36
44
|
});
|
|
37
45
|
}
|
|
@@ -9,8 +9,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { editingDataDiskCache } from './editing-data-cache';
|
|
11
11
|
import { isEditingData } from './editing-data';
|
|
12
|
-
import { QUERY_PARAM_EDITING_SECRET } from './constants';
|
|
12
|
+
import { EDITING_ALLOWED_ORIGINS, QUERY_PARAM_EDITING_SECRET } from './constants';
|
|
13
13
|
import { getJssEditingSecret } from '../utils/utils';
|
|
14
|
+
import { enforceCors } from '@sitecore-jss/sitecore-jss/utils';
|
|
15
|
+
import { debug } from '@sitecore-jss/sitecore-jss';
|
|
14
16
|
/**
|
|
15
17
|
* Middleware / handler for use in the editing data Next.js API dynamic route (e.g. '/api/editing/data/[key]')
|
|
16
18
|
* which is required for Sitecore editing support.
|
|
@@ -25,6 +27,10 @@ export class EditingDataMiddleware {
|
|
|
25
27
|
const { method, query, body } = req;
|
|
26
28
|
const secret = query[QUERY_PARAM_EDITING_SECRET];
|
|
27
29
|
const key = query[this.queryParamKey];
|
|
30
|
+
if (!enforceCors(req, res, EDITING_ALLOWED_ORIGINS)) {
|
|
31
|
+
debug.editing('invalid origin host - set allowed origins in JSS_ALLOWED_ORIGINS environment variable');
|
|
32
|
+
return res.status(401).json({ message: 'Invalid origin' });
|
|
33
|
+
}
|
|
28
34
|
// Validate secret
|
|
29
35
|
if (secret !== getJssEditingSecret()) {
|
|
30
36
|
res.status(401).end('Missing or invalid secret');
|
|
@@ -9,51 +9,58 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { STATIC_PROPS_ID, SERVER_PROPS_ID } from 'next/constants';
|
|
11
11
|
import { AxiosDataFetcher, debug } from '@sitecore-jss/sitecore-jss';
|
|
12
|
-
import { EDITING_COMPONENT_ID, RenderingType } from '@sitecore-jss/sitecore-jss/layout';
|
|
12
|
+
import { EDITING_COMPONENT_ID, EditMode, RenderingType, } from '@sitecore-jss/sitecore-jss/layout';
|
|
13
13
|
import { parse } from 'node-html-parser';
|
|
14
14
|
import { editingDataService } from './editing-data-service';
|
|
15
|
-
import { QUERY_PARAM_EDITING_SECRET } from './constants';
|
|
15
|
+
import { EDITING_ALLOWED_ORIGINS, QUERY_PARAM_EDITING_SECRET } from './constants';
|
|
16
16
|
import { getJssEditingSecret } from '../utils/utils';
|
|
17
17
|
import { RenderMiddlewareBase } from './render-middleware';
|
|
18
|
+
import { enforceCors, getAllowedOriginsFromEnv } from '@sitecore-jss/sitecore-jss/utils';
|
|
18
19
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
20
|
+
* Handler for the Editing Chromes POST requests.
|
|
21
|
+
* This handler is responsible for rendering the page and returning the HTML content that is provided via request.
|
|
21
22
|
*/
|
|
22
|
-
export class
|
|
23
|
-
/**
|
|
24
|
-
* @param {EditingRenderMiddlewareConfig} [config] Editing render middleware config
|
|
25
|
-
*/
|
|
23
|
+
export class ChromesHandler extends RenderMiddlewareBase {
|
|
26
24
|
constructor(config) {
|
|
27
25
|
var _a, _b, _c, _d;
|
|
28
26
|
super();
|
|
29
|
-
this.
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
this.config = config;
|
|
28
|
+
/**
|
|
29
|
+
* Default page URL resolution.
|
|
30
|
+
* @param {Object} args Arguments for resolving the page URL
|
|
31
|
+
* @param {string} args.serverUrl The root server URL e.g. 'http://localhost:3000'
|
|
32
|
+
* @param {string} args.itemPath The Sitecore relative item path e.g. '/styleguide'
|
|
33
|
+
* @returns {string} The URL to render
|
|
34
|
+
*/
|
|
35
|
+
this.defaultResolvePageUrl = ({ serverUrl, itemPath, }) => {
|
|
36
|
+
return `${serverUrl}${itemPath}`;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Default server URL resolution.
|
|
40
|
+
* Note we use https protocol on Vercel due to serverless function architecture.
|
|
41
|
+
* In all other scenarios, including localhost (with or without a proxy e.g. ngrok)
|
|
42
|
+
* and within a nodejs container, http protocol should be used.
|
|
43
|
+
*
|
|
44
|
+
* For information about the VERCEL environment variable, see
|
|
45
|
+
* https://vercel.com/docs/environment-variables#system-environment-variables
|
|
46
|
+
* @param {NextApiRequest} req
|
|
47
|
+
*/
|
|
48
|
+
this.defaultResolveServerUrl = (req) => {
|
|
49
|
+
return `${process.env.VERCEL ? 'https' : 'http'}://${req.headers.host}`;
|
|
50
|
+
};
|
|
51
|
+
this.editingDataService = (_a = config === null || config === void 0 ? void 0 : config.editingDataService) !== null && _a !== void 0 ? _a : editingDataService;
|
|
52
|
+
this.dataFetcher = (_b = config === null || config === void 0 ? void 0 : config.dataFetcher) !== null && _b !== void 0 ? _b : new AxiosDataFetcher({ debugger: debug.editing });
|
|
53
|
+
this.resolvePageUrl = (_c = config === null || config === void 0 ? void 0 : config.resolvePageUrl) !== null && _c !== void 0 ? _c : this.defaultResolvePageUrl;
|
|
54
|
+
this.resolveServerUrl = (_d = config === null || config === void 0 ? void 0 : config.resolveServerUrl) !== null && _d !== void 0 ? _d : this.defaultResolveServerUrl;
|
|
55
|
+
}
|
|
56
|
+
render(req, res) {
|
|
57
|
+
var _a;
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
const { query } = req;
|
|
32
60
|
const startTimestamp = Date.now();
|
|
33
|
-
debug.editing('editing render middleware start: %o', {
|
|
34
|
-
method,
|
|
35
|
-
query,
|
|
36
|
-
headers,
|
|
37
|
-
body,
|
|
38
|
-
});
|
|
39
|
-
if (method !== 'POST') {
|
|
40
|
-
debug.editing('invalid method - sent %s expected POST', method);
|
|
41
|
-
res.setHeader('Allow', 'POST');
|
|
42
|
-
return res.status(405).json({
|
|
43
|
-
html: `<html><body>Invalid request method '${method}'</body></html>`,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
// Validate secret
|
|
47
|
-
const secret = (_e = query[QUERY_PARAM_EDITING_SECRET]) !== null && _e !== void 0 ? _e : body === null || body === void 0 ? void 0 : body.jssEditingSecret;
|
|
48
|
-
if (secret !== getJssEditingSecret()) {
|
|
49
|
-
debug.editing('invalid editing secret - sent "%s" expected "%s"', secret, getJssEditingSecret());
|
|
50
|
-
return res.status(401).json({
|
|
51
|
-
html: '<html><body>Missing or invalid secret</body></html>',
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
61
|
try {
|
|
55
62
|
// Extract data from EE payload
|
|
56
|
-
const editingData = extractEditingData(req);
|
|
63
|
+
const editingData = this.extractEditingData(req);
|
|
57
64
|
// Resolve server URL
|
|
58
65
|
const serverUrl = this.resolveServerUrl(req);
|
|
59
66
|
// Get query string parameters to propagate on subsequent requests (e.g. for deployment protection bypass)
|
|
@@ -69,7 +76,7 @@ export class EditingRenderMiddleware extends RenderMiddlewareBase {
|
|
|
69
76
|
// Make actual render request for page route, passing on preview cookies as well as any approved query string parameters.
|
|
70
77
|
// Note timestamp effectively disables caching the request in Axios (no amount of cache headers seemed to do it)
|
|
71
78
|
debug.editing('fetching page route for %s', editingData.path);
|
|
72
|
-
const requestUrl = new URL(this.resolvePageUrl(serverUrl, editingData.path));
|
|
79
|
+
const requestUrl = new URL(this.resolvePageUrl({ serverUrl, itemPath: editingData.path }));
|
|
73
80
|
for (const key in params) {
|
|
74
81
|
if ({}.hasOwnProperty.call(params, key)) {
|
|
75
82
|
requestUrl.searchParams.append(key, params[key]);
|
|
@@ -106,7 +113,7 @@ export class EditingRenderMiddleware extends RenderMiddlewareBase {
|
|
|
106
113
|
html = html.replace(STATIC_PROPS_ID, SERVER_PROPS_ID);
|
|
107
114
|
if (editingData.layoutData.sitecore.context.renderingType === RenderingType.Component) {
|
|
108
115
|
// Handle component rendering. Extract component markup only
|
|
109
|
-
html = (
|
|
116
|
+
html = (_a = parse(html).getElementById(EDITING_COMPONENT_ID)) === null || _a === void 0 ? void 0 : _a.innerHTML;
|
|
110
117
|
if (!html)
|
|
111
118
|
throw new Error(`Failed to render component for ${editingData.path}`);
|
|
112
119
|
}
|
|
@@ -132,72 +139,174 @@ export class EditingRenderMiddleware extends RenderMiddlewareBase {
|
|
|
132
139
|
});
|
|
133
140
|
}
|
|
134
141
|
});
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
142
|
+
}
|
|
143
|
+
extractEditingData(req) {
|
|
144
|
+
// Sitecore editors will send the following body data structure,
|
|
145
|
+
// though we're only concerned with the "args".
|
|
146
|
+
// {
|
|
147
|
+
// id: 'JSS app name',
|
|
148
|
+
// args: ['path', 'serialized layout data object', 'serialized viewbag object'],
|
|
149
|
+
// functionName: 'renderView',
|
|
150
|
+
// moduleName: 'server.bundle'
|
|
151
|
+
// }
|
|
152
|
+
// The 'serialized viewbag object' structure:
|
|
153
|
+
// {
|
|
154
|
+
// language: 'language',
|
|
155
|
+
// dictionary: 'key-value representation of tokens and their corresponding translations',
|
|
156
|
+
// httpContext: 'serialized request data'
|
|
157
|
+
// }
|
|
158
|
+
var _a;
|
|
159
|
+
// Note req.body _should_ have already been parsed as JSON at this point (via Next.js `bodyParser` API middleware)
|
|
160
|
+
const payload = req.body;
|
|
161
|
+
if (!payload || !payload.args || !Array.isArray(payload.args) || payload.args.length < 3) {
|
|
162
|
+
throw new Error('Unable to extract editing data from request. Ensure `bodyParser` middleware is enabled on your Next.js API route.');
|
|
163
|
+
}
|
|
164
|
+
const layoutData = JSON.parse(payload.args[1]);
|
|
165
|
+
const viewBag = JSON.parse(payload.args[2]);
|
|
166
|
+
// Keep backwards compatibility in case people use an older JSS version that doesn't send the path in the context
|
|
167
|
+
const path = (_a = layoutData.sitecore.context.itemPath) !== null && _a !== void 0 ? _a : viewBag.httpContext.request.path;
|
|
168
|
+
return {
|
|
169
|
+
path,
|
|
170
|
+
layoutData,
|
|
171
|
+
language: viewBag.language,
|
|
172
|
+
dictionary: viewBag.dictionary,
|
|
155
173
|
};
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Type guard for EditingMetadataPreviewData
|
|
178
|
+
* @param {Object} data preview data to check
|
|
179
|
+
* @returns true if the data is EditingMetadataPreviewData
|
|
180
|
+
* @see EditingMetadataPreviewData
|
|
181
|
+
*/
|
|
182
|
+
export const isEditingMetadataPreviewData = (data) => {
|
|
183
|
+
return (typeof data === 'object' &&
|
|
184
|
+
data !== null &&
|
|
185
|
+
'editMode' in data &&
|
|
186
|
+
data.editMode === EditMode.Metadata);
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Handler for the Editing Metadata GET requests.
|
|
190
|
+
* This handler is responsible for redirecting the request to the page route.
|
|
191
|
+
* The page fetches the layout, dictionary and renders the page.
|
|
192
|
+
*/
|
|
193
|
+
export class MetadataHandler {
|
|
194
|
+
constructor(config) {
|
|
195
|
+
this.config = config;
|
|
196
|
+
}
|
|
197
|
+
render(req, res) {
|
|
198
|
+
var _a, _b;
|
|
199
|
+
const { query } = req;
|
|
200
|
+
const startTimestamp = Date.now();
|
|
201
|
+
const requiredQueryParams = [
|
|
202
|
+
'sc_site',
|
|
203
|
+
'sc_itemid',
|
|
204
|
+
'sc_lang',
|
|
205
|
+
'sc_variant',
|
|
206
|
+
'sc_version',
|
|
207
|
+
'mode',
|
|
208
|
+
];
|
|
209
|
+
const missingQueryParams = requiredQueryParams.filter((param) => !query[param]);
|
|
210
|
+
// Validate query parameters
|
|
211
|
+
if (missingQueryParams.length) {
|
|
212
|
+
debug.editing('missing required query parameters: %o', missingQueryParams);
|
|
213
|
+
return res.status(400).json({
|
|
214
|
+
html: `<html><body>Missing required query parameters: ${missingQueryParams.join(', ')}</body></html>`,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
res.setPreviewData({
|
|
218
|
+
site: query.sc_site,
|
|
219
|
+
itemId: query.sc_itemid,
|
|
220
|
+
language: query.sc_lang,
|
|
221
|
+
// sc_variant is an array in the query params, but we only need the first value
|
|
222
|
+
variantId: query.sc_variant.split(',')[0],
|
|
223
|
+
version: query.sc_version,
|
|
224
|
+
editMode: EditMode.Metadata,
|
|
225
|
+
pageState: query.mode,
|
|
226
|
+
},
|
|
227
|
+
// Cache the preview data for 3 seconds to ensure the page is rendered with the correct preview data not the cached one
|
|
228
|
+
{
|
|
229
|
+
path: query.route,
|
|
230
|
+
maxAge: 3,
|
|
231
|
+
});
|
|
232
|
+
const route = ((_b = (_a = this.config).resolvePageUrl) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
|
233
|
+
itemPath: query.route,
|
|
234
|
+
})) || query.route;
|
|
235
|
+
debug.editing('editing render middleware end in %dms: redirect %o', Date.now() - startTimestamp, {
|
|
236
|
+
status: 307,
|
|
237
|
+
route,
|
|
238
|
+
});
|
|
239
|
+
// Restrict the page to be rendered only within the allowed origins
|
|
240
|
+
res.setHeader('Content-Security-Policy', this.getSCPHeader());
|
|
241
|
+
res.redirect(route);
|
|
160
242
|
}
|
|
161
243
|
/**
|
|
162
|
-
* Gets the
|
|
163
|
-
* @returns
|
|
244
|
+
* Gets the Content-Security-Policy header value
|
|
245
|
+
* @returns Content-Security-Policy header value
|
|
164
246
|
*/
|
|
165
|
-
|
|
166
|
-
return
|
|
247
|
+
getSCPHeader() {
|
|
248
|
+
return `frame-ancestors 'self' ${[getAllowedOriginsFromEnv(), ...EDITING_ALLOWED_ORIGINS].join(' ')}`;
|
|
167
249
|
}
|
|
168
250
|
}
|
|
169
251
|
/**
|
|
170
|
-
*
|
|
252
|
+
* Middleware / handler for use in the editing render Next.js API route (e.g. '/api/editing/render')
|
|
253
|
+
* which is required for Sitecore editing support.
|
|
171
254
|
*/
|
|
172
|
-
export
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
255
|
+
export class EditingRenderMiddleware extends RenderMiddlewareBase {
|
|
256
|
+
/**
|
|
257
|
+
* @param {EditingRenderMiddlewareConfig} [config] Editing render middleware config
|
|
258
|
+
*/
|
|
259
|
+
constructor(config) {
|
|
260
|
+
super();
|
|
261
|
+
this.config = config;
|
|
262
|
+
this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
263
|
+
var _a, _b, _c;
|
|
264
|
+
const { query, body, method, headers } = req;
|
|
265
|
+
debug.editing('editing render middleware start: %o', {
|
|
266
|
+
method,
|
|
267
|
+
query,
|
|
268
|
+
headers,
|
|
269
|
+
body,
|
|
270
|
+
});
|
|
271
|
+
if (!enforceCors(req, res, EDITING_ALLOWED_ORIGINS)) {
|
|
272
|
+
debug.editing('invalid origin host - set allowed origins in JSS_ALLOWED_ORIGINS environment variable');
|
|
273
|
+
return res.status(401).json({
|
|
274
|
+
html: `<html><body>Requests from origin ${(_a = req.headers) === null || _a === void 0 ? void 0 : _a.origin} not allowed</body></html>`,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// Validate secret
|
|
278
|
+
const secret = (_b = query[QUERY_PARAM_EDITING_SECRET]) !== null && _b !== void 0 ? _b : body === null || body === void 0 ? void 0 : body.jssEditingSecret;
|
|
279
|
+
if (secret !== getJssEditingSecret()) {
|
|
280
|
+
debug.editing('invalid editing secret - sent "%s" expected "%s"', secret, getJssEditingSecret());
|
|
281
|
+
return res.status(401).json({
|
|
282
|
+
html: '<html><body>Missing or invalid secret</body></html>',
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
switch (req.method) {
|
|
286
|
+
case 'GET': {
|
|
287
|
+
const handler = new MetadataHandler({ resolvePageUrl: (_c = this.config) === null || _c === void 0 ? void 0 : _c.resolvePageUrl });
|
|
288
|
+
yield handler.render(req, res);
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
case 'POST': {
|
|
292
|
+
const handler = new ChromesHandler(this.config);
|
|
293
|
+
yield handler.render(req, res);
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
default:
|
|
297
|
+
debug.editing('invalid method - sent %s expected GET/POST', req.method);
|
|
298
|
+
res.setHeader('Allow', 'GET, POST');
|
|
299
|
+
return res.status(405).json({
|
|
300
|
+
html: `<html><body>Invalid request method '${req.method}'</body></html>`,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Gets the Next.js API route handler
|
|
307
|
+
* @returns route handler
|
|
308
|
+
*/
|
|
309
|
+
getHandler() {
|
|
310
|
+
return this.handler;
|
|
192
311
|
}
|
|
193
|
-
const layoutData = JSON.parse(payload.args[1]);
|
|
194
|
-
const viewBag = JSON.parse(payload.args[2]);
|
|
195
|
-
// Keep backwards compatibility in case people use an older JSS version that doesn't send the path in the context
|
|
196
|
-
const path = (_a = layoutData.sitecore.context.itemPath) !== null && _a !== void 0 ? _a : viewBag.httpContext.request.path;
|
|
197
|
-
return {
|
|
198
|
-
path,
|
|
199
|
-
layoutData,
|
|
200
|
-
language: viewBag.language,
|
|
201
|
-
dictionary: viewBag.dictionary,
|
|
202
|
-
};
|
|
203
312
|
}
|
|
@@ -8,9 +8,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { debug } from '@sitecore-jss/sitecore-jss';
|
|
11
|
-
import { QUERY_PARAM_EDITING_SECRET } from './constants';
|
|
11
|
+
import { EDITING_ALLOWED_ORIGINS, QUERY_PARAM_EDITING_SECRET } from './constants';
|
|
12
12
|
import { getJssEditingSecret } from '../utils/utils';
|
|
13
13
|
import { RenderMiddlewareBase } from './render-middleware';
|
|
14
|
+
import { enforceCors } from '@sitecore-jss/sitecore-jss/utils';
|
|
14
15
|
/**
|
|
15
16
|
* Middleware / handler for use in the feaas render Next.js API route (e.g. '/api/editing/feaas/render')
|
|
16
17
|
* which is required for Sitecore editing support.
|
|
@@ -25,6 +26,7 @@ export class FEAASRenderMiddleware extends RenderMiddlewareBase {
|
|
|
25
26
|
this.config = config;
|
|
26
27
|
this.defaultPageUrl = '/feaas/render';
|
|
27
28
|
this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
29
|
+
var _b;
|
|
28
30
|
const { method, query, headers } = req;
|
|
29
31
|
const startTimestamp = Date.now();
|
|
30
32
|
debug.editing('feaas render middleware start: %o', {
|
|
@@ -32,6 +34,12 @@ export class FEAASRenderMiddleware extends RenderMiddlewareBase {
|
|
|
32
34
|
query,
|
|
33
35
|
headers,
|
|
34
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 ${(_b = req.headers) === null || _b === void 0 ? void 0 : _b.origin} are not allowed</body></html>`);
|
|
42
|
+
}
|
|
35
43
|
if (method !== 'GET') {
|
|
36
44
|
debug.editing('invalid method - sent %s expected GET', method);
|
|
37
45
|
res.setHeader('Allow', 'GET');
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
export { GraphQLEditingService } from '@sitecore-jss/sitecore-jss/editing';
|
|
1
2
|
export { EditingDataDiskCache } from './editing-data-cache';
|
|
2
3
|
export { EditingDataMiddleware } from './editing-data-middleware';
|
|
3
|
-
export { EditingRenderMiddleware, } from './editing-render-middleware';
|
|
4
|
+
export { EditingRenderMiddleware, isEditingMetadataPreviewData, } from './editing-render-middleware';
|
|
4
5
|
export { BasicEditingDataService, ServerlessEditingDataService, editingDataService, } from './editing-data-service';
|
|
5
6
|
export { VercelEditingDataCache } from './vercel-editing-data-cache';
|
|
6
7
|
export { FEAASRenderMiddleware } from './feaas-render-middleware';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { constants, AxiosDataFetcher, NativeDataFetcher, enableDebug, debug, } from '@sitecore-jss/sitecore-jss';
|
|
2
|
-
export { LayoutServicePageState, GraphQLLayoutService, RestLayoutService, getChildPlaceholder, getFieldValue, RenderingType, EDITING_COMPONENT_PLACEHOLDER, EDITING_COMPONENT_ID, getContentStylesheetLink, } from '@sitecore-jss/sitecore-jss/layout';
|
|
2
|
+
export { LayoutServicePageState, GraphQLLayoutService, RestLayoutService, getChildPlaceholder, getFieldValue, RenderingType, EDITING_COMPONENT_PLACEHOLDER, EDITING_COMPONENT_ID, getContentStylesheetLink, EditMode, } from '@sitecore-jss/sitecore-jss/layout';
|
|
3
3
|
export { mediaApi } from '@sitecore-jss/sitecore-jss/media';
|
|
4
4
|
export { trackingApi, } from '@sitecore-jss/sitecore-jss/tracking';
|
|
5
5
|
export { GraphQLDictionaryService, RestDictionaryService, } from '@sitecore-jss/sitecore-jss/i18n';
|
|
@@ -21,4 +21,4 @@ export { FEaaSWrapper };
|
|
|
21
21
|
export { BYOCWrapper };
|
|
22
22
|
export { ComponentBuilder } from './ComponentBuilder';
|
|
23
23
|
export { Context } from './context';
|
|
24
|
-
export { Image, Text, DateField, EditFrame, FEaaSComponent, fetchFEaaSComponentServerProps, BYOCComponent, getComponentLibraryStylesheetLinks, File, VisitorIdentification, SitecoreContext, SitecoreContextReactContext, withSitecoreContext, useSitecoreContext, withEditorChromes, withPlaceholder, withDatasourceCheck, } from '@sitecore-jss/sitecore-jss-react';
|
|
24
|
+
export { Image, Text, DateField, EditFrame, FEaaSComponent, fetchFEaaSComponentServerProps, BYOCComponent, getComponentLibraryStylesheetLinks, File, VisitorIdentification, SitecoreContext, SitecoreContextReactContext, withSitecoreContext, useSitecoreContext, withEditorChromes, withPlaceholder, withDatasourceCheck, withFieldMetadata, EditingScripts, } from '@sitecore-jss/sitecore-jss-react';
|
package/dist/esm/utils/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { getPublicUrl, handleEditorFastRefresh } from './utils';
|
|
2
|
-
export { tryParseEnvValue,
|
|
2
|
+
export { tryParseEnvValue, resolveUrl } from '@sitecore-jss/sitecore-jss/utils';
|
|
3
|
+
export { isEditorActive, resetEditorChromes } from '@sitecore-jss/sitecore-jss/editing';
|
package/dist/esm/utils/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isEditorActive, resetEditorChromes } from '@sitecore-jss/sitecore-jss/
|
|
1
|
+
import { isEditorActive, resetEditorChromes } from '@sitecore-jss/sitecore-jss/editing';
|
|
2
2
|
/**
|
|
3
3
|
* Get the publicUrl.
|
|
4
4
|
* This is used primarily to enable compatibility with Sitecore editors.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sitecore-jss/sitecore-jss-nextjs",
|
|
3
|
-
"version": "22.1.0-canary.
|
|
3
|
+
"version": "22.1.0-canary.30",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --readme none --out ../../ref-docs/sitecore-jss-nextjs --entryPoints src/index.ts --entryPoints src/monitoring/index.ts --entryPoints src/editing/index.ts --entryPoints src/middleware/index.ts --entryPoints src/context/index.ts --entryPoints src/utils/index.ts --entryPoints src/site/index.ts --entryPoints src/graphql/index.ts --githubPages false"
|
|
15
15
|
},
|
|
16
16
|
"engines": {
|
|
17
|
-
"node": ">=
|
|
17
|
+
"node": ">=20"
|
|
18
18
|
},
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Sitecore Corporation",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@types/chai-string": "^1.4.2",
|
|
36
36
|
"@types/enzyme": "^3.10.12",
|
|
37
37
|
"@types/mocha": "^10.0.1",
|
|
38
|
-
"@types/node": "~
|
|
38
|
+
"@types/node": "~20.14.2",
|
|
39
39
|
"@types/prop-types": "^15.7.5",
|
|
40
40
|
"@types/react": "^18.2.22",
|
|
41
41
|
"@types/react-dom": "^18.0.10",
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
"react-dom": "^18.2.0"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@sitecore-jss/sitecore-jss": "^22.1.0-canary.
|
|
76
|
-
"@sitecore-jss/sitecore-jss-dev-tools": "^22.1.0-canary.
|
|
77
|
-
"@sitecore-jss/sitecore-jss-react": "^22.1.0-canary.
|
|
75
|
+
"@sitecore-jss/sitecore-jss": "^22.1.0-canary.30",
|
|
76
|
+
"@sitecore-jss/sitecore-jss-dev-tools": "^22.1.0-canary.30",
|
|
77
|
+
"@sitecore-jss/sitecore-jss-react": "^22.1.0-canary.30",
|
|
78
78
|
"@vercel/kv": "^0.2.1",
|
|
79
79
|
"node-html-parser": "^6.1.4",
|
|
80
80
|
"prop-types": "^15.8.1",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
},
|
|
84
84
|
"description": "",
|
|
85
85
|
"types": "types/index.d.ts",
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "c4bb65e67427a07c5f66ce83dc211550db9612a5",
|
|
87
87
|
"files": [
|
|
88
88
|
"dist",
|
|
89
89
|
"types",
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
import { ComponentFactory } from '@sitecore-jss/sitecore-jss-react';
|
|
1
|
+
import { ComponentFactory, JssComponentType } from '@sitecore-jss/sitecore-jss-react';
|
|
3
2
|
import { Module, ModuleFactory } from './sharedTypes/module-factory';
|
|
4
|
-
import { ComponentType } from 'react';
|
|
5
3
|
/**
|
|
6
4
|
* Represents a component that can be imported dynamically
|
|
7
5
|
*/
|
|
8
6
|
export type LazyModule = {
|
|
9
7
|
module: () => Promise<Module>;
|
|
10
|
-
element: (isEditing?: boolean) =>
|
|
8
|
+
element: (isEditing?: boolean) => JssComponentType;
|
|
11
9
|
};
|
|
12
10
|
/**
|
|
13
11
|
* Component is a module or a lazy module
|
|
14
12
|
*/
|
|
15
|
-
type Component = Module | LazyModule |
|
|
13
|
+
type Component = Module | LazyModule | JssComponentType;
|
|
16
14
|
/**
|
|
17
15
|
* Configuration for ComponentBuilder
|
|
18
16
|
*/
|