@sitecore-content-sdk/nextjs 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +202 -0
- package/README.md +10 -0
- package/dist/cjs/ComponentBuilder.js +63 -0
- package/dist/cjs/components/BYOCWrapper.js +41 -0
- package/dist/cjs/components/ComponentPropsContext.js +57 -0
- package/dist/cjs/components/FEaaSWrapper.js +43 -0
- package/dist/cjs/components/Link.js +87 -0
- package/dist/cjs/components/NextImage.js +82 -0
- package/dist/cjs/components/Placeholder.js +49 -0
- package/dist/cjs/components/RichText.js +95 -0
- package/dist/cjs/editing/constants.js +10 -0
- package/dist/cjs/editing/editing-config-middleware.js +62 -0
- package/dist/cjs/editing/editing-render-middleware.js +182 -0
- package/dist/cjs/editing/feaas-render-middleware.js +101 -0
- package/dist/cjs/editing/index.js +16 -0
- package/dist/cjs/editing/render-middleware.js +43 -0
- package/dist/cjs/graphql/index.js +7 -0
- package/dist/cjs/index.js +119 -0
- package/dist/cjs/middleware/index.js +13 -0
- package/dist/cjs/middleware/middleware.js +97 -0
- package/dist/cjs/middleware/multisite-middleware.js +93 -0
- package/dist/cjs/middleware/personalize-middleware.js +231 -0
- package/dist/cjs/middleware/redirects-middleware.js +264 -0
- package/dist/cjs/monitoring/healthcheck-middleware.js +30 -0
- package/dist/cjs/monitoring/index.js +5 -0
- package/dist/cjs/services/base-graphql-sitemap-service.js +206 -0
- package/dist/cjs/services/component-props-service.js +167 -0
- package/dist/cjs/services/graphql-sitemap-service.js +64 -0
- package/dist/cjs/services/mutisite-graphql-sitemap-service.js +81 -0
- package/dist/cjs/sharedTypes/component-props.js +2 -0
- package/dist/cjs/sharedTypes/module-factory.js +2 -0
- package/dist/cjs/site/index.js +5 -0
- package/dist/cjs/utils/index.js +11 -0
- package/dist/cjs/utils/utils.js +42 -0
- package/dist/esm/ComponentBuilder.js +59 -0
- package/dist/esm/components/BYOCWrapper.js +36 -0
- package/dist/esm/components/ComponentPropsContext.js +19 -0
- package/dist/esm/components/FEaaSWrapper.js +38 -0
- package/dist/esm/components/Link.js +48 -0
- package/dist/esm/components/NextImage.js +76 -0
- package/dist/esm/components/Placeholder.js +12 -0
- package/dist/esm/components/RichText.js +55 -0
- package/dist/esm/editing/constants.js +7 -0
- package/dist/esm/editing/editing-config-middleware.js +58 -0
- package/dist/esm/editing/editing-render-middleware.js +177 -0
- package/dist/esm/editing/feaas-render-middleware.js +97 -0
- package/dist/esm/editing/index.js +5 -0
- package/dist/esm/editing/render-middleware.js +39 -0
- package/dist/esm/graphql/index.js +1 -0
- package/dist/esm/index.js +23 -0
- package/dist/esm/middleware/index.js +5 -0
- package/dist/esm/middleware/middleware.js +93 -0
- package/dist/esm/middleware/multisite-middleware.js +89 -0
- package/dist/esm/middleware/personalize-middleware.js +227 -0
- package/dist/esm/middleware/redirects-middleware.js +257 -0
- package/dist/esm/monitoring/healthcheck-middleware.js +26 -0
- package/dist/esm/monitoring/index.js +1 -0
- package/dist/esm/services/base-graphql-sitemap-service.js +201 -0
- package/dist/esm/services/component-props-service.js +160 -0
- package/dist/esm/services/graphql-sitemap-service.js +59 -0
- package/dist/esm/services/mutisite-graphql-sitemap-service.js +77 -0
- package/dist/esm/sharedTypes/component-props.js +1 -0
- package/dist/esm/sharedTypes/module-factory.js +1 -0
- package/dist/esm/site/index.js +1 -0
- package/dist/esm/utils/index.js +3 -0
- package/dist/esm/utils/utils.js +37 -0
- package/editing.d.ts +1 -0
- package/editing.js +1 -0
- package/global.d.ts +21 -0
- package/graphql.d.ts +1 -0
- package/graphql.js +1 -0
- package/middleware.d.ts +1 -0
- package/middleware.js +1 -0
- package/monitoring.d.ts +1 -0
- package/monitoring.js +1 -0
- package/package.json +92 -0
- package/site.d.ts +1 -0
- package/site.js +1 -0
- package/types/ComponentBuilder.d.ts +59 -0
- package/types/components/BYOCWrapper.d.ts +20 -0
- package/types/components/ComponentPropsContext.d.ts +18 -0
- package/types/components/FEaaSWrapper.d.ts +22 -0
- package/types/components/Link.d.ts +10 -0
- package/types/components/NextImage.d.ts +6 -0
- package/types/components/Placeholder.d.ts +8 -0
- package/types/components/RichText.d.ts +32 -0
- package/types/editing/constants.d.ts +7 -0
- package/types/editing/editing-config-middleware.d.ts +29 -0
- package/types/editing/editing-render-middleware.d.ts +79 -0
- package/types/editing/feaas-render-middleware.d.ts +32 -0
- package/types/editing/index.d.ts +5 -0
- package/types/editing/render-middleware.d.ts +24 -0
- package/types/graphql/index.d.ts +1 -0
- package/types/index.d.ts +24 -0
- package/types/middleware/index.d.ts +5 -0
- package/types/middleware/middleware.d.ts +82 -0
- package/types/middleware/multisite-middleware.d.ts +39 -0
- package/types/middleware/personalize-middleware.d.ts +102 -0
- package/types/middleware/redirects-middleware.d.ts +57 -0
- package/types/monitoring/healthcheck-middleware.d.ts +12 -0
- package/types/monitoring/index.d.ts +1 -0
- package/types/services/base-graphql-sitemap-service.d.ts +148 -0
- package/types/services/component-props-service.d.ts +81 -0
- package/types/services/graphql-sitemap-service.d.ts +51 -0
- package/types/services/mutisite-graphql-sitemap-service.d.ts +42 -0
- package/types/sharedTypes/component-props.d.ts +26 -0
- package/types/sharedTypes/module-factory.d.ts +32 -0
- package/types/site/index.d.ts +1 -0
- package/types/utils/index.d.ts +3 -0
- package/types/utils/utils.d.ts +8 -0
- package/utils.d.ts +1 -0
- package/utils.js +1 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.RedirectsMiddleware = void 0;
|
|
16
|
+
const core_1 = require("@sitecore-content-sdk/core");
|
|
17
|
+
const site_1 = require("@sitecore-content-sdk/core/site");
|
|
18
|
+
const utils_1 = require("@sitecore-content-sdk/core/utils");
|
|
19
|
+
const server_1 = require("next/server");
|
|
20
|
+
const regex_parser_1 = __importDefault(require("regex-parser"));
|
|
21
|
+
const middleware_1 = require("./middleware");
|
|
22
|
+
const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
|
|
23
|
+
const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
|
|
24
|
+
/**
|
|
25
|
+
* Middleware / handler fetches all redirects from Sitecore instance by grapqhl service
|
|
26
|
+
* compares with current url and redirects to target url
|
|
27
|
+
*/
|
|
28
|
+
class RedirectsMiddleware extends middleware_1.MiddlewareBase {
|
|
29
|
+
/**
|
|
30
|
+
* @param {RedirectsMiddlewareConfig} [config] redirects middleware config
|
|
31
|
+
*/
|
|
32
|
+
constructor(config) {
|
|
33
|
+
super(config);
|
|
34
|
+
this.config = config;
|
|
35
|
+
this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
const pathname = req.nextUrl.pathname;
|
|
37
|
+
const language = this.getLanguage(req);
|
|
38
|
+
const hostname = this.getHostHeader(req) || this.defaultHostname;
|
|
39
|
+
let site;
|
|
40
|
+
const startTimestamp = Date.now();
|
|
41
|
+
core_1.debug.redirects('redirects middleware start: %o', {
|
|
42
|
+
pathname,
|
|
43
|
+
language,
|
|
44
|
+
hostname,
|
|
45
|
+
});
|
|
46
|
+
const createResponse = () => __awaiter(this, void 0, void 0, function* () {
|
|
47
|
+
var _a;
|
|
48
|
+
const response = res || server_1.NextResponse.next();
|
|
49
|
+
if (this.config.disabled && this.config.disabled(req, res || server_1.NextResponse.next())) {
|
|
50
|
+
core_1.debug.redirects('skipped (redirects middleware is disabled)');
|
|
51
|
+
return response;
|
|
52
|
+
}
|
|
53
|
+
if (this.isPreview(req) || this.excludeRoute(pathname)) {
|
|
54
|
+
core_1.debug.redirects('skipped (%s)', this.isPreview(req) ? 'preview' : 'route excluded');
|
|
55
|
+
return response;
|
|
56
|
+
}
|
|
57
|
+
// Skip prefetch requests from Next.js, which are not original client requests
|
|
58
|
+
// as they load unnecessary requests that burden the redirects middleware with meaningless traffic
|
|
59
|
+
if (this.isPrefetch(req)) {
|
|
60
|
+
core_1.debug.redirects('skipped (prefetch)');
|
|
61
|
+
response.headers.set('x-middleware-cache', 'no-cache');
|
|
62
|
+
return response;
|
|
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
|
+
core_1.debug.redirects('skipped (redirect does not exist)');
|
|
69
|
+
return response;
|
|
70
|
+
}
|
|
71
|
+
// Find context site language and replace token
|
|
72
|
+
if (REGEXP_CONTEXT_SITE_LANG.test(existsRedirect.target) &&
|
|
73
|
+
!(REGEXP_ABSOLUTE_URL.test(existsRedirect.target) &&
|
|
74
|
+
existsRedirect.target.includes(hostname))) {
|
|
75
|
+
existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
|
|
76
|
+
req.nextUrl.locale = site.language;
|
|
77
|
+
}
|
|
78
|
+
const url = this.normalizeUrl(req.nextUrl.clone());
|
|
79
|
+
if (REGEXP_ABSOLUTE_URL.test(existsRedirect.target)) {
|
|
80
|
+
url.href = existsRedirect.target;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
const isUrl = (0, utils_1.isRegexOrUrl)(existsRedirect.pattern) === 'url';
|
|
84
|
+
const targetParts = existsRedirect.target.split('/');
|
|
85
|
+
const urlFirstPart = targetParts[1];
|
|
86
|
+
if (this.locales.includes(urlFirstPart)) {
|
|
87
|
+
req.nextUrl.locale = urlFirstPart;
|
|
88
|
+
existsRedirect.target = existsRedirect.target.replace(`/${urlFirstPart}`, '');
|
|
89
|
+
}
|
|
90
|
+
const targetSegments = isUrl
|
|
91
|
+
? existsRedirect.target.split('?')
|
|
92
|
+
: url.pathname.replace(/\/*$/gi, '') + existsRedirect.matchedQueryString;
|
|
93
|
+
const [targetPath, targetQueryString] = isUrl
|
|
94
|
+
? targetSegments
|
|
95
|
+
: targetSegments
|
|
96
|
+
.replace((0, regex_parser_1.default)(existsRedirect.pattern), existsRedirect.target)
|
|
97
|
+
.replace(/^\/\//, '/')
|
|
98
|
+
.split('?');
|
|
99
|
+
const mergedQueryString = existsRedirect.isQueryStringPreserved
|
|
100
|
+
? (0, utils_1.mergeURLSearchParams)(new URLSearchParams((_a = url.search) !== null && _a !== void 0 ? _a : ''), new URLSearchParams(targetQueryString || ''))
|
|
101
|
+
: targetQueryString || '';
|
|
102
|
+
const prepareNewURL = new URL(`${targetPath}${mergedQueryString ? '?' + mergedQueryString : ''}`, url.origin);
|
|
103
|
+
url.href = prepareNewURL.href;
|
|
104
|
+
url.pathname = prepareNewURL.pathname;
|
|
105
|
+
url.search = prepareNewURL.search;
|
|
106
|
+
url.locale = req.nextUrl.locale;
|
|
107
|
+
}
|
|
108
|
+
/** return Response redirect with http code of redirect type */
|
|
109
|
+
switch (existsRedirect.redirectType) {
|
|
110
|
+
case site_1.REDIRECT_TYPE_301: {
|
|
111
|
+
return this.createRedirectResponse(url, response, 301, 'Moved Permanently');
|
|
112
|
+
}
|
|
113
|
+
case site_1.REDIRECT_TYPE_302: {
|
|
114
|
+
return this.createRedirectResponse(url, response, 302, 'Found');
|
|
115
|
+
}
|
|
116
|
+
case site_1.REDIRECT_TYPE_SERVER_TRANSFER: {
|
|
117
|
+
return this.rewrite(url.href, req, response);
|
|
118
|
+
}
|
|
119
|
+
default:
|
|
120
|
+
return response;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
const response = yield createResponse();
|
|
124
|
+
core_1.debug.redirects('redirects middleware end in %dms: %o', Date.now() - startTimestamp, {
|
|
125
|
+
redirected: response.redirected,
|
|
126
|
+
status: response.status,
|
|
127
|
+
url: response.url,
|
|
128
|
+
headers: this.extractDebugHeaders(response.headers),
|
|
129
|
+
});
|
|
130
|
+
return response;
|
|
131
|
+
});
|
|
132
|
+
// NOTE: we provide native fetch for compatibility on Next.js Edge Runtime
|
|
133
|
+
// (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
|
|
134
|
+
this.redirectsService = new site_1.GraphQLRedirectsService(Object.assign(Object.assign({}, config), { fetch: fetch }));
|
|
135
|
+
this.locales = config.locales;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Gets the Next.js middleware handler with error handling
|
|
139
|
+
* @returns route handler
|
|
140
|
+
*/
|
|
141
|
+
getHandler() {
|
|
142
|
+
return (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
try {
|
|
144
|
+
return yield this.handler(req, res);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.log('Redirect middleware failed:');
|
|
148
|
+
console.log(error);
|
|
149
|
+
return res || server_1.NextResponse.next();
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Method returns RedirectInfo when matches
|
|
155
|
+
* @param {NextRequest} req request
|
|
156
|
+
* @param {string} siteName site name
|
|
157
|
+
* @returns Promise<RedirectInfo | undefined>
|
|
158
|
+
* @private
|
|
159
|
+
*/
|
|
160
|
+
getExistsRedirect(req, siteName) {
|
|
161
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
162
|
+
const { pathname: targetURL, search: targetQS = '', locale } = this.normalizeUrl(req.nextUrl.clone());
|
|
163
|
+
const normalizedPath = targetURL.replace(/\/*$/gi, '');
|
|
164
|
+
const redirects = yield this.redirectsService.fetchRedirects(siteName);
|
|
165
|
+
const language = this.getLanguage(req);
|
|
166
|
+
const modifyRedirects = structuredClone(redirects);
|
|
167
|
+
let matchedQueryString;
|
|
168
|
+
return modifyRedirects.length
|
|
169
|
+
? modifyRedirects.find((redirect) => {
|
|
170
|
+
var _a;
|
|
171
|
+
if ((0, utils_1.isRegexOrUrl)(redirect.pattern) === 'url') {
|
|
172
|
+
const parseUrlPattern = redirect.pattern.endsWith('/')
|
|
173
|
+
? redirect.pattern.slice(0, -1).split('?')
|
|
174
|
+
: redirect.pattern.split('?');
|
|
175
|
+
return ((parseUrlPattern[0] === normalizedPath ||
|
|
176
|
+
parseUrlPattern[0] === `/${locale}${normalizedPath}`) &&
|
|
177
|
+
(0, utils_1.areURLSearchParamsEqual)(new URLSearchParams((_a = parseUrlPattern[1]) !== null && _a !== void 0 ? _a : ''), new URLSearchParams(targetQS)));
|
|
178
|
+
}
|
|
179
|
+
// Modify the redirect pattern to ignore the language prefix in the path
|
|
180
|
+
// And escapes non-special "?" characters in a string or regex.
|
|
181
|
+
redirect.pattern = (0, utils_1.escapeNonSpecialQuestionMarks)(redirect.pattern.replace(new RegExp(`^[^]?/${language}/`, 'gi'), ''));
|
|
182
|
+
// Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
|
|
183
|
+
redirect.pattern = `/^\/${redirect.pattern
|
|
184
|
+
.replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
|
|
185
|
+
.replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
|
|
186
|
+
.replace(/^\^|\$$/g, '') // Further cleans up anchors
|
|
187
|
+
.replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
|
|
188
|
+
matchedQueryString = [
|
|
189
|
+
(0, regex_parser_1.default)(redirect.pattern).test(`${normalizedPath}${targetQS}`),
|
|
190
|
+
(0, regex_parser_1.default)(redirect.pattern).test(`/${locale}${normalizedPath}${targetQS}`),
|
|
191
|
+
].some(Boolean)
|
|
192
|
+
? targetQS
|
|
193
|
+
: undefined;
|
|
194
|
+
// Save the matched query string (if found) into the redirect object
|
|
195
|
+
redirect.matchedQueryString = matchedQueryString || '';
|
|
196
|
+
return (!!((0, regex_parser_1.default)(redirect.pattern).test(targetURL) ||
|
|
197
|
+
(0, regex_parser_1.default)(redirect.pattern).test(`/${req.nextUrl.locale}${targetURL}`) ||
|
|
198
|
+
matchedQueryString) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
|
|
199
|
+
})
|
|
200
|
+
: undefined;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* When a user clicks on a link generated by the Link component from next/link,
|
|
205
|
+
* Next.js adds special parameters in the route called path.
|
|
206
|
+
* This method removes these special parameters.
|
|
207
|
+
* @param {NextURL} url
|
|
208
|
+
* @returns {string} normalize url
|
|
209
|
+
*/
|
|
210
|
+
normalizeUrl(url) {
|
|
211
|
+
if (!url.search) {
|
|
212
|
+
return url;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Prepare special parameters for exclusion.
|
|
216
|
+
*/
|
|
217
|
+
const splittedPathname = url.pathname
|
|
218
|
+
.split('/')
|
|
219
|
+
.filter((route) => route)
|
|
220
|
+
.map((route) => `path=${route}`);
|
|
221
|
+
/**
|
|
222
|
+
* Remove special parameters(Next.JS)
|
|
223
|
+
* Example: /about/contact/us
|
|
224
|
+
* When a user clicks on this link, Next.js should generate a link for the middleware, formatted like this:
|
|
225
|
+
* http://host/about/contact/us?path=about&path=contact&path=us
|
|
226
|
+
*/
|
|
227
|
+
const newQueryString = url.search
|
|
228
|
+
.replace(/^\?/, '')
|
|
229
|
+
.split('&')
|
|
230
|
+
.filter((param) => {
|
|
231
|
+
if (!splittedPathname.includes(param)) {
|
|
232
|
+
return param;
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
})
|
|
236
|
+
.join('&');
|
|
237
|
+
const newUrl = new URL(`${url.pathname}?${newQueryString}`, url.origin);
|
|
238
|
+
url.search = newUrl.search;
|
|
239
|
+
url.pathname = newUrl.pathname;
|
|
240
|
+
url.href = newUrl.href;
|
|
241
|
+
return url;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Helper function to create a redirect response and remove the x-middleware-next header.
|
|
245
|
+
* @param {NextURL} url The URL to redirect to.
|
|
246
|
+
* @param {Response} res The response object.
|
|
247
|
+
* @param {number} status The HTTP status code of the redirect.
|
|
248
|
+
* @param {string} statusText The status text of the redirect.
|
|
249
|
+
* @returns {NextResponse<unknown>} The redirect response.
|
|
250
|
+
*/
|
|
251
|
+
createRedirectResponse(url, res, status, statusText) {
|
|
252
|
+
const redirect = server_1.NextResponse.redirect(url, {
|
|
253
|
+
status,
|
|
254
|
+
statusText,
|
|
255
|
+
headers: res === null || res === void 0 ? void 0 : res.headers,
|
|
256
|
+
});
|
|
257
|
+
if (res === null || res === void 0 ? void 0 : res.headers) {
|
|
258
|
+
redirect.headers.delete('x-middleware-next');
|
|
259
|
+
redirect.headers.delete('x-middleware-rewrite');
|
|
260
|
+
}
|
|
261
|
+
return redirect;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
exports.RedirectsMiddleware = RedirectsMiddleware;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.HealthcheckMiddleware = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* Middleware / handler for use in healthcheck Next.js API route (e.g. '/api/healthz').
|
|
15
|
+
*/
|
|
16
|
+
class HealthcheckMiddleware {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.handler = (_req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
res.status(200).send('Healthy');
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Gets the Next.js API route handler
|
|
24
|
+
* @returns route handler
|
|
25
|
+
*/
|
|
26
|
+
getHandler() {
|
|
27
|
+
return this.handler;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.HealthcheckMiddleware = HealthcheckMiddleware;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HealthcheckMiddleware = void 0;
|
|
4
|
+
var healthcheck_middleware_1 = require("./healthcheck-middleware");
|
|
5
|
+
Object.defineProperty(exports, "HealthcheckMiddleware", { enumerable: true, get: function () { return healthcheck_middleware_1.HealthcheckMiddleware; } });
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.BaseGraphQLSitemapService = exports.siteError = exports.languageError = void 0;
|
|
13
|
+
exports.getSiteEmptyError = getSiteEmptyError;
|
|
14
|
+
const core_1 = require("@sitecore-content-sdk/core");
|
|
15
|
+
const personalize_1 = require("@sitecore-content-sdk/core/personalize");
|
|
16
|
+
/** @private */
|
|
17
|
+
exports.languageError = 'The list of languages cannot be empty';
|
|
18
|
+
exports.siteError = 'The service needs a site name';
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} siteName to inject into error text
|
|
21
|
+
* @private
|
|
22
|
+
*/
|
|
23
|
+
function getSiteEmptyError(siteName) {
|
|
24
|
+
return `Site "${siteName}" does not exist or site item tree is missing`;
|
|
25
|
+
}
|
|
26
|
+
const languageEmptyError = 'The language must be a non-empty string';
|
|
27
|
+
/**
|
|
28
|
+
* GQL query made dynamic based whether personalization is enabled or not
|
|
29
|
+
* @param {boolean} usesPersonalize flag to detrmine which variation of a query to run
|
|
30
|
+
* @returns GraphQL query to fetch site paths with
|
|
31
|
+
*/
|
|
32
|
+
const defaultQuery = (usesPersonalize) => /* GraphQL */ `
|
|
33
|
+
query ${usesPersonalize ? 'PersonalizeSitemapQuery' : 'DefaultSitemapQuery'}(
|
|
34
|
+
$siteName: String!
|
|
35
|
+
$language: String!
|
|
36
|
+
$includedPaths: [String]
|
|
37
|
+
$excludedPaths: [String]
|
|
38
|
+
$pageSize: Int = 100
|
|
39
|
+
$after: String
|
|
40
|
+
) {
|
|
41
|
+
site {
|
|
42
|
+
siteInfo(site: $siteName) {
|
|
43
|
+
routes(
|
|
44
|
+
language: $language
|
|
45
|
+
includedPaths: $includedPaths
|
|
46
|
+
excludedPaths: $excludedPaths
|
|
47
|
+
first: $pageSize
|
|
48
|
+
after: $after
|
|
49
|
+
){
|
|
50
|
+
total
|
|
51
|
+
pageInfo {
|
|
52
|
+
endCursor
|
|
53
|
+
hasNext
|
|
54
|
+
}
|
|
55
|
+
results {
|
|
56
|
+
path: routePath
|
|
57
|
+
${usesPersonalize
|
|
58
|
+
? `
|
|
59
|
+
route {
|
|
60
|
+
personalization {
|
|
61
|
+
variantIds
|
|
62
|
+
}
|
|
63
|
+
}`
|
|
64
|
+
: ''}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
/**
|
|
72
|
+
* Service that fetches the list of site pages using Sitecore's GraphQL API.
|
|
73
|
+
* Used to handle a single site
|
|
74
|
+
* This list is used for SSG and Export functionality.
|
|
75
|
+
* @mixes SearchQueryService<PageListQueryResult>
|
|
76
|
+
*/
|
|
77
|
+
class BaseGraphQLSitemapService {
|
|
78
|
+
/**
|
|
79
|
+
* Creates an instance of graphQL sitemap service with the provided options
|
|
80
|
+
* @param {GraphQLSitemapServiceConfig} options instance
|
|
81
|
+
*/
|
|
82
|
+
constructor(options) {
|
|
83
|
+
this.options = options;
|
|
84
|
+
this._graphQLClient = this.getGraphQLClient();
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* GraphQL client accessible by descendant classes when needed
|
|
88
|
+
*/
|
|
89
|
+
get graphQLClient() {
|
|
90
|
+
return this._graphQLClient;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Gets the default query used for fetching the list of site pages
|
|
94
|
+
*/
|
|
95
|
+
get query() {
|
|
96
|
+
return defaultQuery(this.options.includePersonalizedRoutes);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Fetch sitemap which could be used for generation of static pages during `next export`.
|
|
100
|
+
* The `locale` parameter will be used in the item query, but since i18n is not supported,
|
|
101
|
+
* the output paths will not include a `language` property.
|
|
102
|
+
* @param {string} locale which application supports
|
|
103
|
+
* @returns an array of @see StaticPath objects
|
|
104
|
+
*/
|
|
105
|
+
fetchExportSitemap(locale) {
|
|
106
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
107
|
+
const formatPath = (path) => ({
|
|
108
|
+
params: {
|
|
109
|
+
path,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
return this.fetchSitemap([locale], formatPath);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Fetch sitemap which could be used for generation of static pages using SSG mode
|
|
117
|
+
* @param {string[]} locales locales which application supports
|
|
118
|
+
* @returns an array of @see StaticPath objects
|
|
119
|
+
*/
|
|
120
|
+
fetchSSGSitemap(locales) {
|
|
121
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
122
|
+
const formatPath = (path, locale) => ({
|
|
123
|
+
params: {
|
|
124
|
+
path,
|
|
125
|
+
},
|
|
126
|
+
locale,
|
|
127
|
+
});
|
|
128
|
+
return this.fetchSitemap(locales, formatPath);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
getTranformedPaths(siteName, languages, formatStaticPath) {
|
|
132
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
133
|
+
const paths = new Array();
|
|
134
|
+
for (const language of languages) {
|
|
135
|
+
if (language === '') {
|
|
136
|
+
throw new RangeError(languageEmptyError);
|
|
137
|
+
}
|
|
138
|
+
core_1.debug.sitemap('fetching sitemap data for %s %s', language, siteName);
|
|
139
|
+
const results = yield this.fetchLanguageSitePaths(language, siteName);
|
|
140
|
+
const transformedPaths = yield this.transformLanguageSitePaths(results, formatStaticPath, language);
|
|
141
|
+
paths.push(...transformedPaths);
|
|
142
|
+
}
|
|
143
|
+
return paths;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
transformLanguageSitePaths(sitePaths, formatStaticPath, language) {
|
|
147
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
148
|
+
const formatPath = (path) => formatStaticPath(path.replace(/^\/|\/$/g, '').split('/'), language);
|
|
149
|
+
const aggregatedPaths = [];
|
|
150
|
+
sitePaths.forEach((item) => {
|
|
151
|
+
var _a, _b, _c;
|
|
152
|
+
if (!item)
|
|
153
|
+
return;
|
|
154
|
+
aggregatedPaths.push(formatPath(item.path));
|
|
155
|
+
const variantIds = (_c = (_b = (_a = item.route) === null || _a === void 0 ? void 0 : _a.personalization) === null || _b === void 0 ? void 0 : _b.variantIds) === null || _c === void 0 ? void 0 : _c.filter((variantId) => !variantId.includes('_') // exclude component A/B test variants
|
|
156
|
+
);
|
|
157
|
+
if (variantIds === null || variantIds === void 0 ? void 0 : variantIds.length) {
|
|
158
|
+
aggregatedPaths.push(...variantIds.map((varId) => formatPath((0, personalize_1.getPersonalizedRewrite)(item.path, [varId]))));
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
return aggregatedPaths;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
fetchLanguageSitePaths(language, siteName) {
|
|
165
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
166
|
+
var _a, _b, _c, _d;
|
|
167
|
+
const args = {
|
|
168
|
+
siteName: siteName,
|
|
169
|
+
language: language,
|
|
170
|
+
pageSize: this.options.pageSize,
|
|
171
|
+
includedPaths: this.options.includedPaths,
|
|
172
|
+
excludedPaths: this.options.excludedPaths,
|
|
173
|
+
};
|
|
174
|
+
let results = [];
|
|
175
|
+
let hasNext = true;
|
|
176
|
+
let after = '';
|
|
177
|
+
while (hasNext) {
|
|
178
|
+
const fetchResponse = yield this.graphQLClient.request(this.query, Object.assign(Object.assign({}, args), { after }));
|
|
179
|
+
if (!((_a = fetchResponse === null || fetchResponse === void 0 ? void 0 : fetchResponse.site) === null || _a === void 0 ? void 0 : _a.siteInfo)) {
|
|
180
|
+
throw new RangeError(getSiteEmptyError(siteName));
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
results = results.concat((_b = fetchResponse.site.siteInfo.routes) === null || _b === void 0 ? void 0 : _b.results);
|
|
184
|
+
hasNext = (_c = fetchResponse.site.siteInfo.routes) === null || _c === void 0 ? void 0 : _c.pageInfo.hasNext;
|
|
185
|
+
after = (_d = fetchResponse.site.siteInfo.routes) === null || _d === void 0 ? void 0 : _d.pageInfo.endCursor;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return results;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default
|
|
193
|
+
* library for fetching graphql data (@see GraphQLRequestClient). Override this method if you
|
|
194
|
+
* want to use something else.
|
|
195
|
+
* @returns {GraphQLClient} implementation
|
|
196
|
+
*/
|
|
197
|
+
getGraphQLClient() {
|
|
198
|
+
if (!this.options.clientFactory) {
|
|
199
|
+
throw new Error('clientFactory needs to be provided when initializing GraphQL client.');
|
|
200
|
+
}
|
|
201
|
+
return this.options.clientFactory({
|
|
202
|
+
debugger: core_1.debug.sitemap,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.BaseGraphQLSitemapService = BaseGraphQLSitemapService;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.ComponentPropsService = void 0;
|
|
16
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
17
|
+
class ComponentPropsService {
|
|
18
|
+
/**
|
|
19
|
+
* SSR mode
|
|
20
|
+
* Fetch component props using getServerSideProps function
|
|
21
|
+
* @param {FetchComponentPropsArguments<GetServerSidePropsContext>} params fetch params
|
|
22
|
+
* @returns {Promise<ComponentPropsCollection>} props
|
|
23
|
+
*/
|
|
24
|
+
fetchServerSideComponentProps(params) {
|
|
25
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
const { moduleFactory, layoutData, context } = params;
|
|
27
|
+
const fetchFunctionFactory = (componentName) => __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
const module = yield moduleFactory(componentName);
|
|
29
|
+
return module === null || module === void 0 ? void 0 : module.getServerSideProps;
|
|
30
|
+
});
|
|
31
|
+
return this.fetchComponentProps(fetchFunctionFactory, layoutData, context);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* SSG mode
|
|
36
|
+
* Fetch component props using getStaticProps function
|
|
37
|
+
* @param {FetchComponentPropsArguments<GetStaticPropsContext>} params fetch arguments
|
|
38
|
+
* @returns {Promise<ComponentPropsCollection>} props
|
|
39
|
+
*/
|
|
40
|
+
fetchStaticComponentProps(params) {
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
const { moduleFactory, layoutData, context } = params;
|
|
43
|
+
const fetchFunctionFactory = (componentName) => __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
const module = yield moduleFactory(componentName);
|
|
45
|
+
return module === null || module === void 0 ? void 0 : module.getStaticProps;
|
|
46
|
+
});
|
|
47
|
+
return this.fetchComponentProps(fetchFunctionFactory, layoutData, context);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Traverse Layout Service data tree and call side effects on component level.
|
|
52
|
+
* Side effect function can be: getStaticProps (SSG) or getServerSideProps (SSR)
|
|
53
|
+
* @param {FetchFunctionFactory<NextContext>} fetchFunctionFactory fetch function factory
|
|
54
|
+
* @param {LayoutServiceData} layoutData layout data
|
|
55
|
+
* @param {NextContext} context next context
|
|
56
|
+
* @returns {Promise<ComponentPropsCollection>} component props
|
|
57
|
+
*/
|
|
58
|
+
fetchComponentProps(fetchFunctionFactory, layoutData, context) {
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
var _a;
|
|
61
|
+
// Array of side effect functions
|
|
62
|
+
const requests = yield this.collectRequests({
|
|
63
|
+
placeholders: (_a = layoutData.sitecore.route) === null || _a === void 0 ? void 0 : _a.placeholders,
|
|
64
|
+
fetchFunctionFactory,
|
|
65
|
+
layoutData,
|
|
66
|
+
context,
|
|
67
|
+
});
|
|
68
|
+
return yield this.execRequests(requests);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Go through layout service data, check all renderings using displayName, which should make some side effects.
|
|
73
|
+
* Write result in requests variable
|
|
74
|
+
* @param {object} params params
|
|
75
|
+
* @param {PlaceholdersData} [params.placeholders]
|
|
76
|
+
* @param {FetchFunctionFactory<NextContext>} params.fetchFunctionFactory
|
|
77
|
+
* @param {LayoutServiceData} params.layoutData
|
|
78
|
+
* @param {NextContext} params.context
|
|
79
|
+
* @param {ComponentPropsRequest<NextContext>[]} params.requests
|
|
80
|
+
* @returns {ComponentPropsRequest<NextContext>[]} array of requests
|
|
81
|
+
*/
|
|
82
|
+
collectRequests(params) {
|
|
83
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
const { placeholders = {}, fetchFunctionFactory, layoutData, context } = params;
|
|
85
|
+
// Will be called on first round
|
|
86
|
+
if (!params.requests) {
|
|
87
|
+
params.requests = [];
|
|
88
|
+
}
|
|
89
|
+
const renderings = this.flatRenderings(placeholders);
|
|
90
|
+
const actions = renderings.map((r) => __awaiter(this, void 0, void 0, function* () {
|
|
91
|
+
const fetchFunc = yield fetchFunctionFactory(r.componentName);
|
|
92
|
+
if (fetchFunc) {
|
|
93
|
+
params.requests &&
|
|
94
|
+
params.requests.push({
|
|
95
|
+
fetch: fetchFunc,
|
|
96
|
+
rendering: r,
|
|
97
|
+
layoutData: layoutData,
|
|
98
|
+
context,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// If placeholders exist in current rendering
|
|
102
|
+
if (r.placeholders) {
|
|
103
|
+
yield this.collectRequests(Object.assign(Object.assign({}, params), { placeholders: r.placeholders }));
|
|
104
|
+
}
|
|
105
|
+
}));
|
|
106
|
+
yield Promise.all(actions);
|
|
107
|
+
return params.requests;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Execute request for component props
|
|
112
|
+
* @param {ComponentPropsRequest<NextContext>[]} requests requests
|
|
113
|
+
* @returns {Promise<ComponentPropsCollection>} requests result
|
|
114
|
+
*/
|
|
115
|
+
execRequests(requests) {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
const componentProps = {};
|
|
118
|
+
const promises = requests.map((req) => {
|
|
119
|
+
const { uid } = req.rendering;
|
|
120
|
+
if (!uid) {
|
|
121
|
+
console.log(`Component ${req.rendering.componentName} doesn't have uid, can't store data for this component`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
return req
|
|
125
|
+
.fetch(req.rendering, req.layoutData, req.context)
|
|
126
|
+
.then((result) => {
|
|
127
|
+
// Set component specific data in componentProps store
|
|
128
|
+
componentProps[uid] = result;
|
|
129
|
+
})
|
|
130
|
+
.catch((error) => {
|
|
131
|
+
const errLog = `Error during preload data for component ${req.rendering.componentName} (${uid}): ${error.message || error}`;
|
|
132
|
+
console.error(chalk_1.default.red(errLog));
|
|
133
|
+
componentProps[uid] = {
|
|
134
|
+
error: error.message || errLog,
|
|
135
|
+
componentName: req.rendering.componentName,
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
yield Promise.all(promises);
|
|
140
|
+
return componentProps;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Take renderings from all placeholders and returns a flat array of renderings.
|
|
145
|
+
* @example
|
|
146
|
+
* const placeholders = {
|
|
147
|
+
* x1: [{ uid: 1 }, { uid: 2 }],
|
|
148
|
+
* x2: [{ uid: 11 }, { uid: 22 }]
|
|
149
|
+
* }
|
|
150
|
+
*
|
|
151
|
+
* flatRenderings(placeholders);
|
|
152
|
+
*
|
|
153
|
+
* RESULT: [{ uid: 1 }, { uid: 2 }, { uid: 11 }, { uid: 22 }]
|
|
154
|
+
* @param {PlaceholdersData} placeholders placeholders
|
|
155
|
+
* @returns {ComponentRendering[]} renderings
|
|
156
|
+
*/
|
|
157
|
+
flatRenderings(placeholders) {
|
|
158
|
+
const allComponentRenderings = [];
|
|
159
|
+
const placeholdersArr = Object.values(placeholders);
|
|
160
|
+
placeholdersArr.forEach((pl) => {
|
|
161
|
+
const renderings = pl;
|
|
162
|
+
allComponentRenderings.push(...renderings);
|
|
163
|
+
});
|
|
164
|
+
return allComponentRenderings;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
exports.ComponentPropsService = ComponentPropsService;
|