@sitecore-jss/sitecore-jss-nextjs 22.2.0-canary.9 → 22.2.1-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/NextImage.js +6 -1
- package/dist/cjs/editing/constants.js +1 -1
- package/dist/cjs/editing/editing-render-middleware.js +1 -1
- package/dist/cjs/index.js +2 -3
- package/dist/cjs/middleware/index.js +3 -1
- package/dist/cjs/middleware/personalize-middleware.js +7 -4
- package/dist/cjs/middleware/redirects-middleware.js +157 -42
- package/dist/esm/components/NextImage.js +8 -3
- package/dist/esm/editing/constants.js +1 -1
- package/dist/esm/editing/editing-render-middleware.js +1 -1
- package/dist/esm/index.js +1 -2
- package/dist/esm/middleware/index.js +1 -0
- package/dist/esm/middleware/personalize-middleware.js +6 -3
- package/dist/esm/middleware/redirects-middleware.js +157 -42
- package/package.json +11 -10
- package/types/editing/editing-render-middleware.d.ts +3 -0
- package/types/index.d.ts +1 -2
- package/types/middleware/index.d.ts +1 -0
- package/types/middleware/redirects-middleware.d.ts +29 -1
- package/context.d.ts +0 -1
- package/context.js +0 -1
- package/dist/cjs/context/context.js +0 -83
- package/dist/cjs/context/index.js +0 -5
- package/dist/esm/context/context.js +0 -79
- package/dist/esm/context/index.js +0 -1
- package/types/context/context.d.ts +0 -116
- package/types/context/index.d.ts +0 -1
|
@@ -24,7 +24,9 @@ const sitecore_jss_react_2 = require("@sitecore-jss/sitecore-jss-react");
|
|
|
24
24
|
const sitecore_jss_react_3 = require("@sitecore-jss/sitecore-jss-react");
|
|
25
25
|
const layout_1 = require("@sitecore-jss/sitecore-jss/layout");
|
|
26
26
|
exports.NextImage = (0, sitecore_jss_react_1.withFieldMetadata)((0, sitecore_jss_react_2.withEmptyFieldEditingComponent)((_a) => {
|
|
27
|
+
var _b;
|
|
27
28
|
var { editable = true, imageParams, field, mediaUrlPrefix, fill, priority } = _a, otherProps = __rest(_a, ["editable", "imageParams", "field", "mediaUrlPrefix", "fill", "priority"]);
|
|
29
|
+
const sitecoreContext = react_1.default.useContext(sitecore_jss_react_1.SitecoreContextReactContext);
|
|
28
30
|
// next handles src and we use a custom loader,
|
|
29
31
|
// throw error if these are present
|
|
30
32
|
if (otherProps.src) {
|
|
@@ -46,8 +48,11 @@ exports.NextImage = (0, sitecore_jss_react_1.withFieldMetadata)((0, sitecore_jss
|
|
|
46
48
|
if (!img) {
|
|
47
49
|
return null;
|
|
48
50
|
}
|
|
51
|
+
// disable image optimization for Edit and Preview, but preserve original value if true
|
|
52
|
+
const unoptimized = otherProps.unoptimized ||
|
|
53
|
+
((_b = sitecoreContext.context) === null || _b === void 0 ? void 0 : _b.pageState) !== layout_1.LayoutServicePageState.Normal;
|
|
49
54
|
const attrs = Object.assign(Object.assign(Object.assign({}, img), otherProps), { fill,
|
|
50
|
-
priority, src: media_1.mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix) });
|
|
55
|
+
priority, src: media_1.mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix), unoptimized });
|
|
51
56
|
const imageProps = Object.assign(Object.assign({}, attrs), {
|
|
52
57
|
// force replace /media with /jssmedia in src since we _know_ we will be adding a 'mw' query string parameter
|
|
53
58
|
// this is required for Sitecore media API resizing to work properly
|
|
@@ -12,4 +12,4 @@ exports.EDITING_PASS_THROUGH_HEADERS = ['authorization', 'cookie'];
|
|
|
12
12
|
/**
|
|
13
13
|
* Default allowed origins for editing requests. This is used to enforce CORS, CSP headers.
|
|
14
14
|
*/
|
|
15
|
-
exports.EDITING_ALLOWED_ORIGINS = ['https://pages
|
|
15
|
+
exports.EDITING_ALLOWED_ORIGINS = ['https://pages.sitecorecloud.io'];
|
|
@@ -221,10 +221,10 @@ class MetadataHandler {
|
|
|
221
221
|
version: query.sc_version,
|
|
222
222
|
editMode: layout_1.EditMode.Metadata,
|
|
223
223
|
pageState: query.mode,
|
|
224
|
+
layoutKind: query.sc_layoutKind,
|
|
224
225
|
},
|
|
225
226
|
// Cache the preview data for 3 seconds to ensure the page is rendered with the correct preview data not the cached one
|
|
226
227
|
{
|
|
227
|
-
path: query.route,
|
|
228
228
|
maxAge: 3,
|
|
229
229
|
});
|
|
230
230
|
// Cookies with the SameSite=Lax policy set by Next.js setPreviewData function causes CORS issue
|
package/dist/cjs/index.js
CHANGED
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.FEaaSComponent = exports.EditFrame = exports.DateField = exports.Text = exports.Image = exports.
|
|
26
|
+
exports.FEaaSComponent = exports.EditFrame = exports.DateField = exports.Text = exports.Image = exports.ComponentBuilder = exports.BYOCWrapper = exports.FEaaSWrapper = exports.NextImage = 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.getGroomedVariantIds = exports.getPersonalizedRewriteData = exports.getPersonalizedRewrite = exports.personalizeLayout = exports.RestDictionaryService = exports.GraphQLDictionaryService = exports.trackingApi = exports.mediaApi = exports.EditMode = exports.getContentStylesheetLink = exports.getFieldValue = exports.getChildPlaceholder = exports.RestLayoutService = exports.GraphQLLayoutService = exports.LayoutServicePageState = exports.MemoryCacheClient = exports.debug = exports.enableDebug = exports.NativeDataFetcher = exports.AxiosDataFetcher = exports.constants = void 0;
|
|
27
27
|
exports.EditingScripts = exports.withEmptyFieldEditingComponent = exports.withFieldMetadata = exports.withDatasourceCheck = exports.withPlaceholder = exports.withEditorChromes = exports.useSitecoreContext = exports.withSitecoreContext = exports.SitecoreContextReactContext = exports.SitecoreContext = exports.VisitorIdentification = exports.DefaultEmptyFieldEditingComponentText = exports.DefaultEmptyFieldEditingComponentImage = exports.File = exports.getComponentLibraryStylesheetLinks = exports.BYOCComponent = exports.fetchFEaaSComponentServerProps = 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; } });
|
|
@@ -31,6 +31,7 @@ Object.defineProperty(exports, "AxiosDataFetcher", { enumerable: true, get: func
|
|
|
31
31
|
Object.defineProperty(exports, "NativeDataFetcher", { enumerable: true, get: function () { return sitecore_jss_1.NativeDataFetcher; } });
|
|
32
32
|
Object.defineProperty(exports, "enableDebug", { enumerable: true, get: function () { return sitecore_jss_1.enableDebug; } });
|
|
33
33
|
Object.defineProperty(exports, "debug", { enumerable: true, get: function () { return sitecore_jss_1.debug; } });
|
|
34
|
+
Object.defineProperty(exports, "MemoryCacheClient", { enumerable: true, get: function () { return sitecore_jss_1.MemoryCacheClient; } });
|
|
34
35
|
var layout_1 = require("@sitecore-jss/sitecore-jss/layout");
|
|
35
36
|
Object.defineProperty(exports, "LayoutServicePageState", { enumerable: true, get: function () { return layout_1.LayoutServicePageState; } });
|
|
36
37
|
Object.defineProperty(exports, "GraphQLLayoutService", { enumerable: true, get: function () { return layout_1.GraphQLLayoutService; } });
|
|
@@ -88,8 +89,6 @@ const BYOCWrapper = __importStar(require("./components/BYOCWrapper"));
|
|
|
88
89
|
exports.BYOCWrapper = BYOCWrapper;
|
|
89
90
|
var ComponentBuilder_1 = require("./ComponentBuilder");
|
|
90
91
|
Object.defineProperty(exports, "ComponentBuilder", { enumerable: true, get: function () { return ComponentBuilder_1.ComponentBuilder; } });
|
|
91
|
-
var context_1 = require("./context");
|
|
92
|
-
Object.defineProperty(exports, "Context", { enumerable: true, get: function () { return context_1.Context; } });
|
|
93
92
|
var sitecore_jss_react_1 = require("@sitecore-jss/sitecore-jss-react");
|
|
94
93
|
Object.defineProperty(exports, "Image", { enumerable: true, get: function () { return sitecore_jss_react_1.Image; } });
|
|
95
94
|
Object.defineProperty(exports, "Text", { enumerable: true, get: function () { return sitecore_jss_react_1.Text; } });
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MultisiteMiddleware = exports.PersonalizeMiddleware = exports.RedirectsMiddleware = exports.debug = void 0;
|
|
3
|
+
exports.MultisiteMiddleware = exports.PersonalizeMiddleware = exports.RedirectsMiddleware = exports.MiddlewareBase = exports.debug = void 0;
|
|
4
4
|
var sitecore_jss_1 = require("@sitecore-jss/sitecore-jss");
|
|
5
5
|
Object.defineProperty(exports, "debug", { enumerable: true, get: function () { return sitecore_jss_1.debug; } });
|
|
6
|
+
var middleware_1 = require("./middleware");
|
|
7
|
+
Object.defineProperty(exports, "MiddlewareBase", { enumerable: true, get: function () { return middleware_1.MiddlewareBase; } });
|
|
6
8
|
var redirects_middleware_1 = require("./redirects-middleware");
|
|
7
9
|
Object.defineProperty(exports, "RedirectsMiddleware", { enumerable: true, get: function () { return redirects_middleware_1.RedirectsMiddleware; } });
|
|
8
10
|
var personalize_middleware_1 = require("./personalize-middleware");
|
|
@@ -14,7 +14,8 @@ const server_1 = require("next/server");
|
|
|
14
14
|
const personalize_1 = require("@sitecore-jss/sitecore-jss/personalize");
|
|
15
15
|
const sitecore_jss_1 = require("@sitecore-jss/sitecore-jss");
|
|
16
16
|
const middleware_1 = require("./middleware");
|
|
17
|
-
const server_2 = require("@sitecore-cloudsdk/
|
|
17
|
+
const server_2 = require("@sitecore-cloudsdk/core/server");
|
|
18
|
+
const server_3 = require("@sitecore-cloudsdk/personalize/server");
|
|
18
19
|
/**
|
|
19
20
|
* Middleware / handler to support Sitecore Personalize
|
|
20
21
|
*/
|
|
@@ -136,20 +137,22 @@ class PersonalizeMiddleware extends middleware_1.MiddlewareBase {
|
|
|
136
137
|
}
|
|
137
138
|
initPersonalizeServer({ hostname, siteName, request, response, }) {
|
|
138
139
|
return __awaiter(this, void 0, void 0, function* () {
|
|
139
|
-
yield (0, server_2.
|
|
140
|
+
yield (0, server_2.CloudSDK)(request, response, {
|
|
140
141
|
sitecoreEdgeUrl: this.config.cdpConfig.sitecoreEdgeUrl,
|
|
141
142
|
sitecoreEdgeContextId: this.config.cdpConfig.sitecoreEdgeContextId,
|
|
142
143
|
siteName,
|
|
143
144
|
cookieDomain: hostname,
|
|
144
145
|
enableServerCookie: true,
|
|
145
|
-
})
|
|
146
|
+
})
|
|
147
|
+
.addPersonalize({ enablePersonalizeCookie: true })
|
|
148
|
+
.initialize();
|
|
146
149
|
});
|
|
147
150
|
}
|
|
148
151
|
personalize({ params, friendlyId, language, timeout, variantIds, }, request) {
|
|
149
152
|
var _a;
|
|
150
153
|
return __awaiter(this, void 0, void 0, function* () {
|
|
151
154
|
sitecore_jss_1.debug.personalize('executing experience for %s %o', friendlyId, params);
|
|
152
|
-
return (yield (0,
|
|
155
|
+
return (yield (0, server_3.personalize)(request, {
|
|
153
156
|
channel: this.config.cdpConfig.channel || 'WEB',
|
|
154
157
|
currency: (_a = this.config.cdpConfig.currency) !== null && _a !== void 0 ? _a : 'USD',
|
|
155
158
|
friendlyId,
|
|
@@ -13,10 +13,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.RedirectsMiddleware = void 0;
|
|
16
|
-
const regex_parser_1 = __importDefault(require("regex-parser"));
|
|
17
|
-
const server_1 = require("next/server");
|
|
18
|
-
const site_1 = require("@sitecore-jss/sitecore-jss/site");
|
|
19
16
|
const sitecore_jss_1 = require("@sitecore-jss/sitecore-jss");
|
|
17
|
+
const site_1 = require("@sitecore-jss/sitecore-jss/site");
|
|
18
|
+
const utils_1 = require("@sitecore-jss/sitecore-jss/utils");
|
|
19
|
+
const server_1 = require("next/server");
|
|
20
|
+
const regex_parser_1 = __importDefault(require("regex-parser"));
|
|
20
21
|
const middleware_1 = require("./middleware");
|
|
21
22
|
const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
|
|
22
23
|
const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
|
|
@@ -43,6 +44,7 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
|
|
|
43
44
|
hostname,
|
|
44
45
|
});
|
|
45
46
|
const createResponse = () => __awaiter(this, void 0, void 0, function* () {
|
|
47
|
+
var _a;
|
|
46
48
|
if (this.config.disabled && this.config.disabled(req, res || server_1.NextResponse.next())) {
|
|
47
49
|
sitecore_jss_1.debug.redirects('skipped (redirects middleware is disabled)');
|
|
48
50
|
return res || server_1.NextResponse.next();
|
|
@@ -63,48 +65,50 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
|
|
|
63
65
|
!(REGEXP_ABSOLUTE_URL.test(existsRedirect.target) &&
|
|
64
66
|
existsRedirect.target.includes(hostname))) {
|
|
65
67
|
existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
|
|
68
|
+
req.nextUrl.locale = site.language;
|
|
66
69
|
}
|
|
67
|
-
const url = req.nextUrl.clone();
|
|
70
|
+
const url = this.normalizeUrl(req.nextUrl.clone());
|
|
68
71
|
if (REGEXP_ABSOLUTE_URL.test(existsRedirect.target)) {
|
|
69
72
|
url.href = existsRedirect.target;
|
|
70
73
|
}
|
|
71
74
|
else {
|
|
72
|
-
const source = `${url.pathname}${
|
|
73
|
-
url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
|
|
75
|
+
const source = `${url.pathname.replace(/\/*$/gi, '')}${existsRedirect.matchedQueryString}`;
|
|
74
76
|
const urlFirstPart = existsRedirect.target.split('/')[1];
|
|
75
77
|
if (this.locales.includes(urlFirstPart)) {
|
|
76
|
-
|
|
78
|
+
req.nextUrl.locale = urlFirstPart;
|
|
77
79
|
existsRedirect.target = existsRedirect.target.replace(`/${urlFirstPart}`, '');
|
|
78
80
|
}
|
|
79
81
|
const target = source
|
|
80
82
|
.replace((0, regex_parser_1.default)(existsRedirect.pattern), existsRedirect.target)
|
|
81
83
|
.replace(/^\/\//, '/')
|
|
82
84
|
.split('?');
|
|
83
|
-
url.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
for (const [key, val] of newParams.entries()) {
|
|
87
|
-
url.searchParams.append(key, val);
|
|
88
|
-
}
|
|
85
|
+
if (url.search && existsRedirect.isQueryStringPreserved) {
|
|
86
|
+
const targetQueryString = (_a = target[1]) !== null && _a !== void 0 ? _a : '';
|
|
87
|
+
url.search = '?' + new URLSearchParams(`${url.search}&${targetQueryString}`).toString();
|
|
89
88
|
}
|
|
89
|
+
else if (target[1]) {
|
|
90
|
+
url.search = '?' + target[1];
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
url.search = '';
|
|
94
|
+
}
|
|
95
|
+
const prepareNewURL = new URL(`${target[0]}${url.search}`, url.origin);
|
|
96
|
+
url.href = prepareNewURL.href;
|
|
97
|
+
url.pathname = prepareNewURL.pathname;
|
|
98
|
+
url.search = prepareNewURL.search;
|
|
99
|
+
url.locale = req.nextUrl.locale;
|
|
90
100
|
}
|
|
91
|
-
const redirectUrl = decodeURIComponent(url.href);
|
|
92
101
|
/** return Response redirect with http code of redirect type **/
|
|
93
102
|
switch (existsRedirect.redirectType) {
|
|
94
|
-
case site_1.REDIRECT_TYPE_301:
|
|
95
|
-
return
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
case site_1.
|
|
101
|
-
return server_1.NextResponse.
|
|
102
|
-
|
|
103
|
-
statusText: 'Found',
|
|
104
|
-
headers: res === null || res === void 0 ? void 0 : res.headers,
|
|
105
|
-
});
|
|
106
|
-
case site_1.REDIRECT_TYPE_SERVER_TRANSFER:
|
|
107
|
-
return server_1.NextResponse.rewrite(redirectUrl, res);
|
|
103
|
+
case site_1.REDIRECT_TYPE_301: {
|
|
104
|
+
return this.createRedirectResponse(url, res, 301, 'Moved Permanently');
|
|
105
|
+
}
|
|
106
|
+
case site_1.REDIRECT_TYPE_302: {
|
|
107
|
+
return this.createRedirectResponse(url, res, 302, 'Found');
|
|
108
|
+
}
|
|
109
|
+
case site_1.REDIRECT_TYPE_SERVER_TRANSFER: {
|
|
110
|
+
return this.rewrite(url.href, req, res || server_1.NextResponse.next());
|
|
111
|
+
}
|
|
108
112
|
default:
|
|
109
113
|
return res || server_1.NextResponse.next();
|
|
110
114
|
}
|
|
@@ -149,29 +153,140 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
|
|
|
149
153
|
getExistsRedirect(req, siteName) {
|
|
150
154
|
return __awaiter(this, void 0, void 0, function* () {
|
|
151
155
|
const redirects = yield this.redirectsService.fetchRedirects(siteName);
|
|
152
|
-
const
|
|
153
|
-
const targetQS = req.nextUrl.search || '';
|
|
156
|
+
const { pathname: targetURL, search: targetQS = '', locale } = this.normalizeUrl(req.nextUrl.clone());
|
|
154
157
|
const language = this.getLanguage(req);
|
|
155
158
|
const modifyRedirects = structuredClone(redirects);
|
|
156
159
|
return modifyRedirects.length
|
|
157
160
|
? modifyRedirects.find((redirect) => {
|
|
161
|
+
// Modify the redirect pattern to ignore the language prefix in the path
|
|
158
162
|
redirect.pattern = redirect.pattern.replace(RegExp(`^[^]?/${language}/`, 'gi'), '');
|
|
163
|
+
// Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
|
|
159
164
|
redirect.pattern = `/^\/${redirect.pattern
|
|
160
|
-
.replace(/^\/|\/$/g, '')
|
|
161
|
-
.replace(/^\^\/|\/\$$/g, '')
|
|
162
|
-
.replace(/^\^|\$$/g, '')
|
|
163
|
-
.replace(/(?<!\\)\?/g, '\\?')
|
|
164
|
-
.replace(/\$\/gi$/g, '')}[\/]?$/
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
165
|
+
.replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
|
|
166
|
+
.replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
|
|
167
|
+
.replace(/^\^|\$$/g, '') // Further cleans up anchors
|
|
168
|
+
.replace(/(?<!\\)\?/g, '\\?') // Escapes question marks in the pattern
|
|
169
|
+
.replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
|
|
170
|
+
/**
|
|
171
|
+
* This line checks whether the current URL query string (and all its possible permutations)
|
|
172
|
+
* matches the redirect pattern.
|
|
173
|
+
*
|
|
174
|
+
* Query parameters in URLs can come in different orders, but logically they represent the
|
|
175
|
+
* same information (e.g., "key1=value1&key2=value2" is the same as "key2=value2&key1=value1").
|
|
176
|
+
* To account for this, the method `isPermutedQueryMatch` generates all possible permutations
|
|
177
|
+
* of the query parameters and checks if any of those permutations match the regex pattern for the redirect.
|
|
178
|
+
*
|
|
179
|
+
* NOTE: This fix is specifically implemented for Netlify, where query parameters are sometimes
|
|
180
|
+
* automatically sorted, which can cause issues with matching redirects if the order of query
|
|
181
|
+
* parameters is important. By checking every possible permutation, we ensure that redirects
|
|
182
|
+
* work correctly on Netlify despite this behavior.
|
|
183
|
+
*
|
|
184
|
+
* It passes several pieces of information to the function:
|
|
185
|
+
* 1. `pathname`: The normalized URL path without query parameters (e.g., '/about').
|
|
186
|
+
* 2. `queryString`: The current query string from the URL, which will be permuted and matched (e.g., '?key1=value1&key2=value2').
|
|
187
|
+
* 3. `pattern`: The regex pattern for the redirect that we are trying to match against the URL (e.g., '/about?key1=value1').
|
|
188
|
+
* 4. `locale`: The locale part of the URL (if any), which helps support multilingual URLs.
|
|
189
|
+
*
|
|
190
|
+
* If one of the permutations of the query string matches the redirect pattern, the function
|
|
191
|
+
* returns the matched query string, which is stored in `matchedQueryString`. If no match is found,
|
|
192
|
+
* it returns `undefined`. The `matchedQueryString` is later used to indicate whether the query
|
|
193
|
+
* string contributed to a successful redirect match.
|
|
194
|
+
*/
|
|
195
|
+
const matchedQueryString = this.isPermutedQueryMatch({
|
|
196
|
+
pathname: targetURL,
|
|
197
|
+
queryString: targetQS,
|
|
198
|
+
pattern: redirect.pattern,
|
|
199
|
+
locale,
|
|
200
|
+
});
|
|
201
|
+
// Save the matched query string (if found) into the redirect object
|
|
202
|
+
redirect.matchedQueryString = matchedQueryString || '';
|
|
203
|
+
// Return the redirect if the URL path or any query string permutation matches the pattern
|
|
204
|
+
return (((0, regex_parser_1.default)(redirect.pattern).test(targetURL) ||
|
|
205
|
+
(0, regex_parser_1.default)(redirect.pattern).test(`/${req.nextUrl.locale}${targetURL}`) ||
|
|
206
|
+
matchedQueryString) &&
|
|
207
|
+
(redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
|
|
172
208
|
})
|
|
173
209
|
: undefined;
|
|
174
210
|
});
|
|
175
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* When a user clicks on a link generated by the Link component from next/link,
|
|
214
|
+
* Next.js adds special parameters in the route called path.
|
|
215
|
+
* This method removes these special parameters.
|
|
216
|
+
* @param {NextURL} url
|
|
217
|
+
* @returns {string} normalize url
|
|
218
|
+
*/
|
|
219
|
+
normalizeUrl(url) {
|
|
220
|
+
if (!url.search) {
|
|
221
|
+
return url;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Prepare special parameters for exclusion.
|
|
225
|
+
*/
|
|
226
|
+
const splittedPathname = url.pathname
|
|
227
|
+
.split('/')
|
|
228
|
+
.filter((route) => route)
|
|
229
|
+
.map((route) => `path=${route}`);
|
|
230
|
+
/**
|
|
231
|
+
* Remove special parameters(Next.JS)
|
|
232
|
+
* Example: /about/contact/us
|
|
233
|
+
* When a user clicks on this link, Next.js should generate a link for the middleware, formatted like this:
|
|
234
|
+
* http://host/about/contact/us?path=about&path=contact&path=us
|
|
235
|
+
*/
|
|
236
|
+
const newQueryString = url.search
|
|
237
|
+
.replace(/^\?/, '')
|
|
238
|
+
.split('&')
|
|
239
|
+
.filter((param) => {
|
|
240
|
+
if (!splittedPathname.includes(param)) {
|
|
241
|
+
return param;
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
})
|
|
245
|
+
.join('&');
|
|
246
|
+
const newUrl = new URL(`${url.pathname}?${newQueryString}`, url.origin);
|
|
247
|
+
url.search = newUrl.search;
|
|
248
|
+
url.pathname = newUrl.pathname;
|
|
249
|
+
url.href = newUrl.href;
|
|
250
|
+
return url;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Helper function to create a redirect response and remove the x-middleware-next header.
|
|
254
|
+
* @param {NextURL} url The URL to redirect to.
|
|
255
|
+
* @param {Response} res The response object.
|
|
256
|
+
* @param {number} status The HTTP status code of the redirect.
|
|
257
|
+
* @param {string} statusText The status text of the redirect.
|
|
258
|
+
* @returns {NextResponse<unknown>} The redirect response.
|
|
259
|
+
*/
|
|
260
|
+
createRedirectResponse(url, res, status, statusText) {
|
|
261
|
+
const redirect = server_1.NextResponse.redirect(url, {
|
|
262
|
+
status,
|
|
263
|
+
statusText,
|
|
264
|
+
headers: res === null || res === void 0 ? void 0 : res.headers,
|
|
265
|
+
});
|
|
266
|
+
if (res === null || res === void 0 ? void 0 : res.headers) {
|
|
267
|
+
redirect.headers.delete('x-middleware-next');
|
|
268
|
+
redirect.headers.delete('x-middleware-rewrite');
|
|
269
|
+
}
|
|
270
|
+
return redirect;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Checks if the current URL query matches the provided pattern, considering all permutations of query parameters.
|
|
274
|
+
* It constructs all possible query parameter permutations and tests them against the pattern.
|
|
275
|
+
* @param {Object} params - The parameters for the URL match.
|
|
276
|
+
* @param {string} params.pathname - The current URL pathname.
|
|
277
|
+
* @param {string} params.queryString - The current URL query string.
|
|
278
|
+
* @param {string} params.pattern - The regex pattern to test the constructed URLs against.
|
|
279
|
+
* @param {string} [params.locale] - The locale prefix to include in the URL if present.
|
|
280
|
+
* @returns {string | undefined} - return query string if any of the query permutations match the provided pattern, undefined otherwise.
|
|
281
|
+
*/
|
|
282
|
+
isPermutedQueryMatch({ pathname, queryString, pattern, locale, }) {
|
|
283
|
+
const paramsArray = Array.from(new URLSearchParams(queryString).entries());
|
|
284
|
+
const listOfPermuted = (0, utils_1.getPermutations)(paramsArray).map((permutation) => '?' + permutation.map(([key, value]) => `${key}=${value}`).join('&'));
|
|
285
|
+
const normalizedPath = pathname.replace(/\/*$/gi, '');
|
|
286
|
+
return listOfPermuted.find((query) => [
|
|
287
|
+
(0, regex_parser_1.default)(pattern).test(`${normalizedPath}${query}`),
|
|
288
|
+
(0, regex_parser_1.default)(pattern).test(`/${locale}${normalizedPath}${query}`),
|
|
289
|
+
].some(Boolean));
|
|
290
|
+
}
|
|
176
291
|
}
|
|
177
292
|
exports.RedirectsMiddleware = RedirectsMiddleware;
|
|
@@ -12,13 +12,15 @@ 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, withFieldMetadata, } 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
18
|
import { DefaultEmptyFieldEditingComponentImage } from '@sitecore-jss/sitecore-jss-react';
|
|
19
|
-
import { isFieldValueEmpty } from '@sitecore-jss/sitecore-jss/layout';
|
|
19
|
+
import { isFieldValueEmpty, LayoutServicePageState } from '@sitecore-jss/sitecore-jss/layout';
|
|
20
20
|
export const NextImage = withFieldMetadata(withEmptyFieldEditingComponent((_a) => {
|
|
21
|
+
var _b;
|
|
21
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);
|
|
22
24
|
// next handles src and we use a custom loader,
|
|
23
25
|
// throw error if these are present
|
|
24
26
|
if (otherProps.src) {
|
|
@@ -40,8 +42,11 @@ export const NextImage = withFieldMetadata(withEmptyFieldEditingComponent((_a) =
|
|
|
40
42
|
if (!img) {
|
|
41
43
|
return null;
|
|
42
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;
|
|
43
48
|
const attrs = Object.assign(Object.assign(Object.assign({}, img), otherProps), { fill,
|
|
44
|
-
priority, src: mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix) });
|
|
49
|
+
priority, src: mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix), unoptimized });
|
|
45
50
|
const imageProps = Object.assign(Object.assign({}, attrs), {
|
|
46
51
|
// force replace /media with /jssmedia in src since we _know_ we will be adding a 'mw' query string parameter
|
|
47
52
|
// this is required for Sitecore media API resizing to work properly
|
|
@@ -9,4 +9,4 @@ export const EDITING_PASS_THROUGH_HEADERS = ['authorization', 'cookie'];
|
|
|
9
9
|
/**
|
|
10
10
|
* Default allowed origins for editing requests. This is used to enforce CORS, CSP headers.
|
|
11
11
|
*/
|
|
12
|
-
export const EDITING_ALLOWED_ORIGINS = ['https://pages
|
|
12
|
+
export const EDITING_ALLOWED_ORIGINS = ['https://pages.sitecorecloud.io'];
|
|
@@ -216,10 +216,10 @@ export class MetadataHandler {
|
|
|
216
216
|
version: query.sc_version,
|
|
217
217
|
editMode: EditMode.Metadata,
|
|
218
218
|
pageState: query.mode,
|
|
219
|
+
layoutKind: query.sc_layoutKind,
|
|
219
220
|
},
|
|
220
221
|
// Cache the preview data for 3 seconds to ensure the page is rendered with the correct preview data not the cached one
|
|
221
222
|
{
|
|
222
|
-
path: query.route,
|
|
223
223
|
maxAge: 3,
|
|
224
224
|
});
|
|
225
225
|
// Cookies with the SameSite=Lax policy set by Next.js setPreviewData function causes CORS issue
|
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { constants, AxiosDataFetcher, NativeDataFetcher, enableDebug, debug, } from '@sitecore-jss/sitecore-jss';
|
|
1
|
+
export { constants, AxiosDataFetcher, NativeDataFetcher, enableDebug, debug, MemoryCacheClient, } from '@sitecore-jss/sitecore-jss';
|
|
2
2
|
export { LayoutServicePageState, GraphQLLayoutService, RestLayoutService, getChildPlaceholder, getFieldValue, 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';
|
|
@@ -19,5 +19,4 @@ import * as BYOCWrapper from './components/BYOCWrapper';
|
|
|
19
19
|
export { FEaaSWrapper };
|
|
20
20
|
export { BYOCWrapper };
|
|
21
21
|
export { ComponentBuilder } from './ComponentBuilder';
|
|
22
|
-
export { Context } from './context';
|
|
23
22
|
export { Image, Text, DateField, EditFrame, FEaaSComponent, fetchFEaaSComponentServerProps, BYOCComponent, getComponentLibraryStylesheetLinks, File, DefaultEmptyFieldEditingComponentImage, DefaultEmptyFieldEditingComponentText, VisitorIdentification, SitecoreContext, SitecoreContextReactContext, withSitecoreContext, useSitecoreContext, withEditorChromes, withPlaceholder, withDatasourceCheck, withFieldMetadata, withEmptyFieldEditingComponent, EditingScripts, } from '@sitecore-jss/sitecore-jss-react';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { debug } from '@sitecore-jss/sitecore-jss';
|
|
2
|
+
export { MiddlewareBase } from './middleware';
|
|
2
3
|
export { RedirectsMiddleware } from './redirects-middleware';
|
|
3
4
|
export { PersonalizeMiddleware } from './personalize-middleware';
|
|
4
5
|
export { MultisiteMiddleware } from './multisite-middleware';
|
|
@@ -11,7 +11,8 @@ import { NextResponse } from 'next/server';
|
|
|
11
11
|
import { GraphQLPersonalizeService, getPersonalizedRewrite, CdpHelper, DEFAULT_VARIANT, } from '@sitecore-jss/sitecore-jss/personalize';
|
|
12
12
|
import { debug } from '@sitecore-jss/sitecore-jss';
|
|
13
13
|
import { MiddlewareBase } from './middleware';
|
|
14
|
-
import {
|
|
14
|
+
import { CloudSDK } from '@sitecore-cloudsdk/core/server';
|
|
15
|
+
import { personalize } from '@sitecore-cloudsdk/personalize/server';
|
|
15
16
|
/**
|
|
16
17
|
* Middleware / handler to support Sitecore Personalize
|
|
17
18
|
*/
|
|
@@ -133,13 +134,15 @@ export class PersonalizeMiddleware extends MiddlewareBase {
|
|
|
133
134
|
}
|
|
134
135
|
initPersonalizeServer({ hostname, siteName, request, response, }) {
|
|
135
136
|
return __awaiter(this, void 0, void 0, function* () {
|
|
136
|
-
yield
|
|
137
|
+
yield CloudSDK(request, response, {
|
|
137
138
|
sitecoreEdgeUrl: this.config.cdpConfig.sitecoreEdgeUrl,
|
|
138
139
|
sitecoreEdgeContextId: this.config.cdpConfig.sitecoreEdgeContextId,
|
|
139
140
|
siteName,
|
|
140
141
|
cookieDomain: hostname,
|
|
141
142
|
enableServerCookie: true,
|
|
142
|
-
})
|
|
143
|
+
})
|
|
144
|
+
.addPersonalize({ enablePersonalizeCookie: true })
|
|
145
|
+
.initialize();
|
|
143
146
|
});
|
|
144
147
|
}
|
|
145
148
|
personalize({ params, friendlyId, language, timeout, variantIds, }, request) {
|
|
@@ -7,10 +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 regexParser from 'regex-parser';
|
|
11
|
-
import { NextResponse } from 'next/server';
|
|
12
|
-
import { GraphQLRedirectsService, REDIRECT_TYPE_301, REDIRECT_TYPE_302, REDIRECT_TYPE_SERVER_TRANSFER, } from '@sitecore-jss/sitecore-jss/site';
|
|
13
10
|
import { debug } from '@sitecore-jss/sitecore-jss';
|
|
11
|
+
import { GraphQLRedirectsService, REDIRECT_TYPE_301, REDIRECT_TYPE_302, REDIRECT_TYPE_SERVER_TRANSFER, } from '@sitecore-jss/sitecore-jss/site';
|
|
12
|
+
import { getPermutations } from '@sitecore-jss/sitecore-jss/utils';
|
|
13
|
+
import { NextResponse } from 'next/server';
|
|
14
|
+
import regexParser from 'regex-parser';
|
|
14
15
|
import { MiddlewareBase } from './middleware';
|
|
15
16
|
const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
|
|
16
17
|
const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
|
|
@@ -37,6 +38,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
|
|
|
37
38
|
hostname,
|
|
38
39
|
});
|
|
39
40
|
const createResponse = () => __awaiter(this, void 0, void 0, function* () {
|
|
41
|
+
var _a;
|
|
40
42
|
if (this.config.disabled && this.config.disabled(req, res || NextResponse.next())) {
|
|
41
43
|
debug.redirects('skipped (redirects middleware is disabled)');
|
|
42
44
|
return res || NextResponse.next();
|
|
@@ -57,48 +59,50 @@ export class RedirectsMiddleware extends MiddlewareBase {
|
|
|
57
59
|
!(REGEXP_ABSOLUTE_URL.test(existsRedirect.target) &&
|
|
58
60
|
existsRedirect.target.includes(hostname))) {
|
|
59
61
|
existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
|
|
62
|
+
req.nextUrl.locale = site.language;
|
|
60
63
|
}
|
|
61
|
-
const url = req.nextUrl.clone();
|
|
64
|
+
const url = this.normalizeUrl(req.nextUrl.clone());
|
|
62
65
|
if (REGEXP_ABSOLUTE_URL.test(existsRedirect.target)) {
|
|
63
66
|
url.href = existsRedirect.target;
|
|
64
67
|
}
|
|
65
68
|
else {
|
|
66
|
-
const source = `${url.pathname}${
|
|
67
|
-
url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
|
|
69
|
+
const source = `${url.pathname.replace(/\/*$/gi, '')}${existsRedirect.matchedQueryString}`;
|
|
68
70
|
const urlFirstPart = existsRedirect.target.split('/')[1];
|
|
69
71
|
if (this.locales.includes(urlFirstPart)) {
|
|
70
|
-
|
|
72
|
+
req.nextUrl.locale = urlFirstPart;
|
|
71
73
|
existsRedirect.target = existsRedirect.target.replace(`/${urlFirstPart}`, '');
|
|
72
74
|
}
|
|
73
75
|
const target = source
|
|
74
76
|
.replace(regexParser(existsRedirect.pattern), existsRedirect.target)
|
|
75
77
|
.replace(/^\/\//, '/')
|
|
76
78
|
.split('?');
|
|
77
|
-
url.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
for (const [key, val] of newParams.entries()) {
|
|
81
|
-
url.searchParams.append(key, val);
|
|
82
|
-
}
|
|
79
|
+
if (url.search && existsRedirect.isQueryStringPreserved) {
|
|
80
|
+
const targetQueryString = (_a = target[1]) !== null && _a !== void 0 ? _a : '';
|
|
81
|
+
url.search = '?' + new URLSearchParams(`${url.search}&${targetQueryString}`).toString();
|
|
83
82
|
}
|
|
83
|
+
else if (target[1]) {
|
|
84
|
+
url.search = '?' + target[1];
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
url.search = '';
|
|
88
|
+
}
|
|
89
|
+
const prepareNewURL = new URL(`${target[0]}${url.search}`, url.origin);
|
|
90
|
+
url.href = prepareNewURL.href;
|
|
91
|
+
url.pathname = prepareNewURL.pathname;
|
|
92
|
+
url.search = prepareNewURL.search;
|
|
93
|
+
url.locale = req.nextUrl.locale;
|
|
84
94
|
}
|
|
85
|
-
const redirectUrl = decodeURIComponent(url.href);
|
|
86
95
|
/** return Response redirect with http code of redirect type **/
|
|
87
96
|
switch (existsRedirect.redirectType) {
|
|
88
|
-
case REDIRECT_TYPE_301:
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
case
|
|
95
|
-
return
|
|
96
|
-
|
|
97
|
-
statusText: 'Found',
|
|
98
|
-
headers: res === null || res === void 0 ? void 0 : res.headers,
|
|
99
|
-
});
|
|
100
|
-
case REDIRECT_TYPE_SERVER_TRANSFER:
|
|
101
|
-
return NextResponse.rewrite(redirectUrl, res);
|
|
97
|
+
case REDIRECT_TYPE_301: {
|
|
98
|
+
return this.createRedirectResponse(url, res, 301, 'Moved Permanently');
|
|
99
|
+
}
|
|
100
|
+
case REDIRECT_TYPE_302: {
|
|
101
|
+
return this.createRedirectResponse(url, res, 302, 'Found');
|
|
102
|
+
}
|
|
103
|
+
case REDIRECT_TYPE_SERVER_TRANSFER: {
|
|
104
|
+
return this.rewrite(url.href, req, res || NextResponse.next());
|
|
105
|
+
}
|
|
102
106
|
default:
|
|
103
107
|
return res || NextResponse.next();
|
|
104
108
|
}
|
|
@@ -143,28 +147,139 @@ export class RedirectsMiddleware extends MiddlewareBase {
|
|
|
143
147
|
getExistsRedirect(req, siteName) {
|
|
144
148
|
return __awaiter(this, void 0, void 0, function* () {
|
|
145
149
|
const redirects = yield this.redirectsService.fetchRedirects(siteName);
|
|
146
|
-
const
|
|
147
|
-
const targetQS = req.nextUrl.search || '';
|
|
150
|
+
const { pathname: targetURL, search: targetQS = '', locale } = this.normalizeUrl(req.nextUrl.clone());
|
|
148
151
|
const language = this.getLanguage(req);
|
|
149
152
|
const modifyRedirects = structuredClone(redirects);
|
|
150
153
|
return modifyRedirects.length
|
|
151
154
|
? modifyRedirects.find((redirect) => {
|
|
155
|
+
// Modify the redirect pattern to ignore the language prefix in the path
|
|
152
156
|
redirect.pattern = redirect.pattern.replace(RegExp(`^[^]?/${language}/`, 'gi'), '');
|
|
157
|
+
// Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
|
|
153
158
|
redirect.pattern = `/^\/${redirect.pattern
|
|
154
|
-
.replace(/^\/|\/$/g, '')
|
|
155
|
-
.replace(/^\^\/|\/\$$/g, '')
|
|
156
|
-
.replace(/^\^|\$$/g, '')
|
|
157
|
-
.replace(/(?<!\\)\?/g, '\\?')
|
|
158
|
-
.replace(/\$\/gi$/g, '')}[\/]?$/
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
159
|
+
.replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
|
|
160
|
+
.replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
|
|
161
|
+
.replace(/^\^|\$$/g, '') // Further cleans up anchors
|
|
162
|
+
.replace(/(?<!\\)\?/g, '\\?') // Escapes question marks in the pattern
|
|
163
|
+
.replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
|
|
164
|
+
/**
|
|
165
|
+
* This line checks whether the current URL query string (and all its possible permutations)
|
|
166
|
+
* matches the redirect pattern.
|
|
167
|
+
*
|
|
168
|
+
* Query parameters in URLs can come in different orders, but logically they represent the
|
|
169
|
+
* same information (e.g., "key1=value1&key2=value2" is the same as "key2=value2&key1=value1").
|
|
170
|
+
* To account for this, the method `isPermutedQueryMatch` generates all possible permutations
|
|
171
|
+
* of the query parameters and checks if any of those permutations match the regex pattern for the redirect.
|
|
172
|
+
*
|
|
173
|
+
* NOTE: This fix is specifically implemented for Netlify, where query parameters are sometimes
|
|
174
|
+
* automatically sorted, which can cause issues with matching redirects if the order of query
|
|
175
|
+
* parameters is important. By checking every possible permutation, we ensure that redirects
|
|
176
|
+
* work correctly on Netlify despite this behavior.
|
|
177
|
+
*
|
|
178
|
+
* It passes several pieces of information to the function:
|
|
179
|
+
* 1. `pathname`: The normalized URL path without query parameters (e.g., '/about').
|
|
180
|
+
* 2. `queryString`: The current query string from the URL, which will be permuted and matched (e.g., '?key1=value1&key2=value2').
|
|
181
|
+
* 3. `pattern`: The regex pattern for the redirect that we are trying to match against the URL (e.g., '/about?key1=value1').
|
|
182
|
+
* 4. `locale`: The locale part of the URL (if any), which helps support multilingual URLs.
|
|
183
|
+
*
|
|
184
|
+
* If one of the permutations of the query string matches the redirect pattern, the function
|
|
185
|
+
* returns the matched query string, which is stored in `matchedQueryString`. If no match is found,
|
|
186
|
+
* it returns `undefined`. The `matchedQueryString` is later used to indicate whether the query
|
|
187
|
+
* string contributed to a successful redirect match.
|
|
188
|
+
*/
|
|
189
|
+
const matchedQueryString = this.isPermutedQueryMatch({
|
|
190
|
+
pathname: targetURL,
|
|
191
|
+
queryString: targetQS,
|
|
192
|
+
pattern: redirect.pattern,
|
|
193
|
+
locale,
|
|
194
|
+
});
|
|
195
|
+
// Save the matched query string (if found) into the redirect object
|
|
196
|
+
redirect.matchedQueryString = matchedQueryString || '';
|
|
197
|
+
// Return the redirect if the URL path or any query string permutation matches the pattern
|
|
198
|
+
return ((regexParser(redirect.pattern).test(targetURL) ||
|
|
199
|
+
regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${targetURL}`) ||
|
|
200
|
+
matchedQueryString) &&
|
|
201
|
+
(redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
|
|
166
202
|
})
|
|
167
203
|
: undefined;
|
|
168
204
|
});
|
|
169
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* When a user clicks on a link generated by the Link component from next/link,
|
|
208
|
+
* Next.js adds special parameters in the route called path.
|
|
209
|
+
* This method removes these special parameters.
|
|
210
|
+
* @param {NextURL} url
|
|
211
|
+
* @returns {string} normalize url
|
|
212
|
+
*/
|
|
213
|
+
normalizeUrl(url) {
|
|
214
|
+
if (!url.search) {
|
|
215
|
+
return url;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Prepare special parameters for exclusion.
|
|
219
|
+
*/
|
|
220
|
+
const splittedPathname = url.pathname
|
|
221
|
+
.split('/')
|
|
222
|
+
.filter((route) => route)
|
|
223
|
+
.map((route) => `path=${route}`);
|
|
224
|
+
/**
|
|
225
|
+
* Remove special parameters(Next.JS)
|
|
226
|
+
* Example: /about/contact/us
|
|
227
|
+
* When a user clicks on this link, Next.js should generate a link for the middleware, formatted like this:
|
|
228
|
+
* http://host/about/contact/us?path=about&path=contact&path=us
|
|
229
|
+
*/
|
|
230
|
+
const newQueryString = url.search
|
|
231
|
+
.replace(/^\?/, '')
|
|
232
|
+
.split('&')
|
|
233
|
+
.filter((param) => {
|
|
234
|
+
if (!splittedPathname.includes(param)) {
|
|
235
|
+
return param;
|
|
236
|
+
}
|
|
237
|
+
return false;
|
|
238
|
+
})
|
|
239
|
+
.join('&');
|
|
240
|
+
const newUrl = new URL(`${url.pathname}?${newQueryString}`, url.origin);
|
|
241
|
+
url.search = newUrl.search;
|
|
242
|
+
url.pathname = newUrl.pathname;
|
|
243
|
+
url.href = newUrl.href;
|
|
244
|
+
return url;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Helper function to create a redirect response and remove the x-middleware-next header.
|
|
248
|
+
* @param {NextURL} url The URL to redirect to.
|
|
249
|
+
* @param {Response} res The response object.
|
|
250
|
+
* @param {number} status The HTTP status code of the redirect.
|
|
251
|
+
* @param {string} statusText The status text of the redirect.
|
|
252
|
+
* @returns {NextResponse<unknown>} The redirect response.
|
|
253
|
+
*/
|
|
254
|
+
createRedirectResponse(url, res, status, statusText) {
|
|
255
|
+
const redirect = NextResponse.redirect(url, {
|
|
256
|
+
status,
|
|
257
|
+
statusText,
|
|
258
|
+
headers: res === null || res === void 0 ? void 0 : res.headers,
|
|
259
|
+
});
|
|
260
|
+
if (res === null || res === void 0 ? void 0 : res.headers) {
|
|
261
|
+
redirect.headers.delete('x-middleware-next');
|
|
262
|
+
redirect.headers.delete('x-middleware-rewrite');
|
|
263
|
+
}
|
|
264
|
+
return redirect;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Checks if the current URL query matches the provided pattern, considering all permutations of query parameters.
|
|
268
|
+
* It constructs all possible query parameter permutations and tests them against the pattern.
|
|
269
|
+
* @param {Object} params - The parameters for the URL match.
|
|
270
|
+
* @param {string} params.pathname - The current URL pathname.
|
|
271
|
+
* @param {string} params.queryString - The current URL query string.
|
|
272
|
+
* @param {string} params.pattern - The regex pattern to test the constructed URLs against.
|
|
273
|
+
* @param {string} [params.locale] - The locale prefix to include in the URL if present.
|
|
274
|
+
* @returns {string | undefined} - return query string if any of the query permutations match the provided pattern, undefined otherwise.
|
|
275
|
+
*/
|
|
276
|
+
isPermutedQueryMatch({ pathname, queryString, pattern, locale, }) {
|
|
277
|
+
const paramsArray = Array.from(new URLSearchParams(queryString).entries());
|
|
278
|
+
const listOfPermuted = getPermutations(paramsArray).map((permutation) => '?' + permutation.map(([key, value]) => `${key}=${value}`).join('&'));
|
|
279
|
+
const normalizedPath = pathname.replace(/\/*$/gi, '');
|
|
280
|
+
return listOfPermuted.find((query) => [
|
|
281
|
+
regexParser(pattern).test(`${normalizedPath}${query}`),
|
|
282
|
+
regexParser(pattern).test(`/${locale}${normalizedPath}${query}`),
|
|
283
|
+
].some(Boolean));
|
|
284
|
+
}
|
|
170
285
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sitecore-jss/sitecore-jss-nextjs",
|
|
3
|
-
"version": "22.2.
|
|
3
|
+
"version": "22.2.1-canary.1",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"url": "https://github.com/sitecore/jss/issues"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@sitecore-cloudsdk/
|
|
32
|
+
"@sitecore-cloudsdk/core": "^0.4.0",
|
|
33
|
+
"@sitecore-cloudsdk/personalize": "^0.4.0",
|
|
33
34
|
"@types/chai": "^4.3.4",
|
|
34
35
|
"@types/chai-as-promised": "^7.1.5",
|
|
35
36
|
"@types/chai-string": "^1.4.2",
|
|
@@ -54,7 +55,7 @@
|
|
|
54
55
|
"eslint-plugin-react": "^7.32.1",
|
|
55
56
|
"jsdom": "^21.1.0",
|
|
56
57
|
"mocha": "^10.2.0",
|
|
57
|
-
"next": "^14.
|
|
58
|
+
"next": "^14.2.7",
|
|
58
59
|
"nock": "^13.3.0",
|
|
59
60
|
"nyc": "^15.1.0",
|
|
60
61
|
"react": "^18.2.0",
|
|
@@ -65,16 +66,16 @@
|
|
|
65
66
|
"typescript": "~4.9.4"
|
|
66
67
|
},
|
|
67
68
|
"peerDependencies": {
|
|
68
|
-
"@sitecore-cloudsdk/
|
|
69
|
-
"@sitecore-cloudsdk/personalize": "^0.
|
|
70
|
-
"next": "^14.
|
|
69
|
+
"@sitecore-cloudsdk/core": "^0.4.0",
|
|
70
|
+
"@sitecore-cloudsdk/personalize": "^0.4.0",
|
|
71
|
+
"next": "^14.2.7",
|
|
71
72
|
"react": "^18.2.0",
|
|
72
73
|
"react-dom": "^18.2.0"
|
|
73
74
|
},
|
|
74
75
|
"dependencies": {
|
|
75
|
-
"@sitecore-jss/sitecore-jss": "^22.2.
|
|
76
|
-
"@sitecore-jss/sitecore-jss-dev-tools": "^22.2.
|
|
77
|
-
"@sitecore-jss/sitecore-jss-react": "^22.2.
|
|
76
|
+
"@sitecore-jss/sitecore-jss": "^22.2.1-canary.1",
|
|
77
|
+
"@sitecore-jss/sitecore-jss-dev-tools": "^22.2.1-canary.1",
|
|
78
|
+
"@sitecore-jss/sitecore-jss-react": "^22.2.1-canary.1",
|
|
78
79
|
"@vercel/kv": "^0.2.1",
|
|
79
80
|
"prop-types": "^15.8.1",
|
|
80
81
|
"regex-parser": "^2.2.11",
|
|
@@ -82,7 +83,7 @@
|
|
|
82
83
|
},
|
|
83
84
|
"description": "",
|
|
84
85
|
"types": "types/index.d.ts",
|
|
85
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "74995e778ae28463d857b92a8ea5051333c18795",
|
|
86
87
|
"files": [
|
|
87
88
|
"dist",
|
|
88
89
|
"types",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NextApiRequest, NextApiResponse } from 'next';
|
|
2
2
|
import { AxiosDataFetcher } from '@sitecore-jss/sitecore-jss';
|
|
3
3
|
import { EditMode, LayoutServicePageState } from '@sitecore-jss/sitecore-jss/layout';
|
|
4
|
+
import { LayoutKind } from '@sitecore-jss/sitecore-jss/editing';
|
|
4
5
|
import { EditingDataService } from './editing-data-service';
|
|
5
6
|
import { RenderMiddlewareBase } from './render-middleware';
|
|
6
7
|
/**
|
|
@@ -108,6 +109,7 @@ export type MetadataQueryParams = {
|
|
|
108
109
|
mode: Exclude<LayoutServicePageState, 'normal'>;
|
|
109
110
|
sc_variant?: string;
|
|
110
111
|
sc_version?: string;
|
|
112
|
+
sc_layoutKind?: LayoutKind;
|
|
111
113
|
};
|
|
112
114
|
/**
|
|
113
115
|
* Next.js API request with Metadata query parameters.
|
|
@@ -126,6 +128,7 @@ export type EditingMetadataPreviewData = {
|
|
|
126
128
|
pageState: Exclude<LayoutServicePageState, 'Normal'>;
|
|
127
129
|
variantIds: string[];
|
|
128
130
|
version?: string;
|
|
131
|
+
layoutKind?: LayoutKind;
|
|
129
132
|
};
|
|
130
133
|
/**
|
|
131
134
|
* Type guard for EditingMetadataPreviewData
|
package/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { constants, HttpDataFetcher, HttpResponse, AxiosResponse, AxiosDataFetcher, AxiosDataFetcherConfig, NativeDataFetcher, NativeDataFetcherConfig, HTMLLink, enableDebug, debug, } from '@sitecore-jss/sitecore-jss';
|
|
1
|
+
export { constants, HttpDataFetcher, HttpResponse, AxiosResponse, AxiosDataFetcher, AxiosDataFetcherConfig, NativeDataFetcher, NativeDataFetcherConfig, HTMLLink, enableDebug, debug, CacheClient, CacheOptions, MemoryCacheClient, } from '@sitecore-jss/sitecore-jss';
|
|
2
2
|
export { LayoutService, LayoutServiceData, LayoutServicePageState, LayoutServiceContext, LayoutServiceContextData, GraphQLLayoutService, GraphQLLayoutServiceConfig, RestLayoutService, RestLayoutServiceConfig, PlaceholderData, PlaceholdersData, RouteData, Field, Item, HtmlElementRendering, getChildPlaceholder, getFieldValue, ComponentRendering, ComponentFields, ComponentParams, getContentStylesheetLink, EditMode, } from '@sitecore-jss/sitecore-jss/layout';
|
|
3
3
|
export { mediaApi } from '@sitecore-jss/sitecore-jss/media';
|
|
4
4
|
export { trackingApi, TrackingRequestOptions, CampaignInstance, GoalInstance, OutcomeInstance, EventInstance, PageViewInstance, } from '@sitecore-jss/sitecore-jss/tracking';
|
|
@@ -22,5 +22,4 @@ import * as BYOCWrapper from './components/BYOCWrapper';
|
|
|
22
22
|
export { FEaaSWrapper };
|
|
23
23
|
export { BYOCWrapper };
|
|
24
24
|
export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder';
|
|
25
|
-
export { Context, ContextConfig, SDK } from './context';
|
|
26
25
|
export { ComponentFactory, Image, ImageField, ImageFieldValue, ImageProps, LinkField, LinkFieldValue, Text, TextField, DateField, EditFrame, FEaaSComponent, FEaaSComponentProps, FEaaSComponentParams, fetchFEaaSComponentServerProps, BYOCComponentParams, BYOCComponent, BYOCComponentProps, getComponentLibraryStylesheetLinks, File, FileField, RichTextField, DefaultEmptyFieldEditingComponentImage, DefaultEmptyFieldEditingComponentText, VisitorIdentification, PlaceholderComponentProps, SitecoreContext, SitecoreContextState, SitecoreContextValue, SitecoreContextReactContext, withSitecoreContext, useSitecoreContext, withEditorChromes, withPlaceholder, withDatasourceCheck, ImageSizeParameters, WithSitecoreContextOptions, WithSitecoreContextProps, WithSitecoreContextHocProps, withFieldMetadata, withEmptyFieldEditingComponent, EditingScripts, } from '@sitecore-jss/sitecore-jss-react';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { debug } from '@sitecore-jss/sitecore-jss';
|
|
2
|
+
export { MiddlewareBase, MiddlewareBaseConfig } from './middleware';
|
|
2
3
|
export { RedirectsMiddleware, RedirectsMiddlewareConfig } from './redirects-middleware';
|
|
3
4
|
export { PersonalizeMiddleware, PersonalizeMiddlewareConfig } from './personalize-middleware';
|
|
4
5
|
export { MultisiteMiddleware, MultisiteMiddlewareConfig } from './multisite-middleware';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { NextResponse, NextRequest } from 'next/server';
|
|
2
1
|
import { GraphQLRedirectsServiceConfig } from '@sitecore-jss/sitecore-jss/site';
|
|
2
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
3
3
|
import { MiddlewareBase, MiddlewareBaseConfig } from './middleware';
|
|
4
4
|
/**
|
|
5
5
|
* extended RedirectsMiddlewareConfig config type for RedirectsMiddleware
|
|
@@ -37,4 +37,32 @@ export declare class RedirectsMiddleware extends MiddlewareBase {
|
|
|
37
37
|
* @private
|
|
38
38
|
*/
|
|
39
39
|
private getExistsRedirect;
|
|
40
|
+
/**
|
|
41
|
+
* When a user clicks on a link generated by the Link component from next/link,
|
|
42
|
+
* Next.js adds special parameters in the route called path.
|
|
43
|
+
* This method removes these special parameters.
|
|
44
|
+
* @param {NextURL} url
|
|
45
|
+
* @returns {string} normalize url
|
|
46
|
+
*/
|
|
47
|
+
private normalizeUrl;
|
|
48
|
+
/**
|
|
49
|
+
* Helper function to create a redirect response and remove the x-middleware-next header.
|
|
50
|
+
* @param {NextURL} url The URL to redirect to.
|
|
51
|
+
* @param {Response} res The response object.
|
|
52
|
+
* @param {number} status The HTTP status code of the redirect.
|
|
53
|
+
* @param {string} statusText The status text of the redirect.
|
|
54
|
+
* @returns {NextResponse<unknown>} The redirect response.
|
|
55
|
+
*/
|
|
56
|
+
private createRedirectResponse;
|
|
57
|
+
/**
|
|
58
|
+
* Checks if the current URL query matches the provided pattern, considering all permutations of query parameters.
|
|
59
|
+
* It constructs all possible query parameter permutations and tests them against the pattern.
|
|
60
|
+
* @param {Object} params - The parameters for the URL match.
|
|
61
|
+
* @param {string} params.pathname - The current URL pathname.
|
|
62
|
+
* @param {string} params.queryString - The current URL query string.
|
|
63
|
+
* @param {string} params.pattern - The regex pattern to test the constructed URLs against.
|
|
64
|
+
* @param {string} [params.locale] - The locale prefix to include in the URL if present.
|
|
65
|
+
* @returns {string | undefined} - return query string if any of the query permutations match the provided pattern, undefined otherwise.
|
|
66
|
+
*/
|
|
67
|
+
private isPermutedQueryMatch;
|
|
40
68
|
}
|
package/context.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './types/context/index';
|
package/context.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = require('./dist/cjs/context/index');
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Context = void 0;
|
|
4
|
-
const sitecore_jss_react_1 = require("@sitecore-jss/sitecore-jss-react");
|
|
5
|
-
/**
|
|
6
|
-
* Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs).
|
|
7
|
-
*/
|
|
8
|
-
class Context {
|
|
9
|
-
constructor(props) {
|
|
10
|
-
this.props = props;
|
|
11
|
-
/**
|
|
12
|
-
* Indicates whether the Context and SDK(s) have been initialized
|
|
13
|
-
*/
|
|
14
|
-
this.isInitialized = false;
|
|
15
|
-
/**
|
|
16
|
-
* Software Development Kits (SDKs) to be initialized
|
|
17
|
-
*/
|
|
18
|
-
this.sdks = {};
|
|
19
|
-
/**
|
|
20
|
-
* Promises for the SDKs
|
|
21
|
-
*/
|
|
22
|
-
this.sdkPromises = {};
|
|
23
|
-
this.sdkErrors = {};
|
|
24
|
-
/**
|
|
25
|
-
* Retrieves the Software Development Kit (SDK) instance, ensuring it is initialized before returning
|
|
26
|
-
*
|
|
27
|
-
* @param {string} name SDK name
|
|
28
|
-
* @returns initialized SDK
|
|
29
|
-
*/
|
|
30
|
-
this.getSDK = (name) => {
|
|
31
|
-
if (!this.sdkPromises[name]) {
|
|
32
|
-
return Promise.reject(`Unknown SDK '${String(name)}'`);
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
return this.sdkPromises[name].then((result) => {
|
|
36
|
-
return ((this.sdkErrors[name] && Promise.reject(this.sdkErrors[name])) || Promise.resolve(result));
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
this.sitecoreEdgeUrl = props.sitecoreEdgeUrl;
|
|
41
|
-
this.sitecoreEdgeContextId = props.sitecoreEdgeContextId;
|
|
42
|
-
this.siteName = props.siteName;
|
|
43
|
-
this.pageState = sitecore_jss_react_1.LayoutServicePageState.Normal;
|
|
44
|
-
}
|
|
45
|
-
init(props = {}) {
|
|
46
|
-
// Context and SDKs are initialized only once
|
|
47
|
-
if (this.isInitialized)
|
|
48
|
-
return;
|
|
49
|
-
this.isInitialized = true;
|
|
50
|
-
if (props.siteName) {
|
|
51
|
-
this.siteName = props.siteName;
|
|
52
|
-
}
|
|
53
|
-
if (props.pageState) {
|
|
54
|
-
this.pageState = props.pageState;
|
|
55
|
-
}
|
|
56
|
-
// iterate over the SDKs and initialize them
|
|
57
|
-
for (const sdkName of Object.keys(this.props.sdks)) {
|
|
58
|
-
this.initSDK(sdkName);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Initializes the Software Development Kit (SDK)
|
|
63
|
-
*
|
|
64
|
-
* @param {T} name SDK name
|
|
65
|
-
* @returns {void}
|
|
66
|
-
*/
|
|
67
|
-
initSDK(name) {
|
|
68
|
-
this.sdkPromises[name] = new Promise((resolve) => {
|
|
69
|
-
this.props.sdks[name]
|
|
70
|
-
.init(this)
|
|
71
|
-
.then(() => {
|
|
72
|
-
this.sdks[name] = this.props.sdks[name].sdk;
|
|
73
|
-
resolve(this.sdks[name]);
|
|
74
|
-
})
|
|
75
|
-
.catch((e) => {
|
|
76
|
-
// if init rejects, we mark SDK as failed - so getSDK call would reject with a reason
|
|
77
|
-
this.sdkErrors[name] = e;
|
|
78
|
-
resolve(undefined);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
exports.Context = Context;
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { LayoutServicePageState } from '@sitecore-jss/sitecore-jss-react';
|
|
2
|
-
/**
|
|
3
|
-
* Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs).
|
|
4
|
-
*/
|
|
5
|
-
export class Context {
|
|
6
|
-
constructor(props) {
|
|
7
|
-
this.props = props;
|
|
8
|
-
/**
|
|
9
|
-
* Indicates whether the Context and SDK(s) have been initialized
|
|
10
|
-
*/
|
|
11
|
-
this.isInitialized = false;
|
|
12
|
-
/**
|
|
13
|
-
* Software Development Kits (SDKs) to be initialized
|
|
14
|
-
*/
|
|
15
|
-
this.sdks = {};
|
|
16
|
-
/**
|
|
17
|
-
* Promises for the SDKs
|
|
18
|
-
*/
|
|
19
|
-
this.sdkPromises = {};
|
|
20
|
-
this.sdkErrors = {};
|
|
21
|
-
/**
|
|
22
|
-
* Retrieves the Software Development Kit (SDK) instance, ensuring it is initialized before returning
|
|
23
|
-
*
|
|
24
|
-
* @param {string} name SDK name
|
|
25
|
-
* @returns initialized SDK
|
|
26
|
-
*/
|
|
27
|
-
this.getSDK = (name) => {
|
|
28
|
-
if (!this.sdkPromises[name]) {
|
|
29
|
-
return Promise.reject(`Unknown SDK '${String(name)}'`);
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
return this.sdkPromises[name].then((result) => {
|
|
33
|
-
return ((this.sdkErrors[name] && Promise.reject(this.sdkErrors[name])) || Promise.resolve(result));
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
this.sitecoreEdgeUrl = props.sitecoreEdgeUrl;
|
|
38
|
-
this.sitecoreEdgeContextId = props.sitecoreEdgeContextId;
|
|
39
|
-
this.siteName = props.siteName;
|
|
40
|
-
this.pageState = LayoutServicePageState.Normal;
|
|
41
|
-
}
|
|
42
|
-
init(props = {}) {
|
|
43
|
-
// Context and SDKs are initialized only once
|
|
44
|
-
if (this.isInitialized)
|
|
45
|
-
return;
|
|
46
|
-
this.isInitialized = true;
|
|
47
|
-
if (props.siteName) {
|
|
48
|
-
this.siteName = props.siteName;
|
|
49
|
-
}
|
|
50
|
-
if (props.pageState) {
|
|
51
|
-
this.pageState = props.pageState;
|
|
52
|
-
}
|
|
53
|
-
// iterate over the SDKs and initialize them
|
|
54
|
-
for (const sdkName of Object.keys(this.props.sdks)) {
|
|
55
|
-
this.initSDK(sdkName);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Initializes the Software Development Kit (SDK)
|
|
60
|
-
*
|
|
61
|
-
* @param {T} name SDK name
|
|
62
|
-
* @returns {void}
|
|
63
|
-
*/
|
|
64
|
-
initSDK(name) {
|
|
65
|
-
this.sdkPromises[name] = new Promise((resolve) => {
|
|
66
|
-
this.props.sdks[name]
|
|
67
|
-
.init(this)
|
|
68
|
-
.then(() => {
|
|
69
|
-
this.sdks[name] = this.props.sdks[name].sdk;
|
|
70
|
-
resolve(this.sdks[name]);
|
|
71
|
-
})
|
|
72
|
-
.catch((e) => {
|
|
73
|
-
// if init rejects, we mark SDK as failed - so getSDK call would reject with a reason
|
|
74
|
-
this.sdkErrors[name] = e;
|
|
75
|
-
resolve(undefined);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { Context } from './context';
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { LayoutServicePageState } from '@sitecore-jss/sitecore-jss-react';
|
|
2
|
-
/**
|
|
3
|
-
* Software Development Kit (SDK) instance
|
|
4
|
-
*/
|
|
5
|
-
export type SDK<SDKType = unknown> = {
|
|
6
|
-
/**
|
|
7
|
-
* The Software Development Kit (SDK) library instance
|
|
8
|
-
*/
|
|
9
|
-
sdk: SDKType;
|
|
10
|
-
/**
|
|
11
|
-
* Initializes the Software Development Kit (SDK)
|
|
12
|
-
*/
|
|
13
|
-
init: (props: InitSDKProps) => Promise<void>;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Software Development Kits (SDKs) to be initialized
|
|
17
|
-
*/
|
|
18
|
-
type SDKModulesType = Record<string, SDK>;
|
|
19
|
-
/**
|
|
20
|
-
* Properties that are passed to the Context.
|
|
21
|
-
*/
|
|
22
|
-
export interface ContextInitProps {
|
|
23
|
-
/**
|
|
24
|
-
* Your Sitecore site name
|
|
25
|
-
*/
|
|
26
|
-
siteName?: string;
|
|
27
|
-
/**
|
|
28
|
-
* Sitecore page state (normal, preview, edit)
|
|
29
|
-
*/
|
|
30
|
-
pageState?: LayoutServicePageState;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Configuration that is passed to the Context.
|
|
34
|
-
*/
|
|
35
|
-
export interface ContextConfig<SDKModules extends SDKModulesType> {
|
|
36
|
-
/**
|
|
37
|
-
* Your Sitecore Edge URL
|
|
38
|
-
*/
|
|
39
|
-
sitecoreEdgeUrl: string;
|
|
40
|
-
/**
|
|
41
|
-
* Your Sitecore Edge Context ID
|
|
42
|
-
*/
|
|
43
|
-
sitecoreEdgeContextId: string;
|
|
44
|
-
/**
|
|
45
|
-
* Your Sitecore site name
|
|
46
|
-
*/
|
|
47
|
-
siteName: string;
|
|
48
|
-
/**
|
|
49
|
-
* Software Development Kits (SDKs) to be initialized
|
|
50
|
-
*/
|
|
51
|
-
sdks: {
|
|
52
|
-
[module in keyof SDKModules]: SDKModules[module];
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Properties that are passed to the Software Development Kit (SDK) initialization function.
|
|
57
|
-
*/
|
|
58
|
-
type InitSDKProps = Omit<ContextConfig<SDKModulesType>, 'sdks'>;
|
|
59
|
-
/**
|
|
60
|
-
* Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs).
|
|
61
|
-
*/
|
|
62
|
-
export declare class Context<SDKModules extends SDKModulesType> {
|
|
63
|
-
protected props: ContextConfig<SDKModules>;
|
|
64
|
-
/**
|
|
65
|
-
* Indicates whether the Context and SDK(s) have been initialized
|
|
66
|
-
*/
|
|
67
|
-
isInitialized: boolean;
|
|
68
|
-
/**
|
|
69
|
-
* The Sitecore Edge URL
|
|
70
|
-
*/
|
|
71
|
-
readonly sitecoreEdgeUrl: string;
|
|
72
|
-
/**
|
|
73
|
-
* The Sitecore Edge Context ID
|
|
74
|
-
*/
|
|
75
|
-
readonly sitecoreEdgeContextId: string;
|
|
76
|
-
/**
|
|
77
|
-
* The Sitecore site name
|
|
78
|
-
*/
|
|
79
|
-
siteName: string;
|
|
80
|
-
/**
|
|
81
|
-
* Sitecore page state (normal, preview, edit)
|
|
82
|
-
*/
|
|
83
|
-
pageState: LayoutServicePageState;
|
|
84
|
-
/**
|
|
85
|
-
* Software Development Kits (SDKs) to be initialized
|
|
86
|
-
*/
|
|
87
|
-
readonly sdks: {
|
|
88
|
-
[module in keyof SDKModules]?: SDKModules[module]['sdk'];
|
|
89
|
-
};
|
|
90
|
-
/**
|
|
91
|
-
* Promises for the SDKs
|
|
92
|
-
*/
|
|
93
|
-
protected sdkPromises: {
|
|
94
|
-
[module in keyof SDKModules]?: Promise<SDKModules[module]['sdk']>;
|
|
95
|
-
};
|
|
96
|
-
protected sdkErrors: {
|
|
97
|
-
[module in keyof SDKModules]?: string;
|
|
98
|
-
};
|
|
99
|
-
constructor(props: ContextConfig<SDKModules>);
|
|
100
|
-
init(props?: ContextInitProps): void;
|
|
101
|
-
/**
|
|
102
|
-
* Retrieves the Software Development Kit (SDK) instance, ensuring it is initialized before returning
|
|
103
|
-
*
|
|
104
|
-
* @param {string} name SDK name
|
|
105
|
-
* @returns initialized SDK
|
|
106
|
-
*/
|
|
107
|
-
getSDK: <T extends keyof SDKModules>(name: T) => Promise<SDKModules[T]["sdk"]>;
|
|
108
|
-
/**
|
|
109
|
-
* Initializes the Software Development Kit (SDK)
|
|
110
|
-
*
|
|
111
|
-
* @param {T} name SDK name
|
|
112
|
-
* @returns {void}
|
|
113
|
-
*/
|
|
114
|
-
protected initSDK<T extends keyof SDKModules>(name: T): void;
|
|
115
|
-
}
|
|
116
|
-
export {};
|
package/types/context/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { Context, ContextConfig, SDK } from './context';
|