@elytracms/next 0.0.86 → 0.0.88
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/dist/index.d.ts +82 -216
- package/dist/index.js +64 -202
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,48 +1,21 @@
|
|
|
1
1
|
import { ComponentRegistry } from '@elytracms/core/component-registry';
|
|
2
2
|
export { defineComponent } from '@elytracms/core/component-registry';
|
|
3
|
-
import { basePrimitives, getImplementation, renderDocumentBodyItems, documentBodyItems, ImageManifest, serializeListingQuery, RenderErrorBoundary, documentBodyRichTextValues, documentBodyCompositionNodes, richTextEmbedsInValue, collectReferencedAssetIds, collectRichTextRefsInValue, collectReferencedRelations, collectReferencedListings, collectReferencedRichTextLinks, normalizeImageSource } from '@elytracms/core/runtime-renderer';
|
|
3
|
+
import { basePrimitives, getImplementation, registerImplementations, renderDocumentBodyItems, documentBodyItems, ImageManifest, serializeListingQuery, RenderErrorBoundary, documentBodyRichTextValues, documentBodyCompositionNodes, richTextEmbedsInValue, collectReferencedAssetIds, collectRichTextRefsInValue, collectReferencedRelations, collectReferencedListings, collectReferencedRichTextLinks, normalizeImageSource } from '@elytracms/core/runtime-renderer';
|
|
4
4
|
import { isValidElement } from 'react';
|
|
5
5
|
import { isBlockSequenceValueShape } from '@elytracms/core/rich-text';
|
|
6
6
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
import { cacheTags,
|
|
7
|
+
import { cacheTags, isDocumentHistory, resolvedAsset, createContentClient, createContentContext } from '@elytracms/core/content';
|
|
8
8
|
import { documentKey, splitLocalePath, hierarchyMount, composePath, localeUrlPath, mergeRouteRecords, fallbackHomeRoutes, presentationBodyFields, splitPath, documentSchema, redirectRecordSchema } from '@elytracms/core/cms-core';
|
|
9
9
|
export { documentSchema, fallbackHomeRoutes, localeConfigSchema, localeUrlPath, mergeRouteRecords, redirectRecordSchema, resolveLocale, routeRecordSchema, splitLocalePath, textDirection } from '@elytracms/core/cms-core';
|
|
10
10
|
import { revalidateTag, unstable_cache } from 'next/cache';
|
|
11
11
|
import { draftMode } from 'next/headers';
|
|
12
12
|
import { redirect, notFound, permanentRedirect } from 'next/navigation';
|
|
13
|
+
import { COMPOSITE_KEY_SEPARATOR } from '@elytracms/core/project-graph';
|
|
13
14
|
import { createHmac, timingSafeEqual } from 'crypto';
|
|
14
15
|
import { z } from 'zod';
|
|
15
16
|
export { z } from 'zod';
|
|
16
17
|
import NextImage from 'next/image';
|
|
17
18
|
|
|
18
|
-
// src/components.ts
|
|
19
|
-
|
|
20
|
-
// src/is-component.ts
|
|
21
|
-
var EXOTIC_COMPONENT_TYPES = /* @__PURE__ */ new Set([
|
|
22
|
-
/* @__PURE__ */ Symbol.for("react.memo"),
|
|
23
|
-
/* @__PURE__ */ Symbol.for("react.forward_ref"),
|
|
24
|
-
/* @__PURE__ */ Symbol.for("react.lazy")
|
|
25
|
-
]);
|
|
26
|
-
function isComponentImplementation(value) {
|
|
27
|
-
if (typeof value === "function") return true;
|
|
28
|
-
if (typeof value !== "object" || value === null) return false;
|
|
29
|
-
const marker = value.$$typeof;
|
|
30
|
-
return typeof marker === "symbol" && EXOTIC_COMPONENT_TYPES.has(marker);
|
|
31
|
-
}
|
|
32
|
-
function describeImplementation(value) {
|
|
33
|
-
if (value === null) return "null";
|
|
34
|
-
if (value === void 0) return "undefined";
|
|
35
|
-
if (typeof value === "string") {
|
|
36
|
-
return `the string ${JSON.stringify(value)} (which React would render silently as an HTML tag)`;
|
|
37
|
-
}
|
|
38
|
-
if (Array.isArray(value)) return "an array";
|
|
39
|
-
if (typeof value === "object") {
|
|
40
|
-
const marker = value.$$typeof;
|
|
41
|
-
return typeof marker === "symbol" ? `an object tagged ${String(marker)}` : "a plain object";
|
|
42
|
-
}
|
|
43
|
-
return `a ${typeof value}`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
19
|
// src/components.ts
|
|
47
20
|
function defineHostComponents(components = [], options = {}) {
|
|
48
21
|
const includeBase = options.includeBasePrimitives !== false;
|
|
@@ -53,74 +26,9 @@ function defineHostComponents(components = [], options = {}) {
|
|
|
53
26
|
const implementation = getImplementation(base.implementations, manifest.id);
|
|
54
27
|
if (implementation) implementations[manifest.id] = implementation;
|
|
55
28
|
}
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (isComponentImplementation(component.implementation)) {
|
|
60
|
-
implementations[id] = component.implementation;
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
implementationIssues.push({
|
|
64
|
-
code: "invalid-implementation",
|
|
65
|
-
componentId: id,
|
|
66
|
-
message: `Component "${id}": implementation is not a React component \u2014 received ${describeImplementation(component.implementation)}; expected a function or class component, or a React.memo / forwardRef / lazy wrapper. The entry is registered without an implementation, so its nodes render the missing-component fallback.`
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
return { registry, implementations, issues: [...registry.issues, ...implementationIssues] };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// src/payloads.ts
|
|
73
|
-
var SOURCE_PAYLOAD_ERROR_KEY = "__elytraSourcePayloadError";
|
|
74
|
-
function sourcePayloadError(sourceId, messages) {
|
|
75
|
-
return { [SOURCE_PAYLOAD_ERROR_KEY]: true, sourceId, messages: [...messages] };
|
|
76
|
-
}
|
|
77
|
-
function isSourcePayloadError(value) {
|
|
78
|
-
return typeof value === "object" && value !== null && value[SOURCE_PAYLOAD_ERROR_KEY] === true && typeof value.sourceId === "string" && Array.isArray(value.messages);
|
|
79
|
-
}
|
|
80
|
-
function unescapeRefToken(token) {
|
|
81
|
-
return token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
82
|
-
}
|
|
83
|
-
function pathFromToken(token) {
|
|
84
|
-
if (token === "" || token === "/") return [];
|
|
85
|
-
if (!token.startsWith("/")) return void 0;
|
|
86
|
-
return token.slice(1).split("/").map(unescapeRefToken);
|
|
87
|
-
}
|
|
88
|
-
var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
89
|
-
function resolvePayloadToken(payload, token) {
|
|
90
|
-
const path = pathFromToken(token);
|
|
91
|
-
if (path === void 0) return void 0;
|
|
92
|
-
let current = payload;
|
|
93
|
-
for (const segment of path) {
|
|
94
|
-
if (Array.isArray(current)) {
|
|
95
|
-
if (!/^(0|[1-9][0-9]*)$/.test(segment)) return void 0;
|
|
96
|
-
const index = Number(segment);
|
|
97
|
-
if (index >= current.length) return void 0;
|
|
98
|
-
current = current[index];
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
if (isPlainObject(current)) {
|
|
102
|
-
if (!Object.prototype.hasOwnProperty.call(current, segment)) return void 0;
|
|
103
|
-
current = current[segment];
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
return void 0;
|
|
107
|
-
}
|
|
108
|
-
return current;
|
|
109
|
-
}
|
|
110
|
-
function createBindingResolver(payloads) {
|
|
111
|
-
return (ref, item) => {
|
|
112
|
-
if (ref.mode === "repeaterItem") {
|
|
113
|
-
return item === void 0 ? void 0 : resolvePayloadToken(item.item, ref.token);
|
|
114
|
-
}
|
|
115
|
-
if (!Object.prototype.hasOwnProperty.call(payloads, ref.sourceId)) return void 0;
|
|
116
|
-
const payload = payloads[ref.sourceId];
|
|
117
|
-
if (isSourcePayloadError(payload)) {
|
|
118
|
-
throw new Error(
|
|
119
|
-
`Source "${payload.sourceId}" query was rejected: ${payload.messages.join("; ")}`
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
return resolvePayloadToken(payload, ref.token);
|
|
123
|
-
};
|
|
29
|
+
const host = registerImplementations(components);
|
|
30
|
+
Object.assign(implementations, host.implementations);
|
|
31
|
+
return { registry, implementations, issues: [...registry.issues, ...host.issues] };
|
|
124
32
|
}
|
|
125
33
|
function relationResolver(relations) {
|
|
126
34
|
return (ref) => relations[`${ref.collection}:${ref.id}`] ?? null;
|
|
@@ -159,19 +67,14 @@ function stripServerErrorBoundary(_node, rendered) {
|
|
|
159
67
|
}
|
|
160
68
|
return rendered;
|
|
161
69
|
}
|
|
162
|
-
function defaultBindingPayloads(result) {
|
|
163
|
-
return { document: result.document, params: result.dynamicParams };
|
|
164
|
-
}
|
|
165
70
|
async function CanvasRenderer(props) {
|
|
166
71
|
const { result, components } = props;
|
|
167
72
|
if (result.status !== "ok") {
|
|
168
73
|
return /* @__PURE__ */ jsx(UnresolvedPageFallback, { status: result.status });
|
|
169
74
|
}
|
|
170
|
-
const resolveBinding = props.resolveBinding ?? createBindingResolver(props.payloads ?? defaultBindingPayloads(result));
|
|
171
75
|
const ctx = {
|
|
172
76
|
registry: components.registry,
|
|
173
77
|
implementations: components.implementations,
|
|
174
|
-
resolveBinding,
|
|
175
78
|
resolveAsset: assetResolver(props.assets ?? []),
|
|
176
79
|
// EC-254: wire relation population only when the host baked the populated
|
|
177
80
|
// targets, so a host that did not opt in keeps the raw-ref passthrough.
|
|
@@ -230,42 +133,6 @@ function mergeCacheTags(first, ...rest) {
|
|
|
230
133
|
locale: first.locale
|
|
231
134
|
});
|
|
232
135
|
}
|
|
233
|
-
function collectBindingSourceIds(node, into = /* @__PURE__ */ new Set()) {
|
|
234
|
-
for (const value of Object.values(node.props ?? {})) {
|
|
235
|
-
if (value.kind === "binding") into.add(value.binding.sourceId);
|
|
236
|
-
}
|
|
237
|
-
if (node.condition) into.add(node.condition.source.sourceId);
|
|
238
|
-
for (const children of Object.values(node.slots ?? {})) {
|
|
239
|
-
for (const child of children) collectBindingSourceIds(child, into);
|
|
240
|
-
}
|
|
241
|
-
return into;
|
|
242
|
-
}
|
|
243
|
-
function materializeSourcePayloads(input) {
|
|
244
|
-
const { client, result, sources } = input;
|
|
245
|
-
const payloads = {};
|
|
246
|
-
const tags = [];
|
|
247
|
-
if (result.status !== "ok") return { payloads, tags };
|
|
248
|
-
const referenced = /* @__PURE__ */ new Set();
|
|
249
|
-
const byId = new Map(sources.map((source) => [source.id, source]));
|
|
250
|
-
for (const sourceId of [...referenced].sort()) {
|
|
251
|
-
if (!isSectionSourceId(sourceId)) continue;
|
|
252
|
-
const definition = byId.get(sourceId);
|
|
253
|
-
if (!definition || definition.kind !== "cms") continue;
|
|
254
|
-
const config = parseCmsSourceQueryConfig(definition.config);
|
|
255
|
-
if (!config || config.documentId !== void 0) continue;
|
|
256
|
-
const list = client.listDocuments(config.collectionId, listQueryOfCmsConfig(config));
|
|
257
|
-
tags.push(list.tags);
|
|
258
|
-
if (list.ok) {
|
|
259
|
-
payloads[sourceId] = list.documents;
|
|
260
|
-
} else {
|
|
261
|
-
payloads[sourceId] = sourcePayloadError(
|
|
262
|
-
sourceId,
|
|
263
|
-
list.errors.map((error) => error.message)
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return { payloads, tags };
|
|
268
|
-
}
|
|
269
136
|
function createStaticContentSource(data) {
|
|
270
137
|
const locales = data.locales;
|
|
271
138
|
const routes = data.routes ?? [];
|
|
@@ -311,7 +178,6 @@ function createStaticContentSource(data) {
|
|
|
311
178
|
// dimensions. Baked into the (serializable) render outcome by the route
|
|
312
179
|
// helper, so a cached page keeps its images during a backend outage.
|
|
313
180
|
assets: () => [...assets.values()].map((record2) => resolvedAsset(record2, record2.url ?? assetUrl(record2))),
|
|
314
|
-
...data.sources ? { sources: data.sources } : {},
|
|
315
181
|
// EC-499: the schema the route helper reads a collection's declared
|
|
316
182
|
// `presentation` from (which field is the delivery body, which component
|
|
317
183
|
// wraps it). Already prefetched for the lookup above — exposing it costs
|
|
@@ -351,7 +217,6 @@ async function createContentSnapshot(adapter, projectId, options) {
|
|
|
351
217
|
locales: options.locales,
|
|
352
218
|
...options.routes ? { routes: options.routes } : {},
|
|
353
219
|
redirects: options.redirects ? [...options.redirects] : redirects,
|
|
354
|
-
...options.sources ? { sources: options.sources } : {},
|
|
355
220
|
...schema ? { collections: schema.collections } : {},
|
|
356
221
|
documents,
|
|
357
222
|
assets: assetRecords,
|
|
@@ -474,26 +339,6 @@ async function resolveCanvasPage(source, url, perspective, options = {}) {
|
|
|
474
339
|
if (result.status !== "ok") {
|
|
475
340
|
return { kind: "not-found", tags: result.tags };
|
|
476
341
|
}
|
|
477
|
-
const extraTags = [];
|
|
478
|
-
let payloads = defaultBindingPayloads(result);
|
|
479
|
-
const materialized = materializeSourcePayloads({
|
|
480
|
-
client,
|
|
481
|
-
result,
|
|
482
|
-
sources: source.sources ?? []
|
|
483
|
-
});
|
|
484
|
-
payloads = { ...payloads, ...materialized.payloads };
|
|
485
|
-
extraTags.push(...materialized.tags);
|
|
486
|
-
if (options.payloads) {
|
|
487
|
-
const custom = await options.payloads({
|
|
488
|
-
client,
|
|
489
|
-
result,
|
|
490
|
-
perspective,
|
|
491
|
-
path,
|
|
492
|
-
params: result.dynamicParams,
|
|
493
|
-
addTags: (tags) => extraTags.push(tags)
|
|
494
|
-
});
|
|
495
|
-
payloads = { ...payloads, ...custom };
|
|
496
|
-
}
|
|
497
342
|
const presentation = presentationFor(source, result);
|
|
498
343
|
const body = pageBodyOf(result, presentation);
|
|
499
344
|
const allAssets = source.assets?.() ?? [];
|
|
@@ -524,7 +369,6 @@ async function resolveCanvasPage(source, url, perspective, options = {}) {
|
|
|
524
369
|
return {
|
|
525
370
|
kind: "render",
|
|
526
371
|
result,
|
|
527
|
-
payloads,
|
|
528
372
|
assets: bakedAssets,
|
|
529
373
|
relations,
|
|
530
374
|
listings,
|
|
@@ -532,7 +376,6 @@ async function resolveCanvasPage(source, url, perspective, options = {}) {
|
|
|
532
376
|
...presentation ? { presentation } : {},
|
|
533
377
|
tags: mergeCacheTags(
|
|
534
378
|
result.tags,
|
|
535
|
-
...extraTags,
|
|
536
379
|
...listingTags,
|
|
537
380
|
assetTags,
|
|
538
381
|
relationTags,
|
|
@@ -624,7 +467,7 @@ function resolveOgImage(value, assets) {
|
|
|
624
467
|
return assets.find((asset) => asset.id === value)?.url ?? null;
|
|
625
468
|
}
|
|
626
469
|
function openGraphLocale(locale) {
|
|
627
|
-
return locale.replace(/-/g, "_");
|
|
470
|
+
return locale.includes("-") ? locale.replace(/-/g, "_") : null;
|
|
628
471
|
}
|
|
629
472
|
function canvasPageMetadata(result, assets = []) {
|
|
630
473
|
const doc = result.document;
|
|
@@ -641,17 +484,32 @@ function canvasPageMetadata(result, assets = []) {
|
|
|
641
484
|
if (seoDescription) metadata.description = seoDescription;
|
|
642
485
|
const ogTitle = seoOgTitle ?? title;
|
|
643
486
|
if (doc) {
|
|
487
|
+
const ogLocale = openGraphLocale(result.tags.locale);
|
|
644
488
|
metadata.openGraph = {
|
|
645
489
|
...ogTitle !== null ? { title: ogTitle } : {},
|
|
646
490
|
...seoDescription ? { description: seoDescription } : {},
|
|
647
491
|
...seoOgImage ? { images: [{ url: seoOgImage }] } : {},
|
|
648
|
-
locale:
|
|
492
|
+
...ogLocale !== null ? { locale: ogLocale } : {}
|
|
649
493
|
};
|
|
650
494
|
}
|
|
651
495
|
if (seoNoindex) metadata.robots = { index: false };
|
|
652
496
|
if (seoCanonical) metadata.alternates = { canonical: seoCanonical };
|
|
653
497
|
return metadata;
|
|
654
498
|
}
|
|
499
|
+
function mergeMetadataOverParent(parent, page) {
|
|
500
|
+
if (!parent) return page;
|
|
501
|
+
const merged = { ...page };
|
|
502
|
+
for (const [key, pageValue] of Object.entries(page)) {
|
|
503
|
+
const parentValue = parent[key];
|
|
504
|
+
if (isPlainObject(pageValue) && isPlainObject(parentValue)) {
|
|
505
|
+
merged[key] = { ...parentValue, ...pageValue };
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return merged;
|
|
509
|
+
}
|
|
510
|
+
function isPlainObject(value) {
|
|
511
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
512
|
+
}
|
|
655
513
|
function defaultMetadata(result, assets) {
|
|
656
514
|
return canvasPageMetadata(result, assets);
|
|
657
515
|
}
|
|
@@ -665,7 +523,6 @@ function createCanvasRoute(options) {
|
|
|
665
523
|
const resolveFresh = async () => {
|
|
666
524
|
const source = await options.loadContent({ perspective });
|
|
667
525
|
return resolveCanvasPage(source, url, perspective, {
|
|
668
|
-
...options.payloads ? { payloads: options.payloads } : {},
|
|
669
526
|
// EC-205: page-scope the baked assets to what the page actually uses.
|
|
670
527
|
registry: options.components.registry
|
|
671
528
|
});
|
|
@@ -701,7 +558,6 @@ function createCanvasRoute(options) {
|
|
|
701
558
|
{
|
|
702
559
|
result: outcome.result,
|
|
703
560
|
components: options.components,
|
|
704
|
-
payloads: outcome.payloads,
|
|
705
561
|
assets: outcome.assets,
|
|
706
562
|
relations: outcome.relations,
|
|
707
563
|
listings: outcome.listings,
|
|
@@ -714,7 +570,7 @@ function createCanvasRoute(options) {
|
|
|
714
570
|
}
|
|
715
571
|
}
|
|
716
572
|
};
|
|
717
|
-
const
|
|
573
|
+
const pageMetadata = async (props) => {
|
|
718
574
|
const { slug } = await props.params;
|
|
719
575
|
const url = pathFromSlug(slug);
|
|
720
576
|
const perspective = await requestPerspective();
|
|
@@ -728,6 +584,10 @@ function createCanvasRoute(options) {
|
|
|
728
584
|
if (outcome.kind !== "render") return {};
|
|
729
585
|
return options.metadata ? options.metadata(outcome.result, { url, perspective }) : defaultMetadata(outcome.result, outcome.assets);
|
|
730
586
|
};
|
|
587
|
+
const generateMetadata = async (props, parent) => {
|
|
588
|
+
const [page, resolvedParent] = await Promise.all([pageMetadata(props), parent ?? null]);
|
|
589
|
+
return mergeMetadataOverParent(resolvedParent, page);
|
|
590
|
+
};
|
|
731
591
|
return { Page, generateMetadata };
|
|
732
592
|
}
|
|
733
593
|
function paramNamesOf(pattern) {
|
|
@@ -830,7 +690,7 @@ function canvasSitemapEntries(source, options) {
|
|
|
830
690
|
continue;
|
|
831
691
|
}
|
|
832
692
|
const absolute = originOf(options.baseUrl) + urlPath;
|
|
833
|
-
const group = result.document ? `${route.id}
|
|
693
|
+
const group = result.document ? `${route.id}${COMPOSITE_KEY_SEPARATOR}doc${COMPOSITE_KEY_SEPARATOR}${result.document.collection}${COMPOSITE_KEY_SEPARATOR}${result.document.id}` : `${route.id}${COMPOSITE_KEY_SEPARATOR}path${COMPOSITE_KEY_SEPARATOR}${path}`;
|
|
834
694
|
if (!byUrl.has(absolute)) byUrl.set(absolute, { group, locale });
|
|
835
695
|
const perLocale = byGroup.get(group) ?? /* @__PURE__ */ new Map();
|
|
836
696
|
if (!perLocale.has(locale)) perLocale.set(locale, absolute);
|
|
@@ -1080,21 +940,6 @@ function parseRedirects(rows) {
|
|
|
1080
940
|
}
|
|
1081
941
|
return redirects;
|
|
1082
942
|
}
|
|
1083
|
-
var canvasDataSourceSchema = z.object({
|
|
1084
|
-
kind: z.string().min(1),
|
|
1085
|
-
id: z.string().min(1),
|
|
1086
|
-
label: z.string().optional(),
|
|
1087
|
-
config: z.unknown().optional()
|
|
1088
|
-
});
|
|
1089
|
-
function parseSources(rows) {
|
|
1090
|
-
if (!Array.isArray(rows)) return [];
|
|
1091
|
-
const sources = [];
|
|
1092
|
-
for (const row of rows) {
|
|
1093
|
-
const parsed = canvasDataSourceSchema.safeParse(row);
|
|
1094
|
-
if (parsed.success) sources.push(parsed.data);
|
|
1095
|
-
}
|
|
1096
|
-
return sources;
|
|
1097
|
-
}
|
|
1098
943
|
function assembleLiveSource(payload, options, perspective) {
|
|
1099
944
|
const locales = options.locales;
|
|
1100
945
|
const documents = parseDocuments(payload.documents);
|
|
@@ -1110,10 +955,7 @@ function assembleLiveSource(payload, options, perspective) {
|
|
|
1110
955
|
redirects: parseRedirects(payload.redirects ?? []),
|
|
1111
956
|
collections: [...options.collections],
|
|
1112
957
|
documents: documentSources,
|
|
1113
|
-
assets: payload.assets
|
|
1114
|
-
// EC-166 LIVE mode: the builder's persisted section/template sources, so
|
|
1115
|
-
// the route helper materializes their payloads.
|
|
1116
|
-
sources: parseSources(payload.sources)
|
|
958
|
+
assets: payload.assets
|
|
1117
959
|
});
|
|
1118
960
|
}
|
|
1119
961
|
async function loadLiveContent(options, perspective) {
|
|
@@ -1252,12 +1094,30 @@ function resolveGlobals(source, options = {}) {
|
|
|
1252
1094
|
}
|
|
1253
1095
|
|
|
1254
1096
|
// src/preview-core.ts
|
|
1097
|
+
var RESOLUTION_ORIGIN = "https://redirect-target.invalid";
|
|
1098
|
+
function resolveOnOrigin(value) {
|
|
1099
|
+
let resolved;
|
|
1100
|
+
try {
|
|
1101
|
+
resolved = new URL(value, RESOLUTION_ORIGIN);
|
|
1102
|
+
} catch {
|
|
1103
|
+
return null;
|
|
1104
|
+
}
|
|
1105
|
+
return resolved.origin === RESOLUTION_ORIGIN ? resolved : null;
|
|
1106
|
+
}
|
|
1107
|
+
function hasControlCharacter(value) {
|
|
1108
|
+
for (let i = 0; i < value.length; i++) {
|
|
1109
|
+
const code = value.charCodeAt(i);
|
|
1110
|
+
if (code <= 31 || code === 127) return true;
|
|
1111
|
+
}
|
|
1112
|
+
return false;
|
|
1113
|
+
}
|
|
1255
1114
|
function safeRedirectTarget(value, fallback = "/") {
|
|
1256
1115
|
if (value === null || value === void 0 || value === "") return fallback;
|
|
1257
|
-
if (!value.startsWith("/")) return null;
|
|
1258
|
-
|
|
1259
|
-
if (
|
|
1260
|
-
|
|
1116
|
+
if (!value.startsWith("/") || hasControlCharacter(value)) return null;
|
|
1117
|
+
const resolved = resolveOnOrigin(value);
|
|
1118
|
+
if (resolved === null) return null;
|
|
1119
|
+
const target = resolved.pathname + resolved.search + resolved.hash;
|
|
1120
|
+
return resolveOnOrigin(target) === null ? null : target;
|
|
1261
1121
|
}
|
|
1262
1122
|
function evaluatePreviewRequest(requestUrl, expectedToken) {
|
|
1263
1123
|
if (expectedToken === void 0 || expectedToken.length === 0) {
|
|
@@ -1279,6 +1139,15 @@ function evaluatePreviewRequest(requestUrl, expectedToken) {
|
|
|
1279
1139
|
}
|
|
1280
1140
|
return { ok: true, redirectTo };
|
|
1281
1141
|
}
|
|
1142
|
+
function previewDisableTarget(requestUrl) {
|
|
1143
|
+
let url;
|
|
1144
|
+
try {
|
|
1145
|
+
url = typeof requestUrl === "string" ? new URL(requestUrl, "http://localhost") : requestUrl;
|
|
1146
|
+
} catch {
|
|
1147
|
+
return "/";
|
|
1148
|
+
}
|
|
1149
|
+
return safeRedirectTarget(url.searchParams.get("redirect")) ?? "/";
|
|
1150
|
+
}
|
|
1282
1151
|
|
|
1283
1152
|
// src/preview.ts
|
|
1284
1153
|
function createPreviewRoute(options) {
|
|
@@ -1299,13 +1168,7 @@ function createPreviewDisableRoute() {
|
|
|
1299
1168
|
async GET(request) {
|
|
1300
1169
|
const draft = await draftMode();
|
|
1301
1170
|
draft.disable();
|
|
1302
|
-
|
|
1303
|
-
try {
|
|
1304
|
-
target = safeRedirectTarget(new URL(request.url).searchParams.get("redirect"));
|
|
1305
|
-
} catch {
|
|
1306
|
-
target = "/";
|
|
1307
|
-
}
|
|
1308
|
-
redirect(target ?? "/");
|
|
1171
|
+
redirect(previewDisableTarget(request.url));
|
|
1309
1172
|
}
|
|
1310
1173
|
};
|
|
1311
1174
|
}
|
|
@@ -1345,8 +1208,7 @@ function createElytraHost(options) {
|
|
|
1345
1208
|
redirects: [],
|
|
1346
1209
|
collections: [...options.collections],
|
|
1347
1210
|
documents: [],
|
|
1348
|
-
assets: []
|
|
1349
|
-
sources: []
|
|
1211
|
+
assets: []
|
|
1350
1212
|
});
|
|
1351
1213
|
const loadContent = (request) => {
|
|
1352
1214
|
if (live) {
|
|
@@ -1422,6 +1284,6 @@ function createElytraHost(options) {
|
|
|
1422
1284
|
}
|
|
1423
1285
|
var PACKAGE = "@elytracms/next";
|
|
1424
1286
|
|
|
1425
|
-
export { CanvasRenderer, ELYTRA_HOST_ENV_KEYS, ElytraImage, PACKAGE, REVALIDATE_SIGNATURE_HEADER, assembleLiveSource, bodyItemsOf, canvasPageMetadata, canvasSitemapEntries,
|
|
1287
|
+
export { CanvasRenderer, ELYTRA_HOST_ENV_KEYS, ElytraImage, PACKAGE, REVALIDATE_SIGNATURE_HEADER, assembleLiveSource, bodyItemsOf, canvasPageMetadata, canvasSitemapEntries, collectionCacheTag, createAssetImageLoader, createCanvasRoute, createCanvasSitemap, createContentSnapshot, createElytraHost, createPreviewDisableRoute, createPreviewRoute, createRevalidateRoute, createStaticContentSource, defineHostComponents, docCacheTag, elytraHostImage, evaluatePreviewRequest, evaluateRevalidateRequest, firstBlockFullBleed, loadCachedEntry, loadLiveContent, mergeCacheTags, mergeMetadataOverParent, nextCacheTags, nextImagePrimitive, openGraphLocale, pathFromSlug, projectSweepTag, resolveCanvasPage, resolveGlobals, resolveHostEnv, revalidatePayloadSchema, routeCacheTag, safeRedirectTarget, scopeCacheTag, signRevalidateBody };
|
|
1426
1288
|
//# sourceMappingURL=index.js.map
|
|
1427
1289
|
//# sourceMappingURL=index.js.map
|