@elytracms/next 0.0.92 → 0.0.94
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/host-image.d.ts +14 -18
- package/dist/host-image.js.map +1 -1
- package/dist/index.d.ts +232 -1
- package/dist/index.js +231 -85
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { registerComponents, renderDocumentBodyItems, documentBodyItems, serializeListingQuery, documentBodyRichTextValues, documentBodyCompositionNodes, richTextEmbedsInValue, collectReferencedAssetIds, collectRichTextRefsInValue, collectReferencedRelations, collectReferencedListings, collectReferencedRichTextLinks,
|
|
2
|
-
import { Fragment as Fragment$1, isValidElement } from 'react';
|
|
1
|
+
import { registerComponents, createRenderContext, renderDocumentBodyItems, documentBodyItems, serializeListingQuery, documentBodyRichTextValues, documentBodyCompositionNodes, richTextEmbedsInValue, collectReferencedAssetIds, collectRichTextRefsInValue, collectReferencedRelations, collectReferencedListings, collectReferencedRichTextLinks, listingRequestOf } from '@elytracms/core/runtime-renderer';
|
|
3
2
|
import { isBlockSequenceValueShape } from '@elytracms/core/rich-text';
|
|
4
3
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
5
|
-
import { cacheTags, isDocumentHistory, resolvedAsset, createContentClient, createContentContext } from '@elytracms/core/content';
|
|
6
|
-
import { DEFAULT_SEO_FIELD, parseLocaleConfig, documentKey, liveRedirectRecords, SEO_SUBFIELDS, splitLocalePath, DRAFT_PERSPECTIVE, splitPath, localeUrlPath, variantOf, DRAFT_VARIANT, createRouter, presentationBodyFields,
|
|
4
|
+
import { cacheTags, isDocumentHistory, resolvedAsset, parseDeliveredDocuments, createContentClient, createContentContext } from '@elytracms/core/content';
|
|
5
|
+
import { DEFAULT_SEO_FIELD, parseLocaleConfig, documentKey, liveRedirectRecords, SEO_SUBFIELDS, splitLocalePath, DRAFT_PERSPECTIVE, splitPath, localeUrlPath, variantOf, DRAFT_VARIANT, createRouter, presentationBodyFields, redirectRecordSchema } from '@elytracms/core/cms-core';
|
|
7
6
|
export { DRAFT_PERSPECTIVE, documentSchema, localeConfigSchema, localeUrlPath, parseLocaleConfig, redirectRecordSchema, resolveLocale, routeRecordSchema, splitLocalePath, textDirection, variantOf } from '@elytracms/core/cms-core';
|
|
7
|
+
import { redirectRecordKind } from '@elytracms/core/persistence';
|
|
8
8
|
import { revalidateTag, cacheTag } from 'next/cache';
|
|
9
9
|
import { draftMode } from 'next/headers';
|
|
10
10
|
import { redirect, notFound, permanentRedirect } from 'next/navigation';
|
|
11
11
|
import { COMPOSITE_KEY_SEPARATOR } from '@elytracms/core/project-graph';
|
|
12
12
|
import { verifyRevalidationSignature, readRevalidationBody, REVALIDATE_SIGNATURE_HEADER } from '@elytracms/core/revalidation';
|
|
13
13
|
export { REVALIDATE_SIGNATURE_HEADER, revalidationEventSchema, signRevalidationBody, signedRevalidationRequest } from '@elytracms/core/revalidation';
|
|
14
|
+
import { INGEST_MAX_BODY_BYTES, readIngestPayload, INGEST_RECEIVER_PATH, signedIngestRequest, convexSiteUrl } from '@elytracms/core/ingest';
|
|
14
15
|
import NextImage from 'next/image';
|
|
15
16
|
import { normalizeImageSource } from '@elytracms/components';
|
|
16
17
|
import { projectFrontendUrl, resolveProject } from '@elytracms/core/studio-core';
|
|
@@ -34,33 +35,15 @@ function listingResolver(listings) {
|
|
|
34
35
|
return Array.isArray(baked) ? baked : null;
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
|
-
function
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (listing.state === "invalid") return listing.messages;
|
|
43
|
-
const baked = listings?.[serializeListingQuery(listing.query)];
|
|
44
|
-
return baked && !Array.isArray(baked) ? baked.errors.map((error) => error.message) : null;
|
|
45
|
-
}
|
|
46
|
-
function InvalidListingNotice(props) {
|
|
47
|
-
return /* @__PURE__ */ jsx("div", { "data-ec-fallback": "invalid-listing", "data-ec-node-id": props.nodeId, role: "note", children: `This listing can't run: ${props.messages.join(" ")}` });
|
|
38
|
+
function bakedListingFailure(listings) {
|
|
39
|
+
return (query) => {
|
|
40
|
+
const baked = listings[serializeListingQuery(query)];
|
|
41
|
+
return baked && !Array.isArray(baked) ? baked.errors.map((error) => error.message) : null;
|
|
42
|
+
};
|
|
48
43
|
}
|
|
49
44
|
function assetResolver(assets) {
|
|
50
45
|
const byId = new Map(assets.map((asset) => [asset.id, asset]));
|
|
51
|
-
return (id) =>
|
|
52
|
-
const asset = byId.get(id);
|
|
53
|
-
return asset ? {
|
|
54
|
-
url: asset.url,
|
|
55
|
-
width: asset.width,
|
|
56
|
-
height: asset.height,
|
|
57
|
-
alt: asset.alt,
|
|
58
|
-
focalPoint: asset.focalPoint,
|
|
59
|
-
// EC-420: carry the LQIP so a placed Image blurs up on delivery.
|
|
60
|
-
lqip: asset.lqip,
|
|
61
|
-
meta: asset.meta
|
|
62
|
-
} : null;
|
|
63
|
-
};
|
|
46
|
+
return (id) => byId.get(id) ?? null;
|
|
64
47
|
}
|
|
65
48
|
function UnresolvedPageFallback(props) {
|
|
66
49
|
return /* @__PURE__ */ jsx("div", { "data-ec-fallback": "unresolved-page", "data-ec-status": props.status, children: `This URL did not resolve to a page (status: ${props.status}).` });
|
|
@@ -68,41 +51,28 @@ function UnresolvedPageFallback(props) {
|
|
|
68
51
|
function MissingDocumentFallback() {
|
|
69
52
|
return /* @__PURE__ */ jsx("div", { "data-ec-fallback": "missing-document", children: `Route resolved but carried no page document to render.` });
|
|
70
53
|
}
|
|
71
|
-
function stripServerErrorBoundary(_node, rendered) {
|
|
72
|
-
if (isValidElement(rendered) && rendered.type === RenderErrorBoundary) {
|
|
73
|
-
return rendered.props.children ?? null;
|
|
74
|
-
}
|
|
75
|
-
return rendered;
|
|
76
|
-
}
|
|
77
54
|
async function CanvasRenderer(props) {
|
|
78
55
|
const { result, components } = props;
|
|
79
56
|
if (result.status !== "ok") {
|
|
80
57
|
return /* @__PURE__ */ jsx(UnresolvedPageFallback, { status: result.status });
|
|
81
58
|
}
|
|
82
|
-
const ctx = {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const stripped = stripServerErrorBoundary(node, rendered);
|
|
98
|
-
const failure = listingFailureOf(node, components, props.listings);
|
|
99
|
-
if (!failure) return stripped;
|
|
100
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
101
|
-
/* @__PURE__ */ jsx(InvalidListingNotice, { nodeId: node.id, messages: failure }),
|
|
102
|
-
stripped
|
|
103
|
-
] }, node.id);
|
|
59
|
+
const ctx = createRenderContext({
|
|
60
|
+
components,
|
|
61
|
+
mode: "delivery",
|
|
62
|
+
source: {
|
|
63
|
+
resolveAsset: assetResolver(props.assets ?? []),
|
|
64
|
+
// EC-254: wire relation population only when the host baked the populated
|
|
65
|
+
// targets, so a host that did not opt in keeps the raw-ref passthrough.
|
|
66
|
+
...props.relations ? { resolveRelation: relationResolver(props.relations) } : {},
|
|
67
|
+
// EC-255: wire the listing resolver only when the host baked the query results,
|
|
68
|
+
// so a host that did not opt in keeps listing props empty (the block's own state).
|
|
69
|
+
...props.listings ? { resolveListing: listingResolver(props.listings), listingFailure: bakedListingFailure(props.listings) } : {},
|
|
70
|
+
// EC-291: wire the rich-text link resolver only when the host baked the link map,
|
|
71
|
+
// so a host that did not opt in degrades a reference link to the visible
|
|
72
|
+
// `unresolved-ref` issue (EC-290) rather than a wrong href.
|
|
73
|
+
...props.links ? { resolveLink: linkResolver(props.links) } : {}
|
|
104
74
|
}
|
|
105
|
-
};
|
|
75
|
+
});
|
|
106
76
|
const document = result.document;
|
|
107
77
|
if (document) {
|
|
108
78
|
return /* @__PURE__ */ jsx(Fragment, { children: renderDocumentBodyItems(bodyItemsOf(document, props.presentation), ctx) });
|
|
@@ -181,7 +151,8 @@ function createStaticContentSource(data) {
|
|
|
181
151
|
locales
|
|
182
152
|
}),
|
|
183
153
|
routes,
|
|
184
|
-
redirects
|
|
154
|
+
redirects,
|
|
155
|
+
...data.sourceIssues ? { sourceIssues: data.sourceIssues } : {}
|
|
185
156
|
});
|
|
186
157
|
return {
|
|
187
158
|
projectId: data.projectId,
|
|
@@ -204,7 +175,7 @@ async function createContentSnapshot(adapter, projectId, options) {
|
|
|
204
175
|
const documentRecords = await adapter.cms.listDocuments(projectId);
|
|
205
176
|
const versionRecords = await adapter.cms.listAllDocumentVersions(projectId);
|
|
206
177
|
const assetRecords = await adapter.assets.listAssets(projectId);
|
|
207
|
-
const redirectRecords = await adapter.
|
|
178
|
+
const redirectRecords = await adapter.records(redirectRecordKind).list(projectId);
|
|
208
179
|
const redirects = liveRedirectRecords(redirectRecords);
|
|
209
180
|
const versionsByKey = /* @__PURE__ */ new Map();
|
|
210
181
|
for (const record2 of versionRecords) {
|
|
@@ -243,16 +214,12 @@ function seoContextOf(result, assets = []) {
|
|
|
243
214
|
function str(value) {
|
|
244
215
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
245
216
|
}
|
|
246
|
-
function looksLikeUrl(value) {
|
|
247
|
-
return /^(https?:)?\/\//.test(value) || value.startsWith("/");
|
|
248
|
-
}
|
|
249
217
|
function resolveSeoImage(value, assets) {
|
|
250
218
|
if (value && typeof value === "object") {
|
|
251
219
|
const url = value.url;
|
|
252
220
|
return typeof url === "string" && url.length > 0 ? url : null;
|
|
253
221
|
}
|
|
254
222
|
if (typeof value !== "string" || value.length === 0) return null;
|
|
255
|
-
if (looksLikeUrl(value)) return value;
|
|
256
223
|
return assets.find((asset) => asset.id === value)?.url ?? null;
|
|
257
224
|
}
|
|
258
225
|
function openGraphLocale(locale) {
|
|
@@ -318,6 +285,15 @@ function canvasWrapContext(result, info) {
|
|
|
318
285
|
result
|
|
319
286
|
};
|
|
320
287
|
}
|
|
288
|
+
function canvasBeaconProps(result, info) {
|
|
289
|
+
const document = result.document;
|
|
290
|
+
return {
|
|
291
|
+
path: info.url,
|
|
292
|
+
status: 200,
|
|
293
|
+
locale: result.tags.locale,
|
|
294
|
+
...document ? { collection: document.collection, documentId: document.id } : {}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
321
297
|
function pathFromSlug(slug) {
|
|
322
298
|
return "/" + (slug ?? []).join("/");
|
|
323
299
|
}
|
|
@@ -625,8 +601,13 @@ function createCanvasRoute(options) {
|
|
|
625
601
|
presentation: outcome.presentation
|
|
626
602
|
}
|
|
627
603
|
);
|
|
628
|
-
|
|
629
|
-
|
|
604
|
+
const page = options.wrap ? options.wrap(rendered, canvasWrapContext(outcome.result, { url, perspective })) : rendered;
|
|
605
|
+
const Beacon = options.analytics;
|
|
606
|
+
if (!Beacon || perspective !== "published") return page;
|
|
607
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
608
|
+
page,
|
|
609
|
+
/* @__PURE__ */ jsx(Beacon, { ...canvasBeaconProps(outcome.result, { url}) })
|
|
610
|
+
] });
|
|
630
611
|
}
|
|
631
612
|
}
|
|
632
613
|
};
|
|
@@ -793,6 +774,177 @@ function createRevalidateRoute(options) {
|
|
|
793
774
|
}
|
|
794
775
|
};
|
|
795
776
|
}
|
|
777
|
+
function createFixedWindowLimiter(options) {
|
|
778
|
+
const windowMs = options.windowMs ?? 6e4;
|
|
779
|
+
const now = options.now ?? Date.now;
|
|
780
|
+
const windows = /* @__PURE__ */ new Map();
|
|
781
|
+
return {
|
|
782
|
+
hit(key) {
|
|
783
|
+
const at = now();
|
|
784
|
+
for (const [held, window2] of windows) if (at - window2.windowStart >= windowMs) windows.delete(held);
|
|
785
|
+
const window = windows.get(key);
|
|
786
|
+
if (!window) {
|
|
787
|
+
windows.set(key, { windowStart: at, count: 1 });
|
|
788
|
+
return { allowed: true };
|
|
789
|
+
}
|
|
790
|
+
if (window.count >= options.limit) {
|
|
791
|
+
return { allowed: false, retryAfterSeconds: Math.max(1, Math.ceil((window.windowStart + windowMs - at) / 1e3)) };
|
|
792
|
+
}
|
|
793
|
+
window.count += 1;
|
|
794
|
+
return { allowed: true };
|
|
795
|
+
},
|
|
796
|
+
size: () => windows.size
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
function defaultIngestClientKey(request) {
|
|
800
|
+
const forwarded = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim();
|
|
801
|
+
if (forwarded) return forwarded;
|
|
802
|
+
const real = request.headers.get("x-real-ip")?.trim();
|
|
803
|
+
return real || "unknown";
|
|
804
|
+
}
|
|
805
|
+
function isCrossOrigin(request) {
|
|
806
|
+
const site = request.headers.get("sec-fetch-site");
|
|
807
|
+
if (site && site !== "same-origin" && site !== "none") return true;
|
|
808
|
+
const origin = request.headers.get("origin");
|
|
809
|
+
if (!origin) return false;
|
|
810
|
+
try {
|
|
811
|
+
return new URL(origin).origin !== new URL(request.url).origin;
|
|
812
|
+
} catch {
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
async function evaluateIngestRequest(input) {
|
|
817
|
+
const { request } = input;
|
|
818
|
+
if (request.method !== "POST") {
|
|
819
|
+
return { ok: false, status: 405, body: { error: "Only POST is accepted." }, headers: { allow: "POST" } };
|
|
820
|
+
}
|
|
821
|
+
if (!input.configured) {
|
|
822
|
+
return { ok: false, status: 503, body: { error: "Ingest is not configured on this site." } };
|
|
823
|
+
}
|
|
824
|
+
if (isCrossOrigin(request)) {
|
|
825
|
+
return { ok: false, status: 403, body: { error: "Cross-origin requests are not accepted." } };
|
|
826
|
+
}
|
|
827
|
+
const limited = input.limiter.hit(input.clientKey(request));
|
|
828
|
+
if (!limited.allowed) {
|
|
829
|
+
return {
|
|
830
|
+
ok: false,
|
|
831
|
+
status: 429,
|
|
832
|
+
body: { error: "Too many requests \u2014 try again shortly." },
|
|
833
|
+
headers: { "retry-after": String(limited.retryAfterSeconds) }
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
if (input.rawBody === void 0) {
|
|
837
|
+
return { ok: false, status: 413, body: { error: "The payload is too large." } };
|
|
838
|
+
}
|
|
839
|
+
const read = readIngestPayload(input.rawBody);
|
|
840
|
+
if (!read.ok) {
|
|
841
|
+
return { ok: false, status: 400, body: { error: read.message, ...read.issues ? { issues: read.issues } : {} } };
|
|
842
|
+
}
|
|
843
|
+
if (input.botGate) {
|
|
844
|
+
const verdict = await input.botGate({ request, payload: read.value });
|
|
845
|
+
if (!verdict.allow) {
|
|
846
|
+
return { ok: false, status: 403, body: { error: "Refused by the bot gate.", gate: "bot", reason: verdict.reason } };
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
if (!input.enrich) return { ok: true, payload: read.value };
|
|
850
|
+
return enrichPayload(input.enrich, request, read.value);
|
|
851
|
+
}
|
|
852
|
+
async function enrichPayload(enrich, request, payload) {
|
|
853
|
+
let enriched;
|
|
854
|
+
try {
|
|
855
|
+
enriched = await enrich({ request, payload });
|
|
856
|
+
} catch (error) {
|
|
857
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
858
|
+
return { ok: false, status: 500, body: { error: `The site's ingest enrichment failed: ${message}` } };
|
|
859
|
+
}
|
|
860
|
+
const body = JSON.stringify(enriched ?? null);
|
|
861
|
+
const reread = readIngestPayload(body);
|
|
862
|
+
if (!reread.ok) {
|
|
863
|
+
return {
|
|
864
|
+
ok: false,
|
|
865
|
+
status: 500,
|
|
866
|
+
body: { error: "The site's ingest enrichment returned an invalid payload.", ...reread.issues ? { issues: reread.issues } : {} }
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
if (new TextEncoder().encode(body).byteLength > INGEST_MAX_BODY_BYTES) {
|
|
870
|
+
return { ok: false, status: 500, body: { error: "The site's ingest enrichment grew the payload past the receiver's cap." } };
|
|
871
|
+
}
|
|
872
|
+
return { ok: true, payload: reread.value };
|
|
873
|
+
}
|
|
874
|
+
async function readCappedBody(request, maxBytes = INGEST_MAX_BODY_BYTES) {
|
|
875
|
+
const declared = Number(request.headers.get("content-length"));
|
|
876
|
+
if (Number.isFinite(declared) && declared > maxBytes) return void 0;
|
|
877
|
+
if (!request.body) return "";
|
|
878
|
+
const reader = request.body.getReader();
|
|
879
|
+
const chunks = [];
|
|
880
|
+
let total = 0;
|
|
881
|
+
for (; ; ) {
|
|
882
|
+
const { done, value } = await reader.read();
|
|
883
|
+
if (done) break;
|
|
884
|
+
total += value.byteLength;
|
|
885
|
+
if (total > maxBytes) {
|
|
886
|
+
await reader.cancel().catch(() => void 0);
|
|
887
|
+
return void 0;
|
|
888
|
+
}
|
|
889
|
+
chunks.push(value);
|
|
890
|
+
}
|
|
891
|
+
const bytes = new Uint8Array(total);
|
|
892
|
+
let offset = 0;
|
|
893
|
+
for (const chunk of chunks) {
|
|
894
|
+
bytes.set(chunk, offset);
|
|
895
|
+
offset += chunk.byteLength;
|
|
896
|
+
}
|
|
897
|
+
return new TextDecoder().decode(bytes);
|
|
898
|
+
}
|
|
899
|
+
var NO_STORE = { "cache-control": "no-store" };
|
|
900
|
+
function createIngestRoute(options) {
|
|
901
|
+
const maxBodyBytes = options.maxBodyBytes ?? INGEST_MAX_BODY_BYTES;
|
|
902
|
+
if (maxBodyBytes > INGEST_MAX_BODY_BYTES || maxBodyBytes <= 0) {
|
|
903
|
+
throw new Error(`createIngestRoute: maxBodyBytes must be between 1 and ${INGEST_MAX_BODY_BYTES} (the receiver's cap).`);
|
|
904
|
+
}
|
|
905
|
+
const limiter = createFixedWindowLimiter({
|
|
906
|
+
limit: options.maxRequestsPerMinute ?? 30,
|
|
907
|
+
...options.now ? { now: options.now } : {}
|
|
908
|
+
});
|
|
909
|
+
const clientKey = options.clientKey ?? defaultIngestClientKey;
|
|
910
|
+
const receiver = options.siteUrl ? `${options.siteUrl.replace(/\/+$/, "")}${INGEST_RECEIVER_PATH}` : void 0;
|
|
911
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
912
|
+
return {
|
|
913
|
+
async POST(request) {
|
|
914
|
+
const rawBody = request.method === "POST" ? await readCappedBody(request, maxBodyBytes) : "";
|
|
915
|
+
const evaluation = await evaluateIngestRequest({
|
|
916
|
+
request,
|
|
917
|
+
rawBody,
|
|
918
|
+
configured: options.secret !== void 0 && options.secret.length > 0 && receiver !== void 0,
|
|
919
|
+
limiter,
|
|
920
|
+
clientKey,
|
|
921
|
+
...options.botGate ? { botGate: options.botGate } : {},
|
|
922
|
+
...options.enrich ? { enrich: options.enrich } : {}
|
|
923
|
+
});
|
|
924
|
+
if (!evaluation.ok) {
|
|
925
|
+
return Response.json(evaluation.body, { status: evaluation.status, headers: { ...NO_STORE, ...evaluation.headers } });
|
|
926
|
+
}
|
|
927
|
+
const signed = await signedIngestRequest(evaluation.payload, options.secret);
|
|
928
|
+
let answer;
|
|
929
|
+
try {
|
|
930
|
+
answer = await fetchImpl(receiver, { method: "POST", headers: signed.headers, body: signed.body });
|
|
931
|
+
} catch {
|
|
932
|
+
return Response.json({ error: "The ingest receiver could not be reached." }, { status: 502, headers: NO_STORE });
|
|
933
|
+
}
|
|
934
|
+
if (answer.status === 401) {
|
|
935
|
+
return Response.json(
|
|
936
|
+
{ error: "The ingest receiver refused this site's signature \u2014 INGEST_SECRET must equal the deployment's ELYTRA_INGEST_SECRET." },
|
|
937
|
+
{ status: 502, headers: NO_STORE }
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
const text = await answer.text();
|
|
941
|
+
return new Response(text, {
|
|
942
|
+
status: answer.status,
|
|
943
|
+
headers: { ...NO_STORE, "content-type": answer.headers.get("content-type") ?? "application/json" }
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
}
|
|
796
948
|
var LQIP_MAX_EDGE = 24;
|
|
797
949
|
function fitLqipLongEdge(width, height) {
|
|
798
950
|
const scale = Math.min(1, LQIP_MAX_EDGE / Math.max(width, height));
|
|
@@ -878,7 +1030,7 @@ function elytraHostImage(options = {}) {
|
|
|
878
1030
|
return ({ asset, alt, className, priority, sizes, loading }) => /* @__PURE__ */ jsx(
|
|
879
1031
|
ElytraImage,
|
|
880
1032
|
{
|
|
881
|
-
asset: { ...asset, alt },
|
|
1033
|
+
asset: { ...asset, alt: alt ?? asset.alt },
|
|
882
1034
|
...className !== void 0 ? { className } : {},
|
|
883
1035
|
...priority ? { priority: true } : {},
|
|
884
1036
|
...sizes !== void 0 ? { sizes } : {},
|
|
@@ -901,7 +1053,8 @@ var ELYTRA_HOST_ENV_KEYS = {
|
|
|
901
1053
|
convexUrl: "ELYTRA_CONVEX_URL",
|
|
902
1054
|
projectId: "ELYTRA_PROJECT_ID",
|
|
903
1055
|
draftToken: "DRAFT_PREVIEW_TOKEN",
|
|
904
|
-
revalidateSecret: "REVALIDATE_SECRET"
|
|
1056
|
+
revalidateSecret: "REVALIDATE_SECRET",
|
|
1057
|
+
ingestSecret: "INGEST_SECRET"
|
|
905
1058
|
};
|
|
906
1059
|
function cleaned(value) {
|
|
907
1060
|
const trimmed = value?.trim();
|
|
@@ -930,14 +1083,6 @@ async function runConvexQuery(fetchImpl, convexUrl, path, args) {
|
|
|
930
1083
|
}
|
|
931
1084
|
return body.value;
|
|
932
1085
|
}
|
|
933
|
-
function parseDocuments(rows) {
|
|
934
|
-
const documents = [];
|
|
935
|
-
for (const row of rows) {
|
|
936
|
-
const parsed = documentSchema.safeParse(row);
|
|
937
|
-
if (parsed.success) documents.push(parsed.data);
|
|
938
|
-
}
|
|
939
|
-
return documents;
|
|
940
|
-
}
|
|
941
1086
|
function parseRedirects(rows) {
|
|
942
1087
|
const redirects = [];
|
|
943
1088
|
for (const row of rows) {
|
|
@@ -948,7 +1093,7 @@ function parseRedirects(rows) {
|
|
|
948
1093
|
}
|
|
949
1094
|
function assembleLiveSource(payload, options, perspective) {
|
|
950
1095
|
const locales = options.locales;
|
|
951
|
-
const documents =
|
|
1096
|
+
const { documents, issues: sourceIssues } = parseDeliveredDocuments(payload.documents);
|
|
952
1097
|
const routes = [...options.routes];
|
|
953
1098
|
const documentSources = perspective === "published" ? documents.map((doc) => ({ draft: doc, published: doc, versions: [] })) : documents;
|
|
954
1099
|
return createStaticContentSource({
|
|
@@ -961,7 +1106,8 @@ function assembleLiveSource(payload, options, perspective) {
|
|
|
961
1106
|
redirects: parseRedirects(payload.redirects ?? []),
|
|
962
1107
|
collections: [...options.collections],
|
|
963
1108
|
documents: documentSources,
|
|
964
|
-
assets: payload.assets
|
|
1109
|
+
assets: payload.assets,
|
|
1110
|
+
...sourceIssues.length > 0 ? { sourceIssues } : {}
|
|
965
1111
|
});
|
|
966
1112
|
}
|
|
967
1113
|
async function loadLiveContent(options, perspective) {
|
|
@@ -1023,11 +1169,7 @@ function resolveGlobals(source, options = {}) {
|
|
|
1023
1169
|
const navDocs = listDocs(navigationCollection);
|
|
1024
1170
|
const navigation = (slot) => navDocs.find((doc) => doc.alias === slot) ?? null;
|
|
1025
1171
|
const assetUrl = (value) => {
|
|
1026
|
-
if (typeof value === "string")
|
|
1027
|
-
const found = assetById.get(value);
|
|
1028
|
-
if (found) return found.url;
|
|
1029
|
-
return value.startsWith("http") || value.startsWith("/") ? value : void 0;
|
|
1030
|
-
}
|
|
1172
|
+
if (typeof value === "string") return assetById.get(value)?.url;
|
|
1031
1173
|
const url = record(value).url;
|
|
1032
1174
|
return typeof url === "string" && url.length > 0 ? url : void 0;
|
|
1033
1175
|
};
|
|
@@ -1182,8 +1324,6 @@ function createPreviewDisableRoute() {
|
|
|
1182
1324
|
}
|
|
1183
1325
|
};
|
|
1184
1326
|
}
|
|
1185
|
-
|
|
1186
|
-
// src/host-kit.ts
|
|
1187
1327
|
function resolveHostProject(entry) {
|
|
1188
1328
|
const result = resolveProject(entry);
|
|
1189
1329
|
if (result.ok) return result.project;
|
|
@@ -1284,6 +1424,11 @@ function createElytraHost(options) {
|
|
|
1284
1424
|
});
|
|
1285
1425
|
const previewRoute = () => createPreviewRoute({ token: env.draftToken });
|
|
1286
1426
|
const previewDisableRoute = () => createPreviewDisableRoute();
|
|
1427
|
+
const ingestRoute = (overrides = {}) => createIngestRoute({
|
|
1428
|
+
...overrides,
|
|
1429
|
+
secret: env.ingestSecret,
|
|
1430
|
+
siteUrl: "siteUrl" in overrides ? overrides.siteUrl : convexSiteUrl(env.convexUrl)
|
|
1431
|
+
});
|
|
1287
1432
|
const pageOpensFullBleed = async (slug) => {
|
|
1288
1433
|
const perspective = await requestPerspective();
|
|
1289
1434
|
const source = await loadContent({ perspective });
|
|
@@ -1309,11 +1454,12 @@ function createElytraHost(options) {
|
|
|
1309
1454
|
revalidateRoute,
|
|
1310
1455
|
previewRoute,
|
|
1311
1456
|
previewDisableRoute,
|
|
1457
|
+
ingestRoute,
|
|
1312
1458
|
pageOpensFullBleed
|
|
1313
1459
|
};
|
|
1314
1460
|
}
|
|
1315
1461
|
var PACKAGE = "@elytracms/next";
|
|
1316
1462
|
|
|
1317
|
-
export { CANVAS_VALIDATION_SLUG, CanvasRenderer, ELYTRA_HOST_ENV_KEYS, ElytraImage, PACKAGE, assembleLiveSource, bodyItemsOf, canvasSitemapEntries, collectionCacheTag, createAssetImageLoader, createCanvasRoute, createCanvasSitemap, createContentSnapshot, createElytraHost, createPreviewDisableRoute, createPreviewRoute, createRevalidateRoute, createStaticContentSource, defineHostComponents, docCacheTag, elytraHostImage, evaluatePreviewRequest, evaluateRevalidateRequest, firstBlockFullBleed, loadLiveContent, mergeCacheTags, mergeMetadataOverParent, nextCacheTags, openGraphLocale, pathFromSlug, projectSweepTag, requestPerspective, resolveCanvasPage, resolveGlobals, resolveHostEnv, revalidationTags, routeCacheTag, safeRedirectTarget, scopeCacheTag, seoContextOf, seoMetadata, seoObjectOf, withNextImage };
|
|
1463
|
+
export { CANVAS_VALIDATION_SLUG, CanvasRenderer, ELYTRA_HOST_ENV_KEYS, ElytraImage, PACKAGE, assembleLiveSource, bodyItemsOf, canvasBeaconProps, canvasSitemapEntries, collectionCacheTag, createAssetImageLoader, createCanvasRoute, createCanvasSitemap, createContentSnapshot, createElytraHost, createFixedWindowLimiter, createIngestRoute, createPreviewDisableRoute, createPreviewRoute, createRevalidateRoute, createStaticContentSource, defaultIngestClientKey, defineHostComponents, docCacheTag, elytraHostImage, evaluateIngestRequest, evaluatePreviewRequest, evaluateRevalidateRequest, firstBlockFullBleed, isCrossOrigin, loadLiveContent, mergeCacheTags, mergeMetadataOverParent, nextCacheTags, openGraphLocale, pathFromSlug, projectSweepTag, readCappedBody, requestPerspective, resolveCanvasPage, resolveGlobals, resolveHostEnv, revalidationTags, routeCacheTag, safeRedirectTarget, scopeCacheTag, seoContextOf, seoMetadata, seoObjectOf, withNextImage };
|
|
1318
1464
|
//# sourceMappingURL=index.js.map
|
|
1319
1465
|
//# sourceMappingURL=index.js.map
|