@sitecore-content-sdk/nextjs 1.3.0-canary.9 → 1.4.0-canary.2

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 (234) hide show
  1. package/README.md +11 -11
  2. package/dist/cjs/client/index.js +10 -10
  3. package/dist/cjs/client/models.js +2 -2
  4. package/dist/cjs/client/sitecore-nextjs-client.js +160 -156
  5. package/dist/cjs/components/BYOCWrapper.js +31 -30
  6. package/dist/cjs/components/ComponentPropsContext.js +66 -59
  7. package/dist/cjs/components/FEaaSWrapper.js +33 -32
  8. package/dist/cjs/components/Link.js +117 -90
  9. package/dist/cjs/components/NextImage.js +66 -62
  10. package/dist/cjs/components/Placeholder.js +55 -50
  11. package/dist/cjs/components/RichText.js +133 -128
  12. package/dist/cjs/config/define-config.js +26 -25
  13. package/dist/cjs/config/index.js +5 -5
  14. package/dist/cjs/config-cli/define-cli-config.js +40 -39
  15. package/dist/cjs/config-cli/index.js +5 -5
  16. package/dist/cjs/editing/codegen/import-map.js +129 -118
  17. package/dist/cjs/editing/codegen/index.js +6 -6
  18. package/dist/cjs/editing/constants.js +10 -10
  19. package/dist/cjs/editing/editing-config-middleware.js +70 -69
  20. package/dist/cjs/editing/editing-render-middleware.js +145 -144
  21. package/dist/cjs/editing/feaas-render-middleware.js +102 -101
  22. package/dist/cjs/editing/index.js +19 -19
  23. package/dist/cjs/editing/render-middleware.js +46 -46
  24. package/dist/cjs/editing/utils.js +260 -257
  25. package/dist/cjs/index.js +132 -131
  26. package/dist/cjs/middleware/app-router-multisite-middleware.js +41 -20
  27. package/dist/cjs/middleware/index.js +30 -30
  28. package/dist/cjs/middleware/locale-middleware.js +85 -84
  29. package/dist/cjs/middleware/middleware.js +195 -192
  30. package/dist/cjs/middleware/multisite-middleware.js +141 -118
  31. package/dist/cjs/middleware/personalize-middleware.js +240 -236
  32. package/dist/cjs/middleware/redirects-middleware.js +323 -297
  33. package/dist/cjs/middleware/robots-middleware.js +45 -44
  34. package/dist/cjs/middleware/sitemap-middleware.js +50 -49
  35. package/dist/cjs/monitoring/healthcheck-middleware.js +31 -30
  36. package/dist/cjs/monitoring/index.js +5 -5
  37. package/dist/cjs/route-handler/editing-config-route-handler.js +110 -106
  38. package/dist/cjs/route-handler/editing-render-route-handler.js +270 -165
  39. package/dist/cjs/route-handler/index.js +11 -11
  40. package/dist/cjs/route-handler/robots-route-handler.js +69 -68
  41. package/dist/cjs/route-handler/sitemap-route-handler.js +66 -65
  42. package/dist/cjs/search/index.js +17 -0
  43. package/dist/cjs/services/component-props-service.js +142 -138
  44. package/dist/cjs/sharedTypes/component-props.js +2 -2
  45. package/dist/cjs/sharedTypes/sitecore-page-props.js +2 -2
  46. package/dist/cjs/site/index.js +5 -5
  47. package/dist/cjs/tools/codegen/import-map.js +15 -0
  48. package/dist/cjs/tools/component-props.loader.js +95 -95
  49. package/dist/cjs/tools/generate-map.js +317 -317
  50. package/dist/cjs/tools/index.js +14 -13
  51. package/dist/cjs/tools/templating/byoc-component.js +36 -36
  52. package/dist/cjs/tools/templating/constants.js +7 -7
  53. package/dist/cjs/tools/templating/default-component.js +35 -35
  54. package/dist/cjs/tools/templating/utils.js +200 -0
  55. package/dist/cjs/utils/index.js +14 -14
  56. package/dist/cjs/utils/utils.js +82 -73
  57. package/dist/esm/client/index.js +2 -2
  58. package/dist/esm/client/models.js +1 -1
  59. package/dist/esm/client/sitecore-nextjs-client.js +156 -152
  60. package/dist/esm/components/BYOCWrapper.js +27 -26
  61. package/dist/esm/components/ComponentPropsContext.js +28 -21
  62. package/dist/esm/components/FEaaSWrapper.js +29 -28
  63. package/dist/esm/components/Link.js +78 -51
  64. package/dist/esm/components/NextImage.js +60 -56
  65. package/dist/esm/components/Placeholder.js +18 -13
  66. package/dist/esm/components/RichText.js +96 -91
  67. package/dist/esm/config/define-config.js +21 -20
  68. package/dist/esm/config/index.js +1 -1
  69. package/dist/esm/config-cli/define-cli-config.js +36 -35
  70. package/dist/esm/config-cli/index.js +1 -1
  71. package/dist/esm/editing/codegen/import-map.js +92 -81
  72. package/dist/esm/editing/codegen/index.js +1 -1
  73. package/dist/esm/editing/constants.js +7 -7
  74. package/dist/esm/editing/editing-config-middleware.js +66 -65
  75. package/dist/esm/editing/editing-render-middleware.js +141 -140
  76. package/dist/esm/editing/feaas-render-middleware.js +98 -97
  77. package/dist/esm/editing/index.js +6 -6
  78. package/dist/esm/editing/render-middleware.js +42 -42
  79. package/dist/esm/editing/utils.js +246 -243
  80. package/dist/esm/index.js +25 -25
  81. package/dist/esm/middleware/app-router-multisite-middleware.js +37 -16
  82. package/dist/esm/middleware/index.js +11 -11
  83. package/dist/esm/middleware/locale-middleware.js +81 -80
  84. package/dist/esm/middleware/middleware.js +189 -186
  85. package/dist/esm/middleware/multisite-middleware.js +137 -114
  86. package/dist/esm/middleware/personalize-middleware.js +236 -232
  87. package/dist/esm/middleware/redirects-middleware.js +316 -290
  88. package/dist/esm/middleware/robots-middleware.js +41 -40
  89. package/dist/esm/middleware/sitemap-middleware.js +46 -45
  90. package/dist/esm/monitoring/healthcheck-middleware.js +27 -26
  91. package/dist/esm/monitoring/index.js +1 -1
  92. package/dist/esm/route-handler/editing-config-route-handler.js +106 -102
  93. package/dist/esm/route-handler/editing-render-route-handler.js +265 -160
  94. package/dist/esm/route-handler/index.js +4 -4
  95. package/dist/esm/route-handler/robots-route-handler.js +65 -64
  96. package/dist/esm/route-handler/sitemap-route-handler.js +63 -62
  97. package/dist/esm/search/index.js +1 -0
  98. package/dist/esm/services/component-props-service.js +135 -131
  99. package/dist/esm/sharedTypes/component-props.js +1 -1
  100. package/dist/esm/sharedTypes/sitecore-page-props.js +1 -1
  101. package/dist/esm/site/index.js +1 -1
  102. package/dist/esm/tools/codegen/import-map.js +11 -0
  103. package/dist/esm/tools/component-props.loader.js +59 -59
  104. package/dist/esm/tools/generate-map.js +279 -279
  105. package/dist/esm/tools/index.js +3 -2
  106. package/dist/esm/tools/templating/byoc-component.js +30 -30
  107. package/dist/esm/tools/templating/constants.js +4 -4
  108. package/dist/esm/tools/templating/default-component.js +29 -29
  109. package/dist/esm/tools/templating/utils.js +190 -0
  110. package/dist/esm/utils/index.js +3 -3
  111. package/dist/esm/utils/utils.js +74 -65
  112. package/package.json +87 -13
  113. package/search.d.ts +1 -0
  114. package/types/client/index.d.ts +3 -2
  115. package/types/client/index.d.ts.map +1 -0
  116. package/types/client/models.d.ts +9 -8
  117. package/types/client/models.d.ts.map +1 -0
  118. package/types/client/sitecore-nextjs-client.d.ts +68 -63
  119. package/types/client/sitecore-nextjs-client.d.ts.map +1 -0
  120. package/types/components/BYOCWrapper.d.ts +16 -14
  121. package/types/components/BYOCWrapper.d.ts.map +1 -0
  122. package/types/components/ComponentPropsContext.d.ts +30 -18
  123. package/types/components/ComponentPropsContext.d.ts.map +1 -0
  124. package/types/components/FEaaSWrapper.d.ts +17 -15
  125. package/types/components/FEaaSWrapper.d.ts.map +1 -0
  126. package/types/components/Link.d.ts +25 -15
  127. package/types/components/Link.d.ts.map +1 -0
  128. package/types/components/NextImage.d.ts +11 -6
  129. package/types/components/NextImage.d.ts.map +1 -0
  130. package/types/components/Placeholder.d.ts +14 -8
  131. package/types/components/Placeholder.d.ts.map +1 -0
  132. package/types/components/RichText.d.ts +35 -25
  133. package/types/components/RichText.d.ts.map +1 -0
  134. package/types/config/define-config.d.ts +42 -38
  135. package/types/config/define-config.d.ts.map +1 -0
  136. package/types/config/index.d.ts +2 -1
  137. package/types/config/index.d.ts.map +1 -0
  138. package/types/config-cli/define-cli-config.d.ts +10 -8
  139. package/types/config-cli/define-cli-config.d.ts.map +1 -0
  140. package/types/config-cli/index.d.ts +2 -1
  141. package/types/config-cli/index.d.ts.map +1 -0
  142. package/types/editing/codegen/import-map.d.ts +15 -3
  143. package/types/editing/codegen/import-map.d.ts.map +1 -0
  144. package/types/editing/codegen/index.d.ts +3 -2
  145. package/types/editing/codegen/index.d.ts.map +1 -0
  146. package/types/editing/constants.d.ts +8 -7
  147. package/types/editing/constants.d.ts.map +1 -0
  148. package/types/editing/editing-config-middleware.d.ts +37 -31
  149. package/types/editing/editing-config-middleware.d.ts.map +1 -0
  150. package/types/editing/editing-render-middleware.d.ts +47 -44
  151. package/types/editing/editing-render-middleware.d.ts.map +1 -0
  152. package/types/editing/feaas-render-middleware.d.ts +35 -32
  153. package/types/editing/feaas-render-middleware.d.ts.map +1 -0
  154. package/types/editing/index.d.ts +7 -6
  155. package/types/editing/index.d.ts.map +1 -0
  156. package/types/editing/render-middleware.d.ts +26 -25
  157. package/types/editing/render-middleware.d.ts.map +1 -0
  158. package/types/editing/utils.d.ts +110 -106
  159. package/types/editing/utils.d.ts.map +1 -0
  160. package/types/index.d.ts +25 -24
  161. package/types/index.d.ts.map +1 -0
  162. package/types/middleware/app-router-multisite-middleware.d.ts +28 -13
  163. package/types/middleware/app-router-multisite-middleware.d.ts.map +1 -0
  164. package/types/middleware/index.d.ts +12 -11
  165. package/types/middleware/index.d.ts.map +1 -0
  166. package/types/middleware/locale-middleware.d.ts +35 -32
  167. package/types/middleware/locale-middleware.d.ts.map +1 -0
  168. package/types/middleware/middleware.d.ts +135 -127
  169. package/types/middleware/middleware.d.ts.map +1 -0
  170. package/types/middleware/multisite-middleware.d.ts +54 -37
  171. package/types/middleware/multisite-middleware.d.ts.map +1 -0
  172. package/types/middleware/personalize-middleware.d.ts +81 -65
  173. package/types/middleware/personalize-middleware.d.ts.map +1 -0
  174. package/types/middleware/redirects-middleware.d.ts +68 -65
  175. package/types/middleware/redirects-middleware.d.ts.map +1 -0
  176. package/types/middleware/robots-middleware.d.ts +15 -13
  177. package/types/middleware/robots-middleware.d.ts.map +1 -0
  178. package/types/middleware/sitemap-middleware.d.ts +16 -14
  179. package/types/middleware/sitemap-middleware.d.ts.map +1 -0
  180. package/types/monitoring/healthcheck-middleware.d.ts +14 -12
  181. package/types/monitoring/healthcheck-middleware.d.ts.map +1 -0
  182. package/types/monitoring/index.d.ts +2 -1
  183. package/types/monitoring/index.d.ts.map +1 -0
  184. package/types/route-handler/editing-config-route-handler.d.ts +30 -24
  185. package/types/route-handler/editing-config-route-handler.d.ts.map +1 -0
  186. package/types/route-handler/editing-render-route-handler.d.ts +33 -25
  187. package/types/route-handler/editing-render-route-handler.d.ts.map +1 -0
  188. package/types/route-handler/index.d.ts +5 -4
  189. package/types/route-handler/index.d.ts.map +1 -0
  190. package/types/route-handler/robots-route-handler.d.ts +30 -28
  191. package/types/route-handler/robots-route-handler.d.ts.map +1 -0
  192. package/types/route-handler/sitemap-route-handler.d.ts +30 -28
  193. package/types/route-handler/sitemap-route-handler.d.ts.map +1 -0
  194. package/types/search/index.d.ts +2 -0
  195. package/types/search/index.d.ts.map +1 -0
  196. package/types/services/component-props-service.d.ts +62 -57
  197. package/types/services/component-props-service.d.ts.map +1 -0
  198. package/types/sharedTypes/component-props.d.ts +62 -47
  199. package/types/sharedTypes/component-props.d.ts.map +1 -0
  200. package/types/sharedTypes/sitecore-page-props.d.ts +14 -9
  201. package/types/sharedTypes/sitecore-page-props.d.ts.map +1 -0
  202. package/types/site/index.d.ts +2 -1
  203. package/types/site/index.d.ts.map +1 -0
  204. package/types/tools/codegen/import-map.d.ts +10 -0
  205. package/types/tools/codegen/import-map.d.ts.map +1 -0
  206. package/types/tools/component-props.loader.d.ts +8 -7
  207. package/types/tools/component-props.loader.d.ts.map +1 -0
  208. package/types/tools/generate-map.d.ts +26 -24
  209. package/types/tools/generate-map.d.ts.map +1 -0
  210. package/types/tools/index.d.ts +4 -2
  211. package/types/tools/index.d.ts.map +1 -0
  212. package/types/tools/templating/byoc-component.d.ts +3 -2
  213. package/types/tools/templating/byoc-component.d.ts.map +1 -0
  214. package/types/tools/templating/constants.d.ts +5 -4
  215. package/types/tools/templating/constants.d.ts.map +1 -0
  216. package/types/tools/templating/default-component.d.ts +3 -2
  217. package/types/tools/templating/default-component.d.ts.map +1 -0
  218. package/types/tools/templating/utils.d.ts +44 -0
  219. package/types/tools/templating/utils.d.ts.map +1 -0
  220. package/types/utils/index.d.ts +4 -3
  221. package/types/utils/index.d.ts.map +1 -0
  222. package/types/utils/utils.d.ts +34 -24
  223. package/types/utils/utils.d.ts.map +1 -0
  224. package/client.js +0 -1
  225. package/codegen.js +0 -1
  226. package/config-cli.js +0 -1
  227. package/config.js +0 -1
  228. package/editing.js +0 -1
  229. package/middleware.js +0 -1
  230. package/monitoring.js +0 -1
  231. package/route-handler.js +0 -1
  232. package/site.js +0 -1
  233. package/tools.js +0 -1
  234. package/utils.js +0 -1
