@sitecore-content-sdk/nextjs 2.1.0-canary.5 → 2.1.0-canary.6

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.
@@ -207,14 +207,34 @@ class RedirectsProxy extends proxy_1.ProxyBase {
207
207
  return undefined;
208
208
  }
209
209
  const { pathname: incomingURL, search: incomingQS = '' } = this.normalizeUrl(req.nextUrl.clone());
210
- const locale = this.getLanguage(req);
211
210
  const normalizedPath = incomingURL.replace(/\/*$/gi, '').toLowerCase();
212
211
  const redirects = await this.redirectsService.fetchRedirects(siteName);
213
- const modifyRedirects = structuredClone(redirects);
212
+ // locale of current request (from URL, headers or otherwise), used to match versioned redirect rules
213
+ const requestLocale = this.getLanguage(req);
214
+ const matchedLocaleRedirect = this.matchRedirectItemRedirect(redirects, requestLocale, normalizedPath);
215
+ if (matchedLocaleRedirect) {
216
+ return matchedLocaleRedirect;
217
+ }
218
+ // locale of the url - if present in the url, used for redirect map matching
219
+ const urlLocale = req.nextUrl.locale;
220
+ return this.matchFromRedirectMapRedirect(redirects, urlLocale, incomingURL, incomingQS);
221
+ }
222
+ /**
223
+ * Matches redirect-map rules without a `locale` field against the incoming URL (static or regex patterns).
224
+ * @param {RedirectResult[]} redirects All redirects from the service (non-locale entries are filtered inside).
225
+ * @param {string} urlLocale Locale segment from the request URL (`nextUrl.locale`).
226
+ * @param {string} incomingURL Original pathname used for regex tests.
227
+ * @param {string} incomingQS Query string including leading `?` if present.
228
+ * @returns {RedirectResult | undefined} First matching redirect or undefined.
229
+ * @private
230
+ */
231
+ matchFromRedirectMapRedirect(redirects, urlLocale, incomingURL, incomingQS) {
232
+ const nonLocaleRedirects = redirects.filter((redirect) => !redirect.locale);
214
233
  let matchedQueryString;
215
- const localePath = `/${locale.toLowerCase()}${normalizedPath}`;
216
- return modifyRedirects.length
217
- ? modifyRedirects.find((redirect) => {
234
+ const normalizedPath = incomingURL.replace(/\/*$/gi, '').toLowerCase();
235
+ const localePath = `/${urlLocale.toLowerCase()}${normalizedPath}`;
236
+ return nonLocaleRedirects.length
237
+ ? nonLocaleRedirects.find((redirect) => {
218
238
  // process static URL (non-regex) rules
219
239
  if ((0, tools_1.isRegexOrUrl)(redirect.pattern) === 'url') {
220
240
  const urlArray = redirect.pattern.endsWith('/')
@@ -236,14 +256,14 @@ class RedirectsProxy extends proxy_1.ProxyBase {
236
256
  // process regex rules
237
257
  // Modify the redirect pattern to ignore the language prefix in the path
238
258
  // And escapes non-special "?" characters in a string or regex.
239
- redirect.pattern = (0, tools_1.escapeNonSpecialQuestionMarks)('^' + redirect.pattern.replace(new RegExp(`^[^]?/${locale}/`, 'gi'), '') // ensure function thinks input is regex
259
+ redirect.pattern = (0, tools_1.escapeNonSpecialQuestionMarks)('^' + redirect.pattern.replace(new RegExp(`^[^]?/${urlLocale}/`, 'gi'), '') // ensure function thinks input is regex
240
260
  );
241
261
  // Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
242
262
  redirect.pattern = `/^\/${redirect.pattern
243
263
  .replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
244
264
  .replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
245
265
  .replace(/^\^|\$$/g, '') // Further cleans up anchors
246
- .replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
266
+ .replace(/\$\/g$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
247
267
  // Redirect pattern matches the full incoming URL with query string present
248
268
  matchedQueryString = [
249
269
  (0, regex_parser_1.default)(redirect.pattern).test(`/${localePath}${incomingQS}`),
@@ -253,9 +273,27 @@ class RedirectsProxy extends proxy_1.ProxyBase {
253
273
  : undefined;
254
274
  // Save the matched query string (if found) into the redirect object
255
275
  redirect.matchedQueryString = matchedQueryString || '';
256
- return (!!((0, regex_parser_1.default)(redirect.pattern).test(`/${req.nextUrl.locale}${incomingURL}`) ||
276
+ return (!!((0, regex_parser_1.default)(redirect.pattern).test(`/${urlLocale}${incomingURL}`) ||
257
277
  (0, regex_parser_1.default)(redirect.pattern).test(incomingURL) ||
258
- matchedQueryString) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
278
+ matchedQueryString) &&
279
+ (redirect.locale ? redirect.locale.toLowerCase() === urlLocale.toLowerCase() : true));
280
+ })
281
+ : undefined;
282
+ }
283
+ /**
284
+ * Processes redirect rules from redirect items (language-versioned)
285
+ * @param {RedirectResult[]} redirects redirect entries from Edge
286
+ * @param {string} locale current request locale
287
+ * @param {string} currentPath current request path (without locale)
288
+ * @returns {RedirectResult | undefined} matched redirect item redirect result or undefined
289
+ * @private
290
+ */
291
+ matchRedirectItemRedirect(redirects, locale, currentPath) {
292
+ return redirects.length
293
+ ? redirects.find((redirect) => {
294
+ const patternPath = redirect.pattern.replace(/\/*$/g, '').toLowerCase();
295
+ // locale rules are easy and nice
296
+ return redirect.locale === locale && patternPath === currentPath;
259
297
  })
260
298
  : undefined;
261
299
  }
@@ -201,14 +201,34 @@ export class RedirectsProxy extends ProxyBase {
201
201
  return undefined;
202
202
  }
203
203
  const { pathname: incomingURL, search: incomingQS = '' } = this.normalizeUrl(req.nextUrl.clone());
204
- const locale = this.getLanguage(req);
205
204
  const normalizedPath = incomingURL.replace(/\/*$/gi, '').toLowerCase();
206
205
  const redirects = await this.redirectsService.fetchRedirects(siteName);
207
- const modifyRedirects = structuredClone(redirects);
206
+ // locale of current request (from URL, headers or otherwise), used to match versioned redirect rules
207
+ const requestLocale = this.getLanguage(req);
208
+ const matchedLocaleRedirect = this.matchRedirectItemRedirect(redirects, requestLocale, normalizedPath);
209
+ if (matchedLocaleRedirect) {
210
+ return matchedLocaleRedirect;
211
+ }
212
+ // locale of the url - if present in the url, used for redirect map matching
213
+ const urlLocale = req.nextUrl.locale;
214
+ return this.matchFromRedirectMapRedirect(redirects, urlLocale, incomingURL, incomingQS);
215
+ }
216
+ /**
217
+ * Matches redirect-map rules without a `locale` field against the incoming URL (static or regex patterns).
218
+ * @param {RedirectResult[]} redirects All redirects from the service (non-locale entries are filtered inside).
219
+ * @param {string} urlLocale Locale segment from the request URL (`nextUrl.locale`).
220
+ * @param {string} incomingURL Original pathname used for regex tests.
221
+ * @param {string} incomingQS Query string including leading `?` if present.
222
+ * @returns {RedirectResult | undefined} First matching redirect or undefined.
223
+ * @private
224
+ */
225
+ matchFromRedirectMapRedirect(redirects, urlLocale, incomingURL, incomingQS) {
226
+ const nonLocaleRedirects = redirects.filter((redirect) => !redirect.locale);
208
227
  let matchedQueryString;
209
- const localePath = `/${locale.toLowerCase()}${normalizedPath}`;
210
- return modifyRedirects.length
211
- ? modifyRedirects.find((redirect) => {
228
+ const normalizedPath = incomingURL.replace(/\/*$/gi, '').toLowerCase();
229
+ const localePath = `/${urlLocale.toLowerCase()}${normalizedPath}`;
230
+ return nonLocaleRedirects.length
231
+ ? nonLocaleRedirects.find((redirect) => {
212
232
  // process static URL (non-regex) rules
213
233
  if (isRegexOrUrl(redirect.pattern) === 'url') {
214
234
  const urlArray = redirect.pattern.endsWith('/')
@@ -230,14 +250,14 @@ export class RedirectsProxy extends ProxyBase {
230
250
  // process regex rules
231
251
  // Modify the redirect pattern to ignore the language prefix in the path
232
252
  // And escapes non-special "?" characters in a string or regex.
233
- redirect.pattern = escapeNonSpecialQuestionMarks('^' + redirect.pattern.replace(new RegExp(`^[^]?/${locale}/`, 'gi'), '') // ensure function thinks input is regex
253
+ redirect.pattern = escapeNonSpecialQuestionMarks('^' + redirect.pattern.replace(new RegExp(`^[^]?/${urlLocale}/`, 'gi'), '') // ensure function thinks input is regex
234
254
  );
235
255
  // Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
236
256
  redirect.pattern = `/^\/${redirect.pattern
237
257
  .replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
238
258
  .replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
239
259
  .replace(/^\^|\$$/g, '') // Further cleans up anchors
240
- .replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
260
+ .replace(/\$\/g$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
241
261
  // Redirect pattern matches the full incoming URL with query string present
242
262
  matchedQueryString = [
243
263
  regexParser(redirect.pattern).test(`/${localePath}${incomingQS}`),
@@ -247,9 +267,27 @@ export class RedirectsProxy extends ProxyBase {
247
267
  : undefined;
248
268
  // Save the matched query string (if found) into the redirect object
249
269
  redirect.matchedQueryString = matchedQueryString || '';
250
- return (!!(regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${incomingURL}`) ||
270
+ return (!!(regexParser(redirect.pattern).test(`/${urlLocale}${incomingURL}`) ||
251
271
  regexParser(redirect.pattern).test(incomingURL) ||
252
- matchedQueryString) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
272
+ matchedQueryString) &&
273
+ (redirect.locale ? redirect.locale.toLowerCase() === urlLocale.toLowerCase() : true));
274
+ })
275
+ : undefined;
276
+ }
277
+ /**
278
+ * Processes redirect rules from redirect items (language-versioned)
279
+ * @param {RedirectResult[]} redirects redirect entries from Edge
280
+ * @param {string} locale current request locale
281
+ * @param {string} currentPath current request path (without locale)
282
+ * @returns {RedirectResult | undefined} matched redirect item redirect result or undefined
283
+ * @private
284
+ */
285
+ matchRedirectItemRedirect(redirects, locale, currentPath) {
286
+ return redirects.length
287
+ ? redirects.find((redirect) => {
288
+ const patternPath = redirect.pattern.replace(/\/*$/g, '').toLowerCase();
289
+ // locale rules are easy and nice
290
+ return redirect.locale === locale && patternPath === currentPath;
253
291
  })
254
292
  : undefined;
255
293
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sitecore-content-sdk/nextjs",
3
- "version": "2.1.0-canary.5",
3
+ "version": "2.1.0-canary.6",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "sideEffects": false,
@@ -32,8 +32,8 @@
32
32
  "url": "https://github.com/sitecore/content-sdk/issues"
33
33
  },
34
34
  "devDependencies": {
35
- "@sitecore-content-sdk/analytics-core": "2.1.0-canary.5",
36
- "@sitecore-content-sdk/personalize": "2.1.0-canary.5",
35
+ "@sitecore-content-sdk/analytics-core": "2.1.0-canary.6",
36
+ "@sitecore-content-sdk/personalize": "2.1.0-canary.6",
37
37
  "@stylistic/eslint-plugin": "^5.2.2",
38
38
  "@testing-library/dom": "^10.4.0",
39
39
  "@testing-library/react": "^16.3.0",
@@ -91,10 +91,10 @@
91
91
  },
92
92
  "dependencies": {
93
93
  "@babel/parser": "^7.27.2",
94
- "@sitecore-content-sdk/content": "2.1.0-canary.5",
95
- "@sitecore-content-sdk/core": "2.1.0-canary.5",
96
- "@sitecore-content-sdk/events": "2.1.0-canary.5",
97
- "@sitecore-content-sdk/react": "2.1.0-canary.5",
94
+ "@sitecore-content-sdk/content": "2.1.0-canary.6",
95
+ "@sitecore-content-sdk/core": "2.1.0-canary.6",
96
+ "@sitecore-content-sdk/events": "2.1.0-canary.6",
97
+ "@sitecore-content-sdk/react": "2.1.0-canary.6",
98
98
  "recast": "^0.23.11",
99
99
  "regex-parser": "^2.3.1",
100
100
  "sync-disk-cache": "^2.1.0"
@@ -178,7 +178,7 @@
178
178
  },
179
179
  "description": "",
180
180
  "types": "types/index.d.ts",
181
- "gitHead": "5fca9e8f3b2a80bc37f18ec7030243242559b151",
181
+ "gitHead": "2bb9b6503a49b5a3e9b534b0592859f2f86a3fd3",
182
182
  "files": [
183
183
  "dist",
184
184
  "types",
@@ -36,6 +36,25 @@ export declare class RedirectsProxy extends ProxyBase {
36
36
  * @private
37
37
  */
38
38
  protected getExistsRedirect(req: NextRequest, siteName: string): Promise<RedirectResult | undefined>;
39
+ /**
40
+ * Matches redirect-map rules without a `locale` field against the incoming URL (static or regex patterns).
41
+ * @param {RedirectResult[]} redirects All redirects from the service (non-locale entries are filtered inside).
42
+ * @param {string} urlLocale Locale segment from the request URL (`nextUrl.locale`).
43
+ * @param {string} incomingURL Original pathname used for regex tests.
44
+ * @param {string} incomingQS Query string including leading `?` if present.
45
+ * @returns {RedirectResult | undefined} First matching redirect or undefined.
46
+ * @private
47
+ */
48
+ protected matchFromRedirectMapRedirect(redirects: RedirectResult[], urlLocale: string, incomingURL: string, incomingQS: string): RedirectResult | undefined;
49
+ /**
50
+ * Processes redirect rules from redirect items (language-versioned)
51
+ * @param {RedirectResult[]} redirects redirect entries from Edge
52
+ * @param {string} locale current request locale
53
+ * @param {string} currentPath current request path (without locale)
54
+ * @returns {RedirectResult | undefined} matched redirect item redirect result or undefined
55
+ * @private
56
+ */
57
+ protected matchRedirectItemRedirect(redirects: RedirectResult[], locale: string, currentPath: string): RedirectResult | undefined;
39
58
  /**
40
59
  * When a user clicks on a link generated by the Link component from next/link,
41
60
  * Next.js adds special parameters in the route called path.
@@ -1 +1 @@
1
- {"version":3,"file":"redirects-proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/redirects-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EAItB,YAAY,EAEb,MAAM,oCAAoC,CAAC;AAO5C,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAuB,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAM3C,KAAK,cAAc,GAAG,YAAY,GAAG;IAAE,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,sBAAsB,EAAE,OAAO,GAAG,eAAe,CAAC,GACxF,cAAc,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAC7B,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GACpD,eAAe,GACf,cAAc,CAAC,WAAW,CAAC,GAAG;IAC5B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AACJ;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAO/B,SAAS,CAAC,MAAM,EAAE,oBAAoB;IANlD,SAAS,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpD,OAAO,CAAC,OAAO,CAAW;IAE1B;;OAEG;gBACmB,MAAM,EAAE,oBAAoB;IAmDlD,MAAM,GAAU,KAAK,WAAW,EAAE,KAAK,YAAY,KAAG,OAAO,CAAC,YAAY,CAAC,CA4KzE;IAEF,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,GAAG,SAAS;IAU5E;;;;;;OAMG;cACa,iBAAiB,CAC/B,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IA8EtC;;;;;;OAMG;IACH,SAAS,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO;IA6C7C;;;;;;;;;OASG;IACH,SAAS,CAAC,gBAAgB,CACxB,MAAM,EAAE,OAAO,GAAG,MAAM,EACxB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,YAAY,EACjB,UAAU,UAAQ,GACjB,YAAY;IAkDf;;;;;;;OAOG;IACH,SAAS,CAAC,sBAAsB,CAC9B,GAAG,EAAE,OAAO,GAAG,MAAM,EACrB,GAAG,EAAE,QAAQ,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,YAAY;CAehB"}
1
+ {"version":3,"file":"redirects-proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/redirects-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EAItB,YAAY,EAEb,MAAM,oCAAoC,CAAC;AAO5C,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAuB,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAM3C,KAAK,cAAc,GAAG,YAAY,GAAG;IAAE,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,sBAAsB,EAAE,OAAO,GAAG,eAAe,CAAC,GACxF,cAAc,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAC7B,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GACpD,eAAe,GACf,cAAc,CAAC,WAAW,CAAC,GAAG;IAC5B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AACJ;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAO/B,SAAS,CAAC,MAAM,EAAE,oBAAoB;IANlD,SAAS,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpD,OAAO,CAAC,OAAO,CAAW;IAE1B;;OAEG;gBACmB,MAAM,EAAE,oBAAoB;IAmDlD,MAAM,GAAU,KAAK,WAAW,EAAE,KAAK,YAAY,KAAG,OAAO,CAAC,YAAY,CAAC,CA4KzE;IAEF,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,GAAG,SAAS;IAU5E;;;;;;OAMG;cACa,iBAAiB,CAC/B,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IA2BtC;;;;;;;;OAQG;IACH,SAAS,CAAC,4BAA4B,CACpC,SAAS,EAAE,cAAc,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,cAAc,GAAG,SAAS;IAsE7B;;;;;;;OAOG;IACH,SAAS,CAAC,yBAAyB,CACjC,SAAS,EAAE,cAAc,EAAE,EAC3B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,cAAc,GAAG,SAAS;IAU7B;;;;;;OAMG;IACH,SAAS,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO;IA6C7C;;;;;;;;;OASG;IACH,SAAS,CAAC,gBAAgB,CACxB,MAAM,EAAE,OAAO,GAAG,MAAM,EACxB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,YAAY,EACjB,UAAU,UAAQ,GACjB,YAAY;IAkDf;;;;;;;OAOG;IACH,SAAS,CAAC,sBAAsB,CAC9B,GAAG,EAAE,OAAO,GAAG,MAAM,EACrB,GAAG,EAAE,QAAQ,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,YAAY;CAehB"}