@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.
Files changed (160) hide show
  1. package/LICENSE.txt +202 -0
  2. package/README.md +11 -0
  3. package/dist/cjs/cache-client.js +54 -0
  4. package/dist/cjs/constants.js +13 -0
  5. package/dist/cjs/data-fetcher.js +33 -0
  6. package/dist/cjs/debug.js +43 -0
  7. package/dist/cjs/editing/component-library.js +104 -0
  8. package/dist/cjs/editing/graphql-editing-service.js +186 -0
  9. package/dist/cjs/editing/index.js +23 -0
  10. package/dist/cjs/editing/models.js +23 -0
  11. package/dist/cjs/editing/rest-component-layout-service.js +76 -0
  12. package/dist/cjs/editing/utils.js +86 -0
  13. package/dist/cjs/graphql/app-root-query.js +73 -0
  14. package/dist/cjs/graphql/graphql-edge-proxy.js +21 -0
  15. package/dist/cjs/graphql/index.js +13 -0
  16. package/dist/cjs/graphql/search-service.js +61 -0
  17. package/dist/cjs/graphql-request-client.js +152 -0
  18. package/dist/cjs/i18n/dictionary-service.js +45 -0
  19. package/dist/cjs/i18n/graphql-dictionary-service.js +136 -0
  20. package/dist/cjs/i18n/index.js +7 -0
  21. package/dist/cjs/index.js +55 -0
  22. package/dist/cjs/layout/content-styles.js +70 -0
  23. package/dist/cjs/layout/graphql-layout-service.js +86 -0
  24. package/dist/cjs/layout/index.js +24 -0
  25. package/dist/cjs/layout/layout-service.js +9 -0
  26. package/dist/cjs/layout/models.js +35 -0
  27. package/dist/cjs/layout/themes.js +74 -0
  28. package/dist/cjs/layout/utils.js +110 -0
  29. package/dist/cjs/media/index.js +38 -0
  30. package/dist/cjs/media/media-api.js +96 -0
  31. package/dist/cjs/models.js +2 -0
  32. package/dist/cjs/native-fetcher.js +200 -0
  33. package/dist/cjs/personalize/graphql-personalize-service.js +114 -0
  34. package/dist/cjs/personalize/index.js +14 -0
  35. package/dist/cjs/personalize/layout-personalizer.js +97 -0
  36. package/dist/cjs/personalize/utils.js +136 -0
  37. package/dist/cjs/site/graphql-error-pages-service.js +89 -0
  38. package/dist/cjs/site/graphql-redirects-service.js +105 -0
  39. package/dist/cjs/site/graphql-robots-service.js +83 -0
  40. package/dist/cjs/site/graphql-siteinfo-service.js +107 -0
  41. package/dist/cjs/site/graphql-sitemap-service.js +93 -0
  42. package/dist/cjs/site/index.js +22 -0
  43. package/dist/cjs/site/site-resolver.js +79 -0
  44. package/dist/cjs/site/utils.js +43 -0
  45. package/dist/cjs/utils/env.js +26 -0
  46. package/dist/cjs/utils/index.js +20 -0
  47. package/dist/cjs/utils/is-server.js +10 -0
  48. package/dist/cjs/utils/timeout-promise.js +31 -0
  49. package/dist/cjs/utils/utils.js +222 -0
  50. package/dist/esm/cache-client.js +50 -0
  51. package/dist/esm/constants.js +10 -0
  52. package/dist/esm/data-fetcher.js +28 -0
  53. package/dist/esm/debug.js +36 -0
  54. package/dist/esm/editing/component-library.js +98 -0
  55. package/dist/esm/editing/graphql-editing-service.js +179 -0
  56. package/dist/esm/editing/index.js +5 -0
  57. package/dist/esm/editing/models.js +20 -0
  58. package/dist/esm/editing/rest-component-layout-service.js +72 -0
  59. package/dist/esm/editing/utils.js +76 -0
  60. package/dist/esm/graphql/app-root-query.js +69 -0
  61. package/dist/esm/graphql/graphql-edge-proxy.js +16 -0
  62. package/dist/esm/graphql/index.js +4 -0
  63. package/dist/esm/graphql/search-service.js +57 -0
  64. package/dist/esm/graphql-request-client.js +144 -0
  65. package/dist/esm/i18n/dictionary-service.js +41 -0
  66. package/dist/esm/i18n/graphql-dictionary-service.js +129 -0
  67. package/dist/esm/i18n/index.js +2 -0
  68. package/dist/esm/index.js +9 -0
  69. package/dist/esm/layout/content-styles.js +62 -0
  70. package/dist/esm/layout/graphql-layout-service.js +79 -0
  71. package/dist/esm/layout/index.js +6 -0
  72. package/dist/esm/layout/layout-service.js +5 -0
  73. package/dist/esm/layout/models.js +32 -0
  74. package/dist/esm/layout/themes.js +69 -0
  75. package/dist/esm/layout/utils.js +102 -0
  76. package/dist/esm/media/index.js +2 -0
  77. package/dist/esm/media/media-api.js +86 -0
  78. package/dist/esm/models.js +1 -0
  79. package/dist/esm/native-fetcher.js +193 -0
  80. package/dist/esm/personalize/graphql-personalize-service.js +107 -0
  81. package/dist/esm/personalize/index.js +3 -0
  82. package/dist/esm/personalize/layout-personalizer.js +92 -0
  83. package/dist/esm/personalize/utils.js +128 -0
  84. package/dist/esm/site/graphql-error-pages-service.js +82 -0
  85. package/dist/esm/site/graphql-redirects-service.js +98 -0
  86. package/dist/esm/site/graphql-robots-service.js +76 -0
  87. package/dist/esm/site/graphql-siteinfo-service.js +100 -0
  88. package/dist/esm/site/graphql-sitemap-service.js +86 -0
  89. package/dist/esm/site/index.js +7 -0
  90. package/dist/esm/site/site-resolver.js +75 -0
  91. package/dist/esm/site/utils.js +37 -0
  92. package/dist/esm/utils/env.js +22 -0
  93. package/dist/esm/utils/index.js +3 -0
  94. package/dist/esm/utils/is-server.js +8 -0
  95. package/dist/esm/utils/timeout-promise.js +28 -0
  96. package/dist/esm/utils/utils.js +207 -0
  97. package/editing.d.ts +1 -0
  98. package/editing.js +1 -0
  99. package/graphql.d.ts +1 -0
  100. package/graphql.js +1 -0
  101. package/i18n.d.ts +1 -0
  102. package/i18n.js +1 -0
  103. package/layout.d.ts +1 -0
  104. package/layout.js +1 -0
  105. package/media.d.ts +1 -0
  106. package/media.js +1 -0
  107. package/package.json +76 -0
  108. package/personalize.d.ts +1 -0
  109. package/personalize.js +1 -0
  110. package/site.d.ts +1 -0
  111. package/site.js +1 -0
  112. package/types/cache-client.d.ts +64 -0
  113. package/types/constants.d.ts +7 -0
  114. package/types/data-fetcher.d.ts +34 -0
  115. package/types/debug.d.ts +26 -0
  116. package/types/editing/component-library.d.ts +48 -0
  117. package/types/editing/graphql-editing-service.d.ts +90 -0
  118. package/types/editing/index.d.ts +6 -0
  119. package/types/editing/models.d.ts +52 -0
  120. package/types/editing/rest-component-layout-service.d.ts +100 -0
  121. package/types/editing/utils.d.ts +70 -0
  122. package/types/graphql/app-root-query.d.ts +32 -0
  123. package/types/graphql/graphql-edge-proxy.d.ts +15 -0
  124. package/types/graphql/index.d.ts +4 -0
  125. package/types/graphql/search-service.d.ts +95 -0
  126. package/types/graphql-request-client.d.ts +159 -0
  127. package/types/i18n/dictionary-service.d.ts +56 -0
  128. package/types/i18n/graphql-dictionary-service.d.ts +94 -0
  129. package/types/i18n/index.d.ts +2 -0
  130. package/types/index.d.ts +8 -0
  131. package/types/layout/content-styles.d.ts +18 -0
  132. package/types/layout/graphql-layout-service.d.ts +58 -0
  133. package/types/layout/index.d.ts +6 -0
  134. package/types/layout/layout-service.d.ts +19 -0
  135. package/types/layout/models.d.ts +145 -0
  136. package/types/layout/themes.d.ts +11 -0
  137. package/types/layout/utils.d.ts +40 -0
  138. package/types/media/index.d.ts +2 -0
  139. package/types/media/media-api.d.ts +55 -0
  140. package/types/models.d.ts +6 -0
  141. package/types/native-fetcher.d.ts +121 -0
  142. package/types/personalize/graphql-personalize-service.d.ts +80 -0
  143. package/types/personalize/index.d.ts +3 -0
  144. package/types/personalize/layout-personalizer.d.ts +27 -0
  145. package/types/personalize/utils.d.ts +69 -0
  146. package/types/site/graphql-error-pages-service.d.ts +57 -0
  147. package/types/site/graphql-redirects-service.d.ts +68 -0
  148. package/types/site/graphql-robots-service.d.ts +49 -0
  149. package/types/site/graphql-siteinfo-service.d.ts +70 -0
  150. package/types/site/graphql-sitemap-service.d.ts +55 -0
  151. package/types/site/index.d.ts +7 -0
  152. package/types/site/site-resolver.d.ts +27 -0
  153. package/types/site/utils.d.ts +24 -0
  154. package/types/utils/env.d.ts +7 -0
  155. package/types/utils/index.d.ts +3 -0
  156. package/types/utils/is-server.d.ts +6 -0
  157. package/types/utils/timeout-promise.d.ts +17 -0
  158. package/types/utils/utils.d.ts +71 -0
  159. package/utils.d.ts +1 -0
  160. 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,8 @@
1
+ /**
2
+ * Determines whether the current execution context is server-side
3
+ * @returns true if executing server-side
4
+ */
5
+ function isServer() {
6
+ return !(typeof window !== 'undefined' && window.document);
7
+ }
8
+ export default isServer;
@@ -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');