@sitecore-jss/sitecore-jss-nextjs 22.1.0-canary.3 → 22.1.0-canary.31

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