@@ -1,290 +1,316 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
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';
13
- import { NextResponse } from 'next/server';
14
- import regexParser from 'regex-parser';
15
- import { MiddlewareBase, REWRITE_HEADER_NAME } from './middleware';
16
- const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
17
- const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
18
- /**
19
- * Middleware / handler fetches all redirects from Sitecore instance by grapqhl service
20
- * compares with current url and redirects to target url
21
- */
22
- export class RedirectsMiddleware extends MiddlewareBase {
23
- /**
24
- * @param {RedirectsMiddlewareConfig} [config] redirects middleware config
25
- */
26
- constructor(config) {
27
- var _a;
28
- super(config);
29
- this.config = config;
30
- this.handle = (req, res) => __awaiter(this, void 0, void 0, function* () {
31
- if (!this.config.enabled) {
32
- debug.redirects('skipped (redirects middleware is disabled globally)');
33
- return res;
34
- }
35
- try {
36
- const pathname = req.nextUrl.pathname;
37
- const language = this.getLanguage(req, res);
38
- const hostname = this.getHostHeader(req) || this.defaultHostname;
39
- let site;
40
- const startTimestamp = Date.now();
41
- debug.redirects('redirects middleware start: %o', {
42
- pathname,
43
- language,
44
- hostname,
45
- });
46
- if (this.disabled(req, res)) {
47
- debug.redirects('skipped (redirects middleware is disabled)');
48
- return res;
49
- }
50
- const createResponse = () => __awaiter(this, void 0, void 0, function* () {
51
- var _a;
52
- if (this.isPreview(req)) {
53
- debug.redirects('skipped (preview)');
54
- return res;
55
- }
56
- // Skip prefetch requests from Next.js, which are not original client requests
57
- // as they load unnecessary requests that burden the redirects middleware with meaningless traffic
58
- if (this.isPrefetch(req)) {
59
- debug.redirects('skipped (prefetch)');
60
- res.headers.set('x-middleware-cache', 'no-cache');
61
- res.headers.set('Cache-Control', 'no-store, must-revalidate');
62
- return res;
63
- }
64
- site = this.getSite(req, res);
65
- // Find the redirect from result of RedirectService
66
- const existsRedirect = yield this.getExistsRedirect(req, site.name);
67
- if (!existsRedirect) {
68
- debug.redirects('skipped (redirect does not exist)');
69
- return res;
70
- }
71
- debug.redirects('Matched redirect rule: %o', { existsRedirect });
72
- // Find context site language and replace token
73
- if (REGEXP_CONTEXT_SITE_LANG.test(existsRedirect.target) &&
74
- !(REGEXP_ABSOLUTE_URL.test(existsRedirect.target) &&
75
- existsRedirect.target.includes(hostname))) {
76
- existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
77
- req.nextUrl.locale = site.language;
78
- }
79
- const url = this.normalizeUrl(req.nextUrl.clone());
80
- // Redirect logic for external (absolute) URLS. To avoid locale stripping: use plain string for external URLs to prevent Next.js rewriting.
81
- if (REGEXP_ABSOLUTE_URL.test(existsRedirect.target)) {
82
- return this.dispatchRedirect(existsRedirect.target, existsRedirect.redirectType, req, res, true);
83
- }
84
- else {
85
- const isUrl = isRegexOrUrl(existsRedirect.pattern) === 'url';
86
- const targetParts = existsRedirect.target.split('/');
87
- const urlFirstPart = targetParts[1];
88
- if (this.locales.includes(urlFirstPart)) {
89
- req.nextUrl.locale = urlFirstPart;
90
- existsRedirect.target = existsRedirect.target.replace(`/${urlFirstPart}`, '');
91
- }
92
- const targetSegments = isUrl
93
- ? existsRedirect.target.split('?')
94
- : url.pathname.replace(/\/*$/gi, '') + existsRedirect.matchedQueryString;
95
- const [targetPath, targetQueryString] = isUrl
96
- ? targetSegments
97
- : targetSegments
98
- .replace(regexParser(existsRedirect.pattern), existsRedirect.target)
99
- .replace(/^\/\//, '/')
100
- .split('?');
101
- const mergedQueryString = existsRedirect.isQueryStringPreserved
102
- ? mergeURLSearchParams(new URLSearchParams((_a = url.search) !== null && _a !== void 0 ? _a : ''), new URLSearchParams(targetQueryString || ''))
103
- : targetQueryString || '';
104
- const prepareNewURL = new URL(`${targetPath}${mergedQueryString ? '?' + mergedQueryString : ''}`, url.origin);
105
- url.href = prepareNewURL.href;
106
- url.pathname = prepareNewURL.pathname;
107
- url.search = prepareNewURL.search;
108
- url.locale = req.nextUrl.locale;
109
- }
110
- /** return Response redirect with http code of redirect type */
111
- return this.dispatchRedirect(url, existsRedirect.redirectType, req, res, false);
112
- });
113
- const response = yield createResponse();
114
- debug.redirects('redirects middleware end in %dms: %o', Date.now() - startTimestamp, {
115
- redirected: response.redirected,
116
- status: response.status,
117
- url: response.url,
118
- headers: this.extractDebugHeaders(response.headers),
119
- });
120
- return response;
121
- }
122
- catch (error) {
123
- console.log('Redirect middleware failed:');
124
- console.log(error);
125
- return res;
126
- }
127
- });
128
- const graphQLOptions = {
129
- api: {
130
- edge: {
131
- contextId: this.config.contextId,
132
- clientContextId: this.config.clientContextId,
133
- edgeUrl: this.config.edgeUrl,
134
- },
135
- },
136
- };
137
- // NOTE: we provide native fetch for compatibility on Next.js Edge Runtime
138
- // (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
139
- this.redirectsService =
140
- (_a = this.config.redirectsService) !== null && _a !== void 0 ? _a : new RedirectsService(Object.assign(Object.assign({}, config), { clientFactory: this.getClientFactory(graphQLOptions), fetch: fetch }));
141
- this.locales = config.locales;
142
- }
143
- /**
144
- * Method returns RedirectInfo when matches
145
- * @param {NextRequest} req request
146
- * @param {string} siteName site name
147
- * @returns Promise<RedirectInfo | undefined>
148
- * @private
149
- */
150
- getExistsRedirect(req, siteName) {
151
- return __awaiter(this, void 0, void 0, function* () {
152
- const { pathname: incomingURL, search: incomingQS = '' } = this.normalizeUrl(req.nextUrl.clone());
153
- const locale = this.getLanguage(req);
154
- const normalizedPath = incomingURL.replace(/\/*$/gi, '').toLowerCase();
155
- const redirects = yield this.redirectsService.fetchRedirects(siteName);
156
- const language = this.getLanguage(req);
157
- const modifyRedirects = structuredClone(redirects);
158
- let matchedQueryString;
159
- const localePath = `/${locale.toLowerCase()}${normalizedPath}`;
160
- return modifyRedirects.length
161
- ? modifyRedirects.find((redirect) => {
162
- // process static URL (non-regex) rules
163
- if (isRegexOrUrl(redirect.pattern) === 'url') {
164
- const urlArray = redirect.pattern.endsWith('/')
165
- ? redirect.pattern.slice(0, -1).split('?')
166
- : redirect.pattern.split('?');
167
- const patternQS = urlArray[1];
168
- let patternPath = urlArray[0].toLowerCase();
169
- // nextjs routes are case-sensitive, but locales should be compared case-insensitively
170
- const patternParts = patternPath.split('/');
171
- const maybeLocale = patternParts[1].toLowerCase();
172
- // case insensitive lookup of locales
173
- if (new RegExp(this.locales.join('|'), 'i').test(maybeLocale)) {
174
- patternPath = patternPath.replace(`/${patternParts[1]}`, `/${maybeLocale}`);
175
- }
176
- return ((patternPath === localePath || patternPath === normalizedPath) &&
177
- (!patternQS ||
178
- areURLSearchParamsEqual(new URLSearchParams(patternQS), new URLSearchParams(incomingQS))));
179
- }
180
- // process regex rules
181
- // Modify the redirect pattern to ignore the language prefix in the path
182
- // And escapes non-special "?" characters in a string or regex.
183
- redirect.pattern = escapeNonSpecialQuestionMarks(redirect.pattern.replace(new RegExp(`^[^]?/${language}/`, 'gi'), ''));
184
- // Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
185
- redirect.pattern = `/^\/${redirect.pattern
186
- .replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
187
- .replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
188
- .replace(/^\^|\$$/g, '') // Further cleans up anchors
189
- .replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
190
- // Redirect pattern matches the full incoming URL with query string present
191
- matchedQueryString = [
192
- regexParser(redirect.pattern).test(`/${localePath}${incomingQS}`),
193
- regexParser(redirect.pattern).test(`${normalizedPath}${incomingQS}`),
194
- ].some(Boolean)
195
- ? incomingQS
196
- : undefined;
197
- // Save the matched query string (if found) into the redirect object
198
- redirect.matchedQueryString = matchedQueryString || '';
199
- return (!!(regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${incomingURL}`) ||
200
- regexParser(redirect.pattern).test(incomingURL) ||
201
- matchedQueryString) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
202
- })
203
- : undefined;
204
- });
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.toLowerCase()}?${newQueryString}`, url.origin);
241
- url.search = newUrl.search;
242
- url.pathname = newUrl.pathname.toLocaleLowerCase();
243
- url.href = newUrl.href;
244
- return url;
245
- }
246
- /**
247
- * Helper function to dispatch a redirect or rewrite based on the redirect type.
248
- * @param {NextURL | string} target The final target to redirect/rewrite to.
249
- * @param {string} type One of `REDIRECT_TYPE_301`, `REDIRECT_TYPE_302`, or `REDIRECT_TYPE_SERVER_TRANSFER`.
250
- * @param {NextRequest} req The incoming request.
251
- * @param {NextResponse} res The current response (used for header cleanup / carry-over).
252
- * @param {boolean} isExternal Set to `true` when `target` is an external absolute URL (e.g. `https://…`).
253
- * Passed through to `rewrite` so it can skip locale/basePath stripping for externals.
254
- * @returns {NextResponse} The redirect/rewrite response, or `res` if the type is not recognized.
255
- */
256
- dispatchRedirect(target, type, req, res, isExternal = false) {
257
- switch (type) {
258
- case REDIRECT_TYPE_301:
259
- return this.createRedirectResponse(target, res, 301, 'Moved Permanently');
260
- case REDIRECT_TYPE_302:
261
- return this.createRedirectResponse(target, res, 302, 'Found');
262
- case REDIRECT_TYPE_SERVER_TRANSFER:
263
- // rewrite expects a string; unwrap NextURL if needed
264
- return this.rewrite(typeof target === 'string' ? target : target.href, req, res, isExternal);
265
- default:
266
- return res;
267
- }
268
- }
269
- /**
270
- * Helper function to create a redirect response and remove the x-middleware-next header.
271
- * @param {NextURL | string} url The URL to redirect to.
272
- * @param {Response} res The response object.
273
- * @param {number} status The HTTP status code of the redirect.
274
- * @param {string} statusText The status text of the redirect.
275
- * @returns {NextResponse<unknown>} The redirect response.
276
- */
277
- createRedirectResponse(url, res, status, statusText) {
278
- const redirect = NextResponse.redirect(url, {
279
- status,
280
- statusText,
281
- headers: res === null || res === void 0 ? void 0 : res.headers,
282
- });
283
- if (res === null || res === void 0 ? void 0 : res.headers) {
284
- redirect.headers.delete('x-middleware-next');
285
- redirect.headers.delete('x-middleware-rewrite');
286
- redirect.headers.delete(REWRITE_HEADER_NAME);
287
- }
288
- return redirect;
289
- }
290
- }
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
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';
13
+ import { NextResponse } from 'next/server';
14
+ import regexParser from 'regex-parser';
15
+ import { MiddlewareBase, REWRITE_HEADER_NAME } from './middleware';
16
+ const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
17
+ const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
18
+ /**
19
+ * Middleware / handler fetches all redirects from Sitecore instance by grapqhl service
20
+ * compares with current url and redirects to target url
21
+ * @public
22
+ */
23
+ export class RedirectsMiddleware extends MiddlewareBase {
24
+ /**
25
+ * @param {RedirectsMiddlewareConfig} [config] redirects middleware config
26
+ */
27
+ constructor(config) {
28
+ var _a;
29
+ super(config);
30
+ this.config = config;
31
+ this.handle = (req, res) => __awaiter(this, void 0, void 0, function* () {
32
+ if (!this.config.enabled) {
33
+ debug.redirects('skipped (redirects middleware is disabled globally)');
34
+ return res;
35
+ }
36
+ try {
37
+ const pathname = req.nextUrl.pathname;
38
+ const language = this.getLanguage(req, res);
39
+ const hostname = this.getHostHeader(req) || this.defaultHostname;
40
+ let site;
41
+ const startTimestamp = Date.now();
42
+ debug.redirects('redirects middleware start: %o', {
43
+ pathname,
44
+ language,
45
+ hostname,
46
+ });
47
+ if (this.disabled(req, res)) {
48
+ debug.redirects('skipped (redirects middleware is disabled)');
49
+ return res;
50
+ }
51
+ const isAppRouterRequest = this.isAppRouter(res);
52
+ const createResponse = () => __awaiter(this, void 0, void 0, function* () {
53
+ var _a;
54
+ if (this.isPreview(req)) {
55
+ debug.redirects('skipped (preview)');
56
+ return res;
57
+ }
58
+ // 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
60
+ if (this.isPrefetch(req)) {
61
+ debug.redirects('skipped (prefetch)');
62
+ res.headers.set('x-middleware-cache', 'no-cache');
63
+ res.headers.set('Cache-Control', 'no-store, must-revalidate');
64
+ return res;
65
+ }
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;
72
+ }
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
+ }
82
+ }
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);
99
+ }
100
+ 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
+ url.href = prepareNewURL.href;
124
+ url.pathname = prepareNewURL.pathname;
125
+ url.search = prepareNewURL.search;
126
+ if (!isAppRouterRequest) {
127
+ url.locale = req.nextUrl.locale;
128
+ }
129
+ }
130
+ /** return Response redirect with http code of redirect type */
131
+ return this.dispatchRedirect(url, existsRedirect.redirectType, req, res, false);
132
+ });
133
+ const response = yield createResponse();
134
+ debug.redirects('redirects middleware end in %dms: %o', Date.now() - startTimestamp, {
135
+ redirected: response.redirected,
136
+ status: response.status,
137
+ url: response.url,
138
+ headers: this.extractDebugHeaders(response.headers),
139
+ });
140
+ return response;
141
+ }
142
+ catch (error) {
143
+ console.log('Redirect middleware failed:');
144
+ console.log(error);
145
+ return res;
146
+ }
147
+ });
148
+ const graphQLOptions = {
149
+ api: Object.assign({ edge: {
150
+ contextId: this.config.contextId,
151
+ clientContextId: this.config.clientContextId,
152
+ edgeUrl: this.config.edgeUrl,
153
+ } }, (this.config.apiHost && this.config.apiKey
154
+ ? {
155
+ local: {
156
+ apiHost: this.config.apiHost,
157
+ apiKey: this.config.apiKey,
158
+ path: this.config.path,
159
+ },
160
+ }
161
+ : {})),
162
+ };
163
+ // NOTE: we provide native fetch for compatibility on Next.js Edge Runtime
164
+ // (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
165
+ this.redirectsService =
166
+ (_a = this.config.redirectsService) !== null && _a !== void 0 ? _a : new RedirectsService(Object.assign(Object.assign({}, config), { clientFactory: this.getClientFactory(graphQLOptions), fetch: fetch }));
167
+ this.locales = config.locales;
168
+ }
169
+ /**
170
+ * Method returns RedirectInfo when matches
171
+ * @param {NextRequest} req request
172
+ * @param {string} siteName site name
173
+ * @returns Promise<RedirectInfo | undefined>
174
+ * @private
175
+ */
176
+ getExistsRedirect(req, siteName) {
177
+ return __awaiter(this, void 0, void 0, function* () {
178
+ const { pathname: incomingURL, search: incomingQS = '' } = this.normalizeUrl(req.nextUrl.clone());
179
+ const locale = this.getLanguage(req);
180
+ const normalizedPath = incomingURL.replace(/\/*$/gi, '').toLowerCase();
181
+ const redirects = yield this.redirectsService.fetchRedirects(siteName);
182
+ const language = this.getLanguage(req);
183
+ const modifyRedirects = structuredClone(redirects);
184
+ let matchedQueryString;
185
+ const localePath = `/${locale.toLowerCase()}${normalizedPath}`;
186
+ return modifyRedirects.length
187
+ ? modifyRedirects.find((redirect) => {
188
+ // process static URL (non-regex) rules
189
+ if (isRegexOrUrl(redirect.pattern) === 'url') {
190
+ const urlArray = redirect.pattern.endsWith('/')
191
+ ? redirect.pattern.slice(0, -1).split('?')
192
+ : redirect.pattern.split('?');
193
+ const patternQS = urlArray[1];
194
+ let patternPath = urlArray[0].toLowerCase();
195
+ // nextjs routes are case-sensitive, but locales should be compared case-insensitively
196
+ const patternParts = patternPath.split('/');
197
+ const maybeLocale = patternParts[1].toLowerCase();
198
+ // case insensitive lookup of locales
199
+ if (new RegExp(this.locales.join('|'), 'i').test(maybeLocale)) {
200
+ patternPath = patternPath.replace(`/${patternParts[1]}`, `/${maybeLocale}`);
201
+ }
202
+ return ((patternPath === localePath || patternPath === normalizedPath) &&
203
+ (!patternQS ||
204
+ areURLSearchParamsEqual(new URLSearchParams(patternQS), new URLSearchParams(incomingQS))));
205
+ }
206
+ // process regex rules
207
+ // Modify the redirect pattern to ignore the language prefix in the path
208
+ // And escapes non-special "?" characters in a string or regex.
209
+ redirect.pattern = escapeNonSpecialQuestionMarks(redirect.pattern.replace(new RegExp(`^[^]?/${language}/`, 'gi'), ''));
210
+ // Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
211
+ redirect.pattern = `/^\/${redirect.pattern
212
+ .replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
213
+ .replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
214
+ .replace(/^\^|\$$/g, '') // Further cleans up anchors
215
+ .replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
216
+ // Redirect pattern matches the full incoming URL with query string present
217
+ matchedQueryString = [
218
+ regexParser(redirect.pattern).test(`/${localePath}${incomingQS}`),
219
+ regexParser(redirect.pattern).test(`${normalizedPath}${incomingQS}`),
220
+ ].some(Boolean)
221
+ ? incomingQS
222
+ : undefined;
223
+ // Save the matched query string (if found) into the redirect object
224
+ redirect.matchedQueryString = matchedQueryString || '';
225
+ return (!!(regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${incomingURL}`) ||
226
+ regexParser(redirect.pattern).test(incomingURL) ||
227
+ matchedQueryString) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
228
+ })
229
+ : undefined;
230
+ });
231
+ }
232
+ /**
233
+ * When a user clicks on a link generated by the Link component from next/link,
234
+ * Next.js adds special parameters in the route called path.
235
+ * This method removes these special parameters.
236
+ * @param {NextURL} url
237
+ * @returns {string} normalize url
238
+ */
239
+ normalizeUrl(url) {
240
+ if (!url.search) {
241
+ return url;
242
+ }
243
+ /**
244
+ * Prepare special parameters for exclusion.
245
+ */
246
+ const splittedPathname = url.pathname
247
+ .split('/')
248
+ .filter((route) => route)
249
+ .map((route) => `path=${route}`);
250
+ /**
251
+ * Remove special parameters(Next.JS)
252
+ * Example: /about/contact/us
253
+ * When a user clicks on this link, Next.js should generate a link for the middleware, formatted like this:
254
+ * http://host/about/contact/us?path=about&path=contact&path=us
255
+ */
256
+ const newQueryString = url.search
257
+ .replace(/^\?/, '')
258
+ .split('&')
259
+ .filter((param) => {
260
+ if (!splittedPathname.includes(param)) {
261
+ return param;
262
+ }
263
+ return false;
264
+ })
265
+ .join('&');
266
+ const newUrl = new URL(`${url.pathname.toLowerCase()}?${newQueryString}`, url.origin);
267
+ url.search = newUrl.search;
268
+ url.pathname = newUrl.pathname.toLocaleLowerCase();
269
+ url.href = newUrl.href;
270
+ return url;
271
+ }
272
+ /**
273
+ * Helper function to dispatch a redirect or rewrite based on the redirect type.
274
+ * @param {NextURL | string} target The final target to redirect/rewrite to.
275
+ * @param {string} type One of `REDIRECT_TYPE_301`, `REDIRECT_TYPE_302`, or `REDIRECT_TYPE_SERVER_TRANSFER`.
276
+ * @param {NextRequest} req The incoming request.
277
+ * @param {NextResponse} res The current response (used for header cleanup / carry-over).
278
+ * @param {boolean} isExternal Set to `true` when `target` is an external absolute URL (e.g. `https://…`).
279
+ * Passed through to `rewrite` so it can skip locale/basePath stripping for externals.
280
+ * @returns {NextResponse} The redirect/rewrite response, or `res` if the type is not recognized.
281
+ */
282
+ dispatchRedirect(target, type, req, res, isExternal = false) {
283
+ switch (type) {
284
+ case REDIRECT_TYPE_301:
285
+ return this.createRedirectResponse(target, res, 301, 'Moved Permanently');
286
+ case REDIRECT_TYPE_302:
287
+ return this.createRedirectResponse(target, res, 302, 'Found');
288
+ case REDIRECT_TYPE_SERVER_TRANSFER:
289
+ // rewrite expects a string; unwrap NextURL if needed
290
+ return this.rewrite(typeof target === 'string' ? target : target.href, req, res, isExternal);
291
+ default:
292
+ return res;
293
+ }
294
+ }
295
+ /**
296
+ * Helper function to create a redirect response and remove the x-middleware-next header.
297
+ * @param {NextURL | string} url The URL to redirect to.
298
+ * @param {Response} res The response object.
299
+ * @param {number} status The HTTP status code of the redirect.
300
+ * @param {string} statusText The status text of the redirect.
301
+ * @returns {NextResponse<unknown>} The redirect response.
302
+ */
303
+ createRedirectResponse(url, res, status, statusText) {
304
+ const redirect = NextResponse.redirect(url, {
305
+ status,
306
+ statusText,
307
+ headers: res === null || res === void 0 ? void 0 : res.headers,
308
+ });
309
+ if (res === null || res === void 0 ? void 0 : res.headers) {
310
+ redirect.headers.delete('x-middleware-next');
311
+ redirect.headers.delete('x-middleware-rewrite');
312
+ redirect.headers.delete(REWRITE_HEADER_NAME);
313
+ }
314
+ return redirect;
315
+ }
316
+ }