@sitecore-content-sdk/nextjs 0.1.0-beta.29 → 0.1.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,10 +9,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.defineMiddleware = exports.MiddlewareBase = exports.Middleware = void 0;
12
+ exports.defineMiddleware = exports.MiddlewareBase = exports.Middleware = exports.REWRITE_HEADER_NAME = void 0;
13
13
  const site_1 = require("@sitecore-content-sdk/core/site");
14
14
  const core_1 = require("@sitecore-content-sdk/core");
15
15
  const server_1 = require("next/server");
16
+ exports.REWRITE_HEADER_NAME = 'x-sc-rewrite';
16
17
  /**
17
18
  * Middleware class to be extended by all middleware implementations
18
19
  */
@@ -27,7 +28,6 @@ class MiddlewareBase extends Middleware {
27
28
  super();
28
29
  this.config = config;
29
30
  this.SITE_SYMBOL = 'sc_site';
30
- this.REWRITE_HEADER_NAME = 'x-sc-rewrite';
31
31
  this.siteResolver = new site_1.SiteResolver(config.sites);
32
32
  this.defaultHostname = config.defaultHostname || 'localhost';
33
33
  }
@@ -105,14 +105,17 @@ class MiddlewareBase extends Middleware {
105
105
  * @param {string} rewritePath the destionation path
106
106
  * @param {NextRequest} req the current request
107
107
  * @param {NextResponse} res the current response
108
+ * @param {boolean} [skipHeader] don't write 'x-sc-rewrite' header
108
109
  */
109
- rewrite(rewritePath, req, res) {
110
+ rewrite(rewritePath, req, res, skipHeader) {
110
111
  // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
111
112
  const rewriteUrl = req.nextUrl.clone();
112
113
  rewriteUrl.pathname = rewritePath;
113
114
  const response = server_1.NextResponse.rewrite(rewriteUrl, res);
114
115
  // Share rewrite path with following executed middlewares
115
- response.headers.set(this.REWRITE_HEADER_NAME, rewritePath);
116
+ if (!skipHeader) {
117
+ response.headers.set(exports.REWRITE_HEADER_NAME, rewritePath);
118
+ }
116
119
  return response;
117
120
  }
118
121
  }
@@ -104,7 +104,7 @@ class PersonalizeMiddleware extends middleware_1.MiddlewareBase {
104
104
  return res;
105
105
  }
106
106
  // Path can be rewritten by previously executed middleware
107
- const basePath = (res === null || res === void 0 ? void 0 : res.headers.get('x-sc-rewrite')) || pathname;
107
+ const basePath = (res === null || res === void 0 ? void 0 : res.headers.get(middleware_1.REWRITE_HEADER_NAME)) || pathname;
108
108
  // Rewrite to persononalized path
109
109
  const rewritePath = (0, personalize_1.getPersonalizedRewrite)(basePath, identifiedVariantIds);
110
110
  const response = this.rewrite(rewritePath, req, res);
@@ -72,6 +72,7 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
72
72
  core_1.debug.redirects('skipped (redirect does not exist)');
73
73
  return res;
74
74
  }
75
+ core_1.debug.redirects('Matched redirect rule: %o', { existsRedirect });
75
76
  // Find context site language and replace token
