@sitecore-content-sdk/nextjs 1.5.0 → 2.0.0-canary.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/LICENSE.txt +202 -202
  2. package/dist/cjs/client/index.js +1 -1
  3. package/dist/cjs/client/sitecore-nextjs-client.js +4 -4
  4. package/dist/cjs/components/BYOCWrapper.js +0 -5
  5. package/dist/cjs/components/FEaaSWrapper.js +0 -5
  6. package/dist/cjs/components/NextImage.js +2 -2
  7. package/dist/cjs/config/define-config.js +7 -4
  8. package/dist/cjs/config-cli/define-cli-config.js +1 -1
  9. package/dist/cjs/debug.js +11 -0
  10. package/dist/cjs/editing/codegen/import-map-server.js +1 -1
  11. package/dist/cjs/editing/editing-config-middleware.js +13 -11
  12. package/dist/cjs/editing/editing-render-middleware.js +41 -29
  13. package/dist/cjs/editing/feaas-render-middleware.js +24 -13
  14. package/dist/cjs/editing/index.js +2 -2
  15. package/dist/cjs/editing/types.js +2 -0
  16. package/dist/cjs/editing/utils.js +35 -6
  17. package/dist/cjs/index.js +17 -11
  18. package/dist/cjs/middleware/index.js +1 -24
  19. package/dist/cjs/{middleware/app-router-multisite-middleware.js → proxy/app-router-multisite-proxy.js} +8 -8
  20. package/dist/cjs/proxy/index.js +29 -0
  21. package/dist/cjs/{middleware/locale-middleware.js → proxy/locale-proxy.js} +18 -15
  22. package/dist/cjs/{middleware/multisite-middleware.js → proxy/multisite-proxy.js} +23 -20
  23. package/dist/cjs/{middleware/personalize-middleware.js → proxy/personalize-proxy.js} +31 -28
  24. package/dist/cjs/{middleware/middleware.js → proxy/proxy.js} +31 -28
  25. package/dist/cjs/{middleware/redirects-middleware.js → proxy/redirects-proxy.js} +121 -111
  26. package/dist/cjs/route-handler/editing-config-route-handler.js +16 -13
  27. package/dist/cjs/route-handler/editing-render-route-handler.js +43 -32
  28. package/dist/cjs/route-handler/robots-route-handler.js +14 -6
  29. package/dist/cjs/route-handler/sitemap-route-handler.js +13 -5
  30. package/dist/cjs/site/index.js +1 -1
  31. package/dist/cjs/tools/codegen/import-map.js +2 -2
  32. package/dist/cjs/tools/generate-map.js +1 -1
  33. package/dist/cjs/tools/index.js +7 -8
  34. package/dist/cjs/tools/templating/byoc-component.js +1 -1
  35. package/dist/cjs/tools/templating/default-component.js +1 -1
  36. package/dist/cjs/tools/templating/utils.js +5 -6
  37. package/dist/cjs/utils/index.js +4 -5
  38. package/dist/cjs/utils/utils.js +3 -3
  39. package/dist/esm/client/index.js +1 -1
  40. package/dist/esm/client/sitecore-nextjs-client.js +4 -4
  41. package/dist/esm/components/BYOCWrapper.js +0 -5
  42. package/dist/esm/components/FEaaSWrapper.js +0 -5
  43. package/dist/esm/components/NextImage.js +2 -2
  44. package/dist/esm/config/define-config.js +7 -4
  45. package/dist/esm/config-cli/define-cli-config.js +1 -1
  46. package/dist/esm/debug.js +9 -0
  47. package/dist/esm/editing/codegen/import-map-server.js +1 -1
  48. package/dist/esm/editing/editing-config-middleware.js +4 -5
  49. package/dist/esm/editing/editing-render-middleware.js +22 -13
  50. package/dist/esm/editing/feaas-render-middleware.js +14 -6
  51. package/dist/esm/editing/index.js +2 -2
  52. package/dist/esm/editing/types.js +1 -0
  53. package/dist/esm/editing/utils.js +32 -4
  54. package/dist/esm/index.js +11 -12
  55. package/dist/esm/middleware/index.js +0 -9
  56. package/dist/esm/{middleware/app-router-multisite-middleware.js → proxy/app-router-multisite-proxy.js} +6 -6
  57. package/dist/esm/proxy/index.js +9 -0
  58. package/dist/esm/{middleware/locale-middleware.js → proxy/locale-proxy.js} +12 -12
  59. package/dist/esm/{middleware/multisite-middleware.js → proxy/multisite-proxy.js} +16 -16
  60. package/dist/esm/{middleware/personalize-middleware.js → proxy/personalize-proxy.js} +17 -17
  61. package/dist/esm/{middleware/middleware.js → proxy/proxy.js} +24 -24
  62. package/dist/esm/{middleware/redirects-middleware.js → proxy/redirects-proxy.js} +112 -102
  63. package/dist/esm/route-handler/editing-config-route-handler.js +4 -4
  64. package/dist/esm/route-handler/editing-render-route-handler.js +19 -11
  65. package/dist/esm/route-handler/robots-route-handler.js +8 -3
  66. package/dist/esm/route-handler/sitemap-route-handler.js +8 -3
  67. package/dist/esm/site/index.js +1 -1
  68. package/dist/esm/tools/codegen/import-map.js +1 -1
  69. package/dist/esm/tools/generate-map.js +1 -1
  70. package/dist/esm/tools/index.js +2 -1
  71. package/dist/esm/tools/templating/byoc-component.js +1 -1
  72. package/dist/esm/tools/templating/default-component.js +1 -1
  73. package/dist/esm/tools/templating/utils.js +1 -2
  74. package/dist/esm/utils/index.js +2 -2
  75. package/dist/esm/utils/utils.js +2 -2
  76. package/package.json +187 -181
  77. package/proxy.d.ts +1 -0
  78. package/types/client/index.d.ts +1 -1
  79. package/types/client/index.d.ts.map +1 -1
  80. package/types/client/sitecore-nextjs-client.d.ts +3 -3
  81. package/types/client/sitecore-nextjs-client.d.ts.map +1 -1
  82. package/types/components/BYOCWrapper.d.ts +0 -5
  83. package/types/components/BYOCWrapper.d.ts.map +1 -1
  84. package/types/components/FEaaSWrapper.d.ts +0 -5
  85. package/types/components/FEaaSWrapper.d.ts.map +1 -1
  86. package/types/components/Placeholder.d.ts +2 -2
  87. package/types/components/Placeholder.d.ts.map +1 -1
  88. package/types/config/define-config.d.ts +1 -1
  89. package/types/config/define-config.d.ts.map +1 -1
  90. package/types/config-cli/define-cli-config.d.ts +1 -1
  91. package/types/config-cli/define-cli-config.d.ts.map +1 -1
  92. package/types/debug.d.ts +7 -0
  93. package/types/debug.d.ts.map +1 -0
  94. package/types/editing/codegen/import-map-server.d.ts +1 -1
  95. package/types/editing/codegen/import-map-server.d.ts.map +1 -1
  96. package/types/editing/codegen/import-map-utils.d.ts +1 -1
  97. package/types/editing/codegen/import-map-utils.d.ts.map +1 -1
  98. package/types/editing/codegen/import-map.d.ts +1 -1
  99. package/types/editing/codegen/import-map.d.ts.map +1 -1
  100. package/types/editing/codegen/index.d.ts +1 -1
  101. package/types/editing/codegen/index.d.ts.map +1 -1
  102. package/types/editing/editing-config-middleware.d.ts +1 -1
  103. package/types/editing/editing-config-middleware.d.ts.map +1 -1
  104. package/types/editing/editing-render-middleware.d.ts +8 -1
  105. package/types/editing/editing-render-middleware.d.ts.map +1 -1
  106. package/types/editing/feaas-render-middleware.d.ts +1 -1
  107. package/types/editing/feaas-render-middleware.d.ts.map +1 -1
  108. package/types/editing/index.d.ts +3 -2
  109. package/types/editing/index.d.ts.map +1 -1
  110. package/types/editing/types.d.ts +37 -0
  111. package/types/editing/types.d.ts.map +1 -0
  112. package/types/editing/utils.d.ts +12 -1
  113. package/types/editing/utils.d.ts.map +1 -1
  114. package/types/index.d.ts +13 -11
  115. package/types/index.d.ts.map +1 -1
  116. package/types/middleware/index.d.ts +0 -9
  117. package/types/middleware/index.d.ts.map +1 -1
  118. package/types/middleware/robots-middleware.d.ts +1 -1
  119. package/types/middleware/robots-middleware.d.ts.map +1 -1
  120. package/types/middleware/sitemap-middleware.d.ts +1 -1
  121. package/types/middleware/sitemap-middleware.d.ts.map +1 -1
  122. package/types/{middleware/app-router-multisite-middleware.d.ts → proxy/app-router-multisite-proxy.d.ts} +6 -6
  123. package/types/proxy/app-router-multisite-proxy.d.ts.map +1 -0
  124. package/types/proxy/index.d.ts +10 -0
  125. package/types/proxy/index.d.ts.map +1 -0
  126. package/types/proxy/locale-proxy.d.ts +35 -0
  127. package/types/proxy/locale-proxy.d.ts.map +1 -0
  128. package/types/{middleware/multisite-middleware.d.ts → proxy/multisite-proxy.d.ts} +11 -11
  129. package/types/proxy/multisite-proxy.d.ts.map +1 -0
  130. package/types/{middleware/personalize-middleware.d.ts → proxy/personalize-proxy.d.ts} +10 -10
  131. package/types/proxy/personalize-proxy.d.ts.map +1 -0
  132. package/types/{middleware/middleware.d.ts → proxy/proxy.d.ts} +24 -26
  133. package/types/proxy/proxy.d.ts.map +1 -0
  134. package/types/{middleware/redirects-middleware.d.ts → proxy/redirects-proxy.d.ts} +11 -11
  135. package/types/proxy/redirects-proxy.d.ts.map +1 -0
  136. package/types/route-handler/editing-config-route-handler.d.ts +1 -1
  137. package/types/route-handler/editing-config-route-handler.d.ts.map +1 -1
  138. package/types/route-handler/editing-render-route-handler.d.ts +7 -0
  139. package/types/route-handler/editing-render-route-handler.d.ts.map +1 -1
  140. package/types/route-handler/robots-route-handler.d.ts +2 -2
  141. package/types/route-handler/robots-route-handler.d.ts.map +1 -1
  142. package/types/route-handler/sitemap-route-handler.d.ts +2 -2
  143. package/types/route-handler/sitemap-route-handler.d.ts.map +1 -1
  144. package/types/services/component-props-service.d.ts +1 -1
  145. package/types/services/component-props-service.d.ts.map +1 -1
  146. package/types/sharedTypes/component-props.d.ts +1 -1
  147. package/types/sharedTypes/component-props.d.ts.map +1 -1
  148. package/types/sharedTypes/sitecore-page-props.d.ts +2 -2
  149. package/types/sharedTypes/sitecore-page-props.d.ts.map +1 -1
  150. package/types/site/index.d.ts +1 -1
  151. package/types/site/index.d.ts.map +1 -1
  152. package/types/tools/codegen/import-map.d.ts +3 -3
  153. package/types/tools/codegen/import-map.d.ts.map +1 -1
  154. package/types/tools/generate-map.d.ts +1 -1
  155. package/types/tools/generate-map.d.ts.map +1 -1
  156. package/types/tools/index.d.ts +3 -1
  157. package/types/tools/index.d.ts.map +1 -1
  158. package/types/tools/templating/byoc-component.d.ts +1 -1
  159. package/types/tools/templating/byoc-component.d.ts.map +1 -1
  160. package/types/tools/templating/default-component.d.ts +1 -1
  161. package/types/tools/templating/default-component.d.ts.map +1 -1
  162. package/types/tools/templating/utils.d.ts +2 -2
  163. package/types/tools/templating/utils.d.ts.map +1 -1
  164. package/types/utils/index.d.ts +2 -2
  165. package/types/utils/index.d.ts.map +1 -1
  166. package/types/middleware/app-router-multisite-middleware.d.ts.map +0 -1
  167. package/types/middleware/locale-middleware.d.ts +0 -35
  168. package/types/middleware/locale-middleware.d.ts.map +0 -1
  169. package/types/middleware/middleware.d.ts.map +0 -1
  170. package/types/middleware/multisite-middleware.d.ts.map +0 -1
  171. package/types/middleware/personalize-middleware.d.ts.map +0 -1
  172. package/types/middleware/redirects-middleware.d.ts.map +0 -1
