@sitecore-content-sdk/nextjs 2.1.1 → 2.2.0-canary.20260527110239

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 (43) hide show
  1. package/dist/cjs/cache/sitecore-cache-tags.js +130 -0
  2. package/dist/cjs/cache/sitecore-edge-webhook-revalidation.js +67 -0
  3. package/dist/cjs/cache/sitecore-page-cache-tags.js +83 -0
  4. package/dist/cjs/debug.js +1 -1
  5. package/dist/cjs/editing/utils.js +6 -10
  6. package/dist/cjs/index.js +9 -1
  7. package/dist/cjs/proxy/redirects-proxy.js +55 -23
  8. package/dist/cjs/route-handler/index.js +3 -1
  9. package/dist/cjs/route-handler/sitecore-revalidate-route-handler.js +124 -0
  10. package/dist/cjs/tools/generate-map.js +120 -115
  11. package/dist/cjs/tools/templating/utils.js +22 -10
  12. package/dist/esm/cache/sitecore-cache-tags.js +119 -0
  13. package/dist/esm/cache/sitecore-edge-webhook-revalidation.js +63 -0
  14. package/dist/esm/cache/sitecore-page-cache-tags.js +80 -0
  15. package/dist/esm/debug.js +2 -2
  16. package/dist/esm/editing/utils.js +6 -10
  17. package/dist/esm/index.js +6 -0
  18. package/dist/esm/proxy/redirects-proxy.js +55 -23
  19. package/dist/esm/route-handler/index.js +1 -0
  20. package/dist/esm/route-handler/sitecore-revalidate-route-handler.js +118 -0
  21. package/dist/esm/tools/generate-map.js +119 -115
  22. package/dist/esm/tools/templating/utils.js +21 -10
  23. package/package.json +10 -10
  24. package/types/cache/sitecore-cache-tags.d.ts +108 -0
  25. package/types/cache/sitecore-cache-tags.d.ts.map +1 -0
  26. package/types/cache/sitecore-edge-webhook-revalidation.d.ts +54 -0
  27. package/types/cache/sitecore-edge-webhook-revalidation.d.ts.map +1 -0
  28. package/types/cache/sitecore-page-cache-tags.d.ts +42 -0
  29. package/types/cache/sitecore-page-cache-tags.d.ts.map +1 -0
  30. package/types/debug.d.ts.map +1 -1
  31. package/types/editing/utils.d.ts.map +1 -1
  32. package/types/index.d.ts +2 -0
  33. package/types/index.d.ts.map +1 -1
  34. package/types/proxy/redirects-proxy.d.ts +18 -0
  35. package/types/proxy/redirects-proxy.d.ts.map +1 -1
  36. package/types/route-handler/index.d.ts +1 -0
  37. package/types/route-handler/index.d.ts.map +1 -1
  38. package/types/route-handler/sitecore-revalidate-route-handler.d.ts +68 -0
  39. package/types/route-handler/sitecore-revalidate-route-handler.d.ts.map +1 -0
  40. package/types/tools/generate-map.d.ts +11 -6
  41. package/types/tools/generate-map.d.ts.map +1 -1
  42. package/types/tools/templating/utils.d.ts +11 -0
  43. package/types/tools/templating/utils.d.ts.map +1 -1
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SITECORE_CONTENT_CACHE_TAG_PREFIX = void 0;
4
+ exports.sanitizeSitecoreCacheTagSegment = sanitizeSitecoreCacheTagSegment;
5
+ exports.normalizeSitecoreItemIdForCacheTag = normalizeSitecoreItemIdForCacheTag;
6
+ exports.buildSitecoreRouteCacheTag = buildSitecoreRouteCacheTag;
7
+ exports.buildSitecoreItemCacheTag = buildSitecoreItemCacheTag;
8
+ exports.buildSitecoreDictionaryCacheTag = buildSitecoreDictionaryCacheTag;
9
+ exports.buildSitecoreDictionaryCacheTagsFromSites = buildSitecoreDictionaryCacheTagsFromSites;
10
+ exports.buildSitecoreItemCacheTagFromRouteData = buildSitecoreItemCacheTagFromRouteData;
11
+ exports.dedupeSitecoreCacheTags = dedupeSitecoreCacheTags;
12
+ /**
13
+ * Stable cache tag strings for Sitecore content (Next.js `cacheTag`, `unstable_cache` tags, `revalidateTag`).
14
+ * Tags are deterministic for the same logical inputs so app code and invalidation webhooks stay aligned.
15
+ * @internal
16
+ */
17
+ exports.SITECORE_CONTENT_CACHE_TAG_PREFIX = 'sc';
18
+ /**
19
+ * Sanitizes a single segment for use inside Sitecore cache tags.
20
+ * Colons are reserved as delimiters; slashes and whitespace are normalized for stable keys.
21
+ * @param {string} value - Raw segment (site name, locale, path segment, etc.).
22
+ * @internal
23
+ */
24
+ function sanitizeSitecoreCacheTagSegment(value) {
25
+ return value.trim().toLowerCase().replace(/[/:\s]+/g, '_');
26
+ }
27
+ /**
28
+ * Normalizes a Sitecore item GUID for use in cache tags (lowercase, no braces).
29
+ * @param {string} itemId - Sitecore item id or GUID string.
30
+ * @internal
31
+ */
32
+ function normalizeSitecoreItemIdForCacheTag(itemId) {
33
+ return itemId.trim().toLowerCase().replace(/[{}]/g, '');
34
+ }
35
+ /**
36
+ * Tag for a resolved route (site + language + logical path). Use for URL-level invalidation.
37
+ * @param {BuildSitecoreRouteCacheTagParams} params - Site, locale, and optional path segments.
38
+ * @internal
39
+ */
40
+ function buildSitecoreRouteCacheTag(params) {
41
+ var _a;
42
+ const site = sanitizeSitecoreCacheTagSegment(params.site);
43
+ const locale = sanitizeSitecoreCacheTagSegment(params.locale);
44
+ const segments = ((_a = params.pathSegments) !== null && _a !== void 0 ? _a : []).map((s) => sanitizeSitecoreCacheTagSegment(s));
45
+ const pathKey = segments.length > 0 ? segments.join('/') : '_';
46
+ return `${exports.SITECORE_CONTENT_CACHE_TAG_PREFIX}:route:${site}:${locale}:${pathKey}`;
47
+ }
48
+ /**
49
+ * Tag for a layout/route item (and anything else keyed the same way). Use for item-level invalidation.
50
+ * @param {BuildSitecoreItemCacheTagParams} params - Item id, locale, and optional published version.
51
+ * @internal
52
+ */
53
+ function buildSitecoreItemCacheTag(params) {
54
+ const id = normalizeSitecoreItemIdForCacheTag(params.itemId);
55
+ const locale = sanitizeSitecoreCacheTagSegment(params.locale);
56
+ const ver = params.version !== undefined && Number.isFinite(params.version)
57
+ ? `v${Math.trunc(params.version)}`
58
+ : 'latest';
59
+ return `${exports.SITECORE_CONTENT_CACHE_TAG_PREFIX}:item:${id}:${locale}:${ver}`;
60
+ }
61
+ /**
62
+ * Tag for dictionary data scoped to site + locale.
63
+ * @param {BuildSitecoreDictionaryCacheTagParams} params - Site and locale for the dictionary fetch.
64
+ * @public
65
+ */
66
+ function buildSitecoreDictionaryCacheTag(params) {
67
+ const site = sanitizeSitecoreCacheTagSegment(params.site);
68
+ const locale = sanitizeSitecoreCacheTagSegment(params.locale);
69
+ return `${exports.SITECORE_CONTENT_CACHE_TAG_PREFIX}:dict:${site}:${locale}`;
70
+ }
71
+ /**
72
+ * Builds deduplicated dictionary cache tags from a sites list.
73
+ * @param {BuildSitecoreDictionaryCacheTagsFromSitesParams} params - Sites list and fallback locale.
74
+ * @internal
75
+ */
76
+ function buildSitecoreDictionaryCacheTagsFromSites(params) {
77
+ var _a;
78
+ const seen = new Set();
79
+ const out = [];
80
+ const push = (tag) => {
81
+ if (!seen.has(tag)) {
82
+ seen.add(tag);
83
+ out.push(tag);
84
+ }
85
+ };
86
+ for (const site of params.sites) {
87
+ const locale = ((_a = site.language) === null || _a === void 0 ? void 0 : _a.trim()) ? site.language : params.baseLocale;
88
+ push(buildSitecoreDictionaryCacheTag({ site: site.name, locale }));
89
+ }
90
+ return out;
91
+ }
92
+ /**
93
+ * Builds an item cache tag from Sitecore layout route data when `itemId` is present.
94
+ * Prefers `itemLanguage` from Sitecore when set; otherwise uses `fallbackLocale`.
95
+ * Accepts the same `RouteData` shape returned by the layout service (e.g. `page.layout.sitecore.route`,
96
+ * which is `RouteData | null`) or `undefined` when the page did not resolve.
97
+ * @param {RouteData | null | undefined} route - Route node from layout (item id, language, version).
98
+ * @param {string} fallbackLocale - Locale used when `route.itemLanguage` is not set.
99
+ * @returns `null` when `route` is missing or `route.itemId` is not set.
100
+ * @internal
101
+ */
102
+ function buildSitecoreItemCacheTagFromRouteData(route, fallbackLocale) {
103
+ if (!(route === null || route === void 0 ? void 0 : route.itemId)) {
104
+ return null;
105
+ }
106
+ const locale = route.itemLanguage
107
+ ? sanitizeSitecoreCacheTagSegment(route.itemLanguage)
108
+ : sanitizeSitecoreCacheTagSegment(fallbackLocale);
109
+ const id = normalizeSitecoreItemIdForCacheTag(route.itemId);
110
+ const ver = route.itemVersion !== undefined && Number.isFinite(route.itemVersion)
111
+ ? `v${Math.trunc(route.itemVersion)}`
112
+ : 'latest';
113
+ return `${exports.SITECORE_CONTENT_CACHE_TAG_PREFIX}:item:${id}:${locale}:${ver}`;
114
+ }
115
+ /**
116
+ * Deduplicates tag strings while preserving first-seen order.
117
+ * @param {string[]} tags - Tag strings possibly containing duplicates.
118
+ * @internal
119
+ */
120
+ function dedupeSitecoreCacheTags(tags) {
121
+ const seen = new Set();
122
+ const out = [];
123
+ for (const t of tags) {
124
+ if (!seen.has(t)) {
125
+ seen.add(t);
126
+ out.push(t);
127
+ }
128
+ }
129
+ return out;
130
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractSitecoreEdgeContentId = extractSitecoreEdgeContentId;
4
+ exports.collectSitecoreTagsFromEdgeRevalidateRequestBody = collectSitecoreTagsFromEdgeRevalidateRequestBody;
5
+ const sitecore_cache_tags_1 = require("./sitecore-cache-tags");
6
+ /**
7
+ * Strips Experience Edge style suffixes from an `identifier` so the value can be used as an item id in cache tags.
8
+ * Handles `{GUID}`, `{GUID}-media`, `{GUID}-layout` style strings.
9
+ * @param {string} identifier - Raw identifier from a webhook update row.
10
+ * @internal
11
+ */
12
+ function extractSitecoreEdgeContentId(identifier) {
13
+ if (!identifier || typeof identifier !== 'string') {
14
+ return '';
15
+ }
16
+ const trimmed = identifier.trim();
17
+ return trimmed.replace(/-(?:media|layout)$/i, '');
18
+ }
19
+ const FULL_TAG_PREFIX = `${sitecore_cache_tags_1.SITECORE_CONTENT_CACHE_TAG_PREFIX}:`;
20
+ /**
21
+ * @param {string} value - Candidate tag string from a webhook body.
22
+ * @returns True when `value` is already a full `sc:` content cache tag.
23
+ */
24
+ function isFullSitecoreContentCacheTag(value) {
25
+ return value.startsWith(FULL_TAG_PREFIX);
26
+ }
27
+ /**
28
+ * Maps an Experience Edge webhook JSON body to Content SDK cache tag strings used by
29
+ * {@link collectSitecorePageCacheTags} / {@link buildSitecoreItemCacheTag} (`sc:item:...`), so
30
+ * `revalidateTag` matches tags registered during cached reads.
31
+ * **`updates`** rows resolve to **`sc:item:…`** (locale from `entity_culture` or `defaultLocale`). **`tags`**: full `sc:` strings pass through; bare ids become **`sc:item:…`** with `defaultLocale`. Route/variant tags are not inferred.
32
+ * @param {SitecoreEdgeRevalidateRequestBody | null | undefined} body - Webhook JSON body (tags and/or updates).
33
+ * @param {CollectSitecoreTagsFromEdgeBodyOptions} options - Default locale when culture is missing on an update or bare tag.
34
+ * @internal
35
+ */
36
+ function collectSitecoreTagsFromEdgeRevalidateRequestBody(body, options) {
37
+ var _a, _b, _c, _d;
38
+ const { defaultLocale } = options;
39
+ const out = [];
40
+ for (const raw of (_a = body === null || body === void 0 ? void 0 : body.tags) !== null && _a !== void 0 ? _a : []) {
41
+ if (typeof raw !== 'string') {
42
+ continue;
43
+ }
44
+ const s = raw.trim();
45
+ if (!s) {
46
+ continue;
47
+ }
48
+ if (isFullSitecoreContentCacheTag(s)) {
49
+ out.push(s);
50
+ }
51
+ else {
52
+ const id = extractSitecoreEdgeContentId(s);
53
+ if (id) {
54
+ out.push((0, sitecore_cache_tags_1.buildSitecoreItemCacheTag)({ itemId: id, locale: defaultLocale }));
55
+ }
56
+ }
57
+ }
58
+ for (const u of (_b = body === null || body === void 0 ? void 0 : body.updates) !== null && _b !== void 0 ? _b : []) {
59
+ const id = extractSitecoreEdgeContentId((_c = u === null || u === void 0 ? void 0 : u.identifier) !== null && _c !== void 0 ? _c : '');
60
+ if (!id) {
61
+ continue;
62
+ }
63
+ const locale = ((_d = u === null || u === void 0 ? void 0 : u.entity_culture) === null || _d === void 0 ? void 0 : _d.trim()) || defaultLocale;
64
+ out.push((0, sitecore_cache_tags_1.buildSitecoreItemCacheTag)({ itemId: id, locale }));
65
+ }
66
+ return (0, sitecore_cache_tags_1.dedupeSitecoreCacheTags)(out).filter(Boolean);
67
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.collectSitecorePageCacheTags = collectSitecorePageCacheTags;
4
+ const personalize_1 = require("@sitecore-content-sdk/content/personalize");
5
+ const sitecore_cache_tags_1 = require("./sitecore-cache-tags");
6
+ /** @param {string} pathname - Raw pathname (may omit leading slash). */
7
+ function normalizePathname(pathname) {
8
+ const trimmed = pathname.trim() || '/';
9
+ return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
10
+ }
11
+ /**
12
+ * Trims leading and trailing `/` from a single path segment without regex (linear time; avoids ReDoS flags on path-derived input).
13
+ * @param {string} part - One App Router catch-all path segment.
14
+ */
15
+ function trimSlashes(part) {
16
+ let start = 0;
17
+ let end = part.length;
18
+ while (start < end && part[start] === '/') {
19
+ start++;
20
+ }
21
+ while (end > start && part[end - 1] === '/') {
22
+ end--;
23
+ }
24
+ return part.slice(start, end);
25
+ }
26
+ /**
27
+ * Normalizes App Router catch-all `path` segments the same way as `SitecoreClient.parsePath` for a
28
+ * string array (leading slash, trim segments, drop empty `/` parts).
29
+ * @param {string[]} path - App Router catch-all segments.
30
+ */
31
+ function personalizedPathnameFromPathSegments(path) {
32
+ if (path.length === 0) {
33
+ return '/';
34
+ }
35
+ return `/${path
36
+ .filter((part) => part !== '/')
37
+ .map((part) => trimSlashes(part))
38
+ .join('/')}`;
39
+ }
40
+ /**
41
+ * Route segments after removing personalization rewrite markers, for stable route-level tags.
42
+ * @param {string} personalizedPathname - Pathname that may include personalization rewrite segments.
43
+ */
44
+ function routeSegmentsFromPersonalizedPathname(personalizedPathname) {
45
+ const pathname = normalizePathname(personalizedPathname);
46
+ const n = (0, personalize_1.normalizePersonalizedRewrite)(pathname);
47
+ if (!n || n === '/') {
48
+ return [];
49
+ }
50
+ const noLead = n.startsWith('/') ? n.slice(1) : n;
51
+ return noLead.split('/').filter(Boolean);
52
+ }
53
+ /**
54
+ * Builds cache tags for a Sitecore page read (`getPage`): the route tag and the route's item tag.
55
+ * Dictionary data is not part of `getPage`; tag dictionary fetches separately (for example with
56
+ * `buildSitecoreDictionaryCacheTag` on a dedicated `use cache` helper).
57
+ *
58
+ * Registers **`sc:route:…`** and **`sc:item:…`** (when layout has `itemId`). Edge-style webhooks emit
59
+ * item ids, which the Sitecore revalidate route handler maps to **`sc:item:…`**; route tags are only
60
+ * invalidated when callers send the full `sc:route:…` strings in the `tags[]` array of the same revalidate request.
61
+ *
62
+ * Personalization variants are isolated naturally by URL path (each variant rewrite yields a distinct
63
+ * Cache Components key) so no `sc:pvv:…` tag is added here. If a personalize-specific webhook is wired
64
+ * up later, build that tag in the dedicated helper and add it on top of these.
65
+ * @param {CollectSitecorePageCacheTagsParams} params - Site, locale, path or personalized pathname, and route metadata.
66
+ * @public
67
+ */
68
+ function collectSitecorePageCacheTags(params) {
69
+ var _a, _b;
70
+ const pathnameInput = params.personalizedPathname !== undefined
71
+ ? params.personalizedPathname
72
+ : personalizedPathnameFromPathSegments((_a = params.path) !== null && _a !== void 0 ? _a : []);
73
+ const pathname = normalizePathname(pathnameInput);
74
+ const pathSegments = routeSegmentsFromPersonalizedPathname(pathname);
75
+ return (0, sitecore_cache_tags_1.dedupeSitecoreCacheTags)([
76
+ (0, sitecore_cache_tags_1.buildSitecoreRouteCacheTag)({
77
+ site: params.site,
78
+ locale: params.locale,
79
+ pathSegments,
80
+ }),
81
+ (_b = (0, sitecore_cache_tags_1.buildSitecoreItemCacheTagFromRouteData)(params.route, params.locale)) !== null && _b !== void 0 ? _b : '',
82
+ ]).filter(Boolean);
83
+ }
package/dist/cjs/debug.js CHANGED
@@ -7,5 +7,5 @@ const search_1 = require("@sitecore-content-sdk/react/search");
7
7
  * Unified debug object containing all debug namespaces from referenced content-sdk packages.
8
8
  * @public
9
9
  */
10
- const debug = Object.assign(Object.assign(Object.assign({}, core_1.debug), content_1.debug), { search: search_1.debug });
10
+ const debug = Object.assign(Object.assign(Object.assign({}, core_1.debug), content_1.debug), { search: search_1.debug, revalidate: (0, core_1.debugModule)(`${core_1.debugNamespace}:revalidate`) });
11
11
  exports.default = debug;
@@ -133,15 +133,9 @@ exports.getPreviewCookies = getPreviewCookies;
133
133
  * @returns {string[]} list of required parameters for validation
134
134
  */
135
135
  const getRequiredEditingParamsList = (mode) => {
136
- const editingRequiredParams = ['sc_site', 'sc_itemid', 'sc_lang', 'route', 'mode'];
137
- const componentRequiredParams = [
138
- 'sc_site',
139
- 'sc_itemid',
140
- 'sc_renderingId',
141
- 'sc_uid',
142
- 'sc_lang',
143
- 'mode',
144
- ];
136
+ const baseRequiredParams = ['sc_site', 'sc_itemid', 'sc_lang'];
137
+ const editingRequiredParams = [...baseRequiredParams, 'route', 'mode'];
138
+ const componentRequiredParams = [...baseRequiredParams, 'sc_uid', 'mode'];
145
139
  return (0, editing_1.isDesignLibraryMode)(mode) ? componentRequiredParams : editingRequiredParams;
146
140
  };
147
141
  exports.getRequiredEditingParamsList = getRequiredEditingParamsList;
@@ -212,7 +206,9 @@ const getEditingRequestHtml = async (requestUrl, propagatedQsParams, propagatedH
212
206
  // We need to handle not found error provided by Vercel
213
207
  // for `fallback: false` pages
214
208
  // Or preview content is not found or access is denied
215
- if (err.response.status === 404 || err.response.status === 403) {
209
+ if (err.response.status === 404 ||
210
+ err.response.status === 403 ||
211
+ err.response.status === 500) {
216
212
  return err.response;
217
213
  }
218
214
  throw err;
package/dist/cjs/index.js CHANGED
@@ -37,7 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.BYOCWrapper = exports.FEaaSWrapper = exports.BYOCServerWrapper = exports.BYOCClientWrapper = exports.FEaaSServerWrapper = exports.FEaaSClientWrapper = exports.NextImage = exports.Placeholder = exports.RichText = exports.Link = exports.useComponentProps = exports.ComponentPropsContext = exports.ComponentPropsReactContext = exports.ComponentPropsService = exports.normalizeSiteRewrite = exports.getSiteRewriteData = exports.getSiteRewrite = exports.SiteInfoService = exports.SiteResolver = exports.RobotsService = exports.ErrorPagesService = exports.SitemapXmlService = exports.REDIRECT_TYPE_SERVER_TRANSFER = exports.REDIRECT_TYPE_302 = exports.REDIRECT_TYPE_301 = exports.RedirectsService = exports.SitePathService = exports.PersonalizeService = exports.CdpHelper = exports.normalizePersonalizedRewrite = exports.getGroomedVariantIds = exports.getPersonalizedRewriteData = exports.getPersonalizedRewrite = exports.personalizeLayout = exports.DictionaryService = exports.mediaApi = exports.ComponentLayoutService = exports.ErrorPage = exports.RenderingType = exports.EditMode = exports.getContentStylesheetLink = exports.getFieldValue = exports.getChildPlaceholder = exports.LayoutService = exports.LayoutServicePageState = exports.MemoryCacheClient = exports.enableDebug = exports.NativeDataFetcher = exports.constants = exports.debug = void 0;
40
- exports.setCachedPageParams = exports.getCachedPageParams = exports.DesignLibraryApp = exports.analyticsProxyAdapter = exports.personalizeProxyAdapter = exports.initContentSdk = exports.renderEmptyPlaceholder = exports.AppPlaceholder = exports.ClientEditingChromesUpdate = exports.Form = exports.EditingScripts = exports.withEmptyFieldEditingComponent = exports.withFieldMetadata = exports.withDatasourceCheck = exports.withPlaceholder = exports.withAppPlaceholder = exports.withEditorChromes = exports.useSitecore = exports.withSitecore = exports.SitecoreProviderReactContext = exports.SitecoreProvider = exports.DefaultEmptyFieldEditingComponentText = exports.DefaultEmptyFieldEditingComponentImage = exports.DesignLibrary = exports.File = exports.getDesignLibraryStylesheetLinks = exports.BYOCComponent = exports.fetchFEaaSComponentServerProps = exports.FEaaSComponent = exports.DateField = exports.Text = exports.Image = void 0;
40
+ exports.collectSitecorePageCacheTags = exports.buildSitecoreDictionaryCacheTag = exports.setCachedPageParams = exports.getCachedPageParams = exports.DesignLibraryApp = exports.analyticsProxyAdapter = exports.personalizeProxyAdapter = exports.initContentSdk = exports.renderEmptyPlaceholder = exports.AppPlaceholder = exports.ClientEditingChromesUpdate = exports.Form = exports.EditingScripts = exports.withEmptyFieldEditingComponent = exports.withFieldMetadata = exports.withDatasourceCheck = exports.withPlaceholder = exports.withAppPlaceholder = exports.withEditorChromes = exports.useSitecore = exports.withSitecore = exports.SitecoreProviderReactContext = exports.SitecoreProvider = exports.DefaultEmptyFieldEditingComponentText = exports.DefaultEmptyFieldEditingComponentImage = exports.DesignLibrary = exports.File = exports.getDesignLibraryStylesheetLinks = exports.BYOCComponent = exports.fetchFEaaSComponentServerProps = exports.FEaaSComponent = exports.DateField = exports.Text = exports.Image = void 0;
41
41
  var debug_1 = require("./debug");
42
42
  Object.defineProperty(exports, "debug", { enumerable: true, get: function () { return __importDefault(debug_1).default; } });
43
43
  var core_1 = require("@sitecore-content-sdk/core");
@@ -146,3 +146,11 @@ Object.defineProperty(exports, "DesignLibraryApp", { enumerable: true, get: func
146
146
  var page_params_1 = require("./cache/page-params");
147
147
  Object.defineProperty(exports, "getCachedPageParams", { enumerable: true, get: function () { return page_params_1.getCachedPageParams; } });
148
148
  Object.defineProperty(exports, "setCachedPageParams", { enumerable: true, get: function () { return page_params_1.setCachedPageParams; } });
149
+ // Sitecore cache-tag public surface. Kept intentionally small: the cache helpers in the template
150
+ // (`getSitecorePage`, `getSitecoreErrorPage`, `getSitecoreDictionary`) and the `createSitecoreRevalidateRouteHandler`
151
+ // already cover the end-to-end Sitecore tag flow. Lower-level tag builders and webhook-body parsers remain
152
+ // internal so callers don't hand-build Sitecore cache tag strings or duplicate the route handler's logic.
153
+ var sitecore_cache_tags_1 = require("./cache/sitecore-cache-tags");
154
+ Object.defineProperty(exports, "buildSitecoreDictionaryCacheTag", { enumerable: true, get: function () { return sitecore_cache_tags_1.buildSitecoreDictionaryCacheTag; } });
155
+ var sitecore_page_cache_tags_1 = require("./cache/sitecore-page-cache-tags");
156
+ Object.defineProperty(exports, "collectSitecorePageCacheTags", { enumerable: true, get: function () { return sitecore_page_cache_tags_1.collectSitecorePageCacheTags; } });
@@ -7,7 +7,6 @@ exports.RedirectsProxy = void 0;
7
7
  const site_1 = require("@sitecore-content-sdk/content/site");
8
8
  const tools_1 = require("@sitecore-content-sdk/core/tools");
9
9
  const server_1 = require("next/server");
10
- const regex_parser_1 = __importDefault(require("regex-parser"));
11
10
  const proxy_1 = require("./proxy");
12
11
  const debug_1 = __importDefault(require("../debug"));
13
12
  const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
@@ -126,9 +125,9 @@ class RedirectsProxy extends proxy_1.ProxyBase {
126
125
  existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
127
126
  const reqUrl = this.normalizeUrl(req.nextUrl.clone());
128
127
  // Apply regex replacements to the target URL if the pattern is a regex
129
- const matched = reqUrl.pathname
130
- .replace(/\/*$/gi, '')
131
- .match((0, regex_parser_1.default)(existsRedirect.pattern));
128
+ const sourcePath = existsRedirect.matchedPath || reqUrl.pathname;
129
+ const pathForCaptureMatch = sourcePath.replace(/\/*$/gi, '') || '/';
130
+ const matched = pathForCaptureMatch.match(this.getRedirectPatternRegex(existsRedirect.pattern));
132
131
  if (matched) {
133
132
  existsRedirect.target = existsRedirect.target.replace(/\$(\d+)/g, (_, index) => {
134
133
  return matched[parseInt(index, 10)] || '';
@@ -199,6 +198,7 @@ class RedirectsProxy extends proxy_1.ProxyBase {
199
198
  * Method returns RedirectInfo when matches
200
199
  * @param {NextRequest} req request
201
200
  * @param {string} siteName site name
201
+ * @param {string} requestLocale locale used for locale redirect matching
202
202
  * @returns Promise<RedirectInfo | undefined>
203
203
  * @private
204
204
  */
@@ -253,28 +253,28 @@ class RedirectsProxy extends proxy_1.ProxyBase {
253
253
  (0, tools_1.areURLSearchParamsEqual)(new URLSearchParams(patternQS), new URLSearchParams(incomingQS))));
254
254
  }
255
255
  // process regex rules
256
- // Modify the redirect pattern to ignore the language prefix in the path
257
- // And escapes non-special "?" characters in a string or regex.
258
- redirect.pattern = (0, tools_1.escapeNonSpecialQuestionMarks)('^' + redirect.pattern.replace(new RegExp(`^[^]?/${urlLocale}/`, 'gi'), '') // ensure function thinks input is regex
259
- );
260
- // Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
261
- redirect.pattern = `/^\/${redirect.pattern
262
- .replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
263
- .replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
264
- .replace(/^\^|\$$/g, '') // Further cleans up anchors
265
- .replace(/\$\/g$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
266
- // Redirect pattern matches the full incoming URL with query string present
267
- matchedQueryString = [
268
- (0, regex_parser_1.default)(redirect.pattern).test(`/${localePath}${incomingQS}`),
269
- (0, regex_parser_1.default)(redirect.pattern).test(`${normalizedPath}${incomingQS}`),
270
- ].some(Boolean)
271
- ? incomingQS
256
+ const regex = this.getRedirectPatternRegex(redirect.pattern);
257
+ const testRegex = (value) => {
258
+ regex.lastIndex = 0;
259
+ return regex.test(value);
260
+ };
261
+ const localeStrippedIncoming = this.getLocaleStrippedPath(incomingURL, urlLocale);
262
+ const localeStrippedNormalized = this.getLocaleStrippedPath(normalizedPath, urlLocale);
263
+ const pathCandidates = [
264
+ incomingURL,
265
+ normalizedPath,
266
+ localeStrippedIncoming,
267
+ localeStrippedNormalized,
268
+ ].filter((candidate, index, array) => array.indexOf(candidate) === index);
269
+ const matchedPath = pathCandidates.find((candidate) => testRegex(candidate));
270
+ const matchedPathWithQuery = incomingQS
271
+ ? pathCandidates.find((candidate) => testRegex(`${candidate}${incomingQS}`))
272
272
  : undefined;
273
+ matchedQueryString = matchedPathWithQuery ? incomingQS : undefined;
273
274
  // Save the matched query string (if found) into the redirect object
274
275
  redirect.matchedQueryString = matchedQueryString || '';
275
- return (!!((0, regex_parser_1.default)(redirect.pattern).test(`/${urlLocale}${incomingURL}`) ||
276
- (0, regex_parser_1.default)(redirect.pattern).test(incomingURL) ||
277
- matchedQueryString) &&
276
+ redirect.matchedPath = matchedPath || matchedPathWithQuery || '';
277
+ return (!!(matchedPath || matchedQueryString) &&
278
278
  (redirect.locale ? redirect.locale.toLowerCase() === urlLocale.toLowerCase() : true));
279
279
  })
280
280
  : undefined;
@@ -420,5 +420,37 @@ class RedirectsProxy extends proxy_1.ProxyBase {
420
420
  }
421
421
  return redirect;
422
422
  }
423
+ /**
424
+ * Converts a redirect pattern string into a RegExp.
425
+ * Supports both JS literal form (`/pattern/i`) and plain regex source (`^/path$`).
426
+ * @param {string} pattern redirect pattern from redirect map
427
+ * @returns {RegExp} normalized regex instance
428
+ * @private
429
+ */
430
+ getRedirectPatternRegex(pattern) {
431
+ const normalizedPattern = (0, tools_1.escapeNonSpecialQuestionMarks)(pattern);
432
+ const literalMatch = normalizedPattern.match(/^\/(.+)\/([a-z]*)$/i);
433
+ if (literalMatch) {
434
+ const [, source, flags] = literalMatch;
435
+ const safeFlags = flags || 'i';
436
+ return new RegExp(source, safeFlags);
437
+ }
438
+ return new RegExp(normalizedPattern, 'i');
439
+ }
440
+ /**
441
+ * Strips locale prefix from path when present.
442
+ * @param {string} path incoming request path
443
+ * @param {string} urlLocale locale from Next.js URL
444
+ * @returns {string} locale-stripped path
445
+ * @private
446
+ */
447
+ getLocaleStrippedPath(path, urlLocale) {
448
+ if (!urlLocale) {
449
+ return path;
450
+ }
451
+ const localePrefixRegex = new RegExp(`^/${urlLocale}(?=/|$)`, 'i');
452
+ const strippedPath = path.replace(localePrefixRegex, '') || '/';
453
+ return strippedPath.startsWith('/') ? strippedPath : `/${strippedPath}`;
454
+ }
423
455
  }
424
456
  exports.RedirectsProxy = RedirectsProxy;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createEditingRenderRouteHandlers = exports.createEditingConfigRouteHandler = exports.createRobotsRouteHandler = exports.createSitemapRouteHandler = void 0;
3
+ exports.createSitecoreRevalidateRouteHandler = exports.createEditingRenderRouteHandlers = exports.createEditingConfigRouteHandler = exports.createRobotsRouteHandler = exports.createSitemapRouteHandler = void 0;
4
4
  var sitemap_route_handler_1 = require("./sitemap-route-handler");
5
5
  Object.defineProperty(exports, "createSitemapRouteHandler", { enumerable: true, get: function () { return sitemap_route_handler_1.createSitemapRouteHandler; } });
6
6
  var robots_route_handler_1 = require("./robots-route-handler");
@@ -9,3 +9,5 @@ var editing_config_route_handler_1 = require("./editing-config-route-handler");
9
9
  Object.defineProperty(exports, "createEditingConfigRouteHandler", { enumerable: true, get: function () { return editing_config_route_handler_1.createEditingConfigRouteHandler; } });
10
10
  var editing_render_route_handler_1 = require("./editing-render-route-handler");
11
11
  Object.defineProperty(exports, "createEditingRenderRouteHandlers", { enumerable: true, get: function () { return editing_render_route_handler_1.createEditingRenderRouteHandlers; } });
12
+ var sitecore_revalidate_route_handler_1 = require("./sitecore-revalidate-route-handler");
13
+ Object.defineProperty(exports, "createSitecoreRevalidateRouteHandler", { enumerable: true, get: function () { return sitecore_revalidate_route_handler_1.createSitecoreRevalidateRouteHandler; } });
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createSitecoreRevalidateRouteHandler = createSitecoreRevalidateRouteHandler;
7
+ const sitecore_edge_webhook_revalidation_1 = require("../cache/sitecore-edge-webhook-revalidation");
8
+ const sitecore_cache_tags_1 = require("../cache/sitecore-cache-tags");
9
+ const debug_1 = __importDefault(require("../debug"));
10
+ const cache_1 = require("next/cache");
11
+ const server_1 = require("next/server");
12
+ const DEFAULT_SECRET_ENV_VAR = 'SITECORE_REVALIDATE_SECRET';
13
+ const DEFAULT_SECRET_HEADER = 'x-revalidate-secret';
14
+ /**
15
+ * Returns a non-empty trimmed secret, or `undefined` when unset or whitespace-only.
16
+ * @param {string | undefined} secretOption - Explicit secret from handler options.
17
+ * @param {string | undefined} envValue - Secret from `process.env` (e.g. `SITECORE_REVALIDATE_SECRET`).
18
+ */
19
+ function resolveConfiguredRevalidateSecret(secretOption, envValue) {
20
+ const raw = secretOption !== undefined ? secretOption : envValue;
21
+ const trimmed = raw === null || raw === void 0 ? void 0 : raw.trim();
22
+ return trimmed || undefined;
23
+ }
24
+ /**
25
+ * Creates a single `POST` handler for `/api/revalidate` that consumes Sitecore Experience Edge / Content
26
+ * Operations webhook bodies (and equivalent ad-hoc calls that reuse the same body shape).
27
+ *
28
+ * The body is expected to be a JSON object that resolves to at least one Sitecore cache tag:
29
+ *
30
+ * - **`updates[]`** — Sitecore publish-event rows. Each row's `identifier` (with `-media` / `-layout`
31
+ * suffix stripped) maps to an `sc:item:<id>:<locale>:latest` tag, using `entity_culture` for locale
32
+ * (falling back to the handler's `defaultLocale`).
33
+ * - **`tags[]`** — pass-through and convenience array:
34
+ * - Strings already starting with `sc:` are used verbatim (e.g. `sc:route:...`, `sc:item:...`, `sc:dict:...`).
35
+ * - Bare values are treated as Sitecore item ids and mapped to `sc:item:<id>:<defaultLocale>:latest`.
36
+ *
37
+ * When **`sites`** is configured, the handler also appends one `sc:dict:<site>:<locale>` tag per
38
+ * site so dictionary updates flow through the same call.
39
+ *
40
+ * Auth (optional): when `SITECORE_REVALIDATE_SECRET` (or the `secret` option) is non-empty, callers must
41
+ * send the same value in the **`x-revalidate-secret`** header. When unset or blank, no header is required.
42
+ * @param {SitecoreRevalidateRouteHandlerOptions} [options] - Optional inline `secret`, `cacheProfile`, locale, sites, and dictionary options.
43
+ * @public
44
+ */
45
+ function createSitecoreRevalidateRouteHandler(options = {}) {
46
+ const { defaultLocale = 'en', sites, secret, cacheProfile = 'max' } = options;
47
+ const dictionaryTags = sites !== undefined
48
+ ? (0, sitecore_cache_tags_1.buildSitecoreDictionaryCacheTagsFromSites)({
49
+ sites,
50
+ baseLocale: defaultLocale,
51
+ })
52
+ : [];
53
+ const POST = async (req) => {
54
+ var _a, _b, _c, _d, _e, _f, _g, _h;
55
+ const startTimestamp = Date.now();
56
+ try {
57
+ const configuredSecret = resolveConfiguredRevalidateSecret(secret, process.env[DEFAULT_SECRET_ENV_VAR]);
58
+ if (configuredSecret) {
59
+ const providedSecret = req.headers.get(DEFAULT_SECRET_HEADER);
60
+ if (providedSecret !== configuredSecret) {
61
+ debug_1.default.revalidate('sitecore revalidate: unauthorized (secret mismatch or missing header)');
62
+ return server_1.NextResponse.json({ error: 'Unauthorized.' }, { status: 401 });
63
+ }
64
+ }
65
+ else {
66
+ debug_1.default.revalidate('sitecore revalidate: no secret configured, skipping auth');
67
+ }
68
+ let body;
69
+ try {
70
+ body = await req.json();
71
+ }
72
+ catch (_j) {
73
+ debug_1.default.revalidate('sitecore revalidate: invalid JSON body');
74
+ return server_1.NextResponse.json({ error: 'Request body must be valid JSON.' }, { status: 400 });
75
+ }
76
+ if (typeof body !== 'object' || body === null || Array.isArray(body)) {
77
+ debug_1.default.revalidate('sitecore revalidate: body must be a JSON object');
78
+ return server_1.NextResponse.json({ error: 'Request body must be a JSON object.' }, { status: 400 });
79
+ }
80
+ const webhookBody = body;
81
+ debug_1.default.revalidate('sitecore revalidate start: %o', {
82
+ invocation_id: (_a = webhookBody.invocation_id) !== null && _a !== void 0 ? _a : null,
83
+ continues: (_b = webhookBody.continues) !== null && _b !== void 0 ? _b : false,
84
+ updatesCount: (_d = (_c = webhookBody.updates) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0,
85
+ tagsCount: Array.isArray(webhookBody.tags) ? webhookBody.tags.length : 0,
86
+ dictionaryTagsCount: dictionaryTags.length,
87
+ defaultLocale,
88
+ });
89
+ const tags = (0, sitecore_cache_tags_1.dedupeSitecoreCacheTags)([
90
+ ...(0, sitecore_edge_webhook_revalidation_1.collectSitecoreTagsFromEdgeRevalidateRequestBody)(webhookBody, { defaultLocale }),
91
+ ...dictionaryTags,
92
+ ]);
93
+ if (tags.length === 0) {
94
+ debug_1.default.revalidate('sitecore revalidate: no tags resolved in %dms', Date.now() - startTimestamp);
95
+ return server_1.NextResponse.json({
96
+ error: 'Provide non-empty `updates` (with identifiers) and/or `tags` that resolve to at least one cache tag.',
97
+ }, { status: 400 });
98
+ }
99
+ for (const tag of tags) {
100
+ (0, cache_1.revalidateTag)(tag, cacheProfile);
101
+ }
102
+ debug_1.default.revalidate('sitecore revalidate end in %dms: %o', Date.now() - startTimestamp, {
103
+ tagsCount: tags.length,
104
+ invocation_id: (_e = webhookBody.invocation_id) !== null && _e !== void 0 ? _e : null,
105
+ continues: (_f = webhookBody.continues) !== null && _f !== void 0 ? _f : false,
106
+ });
107
+ return server_1.NextResponse.json({
108
+ revalidated: true,
109
+ tagsCount: tags.length,
110
+ invocation_id: (_g = webhookBody.invocation_id) !== null && _g !== void 0 ? _g : null,
111
+ continues: (_h = webhookBody.continues) !== null && _h !== void 0 ? _h : false,
112
+ });
113
+ }
114
+ catch (error) {
115
+ if (error instanceof Error && error.digest === 'NEXT_PRERENDER_INTERRUPTED') {
116
+ throw error;
117
+ }
118
+ console.log('Sitecore revalidate route handler failed:');
119
+ console.log(error);
120
+ return server_1.NextResponse.json({ error: 'Internal Server Error.' }, { status: 500 });
121
+ }
122
+ };
123
+ return { POST };
124
+ }