@sitecore-jss/sitecore-jss-nextjs 22.1.0-canary.9 → 22.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +202 -202
- package/README.md +11 -11
- package/dist/cjs/components/Link.js +7 -3
- package/dist/cjs/components/NextImage.js +16 -6
- package/dist/cjs/components/RichText.js +2 -2
- package/dist/cjs/editing/constants.js +12 -3
- 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 +228 -103
- package/dist/cjs/editing/feaas-render-middleware.js +8 -0
- package/dist/cjs/editing/index.js +4 -1
- package/dist/cjs/editing/render-middleware.js +18 -4
- package/dist/cjs/index.js +9 -7
- package/dist/cjs/middleware/middleware.js +12 -0
- package/dist/cjs/middleware/personalize-middleware.js +85 -25
- package/dist/cjs/middleware/redirects-middleware.js +63 -25
- package/dist/cjs/services/base-graphql-sitemap-service.js +5 -4
- package/dist/cjs/utils/index.js +4 -3
- package/dist/cjs/utils/utils.js +3 -3
- package/dist/esm/components/Link.js +7 -3
- package/dist/esm/components/NextImage.js +17 -6
- package/dist/esm/components/RichText.js +2 -2
- package/dist/esm/editing/constants.js +11 -2
- 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 +226 -103
- package/dist/esm/editing/feaas-render-middleware.js +9 -1
- package/dist/esm/editing/index.js +2 -1
- package/dist/esm/editing/render-middleware.js +19 -5
- package/dist/esm/index.js +3 -4
- package/dist/esm/middleware/middleware.js +12 -0
- package/dist/esm/middleware/personalize-middleware.js +86 -26
- package/dist/esm/middleware/redirects-middleware.js +63 -25
- package/dist/esm/services/base-graphql-sitemap-service.js +5 -4
- package/dist/esm/utils/index.js +2 -1
- package/dist/esm/utils/utils.js +1 -1
- package/package.json +10 -11
- package/types/ComponentBuilder.d.ts +3 -5
- package/types/components/Placeholder.d.ts +7 -2
- package/types/components/RichText.d.ts +6 -0
- package/types/editing/constants.d.ts +11 -2
- 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/editing/render-middleware.d.ts +9 -0
- package/types/index.d.ts +3 -4
- package/types/middleware/middleware.d.ts +6 -0
- package/types/middleware/personalize-middleware.d.ts +22 -2
- package/types/middleware/redirects-middleware.d.ts +8 -0
- package/types/services/base-graphql-sitemap-service.d.ts +3 -2
- package/types/utils/index.d.ts +2 -1
- package/dist/cjs/components/EditingComponentPlaceholder.js +0 -12
- package/dist/esm/components/EditingComponentPlaceholder.js +0 -5
- package/types/components/EditingComponentPlaceholder.d.ts +0 -4
|
@@ -61,6 +61,15 @@ class PersonalizeMiddleware extends middleware_1.MiddlewareBase {
|
|
|
61
61
|
sitecore_jss_1.debug.personalize('skipped (no personalization configured)');
|
|
62
62
|
return response;
|
|
63
63
|
}
|
|
64
|
+
if (this.isPrefetch(req)) {
|
|
65
|
+
sitecore_jss_1.debug.personalize('skipped (prefetch)');
|
|
66
|
+
// Personalized, but this is a prefetch request.
|
|
67
|
+
// In this case, don't execute a personalize request; otherwise, the metrics for component A/B experiments would be inaccurate.
|
|
68
|
+
// Disable preflight caching to force revalidation on client-side navigation (personalization WILL be influenced).
|
|
69
|
+
// Note the reason we don't move this any earlier in the middleware is that we would then be sacrificing performance for non-personalized pages.
|
|
70
|
+
response.headers.set('x-middleware-cache', 'no-cache');
|
|
71
|
+
return response;
|
|
72
|
+
}
|
|
64
73
|
yield this.initPersonalizeServer({
|
|
65
74
|
hostname,
|
|
66
75
|
siteName: site.name,
|
|
@@ -68,32 +77,36 @@ class PersonalizeMiddleware extends middleware_1.MiddlewareBase {
|
|
|
68
77
|
response,
|
|
69
78
|
});
|
|
70
79
|
const params = this.getExperienceParams(req);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
const executions = this.getPersonalizeExecutions(personalizeInfo, language);
|
|
81
|
+
const identifiedVariantIds = [];
|
|
82
|
+
yield Promise.all(executions.map((execution) => this.personalize({
|
|
83
|
+
friendlyId: execution.friendlyId,
|
|
84
|
+
variantIds: execution.variantIds,
|
|
85
|
+
params,
|
|
86
|
+
language,
|
|
87
|
+
timeout,
|
|
88
|
+
}, req).then((personalization) => {
|
|
89
|
+
const variantId = personalization.variantId;
|
|
90
|
+
if (variantId) {
|
|
91
|
+
if (!execution.variantIds.includes(variantId)) {
|
|
92
|
+
sitecore_jss_1.debug.personalize('invalid variant %s', variantId);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
identifiedVariantIds.push(variantId);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
})));
|
|
99
|
+
if (identifiedVariantIds.length === 0) {
|
|
100
|
+
sitecore_jss_1.debug.personalize('skipped (no variant(s) identified)');
|
|
88
101
|
return response;
|
|
89
102
|
}
|
|
90
103
|
// Path can be rewritten by previously executed middleware
|
|
91
104
|
const basePath = (res === null || res === void 0 ? void 0 : res.headers.get('x-sc-rewrite')) || pathname;
|
|
92
105
|
// Rewrite to persononalized path
|
|
93
|
-
const rewritePath = (0, personalize_1.getPersonalizedRewrite)(basePath,
|
|
106
|
+
const rewritePath = (0, personalize_1.getPersonalizedRewrite)(basePath, identifiedVariantIds);
|
|
94
107
|
response = this.rewrite(rewritePath, req, response);
|
|
95
|
-
// Disable preflight caching to force revalidation on client-side navigation (personalization
|
|
96
|
-
// See https://github.com/vercel/next.js/
|
|
108
|
+
// Disable preflight caching to force revalidation on client-side navigation (personalization MAY be influenced).
|
|
109
|
+
// See https://github.com/vercel/next.js/pull/32767
|
|
97
110
|
response.headers.set('x-middleware-cache', 'no-cache');
|
|
98
111
|
sitecore_jss_1.debug.personalize('personalize middleware end in %dms: %o', Date.now() - startTimestamp, {
|
|
99
112
|
rewritePath,
|
|
@@ -132,17 +145,18 @@ class PersonalizeMiddleware extends middleware_1.MiddlewareBase {
|
|
|
132
145
|
});
|
|
133
146
|
});
|
|
134
147
|
}
|
|
135
|
-
personalize({ params,
|
|
148
|
+
personalize({ params, friendlyId, language, timeout, variantIds, }, request) {
|
|
136
149
|
var _a;
|
|
137
150
|
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
-
|
|
151
|
+
sitecore_jss_1.debug.personalize('executing experience for %s %o', friendlyId, params);
|
|
152
|
+
return (yield (0, server_2.personalize)(request, {
|
|
139
153
|
channel: this.config.cdpConfig.channel || 'WEB',
|
|
140
154
|
currency: (_a = this.config.cdpConfig.currency) !== null && _a !== void 0 ? _a : 'USD',
|
|
141
|
-
friendlyId
|
|
155
|
+
friendlyId,
|
|
142
156
|
params,
|
|
143
157
|
language,
|
|
144
|
-
|
|
145
|
-
|
|
158
|
+
pageVariantIds: variantIds,
|
|
159
|
+
}, { timeout }));
|
|
146
160
|
});
|
|
147
161
|
}
|
|
148
162
|
getExperienceParams(req) {
|
|
@@ -164,5 +178,51 @@ class PersonalizeMiddleware extends middleware_1.MiddlewareBase {
|
|
|
164
178
|
// ignore files
|
|
165
179
|
return pathname.includes('.') || super.excludeRoute(pathname);
|
|
166
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Aggregates personalize executions based on the provided route personalize information and language
|
|
183
|
+
* @param {PersonalizeInfo} personalizeInfo the route personalize information
|
|
184
|
+
* @param {string} language the language
|
|
185
|
+
* @returns An array of personalize executions
|
|
186
|
+
*/
|
|
187
|
+
getPersonalizeExecutions(personalizeInfo, language) {
|
|
188
|
+
if (personalizeInfo.variantIds.length === 0) {
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
const results = [];
|
|
192
|
+
return personalizeInfo.variantIds.reduce((results, variantId) => {
|
|
193
|
+
if (variantId.includes('_')) {
|
|
194
|
+
// Component-level personalization in format "<ComponentID>_<VariantID>"
|
|
195
|
+
const componentId = variantId.split('_')[0];
|
|
196
|
+
const friendlyId = personalize_1.CdpHelper.getComponentFriendlyId(personalizeInfo.pageId, componentId, language, this.config.scope || this.config.edgeConfig.scope);
|
|
197
|
+
const execution = results.find((x) => x.friendlyId === friendlyId);
|
|
198
|
+
if (execution) {
|
|
199
|
+
execution.variantIds.push(variantId);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// The default/control variant (format "<ComponentID>_default") is also a valid value returned by the execution
|
|
203
|
+
const defaultVariant = `${componentId}${personalize_1.DEFAULT_VARIANT}`;
|
|
204
|
+
results.push({
|
|
205
|
+
friendlyId,
|
|
206
|
+
variantIds: [defaultVariant, variantId],
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
// Embedded (page-level) personalization in format "<VariantID>"
|
|
212
|
+
const friendlyId = personalize_1.CdpHelper.getPageFriendlyId(personalizeInfo.pageId, language, this.config.scope || this.config.edgeConfig.scope);
|
|
213
|
+
const execution = results.find((x) => x.friendlyId === friendlyId);
|
|
214
|
+
if (execution) {
|
|
215
|
+
execution.variantIds.push(variantId);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
results.push({
|
|
219
|
+
friendlyId,
|
|
220
|
+
variantIds: [variantId],
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return results;
|
|
225
|
+
}, results);
|
|
226
|
+
}
|
|
167
227
|
}
|
|
168
228
|
exports.PersonalizeMiddleware = PersonalizeMiddleware;
|
|
@@ -43,6 +43,7 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
|
|
|
43
43
|
hostname,
|
|
44
44
|
});
|
|
45
45
|
const createResponse = () => __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
var _a;
|
|
46
47
|
if (this.config.disabled && this.config.disabled(req, res || server_1.NextResponse.next())) {
|
|
47
48
|
sitecore_jss_1.debug.redirects('skipped (redirects middleware is disabled)');
|
|
48
49
|
return res || server_1.NextResponse.next();
|
|
@@ -64,47 +65,44 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
|
|
|
64
65
|
existsRedirect.target.includes(hostname))) {
|
|
65
66
|
existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
|
|
66
67
|
}
|
|
67
|
-
const url = req.nextUrl.clone();
|
|
68
|
+
const url = this.normalizeUrl(req.nextUrl.clone());
|
|
68
69
|
if (REGEXP_ABSOLUTE_URL.test(existsRedirect.target)) {
|
|
69
70
|
url.href = existsRedirect.target;
|
|
70
71
|
}
|
|
71
72
|
else {
|
|
72
|
-
const source = `${url.pathname}${url.search}`;
|
|
73
|
-
url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
|
|
73
|
+
const source = `${url.pathname.replace(/\/*$/gi, '')}${url.search}`;
|
|
74
74
|
const urlFirstPart = existsRedirect.target.split('/')[1];
|
|
75
75
|
if (this.locales.includes(urlFirstPart)) {
|
|
76
|
-
|
|
76
|
+
req.nextUrl.locale = urlFirstPart;
|
|
77
77
|
existsRedirect.target = existsRedirect.target.replace(`/${urlFirstPart}`, '');
|
|
78
78
|
}
|
|
79
79
|
const target = source
|
|
80
80
|
.replace((0, regex_parser_1.default)(existsRedirect.pattern), existsRedirect.target)
|
|
81
81
|
.replace(/^\/\//, '/')
|
|
82
82
|
.split('?');
|
|
83
|
-
url.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
for (const [key, val] of newParams.entries()) {
|
|
87
|
-
url.searchParams.append(key, val);
|
|
88
|
-
}
|
|
83
|
+
if (url.search && existsRedirect.isQueryStringPreserved) {
|
|
84
|
+
const targetQueryString = (_a = target[1]) !== null && _a !== void 0 ? _a : '';
|
|
85
|
+
url.search = '?' + new URLSearchParams(`${url.search}&${targetQueryString}`).toString();
|
|
89
86
|
}
|
|
87
|
+
else if (target[1]) {
|
|
88
|
+
url.search = '?' + target[1];
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
url.search = '';
|
|
92
|
+
}
|
|
93
|
+
const prepareNewURL = new URL(`${target[0]}${url.search}`, url.origin);
|
|
94
|
+
url.href = prepareNewURL.href;
|
|
90
95
|
}
|
|
91
96
|
const redirectUrl = decodeURIComponent(url.href);
|
|
92
97
|
/** return Response redirect with http code of redirect type **/
|
|
93
98
|
switch (existsRedirect.redirectType) {
|
|
94
99
|
case site_1.REDIRECT_TYPE_301:
|
|
95
|
-
return server_1.NextResponse.redirect(redirectUrl, {
|
|
96
|
-
status: 301,
|
|
97
|
-
statusText: 'Moved Permanently',
|
|
98
|
-
headers: res === null || res === void 0 ? void 0 : res.headers,
|
|
99
|
-
});
|
|
100
|
+
return server_1.NextResponse.redirect(redirectUrl, Object.assign(Object.assign({}, res), { status: 301, statusText: 'Moved Permanently', headers: res === null || res === void 0 ? void 0 : res.headers }));
|
|
100
101
|
case site_1.REDIRECT_TYPE_302:
|
|
101
|
-
return server_1.NextResponse.redirect(redirectUrl, {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
106
|
-
case site_1.REDIRECT_TYPE_SERVER_TRANSFER:
|
|
107
|
-
return server_1.NextResponse.rewrite(redirectUrl, res);
|
|
102
|
+
return server_1.NextResponse.redirect(redirectUrl, Object.assign(Object.assign({}, res), { status: 302, statusText: 'Found', headers: res === null || res === void 0 ? void 0 : res.headers }));
|
|
103
|
+
case site_1.REDIRECT_TYPE_SERVER_TRANSFER: {
|
|
104
|
+
return this.rewrite(redirectUrl, req, res || server_1.NextResponse.next());
|
|
105
|
+
}
|
|
108
106
|
default:
|
|
109
107
|
return res || server_1.NextResponse.next();
|
|
110
108
|
}
|
|
@@ -149,8 +147,9 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
|
|
|
149
147
|
getExistsRedirect(req, siteName) {
|
|
150
148
|
return __awaiter(this, void 0, void 0, function* () {
|
|
151
149
|
const redirects = yield this.redirectsService.fetchRedirects(siteName);
|
|
152
|
-
const
|
|
153
|
-
const
|
|
150
|
+
const normalizedUrl = this.normalizeUrl(req.nextUrl.clone());
|
|
151
|
+
const tragetURL = normalizedUrl.pathname;
|
|
152
|
+
const targetQS = normalizedUrl.search || '';
|
|
154
153
|
const language = this.getLanguage(req);
|
|
155
154
|
const modifyRedirects = structuredClone(redirects);
|
|
156
155
|
return modifyRedirects.length
|
|
@@ -163,7 +162,7 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
|
|
|
163
162
|
.replace(/(?<!\\)\?/g, '\\?')
|
|
164
163
|
.replace(/\$\/gi$/g, '')}[\/]?$/gi`;
|
|
165
164
|
return (((0, regex_parser_1.default)(redirect.pattern).test(tragetURL) ||
|
|
166
|
-
(0, regex_parser_1.default)(redirect.pattern).test(`${tragetURL}${targetQS}`) ||
|
|
165
|
+
(0, regex_parser_1.default)(redirect.pattern).test(`${tragetURL.replace(/\/*$/gi, '')}${targetQS}`) ||
|
|
167
166
|
(0, regex_parser_1.default)(redirect.pattern).test(`/${req.nextUrl.locale}${tragetURL}`) ||
|
|
168
167
|
(0, regex_parser_1.default)(redirect.pattern).test(`/${req.nextUrl.locale}${tragetURL}${targetQS}`)) &&
|
|
169
168
|
(redirect.locale
|
|
@@ -173,5 +172,44 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
|
|
|
173
172
|
: undefined;
|
|
174
173
|
});
|
|
175
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* When a user clicks on a link generated by the Link component from next/link,
|
|
177
|
+
* Next.js adds special parameters in the route called path.
|
|
178
|
+
* This method removes these special parameters.
|
|
179
|
+
* @param {URL} url
|
|
180
|
+
* @returns {string} normalize url
|
|
181
|
+
*/
|
|
182
|
+
normalizeUrl(url) {
|
|
183
|
+
if (!url.search) {
|
|
184
|
+
return url;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Prepare special parameters for exclusion.
|
|
188
|
+
*/
|
|
189
|
+
const splittedPathname = url.pathname
|
|
190
|
+
.split('/')
|
|
191
|
+
.filter((route) => route)
|
|
192
|
+
.map((route) => `path=${route}`);
|
|
193
|
+
/**
|
|
194
|
+
* Remove special parameters(Next.JS)
|
|
195
|
+
* Example: /about/contact/us
|
|
196
|
+
* When a user clicks on this link, Next.js should generate a link for the middleware, formatted like this:
|
|
197
|
+
* http://host/about/contact/us?path=about&path=contact&path=us
|
|
198
|
+
*/
|
|
199
|
+
const newQueryString = url.search
|
|
200
|
+
.replace(/^\?/, '')
|
|
201
|
+
.split('&')
|
|
202
|
+
.filter((param) => {
|
|
203
|
+
if (!splittedPathname.includes(param)) {
|
|
204
|
+
return param;
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
})
|
|
208
|
+
.join('&');
|
|
209
|
+
if (newQueryString) {
|
|
210
|
+
return new URL(`${url.pathname}?${newQueryString}`, url.origin);
|
|
211
|
+
}
|
|
212
|
+
return new URL(`${url.pathname}`, url.origin);
|
|
213
|
+
}
|
|
176
214
|
}
|
|
177
215
|
exports.RedirectsMiddleware = RedirectsMiddleware;
|
|
@@ -148,13 +148,14 @@ class BaseGraphQLSitemapService {
|
|
|
148
148
|
const formatPath = (path) => formatStaticPath(path.replace(/^\/|\/$/g, '').split('/'), language);
|
|
149
149
|
const aggregatedPaths = [];
|
|
150
150
|
sitePaths.forEach((item) => {
|
|
151
|
-
var _a, _b, _c
|
|
151
|
+
var _a, _b, _c;
|
|
152
152
|
if (!item)
|
|
153
153
|
return;
|
|
154
154
|
aggregatedPaths.push(formatPath(item.path));
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
const variantIds = (_c = (_b = (_a = item.route) === null || _a === void 0 ? void 0 : _a.personalization) === null || _b === void 0 ? void 0 : _b.variantIds) === null || _c === void 0 ? void 0 : _c.filter((variantId) => !variantId.includes('_') // exclude component A/B test variants
|
|
156
|
+
);
|
|
157
|
+
if (variantIds === null || variantIds === void 0 ? void 0 : variantIds.length) {
|
|
158
|
+
aggregatedPaths.push(...variantIds.map((varId) => formatPath((0, personalize_1.getPersonalizedRewrite)(item.path, [varId]))));
|
|
158
159
|
}
|
|
159
160
|
});
|
|
160
161
|
return aggregatedPaths;
|
package/dist/cjs/utils/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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; } });
|
package/dist/cjs/utils/utils.js
CHANGED
|
@@ -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
|
|
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,
|
|
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,
|
|
53
|
+
(0, editing_1.resetEditorChromes)();
|
|
54
54
|
}, 500);
|
|
55
55
|
};
|
|
56
56
|
};
|
|
@@ -16,14 +16,18 @@ import { Link as ReactLink, LinkPropTypes, } from '@sitecore-jss/sitecore-jss-re
|
|
|
16
16
|
export const Link = forwardRef((props, ref) => {
|
|
17
17
|
const { field, editable = true, children, internalLinkMatcher = /^\//g, showLinkTextWithChildrenPresent } = props, htmlLinkProps = __rest(props, ["field", "editable", "children", "internalLinkMatcher", "showLinkTextWithChildrenPresent"]);
|
|
18
18
|
if (!field ||
|
|
19
|
-
(!field.editable &&
|
|
19
|
+
(!field.editable &&
|
|
20
|
+
!field.value &&
|
|
21
|
+
!field.href &&
|
|
22
|
+
!field.metadata)) {
|
|
20
23
|
return null;
|
|
21
24
|
}
|
|
22
25
|
const value = (field.href
|
|
23
26
|
? field
|
|
24
27
|
: field.value);
|
|
25
|
-
|
|
26
|
-
const
|
|
28
|
+
// fallback to {} if value is undefined; could happen if field is LinkFieldValue, href is empty in metadata mode
|
|
29
|
+
const { href, querystring, anchor } = value || {};
|
|
30
|
+
const isEditing = editable && (field.editable || field.metadata);
|
|
27
31
|
if (href && !isEditing) {
|
|
28
32
|
const text = showLinkTextWithChildrenPresent || !children ? value.text || value.href : null;
|
|
29
33
|
// determine if a link is a route or not.
|
|
@@ -12,18 +12,22 @@ 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, SitecoreContextReactContext, } from '@sitecore-jss/sitecore-jss-react';
|
|
16
16
|
import Image from 'next/image';
|
|
17
|
-
|
|
17
|
+
import { withEmptyFieldEditingComponent } from '@sitecore-jss/sitecore-jss-react';
|
|
18
|
+
import { DefaultEmptyFieldEditingComponentImage } from '@sitecore-jss/sitecore-jss-react';
|
|
19
|
+
import { isFieldValueEmpty, LayoutServicePageState } from '@sitecore-jss/sitecore-jss/layout';
|
|
20
|
+
export const NextImage = withFieldMetadata(withEmptyFieldEditingComponent((_a) => {
|
|
21
|
+
var _b;
|
|
18
22
|
var { editable = true, imageParams, field, mediaUrlPrefix, fill, priority } = _a, otherProps = __rest(_a, ["editable", "imageParams", "field", "mediaUrlPrefix", "fill", "priority"]);
|
|
23
|
+
const sitecoreContext = React.useContext(SitecoreContextReactContext);
|
|
19
24
|
// next handles src and we use a custom loader,
|
|
20
25
|
// throw error if these are present
|
|
21
26
|
if (otherProps.src) {
|
|
22
27
|
throw new Error('Detected src prop. If you wish to use src, use next/image directly.');
|
|
23
28
|
}
|
|
24
29
|
const dynamicMedia = field;
|
|
25
|
-
if (!field ||
|
|
26
|
-
(!dynamicMedia.editable && !dynamicMedia.value && !dynamicMedia.src)) {
|
|
30
|
+
if (!field || (!dynamicMedia.editable && isFieldValueEmpty(dynamicMedia))) {
|
|
27
31
|
return null;
|
|
28
32
|
}
|
|
29
33
|
const imageField = dynamicMedia;
|
|
@@ -38,8 +42,11 @@ export const NextImage = (_a) => {
|
|
|
38
42
|
if (!img) {
|
|
39
43
|
return null;
|
|
40
44
|
}
|
|
45
|
+
// disable image optimization for Edit and Preview, but preserve original value if true
|
|
46
|
+
const unoptimized = otherProps.unoptimized ||
|
|
47
|
+
((_b = sitecoreContext.context) === null || _b === void 0 ? void 0 : _b.pageState) !== LayoutServicePageState.Normal;
|
|
41
48
|
const attrs = Object.assign(Object.assign(Object.assign({}, img), otherProps), { fill,
|
|
42
|
-
priority, src: mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix) });
|
|
49
|
+
priority, src: mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix), unoptimized });
|
|
43
50
|
const imageProps = Object.assign(Object.assign({}, attrs), {
|
|
44
51
|
// force replace /media with /jssmedia in src since we _know_ we will be adding a 'mw' query string parameter
|
|
45
52
|
// this is required for Sitecore media API resizing to work properly
|
|
@@ -53,7 +60,7 @@ export const NextImage = (_a) => {
|
|
|
53
60
|
return React.createElement(Image, Object.assign({ alt: "" }, imageProps));
|
|
54
61
|
}
|
|
55
62
|
return null; // we can't handle the truth
|
|
56
|
-
};
|
|
63
|
+
}, { defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentImage }));
|
|
57
64
|
NextImage.propTypes = {
|
|
58
65
|
field: PropTypes.oneOfType([
|
|
59
66
|
PropTypes.shape({
|
|
@@ -67,5 +74,9 @@ NextImage.propTypes = {
|
|
|
67
74
|
editable: PropTypes.bool,
|
|
68
75
|
mediaUrlPrefix: PropTypes.instanceOf(RegExp),
|
|
69
76
|
imageParams: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.number.isRequired, PropTypes.string.isRequired]).isRequired),
|
|
77
|
+
emptyFieldEditingComponent: PropTypes.oneOfType([
|
|
78
|
+
PropTypes.object,
|
|
79
|
+
PropTypes.func,
|
|
80
|
+
]),
|
|
70
81
|
};
|
|
71
82
|
NextImage.displayName = 'NextImage';
|
|
@@ -17,7 +17,7 @@ const prefetched = {};
|
|
|
17
17
|
export const RichText = (props) => {
|
|
18
18
|
const { internalLinksSelector = 'a[href^="/"]', prefetchLinks = true, editable = true } = props, rest = __rest(props, ["internalLinksSelector", "prefetchLinks", "editable"]);
|
|
19
19
|
const hasText = props.field && props.field.value;
|
|
20
|
-
const isEditing = editable && props.field && props.field.editable;
|
|
20
|
+
const isEditing = editable && props.field && (props.field.editable || props.field.metadata);
|
|
21
21
|
const router = useRouter();
|
|
22
22
|
const richTextRef = useRef(null);
|
|
23
23
|
useEffect(() => {
|
|
@@ -49,7 +49,7 @@ export const RichText = (props) => {
|
|
|
49
49
|
link.addEventListener('click', routeHandler, false);
|
|
50
50
|
});
|
|
51
51
|
};
|
|
52
|
-
return React.createElement(ReactRichText, Object.assign({ ref: richTextRef }, rest));
|
|
52
|
+
return React.createElement(ReactRichText, Object.assign({ ref: richTextRef, editable: editable }, rest));
|
|
53
53
|
};
|
|
54
54
|
RichText.propTypes = Object.assign({ internalLinksSelector: PropTypes.string }, RichTextPropTypes);
|
|
55
55
|
RichText.displayName = 'NextRichText';
|
|
@@ -1,3 +1,12 @@
|
|
|
1
1
|
export const QUERY_PARAM_EDITING_SECRET = 'secret';
|
|
2
|
-
export const
|
|
3
|
-
export const
|
|
2
|
+
export const QUERY_PARAM_VERCEL_PROTECTION_BYPASS = 'x-vercel-protection-bypass';
|
|
3
|
+
export const QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE = 'x-vercel-set-bypass-cookie';
|
|
4
|
+
/**
|
|
5
|
+
* Headers that should be passed along to (Editing Chromes handler) SSR request.
|
|
6
|
+
* Note these are in lowercase format to match expected `IncomingHttpHeaders`.
|
|
7
|
+
*/
|
|
8
|
+
export const EDITING_PASS_THROUGH_HEADERS = ['authorization', 'cookie'];
|
|
9
|
+
/**
|
|
10
|
+
* Default allowed origins for editing requests. This is used to enforce CORS, CSP headers.
|
|
11
|
+
*/
|
|
12
|
+
export const EDITING_ALLOWED_ORIGINS = ['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');
|