@@ -7,30 +7,29 @@ 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 { debug } from '@sitecore-content-sdk/core';
11
- import { RedirectsService, REDIRECT_TYPE_301, REDIRECT_TYPE_302, REDIRECT_TYPE_SERVER_TRANSFER, } from '@sitecore-content-sdk/core/site';
12
- import { areURLSearchParamsEqual, escapeNonSpecialQuestionMarks, isRegexOrUrl, mergeURLSearchParams, } from '@sitecore-content-sdk/core/utils';
10
+ import { RedirectsService, REDIRECT_TYPE_301, REDIRECT_TYPE_302, REDIRECT_TYPE_SERVER_TRANSFER, } from '@sitecore-content-sdk/content/site';
11
+ import { areURLSearchParamsEqual, escapeNonSpecialQuestionMarks, isRegexOrUrl, mergeURLSearchParams, } from '@sitecore-content-sdk/core/tools';
13
12
  import { NextResponse } from 'next/server';
14
13
  import regexParser from 'regex-parser';
15
- import { MiddlewareBase, REWRITE_HEADER_NAME } from './middleware';
14
+ import { ProxyBase, REWRITE_HEADER_NAME } from './proxy';
15
+ import debug from '../debug';
16
16
  const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
17
17
  const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
18
18
  /**
19
- * Middleware / handler fetches all redirects from Sitecore instance by grapqhl service
19
+ * Proxy / handler fetches all redirects from Sitecore instance by grapqhl service
20
20
  * compares with current url and redirects to target url
21
21
  * @public
22
22
  */
