@sitecore-content-sdk/core 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 +11 -0
- package/dist/cjs/cache-client.js +54 -0
- package/dist/cjs/constants.js +13 -0
- package/dist/cjs/data-fetcher.js +33 -0
- package/dist/cjs/debug.js +43 -0
- package/dist/cjs/editing/component-library.js +104 -0
- package/dist/cjs/editing/graphql-editing-service.js +186 -0
- package/dist/cjs/editing/index.js +23 -0
- package/dist/cjs/editing/models.js +23 -0
- package/dist/cjs/editing/rest-component-layout-service.js +76 -0
- package/dist/cjs/editing/utils.js +86 -0
- package/dist/cjs/graphql/app-root-query.js +73 -0
- package/dist/cjs/graphql/graphql-edge-proxy.js +21 -0
- package/dist/cjs/graphql/index.js +13 -0
- package/dist/cjs/graphql/search-service.js +61 -0
- package/dist/cjs/graphql-request-client.js +152 -0
- package/dist/cjs/i18n/dictionary-service.js +45 -0
- package/dist/cjs/i18n/graphql-dictionary-service.js +136 -0
- package/dist/cjs/i18n/index.js +7 -0
- package/dist/cjs/index.js +55 -0
- package/dist/cjs/layout/content-styles.js +70 -0
- package/dist/cjs/layout/graphql-layout-service.js +86 -0
- package/dist/cjs/layout/index.js +24 -0
- package/dist/cjs/layout/layout-service.js +9 -0
- package/dist/cjs/layout/models.js +35 -0
- package/dist/cjs/layout/themes.js +74 -0
- package/dist/cjs/layout/utils.js +110 -0
- package/dist/cjs/media/index.js +38 -0
- package/dist/cjs/media/media-api.js +96 -0
- package/dist/cjs/models.js +2 -0
- package/dist/cjs/native-fetcher.js +200 -0
- package/dist/cjs/personalize/graphql-personalize-service.js +114 -0
- package/dist/cjs/personalize/index.js +14 -0
- package/dist/cjs/personalize/layout-personalizer.js +97 -0
- package/dist/cjs/personalize/utils.js +136 -0
- package/dist/cjs/site/graphql-error-pages-service.js +89 -0
- package/dist/cjs/site/graphql-redirects-service.js +105 -0
- package/dist/cjs/site/graphql-robots-service.js +83 -0
- package/dist/cjs/site/graphql-siteinfo-service.js +107 -0
- package/dist/cjs/site/graphql-sitemap-service.js +93 -0
- package/dist/cjs/site/index.js +22 -0
- package/dist/cjs/site/site-resolver.js +79 -0
- package/dist/cjs/site/utils.js +43 -0
- package/dist/cjs/utils/env.js +26 -0
- package/dist/cjs/utils/index.js +20 -0
- package/dist/cjs/utils/is-server.js +10 -0
- package/dist/cjs/utils/timeout-promise.js +31 -0
- package/dist/cjs/utils/utils.js +222 -0
- package/dist/esm/cache-client.js +50 -0
- package/dist/esm/constants.js +10 -0
- package/dist/esm/data-fetcher.js +28 -0
- package/dist/esm/debug.js +36 -0
- package/dist/esm/editing/component-library.js +98 -0
- package/dist/esm/editing/graphql-editing-service.js +179 -0
- package/dist/esm/editing/index.js +5 -0
- package/dist/esm/editing/models.js +20 -0
- package/dist/esm/editing/rest-component-layout-service.js +72 -0
- package/dist/esm/editing/utils.js +76 -0
- package/dist/esm/graphql/app-root-query.js +69 -0
- package/dist/esm/graphql/graphql-edge-proxy.js +16 -0
- package/dist/esm/graphql/index.js +4 -0
- package/dist/esm/graphql/search-service.js +57 -0
- package/dist/esm/graphql-request-client.js +144 -0
- package/dist/esm/i18n/dictionary-service.js +41 -0
- package/dist/esm/i18n/graphql-dictionary-service.js +129 -0
- package/dist/esm/i18n/index.js +2 -0
- package/dist/esm/index.js +9 -0
- package/dist/esm/layout/content-styles.js +62 -0
- package/dist/esm/layout/graphql-layout-service.js +79 -0
- package/dist/esm/layout/index.js +6 -0
- package/dist/esm/layout/layout-service.js +5 -0
- package/dist/esm/layout/models.js +32 -0
- package/dist/esm/layout/themes.js +69 -0
- package/dist/esm/layout/utils.js +102 -0
- package/dist/esm/media/index.js +2 -0
- package/dist/esm/media/media-api.js +86 -0
- package/dist/esm/models.js +1 -0
- package/dist/esm/native-fetcher.js +193 -0
- package/dist/esm/personalize/graphql-personalize-service.js +107 -0
- package/dist/esm/personalize/index.js +3 -0
- package/dist/esm/personalize/layout-personalizer.js +92 -0
- package/dist/esm/personalize/utils.js +128 -0
- package/dist/esm/site/graphql-error-pages-service.js +82 -0
- package/dist/esm/site/graphql-redirects-service.js +98 -0
- package/dist/esm/site/graphql-robots-service.js +76 -0
- package/dist/esm/site/graphql-siteinfo-service.js +100 -0
- package/dist/esm/site/graphql-sitemap-service.js +86 -0
- package/dist/esm/site/index.js +7 -0
- package/dist/esm/site/site-resolver.js +75 -0
- package/dist/esm/site/utils.js +37 -0
- package/dist/esm/utils/env.js +22 -0
- package/dist/esm/utils/index.js +3 -0
- package/dist/esm/utils/is-server.js +8 -0
- package/dist/esm/utils/timeout-promise.js +28 -0
- package/dist/esm/utils/utils.js +207 -0
- package/editing.d.ts +1 -0
- package/editing.js +1 -0
- package/graphql.d.ts +1 -0
- package/graphql.js +1 -0
- package/i18n.d.ts +1 -0
- package/i18n.js +1 -0
- package/layout.d.ts +1 -0
- package/layout.js +1 -0
- package/media.d.ts +1 -0
- package/media.js +1 -0
- package/package.json +76 -0
- package/personalize.d.ts +1 -0
- package/personalize.js +1 -0
- package/site.d.ts +1 -0
- package/site.js +1 -0
- package/types/cache-client.d.ts +64 -0
- package/types/constants.d.ts +7 -0
- package/types/data-fetcher.d.ts +34 -0
- package/types/debug.d.ts +26 -0
- package/types/editing/component-library.d.ts +48 -0
- package/types/editing/graphql-editing-service.d.ts +90 -0
- package/types/editing/index.d.ts +6 -0
- package/types/editing/models.d.ts +52 -0
- package/types/editing/rest-component-layout-service.d.ts +100 -0
- package/types/editing/utils.d.ts +70 -0
- package/types/graphql/app-root-query.d.ts +32 -0
- package/types/graphql/graphql-edge-proxy.d.ts +15 -0
- package/types/graphql/index.d.ts +4 -0
- package/types/graphql/search-service.d.ts +95 -0
- package/types/graphql-request-client.d.ts +159 -0
- package/types/i18n/dictionary-service.d.ts +56 -0
- package/types/i18n/graphql-dictionary-service.d.ts +94 -0
- package/types/i18n/index.d.ts +2 -0
- package/types/index.d.ts +8 -0
- package/types/layout/content-styles.d.ts +18 -0
- package/types/layout/graphql-layout-service.d.ts +58 -0
- package/types/layout/index.d.ts +6 -0
- package/types/layout/layout-service.d.ts +19 -0
- package/types/layout/models.d.ts +145 -0
- package/types/layout/themes.d.ts +11 -0
- package/types/layout/utils.d.ts +40 -0
- package/types/media/index.d.ts +2 -0
- package/types/media/media-api.d.ts +55 -0
- package/types/models.d.ts +6 -0
- package/types/native-fetcher.d.ts +121 -0
- package/types/personalize/graphql-personalize-service.d.ts +80 -0
- package/types/personalize/index.d.ts +3 -0
- package/types/personalize/layout-personalizer.d.ts +27 -0
- package/types/personalize/utils.d.ts +69 -0
- package/types/site/graphql-error-pages-service.d.ts +57 -0
- package/types/site/graphql-redirects-service.d.ts +68 -0
- package/types/site/graphql-robots-service.d.ts +49 -0
- package/types/site/graphql-siteinfo-service.d.ts +70 -0
- package/types/site/graphql-sitemap-service.d.ts +55 -0
- package/types/site/index.d.ts +7 -0
- package/types/site/site-resolver.d.ts +27 -0
- package/types/site/utils.d.ts +24 -0
- package/types/utils/env.d.ts +7 -0
- package/types/utils/index.d.ts +3 -0
- package/types/utils/is-server.d.ts +6 -0
- package/types/utils/timeout-promise.d.ts +17 -0
- package/types/utils/utils.d.ts +71 -0
- package/utils.d.ts +1 -0
- package/utils.js +1 -0
|
@@ -0,0 +1,100 @@
|
|
|
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 '../debug';
|
|
11
|
+
import { MemoryCacheClient } from '../cache-client';
|
|
12
|
+
const siteQuery = /* GraphQL */ `
|
|
13
|
+
query {
|
|
14
|
+
site {
|
|
15
|
+
siteInfoCollection {
|
|
16
|
+
name
|
|
17
|
+
hostName: hostname
|
|
18
|
+
language
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
export class GraphQLSiteInfoService {
|
|
24
|
+
/**
|
|
25
|
+
* Creates an instance of graphQL service to retrieve site configuration list from Sitecore
|
|
26
|
+
* @param {GraphQLSiteInfoServiceConfig} config instance
|
|
27
|
+
*/
|
|
28
|
+
constructor(config) {
|
|
29
|
+
this.config = config;
|
|
30
|
+
this.graphQLClient = this.getGraphQLClient();
|
|
31
|
+
this.cache = this.getCacheClient();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* site query is available on XM Cloud and XP 10.4+
|
|
35
|
+
*/
|
|
36
|
+
get siteQuery() {
|
|
37
|
+
return siteQuery;
|
|
38
|
+
}
|
|
39
|
+
fetchSiteInfo() {
|
|
40
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
+
const cachedResult = this.cache.getCacheValue(this.getCacheKey());
|
|
42
|
+
if (cachedResult) {
|
|
43
|
+
return cachedResult;
|
|
44
|
+
}
|
|
45
|
+
if (process.env.SITECORE) {
|
|
46
|
+
debug.multisite('Skipping site information fetch (building on XM Cloud)');
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const results = yield this.fetchWithSiteQuery();
|
|
50
|
+
this.cache.setCacheValue(this.getCacheKey(), results);
|
|
51
|
+
return results;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
fetchWithSiteQuery() {
|
|
55
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
56
|
+
var _a, _b;
|
|
57
|
+
const response = yield this.graphQLClient.request(this.siteQuery);
|
|
58
|
+
const result = (_b = (_a = response === null || response === void 0 ? void 0 : response.site) === null || _a === void 0 ? void 0 : _a.siteInfoCollection) === null || _b === void 0 ? void 0 : _b.reduce((result, current) => {
|
|
59
|
+
// filter out built in website
|
|
60
|
+
current.name !== 'website' &&
|
|
61
|
+
result.push({
|
|
62
|
+
name: current.name,
|
|
63
|
+
hostName: current.hostName,
|
|
64
|
+
language: current.language,
|
|
65
|
+
});
|
|
66
|
+
return result;
|
|
67
|
+
}, []);
|
|
68
|
+
return result;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Gets cache client implementation
|
|
73
|
+
* Override this method if custom cache needs to be used
|
|
74
|
+
* @returns CacheClient instance
|
|
75
|
+
*/
|
|
76
|
+
getCacheClient() {
|
|
77
|
+
var _a, _b;
|
|
78
|
+
return new MemoryCacheClient({
|
|
79
|
+
cacheEnabled: (_a = this.config.cacheEnabled) !== null && _a !== void 0 ? _a : true,
|
|
80
|
+
cacheTimeout: (_b = this.config.cacheTimeout) !== null && _b !== void 0 ? _b : 10,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default
|
|
85
|
+
* library for fetching graphql data (@see GraphQLRequestClient). Override this method if you
|
|
86
|
+
* want to use something else.
|
|
87
|
+
* @returns {GraphQLClient} implementation
|
|
88
|
+
*/
|
|
89
|
+
getGraphQLClient() {
|
|
90
|
+
if (!this.config.clientFactory) {
|
|
91
|
+
throw new Error('clientFactory needs to be provided when initializing GraphQL client.');
|
|
92
|
+
}
|
|
93
|
+
return this.config.clientFactory({
|
|
94
|
+
debugger: debug.multisite,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
getCacheKey() {
|
|
98
|
+
return 'siteinfo-service-cache';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
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 { siteNameError } from '../constants';
|
|
11
|
+
import debug from '../debug';
|
|
12
|
+
const PREFIX_NAME_SITEMAP = 'sitemap';
|
|
13
|
+
// The default query for request sitemaps
|
|
14
|
+
const defaultQuery = /* GraphQL */ `
|
|
15
|
+
query SitemapQuery($siteName: String!) {
|
|
16
|
+
site {
|
|
17
|
+
siteInfo(site: $siteName) {
|
|
18
|
+
sitemap
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
/**
|
|
24
|
+
* Service that fetch the sitemaps data using Sitecore's GraphQL API.
|
|
25
|
+
*/
|
|
26
|
+
export class GraphQLSitemapXmlService {
|
|
27
|
+
/**
|
|
28
|
+
* Creates an instance of graphQL sitemaps service with the provided options
|
|
29
|
+
* @param {GraphQLSitemapXmlServiceConfig} options instance
|
|
30
|
+
*/
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.options = options;
|
|
33
|
+
this.graphQLClient = this.getGraphQLClient();
|
|
34
|
+
}
|
|
35
|
+
get query() {
|
|
36
|
+
return defaultQuery;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Fetch list of sitemaps for the site
|
|
40
|
+
* @returns {string[]} list of sitemap paths
|
|
41
|
+
* @throws {Error} if the siteName is empty.
|
|
42
|
+
*/
|
|
43
|
+
fetchSitemaps() {
|
|
44
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
const siteName = this.options.siteName;
|
|
46
|
+
if (!siteName) {
|
|
47
|
+
throw new Error(siteNameError);
|
|
48
|
+
}
|
|
49
|
+
const sitemapResult = this.graphQLClient.request(this.query, {
|
|
50
|
+
siteName,
|
|
51
|
+
});
|
|
52
|
+
try {
|
|
53
|
+
return sitemapResult.then((result) => result.site.siteInfo.sitemap);
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
return Promise.reject(e);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get sitemap file path for sitemap id
|
|
62
|
+
* @param {string} id the sitemap id (can be empty for default 'sitemap.xml' file)
|
|
63
|
+
* @returns {string | undefined} the sitemap file path or undefined if one doesn't exist
|
|
64
|
+
*/
|
|
65
|
+
getSitemap(id) {
|
|
66
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
67
|
+
const searchSitemap = `${PREFIX_NAME_SITEMAP}${id}.xml`;
|
|
68
|
+
const sitemaps = yield this.fetchSitemaps();
|
|
69
|
+
return sitemaps.find((sitemap) => sitemap.includes(searchSitemap));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default
|
|
74
|
+
* library for fetching graphql data (@see GraphQLRequestClient). Override this method if you
|
|
75
|
+
* want to use something else.
|
|
76
|
+
* @returns {GraphQLClient} implementation
|
|
77
|
+
*/
|
|
78
|
+
getGraphQLClient() {
|
|
79
|
+
if (!this.options.clientFactory) {
|
|
80
|
+
throw new Error('clientFactory needs to be provided when initializing GraphQL client.');
|
|
81
|
+
}
|
|
82
|
+
return this.options.clientFactory({
|
|
83
|
+
debugger: debug.sitemap,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { GraphQLRobotsService, } from './graphql-robots-service';
|
|
2
|
+
export { REDIRECT_TYPE_301, REDIRECT_TYPE_302, REDIRECT_TYPE_SERVER_TRANSFER, GraphQLRedirectsService, } from './graphql-redirects-service';
|
|
3
|
+
export { GraphQLSitemapXmlService, } from './graphql-sitemap-service';
|
|
4
|
+
export { GraphQLErrorPagesService, } from './graphql-error-pages-service';
|
|
5
|
+
export { GraphQLSiteInfoService, } from './graphql-siteinfo-service';
|
|
6
|
+
export { getSiteRewrite, getSiteRewriteData, normalizeSiteRewrite } from './utils';
|
|
7
|
+
export { SiteResolver } from './site-resolver';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Delimiters for multi-value hostnames
|
|
2
|
+
const DELIMITERS = /\||,|;/g;
|
|
3
|
+
/**
|
|
4
|
+
* Resolves site based on the provided host or site name
|
|
5
|
+
*/
|
|
6
|
+
export class SiteResolver {
|
|
7
|
+
/**
|
|
8
|
+
* @param {SiteInfo[]} sites Array of sites to be used in resolution
|
|
9
|
+
*/
|
|
10
|
+
constructor(sites) {
|
|
11
|
+
this.sites = sites;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve site by host name
|
|
14
|
+
* @param {string} hostName the host name
|
|
15
|
+
* @returns {SiteInfo} the resolved site
|
|
16
|
+
* @throws {Error} if a matching site is not found
|
|
17
|
+
*/
|
|
18
|
+
this.getByHost = (hostName) => {
|
|
19
|
+
for (const [hostname, site] of this.getHostMap()) {
|
|
20
|
+
if (this.matchesPattern(hostName, hostname)) {
|
|
21
|
+
return site;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`Could not resolve site for host ${hostName}`);
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Resolve site by site name
|
|
28
|
+
* @param {string} siteName the site name
|
|
29
|
+
* @returns {SiteInfo} the resolved site
|
|
30
|
+
* @throws {Error} if a matching site is not found
|
|
31
|
+
*/
|
|
32
|
+
this.getByName = (siteName) => {
|
|
33
|
+
const siteInfo = this.sites.find((info) => info.name.toLocaleLowerCase() === siteName.toLocaleLowerCase());
|
|
34
|
+
if (!siteInfo) {
|
|
35
|
+
throw new Error(`Could not resolve site for name ${siteName}`);
|
|
36
|
+
}
|
|
37
|
+
return siteInfo;
|
|
38
|
+
};
|
|
39
|
+
this.getHostMap = () => {
|
|
40
|
+
const map = new Map();
|
|
41
|
+
// First collect unique hostnames.
|
|
42
|
+
// For sites with same hostname defined, priority is given to the first encountered.
|
|
43
|
+
this.sites.forEach((site) => {
|
|
44
|
+
const hostnames = site.hostName
|
|
45
|
+
.replace(/\s/g, '')
|
|
46
|
+
.toLocaleLowerCase()
|
|
47
|
+
.split(DELIMITERS);
|
|
48
|
+
hostnames.forEach((hostname) => {
|
|
49
|
+
if (!map.has(hostname)) {
|
|
50
|
+
map.set(hostname, site);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
// Now order by specificity.
|
|
55
|
+
// This equivalates to sorting from longest to shortest with the assumption
|
|
56
|
+
// that your match is less specific as wildcards are introduced.
|
|
57
|
+
// E.g. order.eu.site.com → *.eu.site.com → *.site.com → *
|
|
58
|
+
// In case of a tie (e.g. *.site.com vs i.site.com), prefer one with less wildcards.
|
|
59
|
+
return new Map(Array.from(map).sort((a, b) => {
|
|
60
|
+
if (a[0].length === b[0].length) {
|
|
61
|
+
return (a[0].match(/\*/g) || []).length - (b[0].match(/\*/g) || []).length;
|
|
62
|
+
}
|
|
63
|
+
return b[0].length - a[0].length;
|
|
64
|
+
}));
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// b[0].match(/\*/g) || []).length
|
|
68
|
+
matchesPattern(hostname, pattern) {
|
|
69
|
+
// dots should be treated as chars
|
|
70
|
+
// stars should be treated as wildcards
|
|
71
|
+
const regExpPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*');
|
|
72
|
+
const regExp = new RegExp(`^${regExpPattern}$`, 'gi');
|
|
73
|
+
return !!hostname.match(regExp);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const SITE_PREFIX = '_site_';
|
|
2
|
+
/**
|
|
3
|
+
* Get a site rewrite path for given pathname
|
|
4
|
+
* @param {string} pathname the pathname
|
|
5
|
+
* @param {SiteRewriteData} data the site data to include in the rewrite
|
|
6
|
+
* @returns {string} the rewrite path
|
|
7
|
+
*/
|
|
8
|
+
export function getSiteRewrite(pathname, data) {
|
|
9
|
+
const path = pathname.startsWith('/') ? pathname : '/' + pathname;
|
|
10
|
+
return `/${SITE_PREFIX}${data.siteName}${path}`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Get site data from the rewrite path
|
|
14
|
+
* @param {string} pathname the pathname
|
|
15
|
+
* @param {string} defaultSiteName the default site name
|
|
16
|
+
* @returns {SiteRewriteData} the site data from the rewrite
|
|
17
|
+
*/
|
|
18
|
+
export function getSiteRewriteData(pathname, defaultSiteName) {
|
|
19
|
+
const data = {
|
|
20
|
+
siteName: defaultSiteName,
|
|
21
|
+
};
|
|
22
|
+
const path = pathname.endsWith('/') ? pathname : pathname + '/';
|
|
23
|
+
const result = path.match(`${SITE_PREFIX}(.*?)\\/`);
|
|
24
|
+
if (result && result[1] !== '') {
|
|
25
|
+
data.siteName = result[1];
|
|
26
|
+
}
|
|
27
|
+
return data;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Normalize a site rewrite path (remove site data)
|
|
31
|
+
* @param {string} pathname the pathname
|
|
32
|
+
* @returns {string} the pathname with site data removed
|
|
33
|
+
*/
|
|
34
|
+
export function normalizeSiteRewrite(pathname) {
|
|
35
|
+
const result = pathname.match(`${SITE_PREFIX}.*?(?:\\/|$)`);
|
|
36
|
+
return result === null ? pathname : pathname.replace(result[0], '');
|
|
37
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Method to parse JSON-formatted environment variables
|
|
3
|
+
* @param {string} envValue - can be undefined when providing values via process.env
|
|
4
|
+
* @param {T} defaultValue - default value
|
|
5
|
+
* @returns {T | string} parsed value
|
|
6
|
+
*/
|
|
7
|
+
export const tryParseEnvValue = (envValue, defaultValue) => {
|
|
8
|
+
if (!envValue) {
|
|
9
|
+
return defaultValue;
|
|
10
|
+
}
|
|
11
|
+
if (envValue.startsWith('{') && envValue.endsWith('}')) {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(envValue);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
console.warn('Parsing of env variable failed');
|
|
17
|
+
console.warn(`Attempted to parse ${envValue}`);
|
|
18
|
+
return defaultValue;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return defaultValue;
|
|
22
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { default as isServer } from './is-server';
|
|
2
|
+
export { resolveUrl, isAbsoluteUrl, isTimeoutError, enforceCors, getAllowedOriginsFromEnv, isRegexOrUrl, areURLSearchParamsEqual, escapeNonSpecialQuestionMarks, mergeURLSearchParams, } from './utils';
|
|
3
|
+
export { tryParseEnvValue } from './env';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A helper to assign timeouts to fetch or other promises
|
|
3
|
+
* Useful in nextjs middleware until fetch.signal is fully supported by Vercel edge functions
|
|
4
|
+
*/
|
|
5
|
+
export default class TimeoutPromise {
|
|
6
|
+
constructor(timeout) {
|
|
7
|
+
this.timeout = timeout;
|
|
8
|
+
this.timeoutId = undefined;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates a timeout promise
|
|
12
|
+
*/
|
|
13
|
+
get start() {
|
|
14
|
+
return new Promise((_, reject) => {
|
|
15
|
+
this.timeoutId = setTimeout(() => {
|
|
16
|
+
const abortError = new Error(`Request timed out, timeout of ${this.timeout}ms is exceeded`);
|
|
17
|
+
abortError.name = 'AbortError';
|
|
18
|
+
reject(abortError);
|
|
19
|
+
}, this.timeout);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Clears the timeout from timeout promise
|
|
24
|
+
*/
|
|
25
|
+
clear() {
|
|
26
|
+
this.timeoutId && clearTimeout(this.timeoutId);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import isServer from './is-server';
|
|
2
|
+
/**
|
|
3
|
+
* note: encodeURIComponent is available via browser (window) or natively in node.js
|
|
4
|
+
* if you use another js engine for server-side rendering you may not have native encodeURIComponent
|
|
5
|
+
* and would then need to install a package for that functionality
|
|
6
|
+
* @param {ParsedUrlQueryInput} params query string parameters
|
|
7
|
+
* @returns {string} query string
|
|
8
|
+
*/
|
|
9
|
+
function getQueryString(params) {
|
|
10
|
+
return Object.keys(params)
|
|
11
|
+
.map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(String(params[k]))}`)
|
|
12
|
+
.join('&');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Resolves a base URL that may contain query string parameters and an additional set of query
|
|
16
|
+
* string parameters into a unified string representation.
|
|
17
|
+
* @param {string} urlBase the base URL that may contain query string parameters
|
|
18
|
+
* @param {ParsedUrlQueryInput} params query string parameters
|
|
19
|
+
* @returns a URL string
|
|
20
|
+
* @throws {RangeError} if the provided url is an empty string
|
|
21
|
+
*/
|
|
22
|
+
export function resolveUrl(urlBase, params = {}) {
|
|
23
|
+
if (!urlBase) {
|
|
24
|
+
throw new RangeError('url must be a non-empty string');
|
|
25
|
+
}
|
|
26
|
+
// This is a better way to work with URLs since it handles different user input
|
|
27
|
+
// edge cases. This works in Node and all browser except IE11.
|
|
28
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/URL
|
|
29
|
+
// TODO: Verify our browser support requirements.
|
|
30
|
+
if (isServer()) {
|
|
31
|
+
const url = new URL(urlBase);
|
|
32
|
+
for (const key in params) {
|
|
33
|
+
if ({}.hasOwnProperty.call(params, key)) {
|
|
34
|
+
url.searchParams.append(key, String(params[key]));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const result = url.toString();
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
const qs = getQueryString(params);
|
|
41
|
+
const result = urlBase.indexOf('?') !== -1 ? `${urlBase}&${qs}` : `${urlBase}?${qs}`;
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
export const isAbsoluteUrl = (url) => {
|
|
45
|
+
if (!url) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (typeof url !== 'string') {
|
|
49
|
+
throw new TypeError('Expected a string');
|
|
50
|
+
}
|
|
51
|
+
return /^[a-z][a-z0-9+.-]*:/.test(url);
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Indicates whether the error is a timeout error
|
|
55
|
+
* @param {unknown} error error
|
|
56
|
+
* @returns {boolean} is timeout error
|
|
57
|
+
*/
|
|
58
|
+
export const isTimeoutError = (error) => {
|
|
59
|
+
var _a;
|
|
60
|
+
return ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 408 || error.name === 'AbortError';
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Converts a string value in a regex pattern allowing wildcard matching
|
|
64
|
+
* @param {string} pattern input with wildcards i.e. site.*.com
|
|
65
|
+
* @returns {string} modified string that can be used as regexp input
|
|
66
|
+
*/
|
|
67
|
+
const convertToWildcardRegex = (pattern) => {
|
|
68
|
+
return ('^' +
|
|
69
|
+
pattern
|
|
70
|
+
.replace(/\//g, '\\/')
|
|
71
|
+
.replace(/\./g, '\\.')
|
|
72
|
+
.replace(/\*/g, '.*') +
|
|
73
|
+
'$');
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Gets allowed origins from JSS_ALLOWED_ORIGINS env variable
|
|
77
|
+
* @returns {string[]} list of allowed origins from JSS_ALLOWED_ORIGINS env variable
|
|
78
|
+
*/
|
|
79
|
+
export const getAllowedOriginsFromEnv = () => process.env.JSS_ALLOWED_ORIGINS
|
|
80
|
+
? process.env.JSS_ALLOWED_ORIGINS.replace(' ', '').split(',')
|
|
81
|
+
: [];
|
|
82
|
+
/**
|
|
83
|
+
* Tests origin from incoming request against allowed origins list that can be
|
|
84
|
+
* set in JSS's JSS_ALLOWED_ORIGINS env variable, passed via allowedOrigins param and/or
|
|
85
|
+
* be already set in Access-Control-Allow-Origin by other logic.
|
|
86
|
+
* Applies Access-Control-Allow-Origin and Access-Control-Allow-Methods on match
|
|
87
|
+
* Also applies Access-Control-Allow-Headers for preflight requests
|
|
88
|
+
* @param {IncomingMessage} req incoming request
|
|
89
|
+
* @param {OutgoingMessage} res response to set CORS headers for
|
|
90
|
+
* @param {string[]} [allowedOrigins] additional list of origins to test against
|
|
91
|
+
* @returns true if incoming origin matches the allowed lists, false when it does not
|
|
92
|
+
*/
|
|
93
|
+
export const enforceCors = (req, res, allowedOrigins) => {
|
|
94
|
+
// origin in not present for non-CORS requests (e.g. server-side) - so we skip the checks
|
|
95
|
+
if (!req.headers.origin) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
// 3 sources of allowed origins are considered:
|
|
99
|
+
// the env value
|
|
100
|
+
const defaultAllowedOrigins = getAllowedOriginsFromEnv();
|
|
101
|
+
// the allowedOrigins prop
|
|
102
|
+
allowedOrigins = defaultAllowedOrigins.concat(allowedOrigins || []);
|
|
103
|
+
// and the existing CORS header, if present (i.e. set by nextjs config)
|
|
104
|
+
const presetCors = res.getHeader('Access-Control-Allow-Origin');
|
|
105
|
+
if (presetCors) {
|
|
106
|
+
allowedOrigins.push(presetCors);
|
|
107
|
+
}
|
|
108
|
+
const origin = req.headers.origin;
|
|
109
|
+
if (origin &&
|
|
110
|
+
allowedOrigins.some((allowedOrigin) => origin === allowedOrigin || new RegExp(convertToWildcardRegex(allowedOrigin)).test(origin))) {
|
|
111
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
112
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE, PUT, PATCH');
|
|
113
|
+
// set the allowed headers for preflight requests
|
|
114
|
+
if (req.method === 'OPTIONS') {
|
|
115
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Determines whether the given input is a regular expression or resembles a URL.
|
|
123
|
+
* @param {string} input - The input string to evaluate.
|
|
124
|
+
* @returns {'regex' | 'url'} - Returns 'url' if the input looks like a URL, otherwise 'regex'.
|
|
125
|
+
*/
|
|
126
|
+
export const isRegexOrUrl = (input) => {
|
|
127
|
+
// Remove the trailing slash.
|
|
128
|
+
input = input.slice(0, -1);
|
|
129
|
+
// Check if the string resembles a URL.
|
|
130
|
+
const isUrlLike = /^\/[a-zA-Z0-9\-\/]+(\?([a-zA-Z0-9\-_]+=[a-zA-Z0-9\-_]+)(&[a-zA-Z0-9\-_]+=[a-zA-Z0-9\-_]+)*)?$/.test(input);
|
|
131
|
+
if (isUrlLike) {
|
|
132
|
+
return 'url';
|
|
133
|
+
}
|
|
134
|
+
// If it doesn't resemble a URL, it's likely a regular expression.
|
|
135
|
+
return 'regex';
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Compares two URLSearchParams objects to determine if they are equal.
|
|
139
|
+
* @param {URLSearchParams} params1 - The first set of URL search parameters.
|
|
140
|
+
* @param {URLSearchParams} params2 - The second set of URL search parameters.
|
|
141
|
+
* @returns {boolean} - Returns true if the parameters are equal, otherwise false.
|
|
142
|
+
*/
|
|
143
|
+
export const areURLSearchParamsEqual = (params1, params2) => {
|
|
144
|
+
// Generates a sorted string representation of URL search parameters.
|
|
145
|
+
const getSortedParamsString = (params) => {
|
|
146
|
+
return [...params.entries()]
|
|
147
|
+
.sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
|
|
148
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
149
|
+
.join('&');
|
|
150
|
+
};
|
|
151
|
+
// Compare the sorted strings of both parameter sets.
|
|
152
|
+
return getSortedParamsString(params1) === getSortedParamsString(params2);
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Escapes non-special "?" characters in a string or regex.
|
|
156
|
+
* - For regular strings, it escapes all unescaped "?" characters by adding a backslash (`\`).
|
|
157
|
+
* - For regex patterns (strings enclosed in `/.../`), it analyzes each "?" to determine if it has special meaning
|
|
158
|
+
* (e.g., `?` in `(abc)?`, `.*?`, `(?!...)`) or is just a literal character. Only literal "?" characters are escaped.
|
|
159
|
+
* @param {string} input - The input string or regex pattern.
|
|
160
|
+
* @returns {string} - The modified string or regex with non-special "?" characters escaped.
|
|
161
|
+
*/
|
|
162
|
+
export const escapeNonSpecialQuestionMarks = (input) => {
|
|
163
|
+
const regexPattern = /(?<!\\)\?/g; // Match unescaped "?" characters
|
|
164
|
+
const negativeLookaheadPattern = /\(\?!$/; // Detect the start of a Negative Lookahead pattern
|
|
165
|
+
const specialRegexSymbols = /[.*+)\[\]|\(]$/; // Check for special regex symbols before "?"
|
|
166
|
+
let result = '';
|
|
167
|
+
let lastIndex = 0;
|
|
168
|
+
let match;
|
|
169
|
+
while ((match = regexPattern.exec(input)) !== null) {
|
|
170
|
+
const index = match.index; // Position of the "?" in the string
|
|
171
|
+
const before = input.slice(lastIndex, index); // Context before the "?"
|
|
172
|
+
// Check if "?" is part of a Negative Lookahead
|
|
173
|
+
const isNegativeLookahead = negativeLookaheadPattern.test(before.slice(-3));
|
|
174
|
+
// Check if "?" follows a special regex symbol
|
|
175
|
+
const isSpecialRegexSymbol = specialRegexSymbols.test(before.slice(-1));
|
|
176
|
+
if (isNegativeLookahead || isSpecialRegexSymbol) {
|
|
177
|
+
// If it's a special case, keep the "?" as is
|
|
178
|
+
result += input.slice(lastIndex, index + 1);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// Otherwise, escape the "?"
|
|
182
|
+
result += input.slice(lastIndex, index) + '\\?';
|
|
183
|
+
}
|
|
184
|
+
lastIndex = index + 1; // Move to the next part of the string
|
|
185
|
+
}
|
|
186
|
+
// Append the remaining part of the string
|
|
187
|
+
result += input.slice(lastIndex);
|
|
188
|
+
return result;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Merges two URLSearchParams objects. If both objects contain the same key, the value from the second object overrides the first.
|
|
192
|
+
* @param {URLSearchParams} params1 - The first set of URL search parameters.
|
|
193
|
+
* @param {URLSearchParams} params2 - The second set of URL search parameters.
|
|
194
|
+
* @returns {string} - A string representation of the merged URL search parameters.
|
|
195
|
+
*/
|
|
196
|
+
export const mergeURLSearchParams = (params1, params2) => {
|
|
197
|
+
const merged = new URLSearchParams();
|
|
198
|
+
// Add all keys and values from the first object.
|
|
199
|
+
for (const [key, value] of params1.entries()) {
|
|
200
|
+
merged.set(key, value);
|
|
201
|
+
}
|
|
202
|
+
// Add all keys and values from the second object, replacing existing ones.
|
|
203
|
+
for (const [key, value] of params2.entries()) {
|
|
204
|
+
merged.set(key, value);
|
|
205
|
+
}
|
|
206
|
+
return merged.toString();
|
|
207
|
+
};
|
package/editing.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types/editing/index';
|
package/editing.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/cjs/editing/index');
|
package/graphql.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types/graphql/index';
|
package/graphql.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/cjs/graphql/index');
|
package/i18n.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types/i18n/index';
|
package/i18n.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/cjs/i18n/index');
|
package/layout.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types/layout/index';
|
package/layout.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/cjs/layout/index');
|
package/media.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types/media/index';
|
package/media.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/cjs/media/index');
|