76
77
  if (REGEXP_CONTEXT_SITE_LANG.test(existsRedirect.target) &&
77
78
  !(REGEXP_ABSOLUTE_URL.test(existsRedirect.target) &&
@@ -118,7 +119,7 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
118
119
  return this.createRedirectResponse(url, res, 302, 'Found');
119
120
  }
120
121
  case site_1.REDIRECT_TYPE_SERVER_TRANSFER: {
121
- return this.rewrite(url.href, req, res);
122
+ return this.rewrite(url.href, req, res, true);
122
123
  }
123
124
  default:
124
125
  return res;
@@ -153,23 +154,35 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
153
154
  */
154
155
  getExistsRedirect(req, siteName) {
155
156
  return __awaiter(this, void 0, void 0, function* () {
156
- const { pathname: targetURL, search: targetQS = '', locale } = this.normalizeUrl(req.nextUrl.clone());
157
- const normalizedPath = targetURL.replace(/\/*$/gi, '');
157
+ const { pathname: incomingURL, search: incomingQS = '' } = this.normalizeUrl(req.nextUrl.clone());
158
+ const locale = this.getLanguage(req);
159
+ const normalizedPath = incomingURL.replace(/\/*$/gi, '');
158
160
  const redirects = yield this.redirectsService.fetchRedirects(siteName);
159
161
  const language = this.getLanguage(req);
160
162
  const modifyRedirects = structuredClone(redirects);
161
163
  let matchedQueryString;
164
+ const localePath = `/${locale.toLowerCase()}${normalizedPath}`;
162
165
  return modifyRedirects.length
163
166
  ? modifyRedirects.find((redirect) => {
164
- var _a;
167
+ // process static URL (non-regex) rules
165
168
  if ((0, utils_1.isRegexOrUrl)(redirect.pattern) === 'url') {
166
- const parseUrlPattern = redirect.pattern.endsWith('/')
169
+ const urlArray = redirect.pattern.endsWith('/')
167
170
  ? redirect.pattern.slice(0, -1).split('?')
168
171
  : redirect.pattern.split('?');
169
- return ((parseUrlPattern[0] === normalizedPath ||
170
- parseUrlPattern[0] === `/${locale}${normalizedPath}`) &&
171
- (0, utils_1.areURLSearchParamsEqual)(new URLSearchParams((_a = parseUrlPattern[1]) !== null && _a !== void 0 ? _a : ''), new URLSearchParams(targetQS)));
172
+ const patternQS = urlArray[1];
173
+ let patternPath = urlArray[0];
174
+ // nextjs routes are case-sensitive, but locales should be compared case-insensitively
175
+ const patternParts = patternPath.split('/');
176
+ const maybeLocale = patternParts[1].toLowerCase();
177
+ // case insensitive lookup of locales
178
+ if (new RegExp(this.locales.join('|'), 'i').test(maybeLocale)) {
179
+ patternPath = patternPath.replace(`/${patternParts[1]}`, `/${maybeLocale}`);
180
+ }
181
+ return ((patternPath === localePath || patternPath === normalizedPath) &&
182
+ (!patternQS ||
183
+ (0, utils_1.areURLSearchParamsEqual)(new URLSearchParams(patternQS), new URLSearchParams(incomingQS))));
172
184
  }
185
+ // process regex rules
173
186
  // Modify the redirect pattern to ignore the language prefix in the path
174
187
  // And escapes non-special "?" characters in a string or regex.
175
188
  redirect.pattern = (0, utils_1.escapeNonSpecialQuestionMarks)(redirect.pattern.replace(new RegExp(`^[^]?/${language}/`, 'gi'), ''));
@@ -179,16 +192,17 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
179
192
  .replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
180
193
  .replace(/^\^|\$$/g, '') // Further cleans up anchors
181
194
  .replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
195
+ // Redirect pattern matches the full incoming URL with query string present
182
196
  matchedQueryString = [
183
- (0, regex_parser_1.default)(redirect.pattern).test(`${normalizedPath}${targetQS}`),
184
- (0, regex_parser_1.default)(redirect.pattern).test(`/${locale}${normalizedPath}${targetQS}`),
197
+ (0, regex_parser_1.default)(redirect.pattern).test(`/${localePath}${incomingQS}`),
198
+ (0, regex_parser_1.default)(redirect.pattern).test(`${normalizedPath}${incomingQS}`),
185
199
  ].some(Boolean)
186
- ? targetQS
200
+ ? incomingQS
187
201
  : undefined;
188
202
  // Save the matched query string (if found) into the redirect object
189
203
  redirect.matchedQueryString = matchedQueryString || '';
190
- return (!!((0, regex_parser_1.default)(redirect.pattern).test(targetURL) ||
191
- (0, regex_parser_1.default)(redirect.pattern).test(`/${req.nextUrl.locale}${targetURL}`) ||
204
+ return (!!((0, regex_parser_1.default)(redirect.pattern).test(`/${req.nextUrl.locale}${incomingURL}`) ||
205
+ (0, regex_parser_1.default)(redirect.pattern).test(incomingURL) ||
192
206
  matchedQueryString) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
193
207
  })
194
208
  : undefined;
@@ -251,6 +265,7 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
251
265
  if (res === null || res === void 0 ? void 0 : res.headers) {
252
266
  redirect.headers.delete('x-middleware-next');
253
267
  redirect.headers.delete('x-middleware-rewrite');
268
+ redirect.headers.delete(middleware_1.REWRITE_HEADER_NAME);
254
269
  }
255
270
  return redirect;
256
271
  }
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { SiteResolver } from '@sitecore-content-sdk/core/site';
11
11
  import { debug } from '@sitecore-content-sdk/core';
12
12
  import { NextResponse } from 'next/server';
13
+ export const REWRITE_HEADER_NAME = 'x-sc-rewrite';
13
14
  /**
14
15
  * Middleware class to be extended by all middleware implementations
15
16
  */
@@ -23,7 +24,6 @@ export class MiddlewareBase extends Middleware {
23
24
  super();
24
25
  this.config = config;
25
26
  this.SITE_SYMBOL = 'sc_site';
26
- this.REWRITE_HEADER_NAME = 'x-sc-rewrite';
27
27
  this.siteResolver = new SiteResolver(config.sites);
28
28
  this.defaultHostname = config.defaultHostname || 'localhost';
29
29
  }
@@ -101,14 +101,17 @@ export class MiddlewareBase extends Middleware {
101
101
  * @param {string} rewritePath the destionation path
102
102
  * @param {NextRequest} req the current request
103
103
  * @param {NextResponse} res the current response
104
+ * @param {boolean} [skipHeader] don't write 'x-sc-rewrite' header
104
105
  */
105
- rewrite(rewritePath, req, res) {
106
+ rewrite(rewritePath, req, res, skipHeader) {
106
107
  // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
107
108
  const rewriteUrl = req.nextUrl.clone();
108
109
  rewriteUrl.pathname = rewritePath;
109
110
  const response = NextResponse.rewrite(rewriteUrl, res);
110
111
  // Share rewrite path with following executed middlewares
111
- response.headers.set(this.REWRITE_HEADER_NAME, rewritePath);
112
+ if (!skipHeader) {
113
+ response.headers.set(REWRITE_HEADER_NAME, rewritePath);
114
+ }
112
115
  return response;
113
116
  }
114
117
  }
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { GraphQLPersonalizeService, getPersonalizedRewrite, CdpHelper, DEFAULT_VARIANT, } from '@sitecore-content-sdk/core/personalize';
11
11
  import { debug } from '@sitecore-content-sdk/core';
12
- import { MiddlewareBase } from './middleware';
12
+ import { MiddlewareBase, REWRITE_HEADER_NAME } from './middleware';
13
13
  import { CloudSDK } from '@sitecore-cloudsdk/core/server';
14
14
  import { personalize } from '@sitecore-cloudsdk/personalize/server';
15
15
  /**
@@ -101,7 +101,7 @@ export class PersonalizeMiddleware extends MiddlewareBase {
101
101
  return res;
102
102
  }
103
103
  // Path can be rewritten by previously executed middleware
104
- const basePath = (res === null || res === void 0 ? void 0 : res.headers.get('x-sc-rewrite')) || pathname;
104
+ const basePath = (res === null || res === void 0 ? void 0 : res.headers.get(REWRITE_HEADER_NAME)) || pathname;
105
105
  // Rewrite to persononalized path
106
106
  const rewritePath = getPersonalizedRewrite(basePath, identifiedVariantIds);
107
107
  const response = this.rewrite(rewritePath, req, res);
@@ -12,7 +12,7 @@ import { GraphQLRedirectsService, REDIRECT_TYPE_301, REDIRECT_TYPE_302, REDIRECT
12
12
  import { areURLSearchParamsEqual, escapeNonSpecialQuestionMarks, isRegexOrUrl, mergeURLSearchParams, } from '@sitecore-content-sdk/core/utils';
13
13
  import { NextResponse } from 'next/server';
14
14
  import regexParser from 'regex-parser';
15
- import { MiddlewareBase } from './middleware';
15
+ import { MiddlewareBase, REWRITE_HEADER_NAME } from './middleware';
16
16
  const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
17
17
  const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
18
18
  /**
@@ -66,6 +66,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
66
66
  debug.redirects('skipped (redirect does not exist)');
67
67
  return res;
68
68
  }
69
+ debug.redirects('Matched redirect rule: %o', { existsRedirect });
69
70
  // Find context site language and replace token
70
71
  if (REGEXP_CONTEXT_SITE_LANG.test(existsRedirect.target) &&
71
72
  !(REGEXP_ABSOLUTE_URL.test(existsRedirect.target) &&
@@ -112,7 +113,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
112
113
  return this.createRedirectResponse(url, res, 302, 'Found');
113
114
  }
114
115
  case REDIRECT_TYPE_SERVER_TRANSFER: {
115
- return this.rewrite(url.href, req, res);
116
+ return this.rewrite(url.href, req, res, true);
116
117
  }
117
118
  default:
118
119
  return res;
@@ -147,23 +148,35 @@ export class RedirectsMiddleware extends MiddlewareBase {
147
148
  */
148
149
  getExistsRedirect(req, siteName) {
149
150
  return __awaiter(this, void 0, void 0, function* () {
150
- const { pathname: targetURL, search: targetQS = '', locale } = this.normalizeUrl(req.nextUrl.clone());
151
- const normalizedPath = targetURL.replace(/\/*$/gi, '');
151
+ const { pathname: incomingURL, search: incomingQS = '' } = this.normalizeUrl(req.nextUrl.clone());
152
+ const locale = this.getLanguage(req);
153
+ const normalizedPath = incomingURL.replace(/\/*$/gi, '');
152
154
  const redirects = yield this.redirectsService.fetchRedirects(siteName);
153
155
  const language = this.getLanguage(req);
154
156
  const modifyRedirects = structuredClone(redirects);
155
157
  let matchedQueryString;
158
+ const localePath = `/${locale.toLowerCase()}${normalizedPath}`;
156
159
  return modifyRedirects.length
157
160
  ? modifyRedirects.find((redirect) => {
158
- var _a;
161
+ // process static URL (non-regex) rules
159
162
  if (isRegexOrUrl(redirect.pattern) === 'url') {
160
- const parseUrlPattern = redirect.pattern.endsWith('/')
163
+ const urlArray = redirect.pattern.endsWith('/')
161
164
  ? redirect.pattern.slice(0, -1).split('?')
162
165
  : redirect.pattern.split('?');
163
- return ((parseUrlPattern[0] === normalizedPath ||
164
- parseUrlPattern[0] === `/${locale}${normalizedPath}`) &&
165
- areURLSearchParamsEqual(new URLSearchParams((_a = parseUrlPattern[1]) !== null && _a !== void 0 ? _a : ''), new URLSearchParams(targetQS)));
166
+ const patternQS = urlArray[1];
167
+ let patternPath = urlArray[0];
168
+ // nextjs routes are case-sensitive, but locales should be compared case-insensitively
169
+ const patternParts = patternPath.split('/');
170
+ const maybeLocale = patternParts[1].toLowerCase();
171
+ // case insensitive lookup of locales
172
+ if (new RegExp(this.locales.join('|'), 'i').test(maybeLocale)) {
173
+ patternPath = patternPath.replace(`/${patternParts[1]}`, `/${maybeLocale}`);
174
+ }
175
+ return ((patternPath === localePath || patternPath === normalizedPath) &&
176
+ (!patternQS ||
177
+ areURLSearchParamsEqual(new URLSearchParams(patternQS), new URLSearchParams(incomingQS))));
166
178
  }
179
+ // process regex rules
167
180
  // Modify the redirect pattern to ignore the language prefix in the path
168
181
  // And escapes non-special "?" characters in a string or regex.
169
182
  redirect.pattern = escapeNonSpecialQuestionMarks(redirect.pattern.replace(new RegExp(`^[^]?/${language}/`, 'gi'), ''));
@@ -173,16 +186,17 @@ export class RedirectsMiddleware extends MiddlewareBase {
173
186
  .replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
174
187
  .replace(/^\^|\$$/g, '') // Further cleans up anchors
175
188
  .replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
189
+ // Redirect pattern matches the full incoming URL with query string present
176
190
  matchedQueryString = [
177
- regexParser(redirect.pattern).test(`${normalizedPath}${targetQS}`),
178
- regexParser(redirect.pattern).test(`/${locale}${normalizedPath}${targetQS}`),
191
+ regexParser(redirect.pattern).test(`/${localePath}${incomingQS}`),
192
+ regexParser(redirect.pattern).test(`${normalizedPath}${incomingQS}`),
179
193
  ].some(Boolean)
180
- ? targetQS
194
+ ? incomingQS
181
195
  : undefined;
182
196
  // Save the matched query string (if found) into the redirect object
183
197
  redirect.matchedQueryString = matchedQueryString || '';
184
- return (!!(regexParser(redirect.pattern).test(targetURL) ||
185
- regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${targetURL}`) ||
198
+ return (!!(regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${incomingURL}`) ||
199
+ regexParser(redirect.pattern).test(incomingURL) ||
186
200
  matchedQueryString) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
187
201
  })
188
202
  : undefined;
@@ -245,6 +259,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
245
259
  if (res === null || res === void 0 ? void 0 : res.headers) {
246
260
  redirect.headers.delete('x-middleware-next');
247
261
  redirect.headers.delete('x-middleware-rewrite');
262
+ redirect.headers.delete(REWRITE_HEADER_NAME);
248
263
  }
249
264
  return redirect;
250
265
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sitecore-content-sdk/nextjs",
3
- "version": "0.1.0-beta.29",
3
+ "version": "0.1.0-beta.30",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "sideEffects": false,
@@ -75,8 +75,8 @@
75
75
  },
76
76
  "dependencies": {
77
77
  "@babel/parser": "^7.26.10",
78
- "@sitecore-content-sdk/core": "0.1.0-beta.29",
79
- "@sitecore-content-sdk/react": "0.1.0-beta.29",
78
+ "@sitecore-content-sdk/core": "0.1.0-beta.30",
79
+ "@sitecore-content-sdk/react": "0.1.0-beta.30",
80
80
  "@vercel/kv": "^3.0.0",
81
81
  "prop-types": "^15.8.1",
82
82
  "recast": "^0.23.11",
@@ -85,7 +85,7 @@
85
85
  },
86
86
  "description": "",
87
87
  "types": "types/index.d.ts",
88
- "gitHead": "2f4fef1dfc43acdc0221efb4287f62970c24c61b",
88
+ "gitHead": "418ed952ab73eb1872077c05279d53784c3c6ee3",
89
89
  "files": [
90
90
  "dist",
91
91
  "types",
@@ -1,5 +1,6 @@
1
1
  import { SiteInfo, SiteResolver } from '@sitecore-content-sdk/core/site';
2
2
  import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
3
+ export declare const REWRITE_HEADER_NAME = "x-sc-rewrite";
3
4
  export type MiddlewareBaseConfig = {
4
5
  /**
5
6
  * function, determines if middleware execution should be skipped, based on cookie, header, or other considerations
@@ -40,7 +41,6 @@ export declare abstract class Middleware {
40
41
  export declare abstract class MiddlewareBase extends Middleware {
41
42
  protected config: MiddlewareBaseConfig;
42
43
  protected SITE_SYMBOL: string;
43
- protected REWRITE_HEADER_NAME: string;
44
44
  protected defaultHostname: string;
45
45
  protected siteResolver: SiteResolver;
46
46
  constructor(config: MiddlewareBaseConfig);
@@ -90,8 +90,9 @@ export declare abstract class MiddlewareBase extends Middleware {
90
90
  * @param {string} rewritePath the destionation path
91
91
  * @param {NextRequest} req the current request
92
92
  * @param {NextResponse} res the current response
93
+ * @param {boolean} [skipHeader] don't write 'x-sc-rewrite' header
93
94
  */
94
- protected rewrite(rewritePath: string, req: NextRequest, res: NextResponse): NextResponse;
95
+ protected rewrite(rewritePath: string, req: NextRequest, res: NextResponse, skipHeader?: boolean): NextResponse;
95
96
  }
96
97
  /**
97
98
  * Define a middleware with a list of middlewares