23
- export class RedirectsMiddleware extends MiddlewareBase {
23
+ export class RedirectsProxy extends ProxyBase {
24
24
  /**
25
- * @param {RedirectsMiddlewareConfig} [config] redirects middleware config
25
+ * @param {RedirectsProxyConfig} [config] redirects proxy config
26
26
  */
27
27
  constructor(config) {
28
- var _a;
29
28
  super(config);
30
29
  this.config = config;
31
30
  this.handle = (req, res) => __awaiter(this, void 0, void 0, function* () {
32
31
  if (!this.config.enabled) {
33
- debug.redirects('skipped (redirects middleware is disabled globally)');
32
+ debug.redirects('skipped (redirects proxy is disabled globally)');
34
33
  return res;
35
34
  }
36
35
  try {
@@ -39,124 +38,133 @@ export class RedirectsMiddleware extends MiddlewareBase {
39
38
  const hostname = this.getHostHeader(req) || this.defaultHostname;
40
39
  let site;
41
40
  const startTimestamp = Date.now();
42
- debug.redirects('redirects middleware start: %o', {
41
+ debug.redirects('redirects proxy start: %o', {
43
42
  pathname,
44
43
  language,
45
44
  hostname,
46
45
  });
47
46
  if (this.disabled(req, res)) {
48
- debug.redirects('skipped (redirects middleware is disabled)');
47
+ debug.redirects('skipped (redirects proxy is disabled)');
49
48
  return res;
50
49
  }
51
50
  const isAppRouterRequest = this.isAppRouter(res);
52
- const createResponse = () => __awaiter(this, void 0, void 0, function* () {
53
- var _a;
51
+ const validateRequest = () => {
54
52
  if (this.isPreview(req)) {
55
53
  debug.redirects('skipped (preview)');
56
- return res;
54
+ return false;
57
55
  }
58
56
  // Skip prefetch requests from Next.js, which are not original client requests
59
- // as they load unnecessary requests that burden the redirects middleware with meaningless traffic
57
+ // as they load unnecessary requests that burden the redirects proxy with meaningless traffic
60
58
  if (this.isPrefetch(req)) {
61
59
  debug.redirects('skipped (prefetch)');
62
- res.headers.set('x-middleware-cache', 'no-cache');
60
+ res.headers.set('x-proxy-cache', 'no-cache');
63
61
  res.headers.set('Cache-Control', 'no-store, must-revalidate');
64
- return res;
62
+ return false;
63
+ }
64
+ return true;
65
+ };
66
+ if (!validateRequest()) {
67
+ return res;
68
+ }
69
+ site = this.getSite(req, res);
70
+ // Find the redirect from result of RedirectService
71
+ const existsRedirect = yield this.getExistsRedirect(req, site.name);
72
+ if (!existsRedirect) {
73
+ debug.redirects('skipped (redirect does not exist)');
74
+ return res;
75
+ }
76
+ debug.redirects('Matched redirect rule: %o', { existsRedirect });
77
+ const processAbsoluteUrlTarget = (url, existsRedirect) => {
78
+ var _a;
79
+ // Redirect logic for absolute (external or not) URLS. To avoid locale stripping: use plain string for external URLs to prevent Next.js rewriting.
80
+ let targetUrl = existsRedirect.target;
81
+ if (url.search && existsRedirect.isQueryStringPreserved) {
82
+ const incomingQS = new URLSearchParams((_a = url.search) !== null && _a !== void 0 ? _a : '');
83
+ const [targetMainUrl, targetQS] = targetUrl.split('?');
84
+ const mergedQueryString = mergeURLSearchParams(incomingQS, new URLSearchParams(targetQS || ''));
85
+ targetUrl = `${targetMainUrl}?${mergedQueryString}`;
65
86
  }
66
- site = this.getSite(req, res);
67
- // Find the redirect from result of RedirectService
68
- const existsRedirect = yield this.getExistsRedirect(req, site.name);
69
- if (!existsRedirect) {
70
- debug.redirects('skipped (redirect does not exist)');
71
- return res;
87
+ return this.dispatchRedirect(targetUrl, existsRedirect.redirectType, req, res, true);
88
+ };
89
+ const processRelativeUrlTarget = (url, existsRedirect) => {
90
+ var _a;
91
+ let targetUrl = existsRedirect.target;
92
+ const possiblyLocalePrefix = targetUrl.split('/')[1];
93
+ let targetLocale = '';
94
+ if (this.locales.includes(possiblyLocalePrefix)) {
95
+ targetLocale = possiblyLocalePrefix;
96
+ targetUrl = targetUrl.replace(`/${possiblyLocalePrefix}`, '');
72
97
  }
73
- debug.redirects('Matched redirect rule: %o', { existsRedirect });
74
- // Find context site language and replace token
75
- if (REGEXP_CONTEXT_SITE_LANG.test(existsRedirect.target) &&
76
- !(REGEXP_ABSOLUTE_URL.test(existsRedirect.target) &&
77
- existsRedirect.target.includes(hostname))) {
78
- existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
79
- if (!isAppRouterRequest) {
80
- req.nextUrl.locale = site.language;
81
- }
98
+ else if (existsRedirect.isLanguagePreserved) {
99
+ targetLocale = language;
82
100
  }
83
- const url = this.normalizeUrl(req.nextUrl.clone());
84
- // Redirect logic for external (absolute) URLS. To avoid locale stripping: use plain string for external URLs to prevent Next.js rewriting.
85
- if (REGEXP_ABSOLUTE_URL.test(existsRedirect.target)) {
86
- // Perform variable substitution for absolute URLs
87
- let finalTarget = existsRedirect.target;
88
- if (isRegexOrUrl(existsRedirect.pattern) === 'regex') {
89
- const matched = url.pathname
90
- .replace(/\/*$/gi, '')
91
- .match(regexParser(existsRedirect.pattern));
92
- if (matched) {
93
- finalTarget = existsRedirect.target.replace(/\$(\d+)/g, (_, index) => {
94
- return matched[parseInt(index, 10)] || '';
95
- });
96
- }
97
- }
98
- return this.dispatchRedirect(finalTarget, existsRedirect.redirectType, req, res, true);
101
+ let [targetMainUrl, targetQS] = targetUrl.split('?');
102
+ if (url.search && existsRedirect.isQueryStringPreserved) {
103
+ const incomingQS = new URLSearchParams((_a = url.search) !== null && _a !== void 0 ? _a : '');
104
+ targetQS = mergeURLSearchParams(incomingQS, new URLSearchParams(targetQS || ''));
105
+ }
106
+ const prepareNewURL = new URL(`${targetMainUrl}${targetQS ? '?' + targetQS : ''}`, url.origin);
107
+ const basePath = url.basePath; // setting NextUrl.href overrides basePath, so we need to store it
108
+ url.href = prepareNewURL.href;
109
+ url.pathname = prepareNewURL.pathname;
110
+ url.search = prepareNewURL.search;
111
+ // NextUrl setter sets '/' by default if basePath is empty
112
+ // this causes issues when basePath is not configured so we need to set it only if exists
113
+ if (basePath) {
114
+ url.basePath = basePath;
115
+ }
116
+ if (!isAppRouterRequest) {
117
+ // for pages router i18n implementation, apply default locale as backup
118
+ url.locale = targetLocale || req.nextUrl.defaultLocale || 'en';
99
119
  }
100
120
  else {
101
- const isUrl = isRegexOrUrl(existsRedirect.pattern) === 'url';
102
- const targetParts = existsRedirect.target.split('/');
103
- const urlFirstPart = targetParts[1];
104
- if (this.locales.includes(urlFirstPart)) {
105
- if (!isAppRouterRequest) {
106
- req.nextUrl.locale = urlFirstPart;
107
- }
108
- existsRedirect.target = existsRedirect.target.replace(`/${urlFirstPart}`, '');
109
- }
110
- const targetSegments = isUrl
111
- ? existsRedirect.target.split('?')
112
- : url.pathname.replace(/\/*$/gi, '') + existsRedirect.matchedQueryString;
113
- const [targetPath, targetQueryString] = isUrl
114
- ? targetSegments
115
- : targetSegments
116
- .replace(regexParser(existsRedirect.pattern), existsRedirect.target)
117
- .replace(/^\/\//, '/')
118
- .split('?');
119
- const mergedQueryString = existsRedirect.isQueryStringPreserved
120
- ? mergeURLSearchParams(new URLSearchParams((_a = url.search) !== null && _a !== void 0 ? _a : ''), new URLSearchParams(targetQueryString || ''))
121
- : targetQueryString || '';
122
- const prepareNewURL = new URL(`${targetPath}${mergedQueryString ? '?' + mergedQueryString : ''}`, url.origin);
123
- const basePath = url.basePath; // setting NextUrl.href overrides basePath, so we need to store it
124
- url.href = prepareNewURL.href;
125
- url.pathname = prepareNewURL.pathname;
126
- url.search = prepareNewURL.search;
127
- // NextUrl setter sets '/' by default if basePath is empty
128
- // this causes issues when basePath is not configured so we need to set it only if exists
129
- if (basePath) {
130
- url.basePath = basePath;
131
- }
132
- if (!isAppRouterRequest) {
133
- url.locale = req.nextUrl.locale;
134
- }
121
+ // In App Router, we need to set the locale in the pathname, if present
122
+ if (targetLocale)
123
+ url.pathname = `/${targetLocale}${url.pathname}`;
135
124
  }
136
125
  /** return Response redirect with http code of redirect type */
137
- return this.dispatchRedirect(url, existsRedirect.redirectType, req, res, false);
138
- });
139
- const response = yield createResponse();
140
- debug.redirects('redirects middleware end in %dms: %o', Date.now() - startTimestamp, {
141
- redirected: response.redirected,
142
- status: response.status,
143
- url: response.url,
144
- headers: this.extractDebugHeaders(response.headers),
126
+ return this.dispatchRedirect(this.normalizeUrl(url), existsRedirect.redirectType, req, res, false);
127
+ };
128
+ // replace $siteLang token in target if exists
129
+ existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
130
+ const reqUrl = this.normalizeUrl(req.nextUrl.clone());
131
+ // Apply regex replacements to the target URL if the pattern is a regex
132
+ const matched = reqUrl.pathname
133
+ .replace(/\/*$/gi, '')
134
+ .match(regexParser(existsRedirect.pattern));
135
+ if (matched) {
136
+ existsRedirect.target = existsRedirect.target.replace(/\$(\d+)/g, (_, index) => {
137
+ return matched[parseInt(index, 10)] || '';
138
+ });
139
+ }
140
+ const redirectedResponse = REGEXP_ABSOLUTE_URL.test(existsRedirect.target)
141
+ ? processAbsoluteUrlTarget(reqUrl, existsRedirect)
142
+ : processRelativeUrlTarget(reqUrl, existsRedirect);
143
+ debug.redirects('redirects proxy end in %dms: %o', Date.now() - startTimestamp, {
144
+ redirected: redirectedResponse.redirected,
145
+ status: redirectedResponse.status,
146
+ url: redirectedResponse.url,
147
+ headers: this.extractDebugHeaders(redirectedResponse.headers),
145
148
  });
146
- return response;
149
+ return redirectedResponse;
147
150
  }
148
151
  catch (error) {
149
- console.log('Redirect middleware failed:');
152
+ console.log('Redirect proxy failed:');
150
153
  console.log(error);
151
154
  return res;
152
155
  }
153
156
  });
154
157
  this.locales = config.locales;
158
+ // If redirectsService is provided directly (e.g., for testing), use it
159
+ if (this.config.redirectsService) {
160
+ this.redirectsService = this.config.redirectsService;
161
+ return;
162
+ }
155
163
  // Validate API config is present - redirects requires either Edge or local API configuration
156
164
  const hasEdgeConfig = !!(this.config.contextId || this.config.clientContextId);
157
165
  const hasLocalConfig = !!(this.config.apiHost && this.config.apiKey);
158
166
  if (!hasEdgeConfig && !hasLocalConfig) {
159
- console.warn('[RedirectsMiddleware] Redirects middleware requires either Edge configuration (contextId/clientContextId) or local API configuration (apiHost/apiKey). ' +
167
+ console.warn('[RedirectsProxy] Redirects proxy requires either Edge configuration (contextId/clientContextId) or local API configuration (apiHost/apiKey). ' +
160
168
  'Redirects features will be disabled. This is expected when API configuration is not available.');
161
169
  // Set to null to indicate service is disabled
162
170
  this.redirectsService = null;
@@ -179,16 +187,16 @@ export class RedirectsMiddleware extends MiddlewareBase {
179
187
  };
180
188
  // NOTE: we provide native fetch for compatibility on Next.js Edge Runtime
181
189
  // (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
182
- this.redirectsService =
183
- (_a = this.config.redirectsService) !== null && _a !== void 0 ? _a : new RedirectsService(Object.assign(Object.assign({}, config), { clientFactory: this.getClientFactory(graphQLOptions), fetch: fetch }));
190
+ this.redirectsService = new RedirectsService(Object.assign(Object.assign({}, config), { clientFactory: this.getClientFactory(graphQLOptions), fetch: fetch }));
184
191
  }
185
192
  disabled(req, res) {
186
- // Check if API config is missing - if so, disable the middleware
193
+ // Check if API config is missing - if so, disable the proxy
187
194
  if (!this.redirectsService) {
188
195
  debug.redirects('skipped (redirects service not configured - API config required)');
189
196
  return true;
190
197
  }
191
- return super.disabled(req, res);
198
+ // ignore files
199
+ return req.nextUrl.pathname.includes('.') || super.disabled(req, res);
192
200
  }
193
201
  /**
194
202
  * Method returns RedirectInfo when matches
@@ -206,7 +214,6 @@ export class RedirectsMiddleware extends MiddlewareBase {
206
214
  const locale = this.getLanguage(req);
207
215
  const normalizedPath = incomingURL.replace(/\/*$/gi, '').toLowerCase();
208
216
  const redirects = yield this.redirectsService.fetchRedirects(siteName);
209
- const language = this.getLanguage(req);
210
217
  const modifyRedirects = structuredClone(redirects);
211
218
  let matchedQueryString;
212
219
  const localePath = `/${locale.toLowerCase()}${normalizedPath}`;
@@ -233,7 +240,8 @@ export class RedirectsMiddleware extends MiddlewareBase {
233
240
  // process regex rules
234
241
  // Modify the redirect pattern to ignore the language prefix in the path
235
242
  // And escapes non-special "?" characters in a string or regex.
236
- redirect.pattern = escapeNonSpecialQuestionMarks(redirect.pattern.replace(new RegExp(`^[^]?/${language}/`, 'gi'), ''));
243
+ redirect.pattern = escapeNonSpecialQuestionMarks('^' + redirect.pattern.replace(new RegExp(`^[^]?/${locale}/`, 'gi'), '') // ensure function thinks input is regex
244
+ );
237
245
  // Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
238
246
  redirect.pattern = `/^\/${redirect.pattern
239
247
  .replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
@@ -277,7 +285,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
277
285
  /**
278
286
  * Remove special parameters(Next.JS)
279
287
  * Example: /about/contact/us
280
- * When a user clicks on this link, Next.js should generate a link for the middleware, formatted like this:
288
+ * When a user clicks on this link, Next.js should generate a link for the proxy, formatted like this:
281
289
  * http://host/about/contact/us?path=about&path=contact&path=us
282
290
  */
283
291
  const newQueryString = url.search
@@ -357,7 +365,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
357
365
  }
358
366
  }
359
367
  /**
360
- * Helper function to create a redirect response and remove the x-middleware-next header.
368
+ * Helper function to create a redirect response and remove the x-proxy-next header.
361
369
  * @param {NextURL | string} url The URL to redirect to.
362
370
  * @param {Response} res The response object.
363
371
  * @param {number} status The HTTP status code of the redirect.
@@ -365,7 +373,9 @@ export class RedirectsMiddleware extends MiddlewareBase {
365
373
  * @returns {NextResponse<unknown>} The redirect response.
366
374
  */
367
375
  createRedirectResponse(url, res, status, statusText) {
368
- const redirect = NextResponse.redirect(url, {
376
+ // Convert NextURL to string if needed - NextResponse.redirect requires a string URL
377
+ const urlString = typeof url === 'string' ? url : url.href;
378
+ const redirect = NextResponse.redirect(urlString, {
369
379
  status,
370
380
  statusText,
371
381
  headers: res === null || res === void 0 ? void 0 : res.headers,
@@ -7,11 +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 { EDITING_ALLOWED_ORIGINS, QUERY_PARAM_EDITING_SECRET, } from '@sitecore-content-sdk/core/editing';
11
- import { debug } from '@sitecore-content-sdk/core';
12
- import { getEnforcedCorsHeaders } from '@sitecore-content-sdk/core/utils';
13
- import { EditMode } from '@sitecore-content-sdk/core/layout';
10
+ import { EDITING_ALLOWED_ORIGINS, QUERY_PARAM_EDITING_SECRET, } from '@sitecore-content-sdk/content/editing';
11
+ import { getEnforcedCorsHeaders } from '@sitecore-content-sdk/core/tools';
12
+ import { EditMode } from '@sitecore-content-sdk/content/layout';
14
13
  import { getEditingSecret } from '../utils/utils';
14
+ import debug from '../debug';
15
15
  /**
16
16
  * Creates a route handler for the editing config API route (e.g. '/api/editing/config')
17
17
  * Provides configuration information to determine feature compatibility on Pages side.
@@ -7,14 +7,15 @@ 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 { debug, NativeDataFetcher } from '@sitecore-content-sdk/core';
11
- import { EDITING_ALLOWED_ORIGINS, PREVIEW_KEY, QUERY_PARAM_EDITING_SECRET, INVALID_SECRET_HTML_MESSAGE, } from '@sitecore-content-sdk/core/editing';
12
- import { getEnforcedCorsHeaders } from '@sitecore-content-sdk/core/utils';
13
- import { LayoutServicePageState } from '@sitecore-content-sdk/core/layout';
10
+ import { NativeDataFetcher } from '@sitecore-content-sdk/core';
11
+ import { EDITING_ALLOWED_ORIGINS, PREVIEW_KEY, QUERY_PARAM_EDITING_SECRET, INVALID_SECRET_HTML_MESSAGE, } from '@sitecore-content-sdk/content/editing';
12
+ import { getEnforcedCorsHeaders } from '@sitecore-content-sdk/core/tools';
13
+ import { LayoutServicePageState } from '@sitecore-content-sdk/content/layout';
14
14
  import { getEditingSecret } from '../utils/utils';
15
15
  import { draftMode, cookies as nextCokies } from 'next/headers';
16
- import { mapEditingParams, getEditingRequestHtml, cleanupNextPreviewCookies, getHeadersForPropagation, getQueryParamsForPropagation, getRequiredEditingParamsList, getCSPHeader, resolveServerUrl, } from '../editing/utils';
17
- import { SITE_KEY } from '@sitecore-content-sdk/core/site';
16
+ import { mapEditingParams, getEditingRequestHtml, cleanupNextPreviewCookies, getHeadersForPropagation, getQueryParamsForPropagation, getRequiredEditingParamsList, getCSPHeader, resolveServerUrl, getAllowedQueryParams, } from '../editing/utils';
17
+ import { SITE_KEY } from '@sitecore-content-sdk/content/site';
18
+ import debug from '../debug';
18
19
  /**
19
20
  * Helper function to handle cookie operations - can be mocked for testing
20
21
  * @returns {Promise<NextCookies>} Next cookies
@@ -108,11 +109,18 @@ export const createEditingRenderRouteHandlers = (options) => {
108
109
  const mode = query.mode;
109
110
  const requiredQueryParams = getRequiredEditingParamsList(mode);
110
111
  const missingQueryParams = requiredQueryParams.filter((param) => !query[param]);
112
+ const { allowedQueryParams, missingAllowedParams } = getAllowedQueryParams(query, options.allowedQueryParams);
111
113
  // Validate query parameters
112
- if (missingQueryParams.length) {
113
- debug.editing('missing required query parameters: %o', missingQueryParams);
114
+ if (missingQueryParams.length || missingAllowedParams.length) {
115
+ debug.editing('missing required query parameters: %o', [
116
+ ...missingQueryParams,
117
+ ...missingAllowedParams,
118
+ ]);
114
119
  return Response.json({
115
- html: `<html><body>Missing required query parameters: ${missingQueryParams.join(', ')}</body></html>`,
120
+ html: `<html><body>Missing required query parameters: ${[
121
+ ...missingQueryParams,
122
+ ...missingAllowedParams,
123
+ ].join(', ')}</body></html>`,
116
124
  }, { status: 400, headers: responseHeaders });
117
125
  }
118
126
  const encodedRoute = encodeURI(query.route);
@@ -148,7 +156,7 @@ export const createEditingRenderRouteHandlers = (options) => {
148
156
  debug.editing('fetching page route for %s', query.route);
149
157
  // Get query string parameters to propagate on subsequent requests (e.g. for deployment protection bypass)
150
158
  // Additionally ,in app router preview data is passed through query string instead of preview data cookie
151
- const propagatedQsParams = Object.assign(Object.assign({}, getQueryParamsForPropagation(query)), mapEditingParams(query));
159
+ const propagatedQsParams = Object.assign(Object.assign(Object.assign({}, getQueryParamsForPropagation(query)), mapEditingParams(query)), allowedQueryParams);
152
160
  // Get headers to propagate on subsequent requests
153
161
  const propagatedHeaders = getHeadersForPropagation(headers);
154
162
  const html = yield getEditingRequestHtml(requestUrl, propagatedQsParams, propagatedHeaders, convertedCookies, dataFetcher);
@@ -207,7 +215,7 @@ export const createEditingRenderRouteHandlers = (options) => {
207
215
  req.nextUrl.searchParams.forEach((value, key) => {
208
216
  query[key] = value;
209
217
  });
210
- const propagatedQsParams = Object.assign(Object.assign({}, getQueryParamsForPropagation(query)), mapEditingParams(query));
218
+ const propagatedQsParams = Object.assign(Object.assign(Object.assign({}, getQueryParamsForPropagation(query)), mapEditingParams(query)), getAllowedQueryParams(query, options.allowedQueryParams).allowedQueryParams);
211
219
  const base = resolveServerUrl(req);
212
220
  const targetUrl = new URL('/', base);
213
221
  for (const key in propagatedQsParams) {
@@ -7,9 +7,9 @@ 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 { SiteResolver } from '@sitecore-content-sdk/core/site';
11
- import { debug } from '@sitecore-content-sdk/core';
10
+ import { SiteResolver } from '@sitecore-content-sdk/content/site';
12
11
  import { unstable_cache } from 'next/cache';
12
+ import debug from '../debug';
13
13
  /**
14
14
  * Creates a route handler to serve the robots.txt file.
15
15
  * @param {RouteHandlerOptions} options - The options for the route handler.
@@ -28,11 +28,12 @@ export const createRobotsRouteHandler = (options) => {
28
28
  const GET = (req) => __awaiter(void 0, void 0, void 0, function* () {
29
29
  var _a;
30
30
  try {
31
- const startTimestamp = Date.now();
32
31
  const hostName = req.headers.get('x-forwarded-host') ||
33
32
  ((_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0]) ||
34
33
  'localhost';
35
34
  const site = siteResolver.getByHost(hostName);
35
+ // Access request data first, then capture timestamp for Next.js 16 compatibility
36
+ const startTimestamp = Date.now();
36
37
  debug.robots('robots route handler start: %o', {
37
38
  hostName,
38
39
  siteName: site.name,
@@ -56,6 +57,10 @@ export const createRobotsRouteHandler = (options) => {
56
57
  });
57
58
  }
58
59
  catch (error) {
60
+ // Re-throw prerender bail-out errors so Next.js can handle them properly
61
+ if (error instanceof Error && error.digest === 'NEXT_PRERENDER_INTERRUPTED') {
62
+ throw error;
63
+ }
59
64
  console.log('Robots route handler failed:');
60
65
  console.log(error);
61
66
  return new Response('Internal Server Error', {
@@ -7,9 +7,9 @@ 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 { SiteResolver } from '@sitecore-content-sdk/core/site';
11
- import { debug } from '@sitecore-content-sdk/core';
10
+ import { SiteResolver } from '@sitecore-content-sdk/content/site';
12
11
  import { unstable_cache } from 'next/cache';
12
+ import debug from '../debug';
13
13
  /**
14
14
  * Creates a route handler to serve the sitemap.xml file.
15
15
  * @param {RouteHandlerOptions} options - The options for the route handler.
@@ -35,8 +35,9 @@ export function createSitemapRouteHandler(options) {
35
35
  });
36
36
  const GET = (req) => __awaiter(this, void 0, void 0, function* () {
37
37
  try {
38
- const startTimestamp = Date.now();
39
38
  const options = getOptions(req);
39
+ // Access request data first, then capture timestamp for Next.js 16 compatibility
40
+ const startTimestamp = Date.now();
40
41
  debug.sitemap('sitemap route handler start: %o', {
41
42
  options,
42
43
  });
@@ -49,6 +50,10 @@ export function createSitemapRouteHandler(options) {
49
50
  });
50
51
  }
51
52
  catch (error) {
53
+ // Re-throw prerender bail-out errors so Next.js can handle them properly
54
+ if (error instanceof Error && error.digest === 'NEXT_PRERENDER_INTERRUPTED') {
55
+ throw error;
56
+ }
52
57
  console.log('Sitemap route handler failed:');
53
58
  console.log(error);
54
59
  if (error instanceof Error && error.message === 'REDIRECT_404') {
@@ -1 +1 @@
1
- export { SiteResolver } from '@sitecore-content-sdk/core/site';
1
+ export { SiteResolver } from '@sitecore-content-sdk/content/site';
@@ -1,4 +1,4 @@
1
- import { writeImportMap as writeImportMapCoreImpl, } from '@sitecore-content-sdk/core/tools';
1
+ import { writeImportMap as writeImportMapCoreImpl, } from '@sitecore-content-sdk/content/node-tools';
2
2
  import { detectRouterType, nextjsClientMapTemplate, nextjsServertMapTemplate, nextjsDefaultMapTemplate, } from '../templating/utils';
3
3
  let writeImportMapCore = writeImportMapCoreImpl;
4
4
  export const __mockDependencies = (mocks) => {
@@ -1,4 +1,4 @@
1
- import { filterComponentsByType, } from '@sitecore-content-sdk/core/tools';
1
+ import { filterComponentsByType, } from '@sitecore-content-sdk/content/tools';
2
2
  import * as path from 'path';
3
3
  import * as fs from 'fs';
4
4
  import { detectRouterType, getComponentListWithTypes } from './templating/utils';
@@ -1,3 +1,4 @@
1
- export { generateSites, generateMetadata, getComponentList, generatePlugins, ModuleType, extractFiles, } from '@sitecore-content-sdk/core/tools';
1
+ export { generateSites, getComponentList, extractFiles, } from '@sitecore-content-sdk/content/node-tools';
2
+ export { generateMetadata } from '@sitecore-content-sdk/core/node-tools';
2
3
  export { generateMap } from './generate-map';
3
4
  export { writeImportMap } from './codegen/import-map';
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { ComponentTemplateType } from '@sitecore-content-sdk/core/config';
2
+ import { ComponentTemplateType } from '@sitecore-content-sdk/content/config';
3
3
  import { COMPONENT_FILE_EXTENSION } from './constants';
4
4
  /**
5
5
  * Next.js BYOC component boilerplate
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { ComponentTemplateType } from '@sitecore-content-sdk/core/config';
2
+ import { ComponentTemplateType } from '@sitecore-content-sdk/content/config';
3
3
  import { COMPONENT_FILE_EXTENSION } from './constants';
4
4
  /**
5
5
  * Next.js component boilerplate
@@ -1,7 +1,6 @@
1
- import { getComponentList, } from '@sitecore-content-sdk/core/tools';
1
+ import { getComponentList, defaultImportMapTemplate, } from '@sitecore-content-sdk/content/node-tools';
2
2
  import ts from 'typescript';
3
3
  import fs from 'fs';
4
- import { defaultImportMapTemplate } from '@sitecore-content-sdk/core/tools';
5
4
  /**
6
5
  * Detects the Next.js router type (App Router or Pages Router) based on directory structure.
7
6
  * @param {string} projectRoot - The project root directory. Defaults to current working directory.
@@ -1,3 +1,3 @@
1
1
  export { handleEditorFastRefresh, extractPath, isServerSidePropsContext, parseRewriteHeader, } from './utils';
2
- export { tryParseEnvValue, resolveUrl } from '@sitecore-content-sdk/core/utils';
3
- export { isEditorActive, resetEditorChromes } from '@sitecore-content-sdk/core/editing';
2
+ export { resolveUrl } from '@sitecore-content-sdk/core/tools';
3
+ export { isEditorActive, resetEditorChromes } from '@sitecore-content-sdk/content/editing';
@@ -1,5 +1,5 @@
1
- import { isEditorActive, resetEditorChromes } from '@sitecore-content-sdk/core/editing';
2
- import { REWRITE_HEADER_NAME } from '../middleware/middleware';
1
+ import { isEditorActive, resetEditorChromes } from '@sitecore-content-sdk/content/editing';
2
+ import { REWRITE_HEADER_NAME } from '../proxy/proxy';
3
3
  /**
4
4
  * Since Sitecore editors do not support Fast Refresh:
5
5
  * 1. Subscribe on events provided by webpack.