@elytracms/next 0.0.93 → 0.0.95
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 +245 -5
- package/dist/index.js +235 -28
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { registerComponents, createRenderContext, renderDocumentBodyItems, documentBodyItems, serializeListingQuery, documentBodyRichTextValues, documentBodyCompositionNodes, richTextEmbedsInValue, collectReferencedAssetIds, collectRichTextRefsInValue, collectReferencedRelations, collectReferencedListings, collectReferencedRichTextLinks, listingRequestOf } from '@elytracms/core/runtime-renderer';
|
|
2
2
|
import { isBlockSequenceValueShape } from '@elytracms/core/rich-text';
|
|
3
|
-
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
4
|
-
import { cacheTags, isDocumentHistory, resolvedAsset, parseDeliveredDocuments, createContentClient, createContentContext } from '@elytracms/core/content';
|
|
5
|
-
import { DEFAULT_SEO_FIELD, parseLocaleConfig, documentKey,
|
|
3
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
4
|
+
import { cacheTags, isDocumentHistory, assetWithoutUrlIssue, resolvedAsset, parseDeliveredDocuments, parseDeliveredRedirects, createContentClient, createContentContext } from '@elytracms/core/content';
|
|
5
|
+
import { DEFAULT_SEO_FIELD, parseLocaleConfig, documentKey, readLiveRedirects, SEO_SUBFIELDS, splitLocalePath, DRAFT_PERSPECTIVE, splitPath, localeUrlPath, variantOf, DRAFT_VARIANT, createRouter, presentationBodyFields } from '@elytracms/core/cms-core';
|
|
6
6
|
export { DRAFT_PERSPECTIVE, documentSchema, localeConfigSchema, localeUrlPath, parseLocaleConfig, redirectRecordSchema, resolveLocale, routeRecordSchema, splitLocalePath, textDirection, variantOf } from '@elytracms/core/cms-core';
|
|
7
|
+
import { assetReadLocale, redirectRecordKind } from '@elytracms/core/persistence';
|
|
7
8
|
import { revalidateTag, cacheTag } from 'next/cache';
|
|
8
9
|
import { draftMode } from 'next/headers';
|
|
9
10
|
import { redirect, notFound, permanentRedirect } from 'next/navigation';
|
|
10
11
|
import { COMPOSITE_KEY_SEPARATOR } from '@elytracms/core/project-graph';
|
|
11
12
|
import { verifyRevalidationSignature, readRevalidationBody, REVALIDATE_SIGNATURE_HEADER } from '@elytracms/core/revalidation';
|
|
12
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';
|
|
13
15
|
import NextImage from 'next/image';
|
|
14
16
|
import { normalizeImageSource } from '@elytracms/components';
|
|
15
17
|
import { projectFrontendUrl, resolveProject } from '@elytracms/core/studio-core';
|
|
@@ -120,7 +122,7 @@ function createStaticContentSource(data) {
|
|
|
120
122
|
const locales = parseLocaleConfig(data.locales);
|
|
121
123
|
const routes = data.routes ?? [];
|
|
122
124
|
const redirects = data.redirects ?? [];
|
|
123
|
-
const assetUrl =
|
|
125
|
+
const assetUrl = (asset) => asset.url ?? data.assetUrl?.(asset);
|
|
124
126
|
const collections = /* @__PURE__ */ new Map();
|
|
125
127
|
for (const collection of data.collections ?? []) collections.set(collection.id, collection);
|
|
126
128
|
const documents = /* @__PURE__ */ new Map();
|
|
@@ -134,12 +136,16 @@ function createStaticContentSource(data) {
|
|
|
134
136
|
}
|
|
135
137
|
const assets = /* @__PURE__ */ new Map();
|
|
136
138
|
for (const record2 of data.assets ?? []) assets.set(record2.id, record2);
|
|
139
|
+
const sourceIssues = [
|
|
140
|
+
...data.sourceIssues ?? [],
|
|
141
|
+
...[...assets.values()].filter((record2) => !assetUrl(record2)).map((record2) => assetWithoutUrlIssue(record2.id))
|
|
142
|
+
];
|
|
137
143
|
const lookupFor = (_perspective) => ({
|
|
138
144
|
collection: (id) => collections.get(id),
|
|
139
145
|
document: (ref) => documents.get(documentKey(ref)),
|
|
140
146
|
documents: (collectionId) => byCollection.get(collectionId) ?? [],
|
|
141
147
|
asset: (id) => assets.get(id),
|
|
142
|
-
assetUrl
|
|
148
|
+
assetUrl
|
|
143
149
|
});
|
|
144
150
|
const client = (init) => createContentClient({
|
|
145
151
|
lookup: lookupFor(init.perspective),
|
|
@@ -150,7 +156,7 @@ function createStaticContentSource(data) {
|
|
|
150
156
|
}),
|
|
151
157
|
routes,
|
|
152
158
|
redirects,
|
|
153
|
-
...
|
|
159
|
+
...sourceIssues.length > 0 ? { sourceIssues } : {}
|
|
154
160
|
});
|
|
155
161
|
return {
|
|
156
162
|
projectId: data.projectId,
|
|
@@ -161,7 +167,14 @@ function createStaticContentSource(data) {
|
|
|
161
167
|
// `asset`-typed Image prop (an asset id) to a usable url + intrinsic
|
|
162
168
|
// dimensions. Baked into the (serializable) render outcome by the route
|
|
163
169
|
// helper, so a cached page keeps its images during a backend outage.
|
|
164
|
-
|
|
170
|
+
// EC-632: in the requested locale (the route helper passes the request's), so a
|
|
171
|
+
// localized alt is baked in the locale the page renders in.
|
|
172
|
+
// EC-633: a record with no URL is no resolved asset — not baked, so the page renders
|
|
173
|
+
// the empty state the canvas renders for it (and `sourceIssues` names it).
|
|
174
|
+
assets: (locale) => [...assets.values()].flatMap((record2) => {
|
|
175
|
+
const asset = resolvedAsset(record2, assetUrl(record2), assetReadLocale(locales, locale));
|
|
176
|
+
return asset ? [asset] : [];
|
|
177
|
+
}),
|
|
165
178
|
// EC-499: the schema the route helper reads a collection's declared
|
|
166
179
|
// `presentation` from (which field is the delivery body, which component
|
|
167
180
|
// wraps it). Already prefetched for the lookup above — exposing it costs
|
|
@@ -173,8 +186,16 @@ async function createContentSnapshot(adapter, projectId, options) {
|
|
|
173
186
|
const documentRecords = await adapter.cms.listDocuments(projectId);
|
|
174
187
|
const versionRecords = await adapter.cms.listAllDocumentVersions(projectId);
|
|
175
188
|
const assetRecords = await adapter.assets.listAssets(projectId);
|
|
176
|
-
const
|
|
177
|
-
const
|
|
189
|
+
const blobs = adapter.blobs;
|
|
190
|
+
const servedUrls = /* @__PURE__ */ new Map();
|
|
191
|
+
await Promise.all(
|
|
192
|
+
assetRecords.filter((record2) => !record2.url).map(async (record2) => {
|
|
193
|
+
const url = blobs.resolveUrl ? await blobs.resolveUrl(record2.storageKey).catch(() => void 0) : blobs.url(record2.storageKey);
|
|
194
|
+
servedUrls.set(record2.id, url || void 0);
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
const redirectRecords = await adapter.records(redirectRecordKind).list(projectId);
|
|
198
|
+
const { redirects, issues: redirectIssues } = readLiveRedirects(redirectRecords);
|
|
178
199
|
const versionsByKey = /* @__PURE__ */ new Map();
|
|
179
200
|
for (const record2 of versionRecords) {
|
|
180
201
|
const key = `${record2.collection}:${record2.docId}`;
|
|
@@ -192,13 +213,15 @@ async function createContentSnapshot(adapter, projectId, options) {
|
|
|
192
213
|
});
|
|
193
214
|
return createStaticContentSource({
|
|
194
215
|
projectId,
|
|
216
|
+
// EC-628: only for the stored redirects this source serves (an explicit override replaces them).
|
|
217
|
+
...options.redirects === void 0 && redirectIssues.length > 0 ? { sourceIssues: redirectIssues } : {},
|
|
195
218
|
locales: options.locales,
|
|
196
219
|
...options.routes ? { routes: options.routes } : {},
|
|
197
220
|
redirects: options.redirects ? [...options.redirects] : redirects,
|
|
198
221
|
collections: options.collections,
|
|
199
222
|
documents,
|
|
200
223
|
assets: assetRecords,
|
|
201
|
-
assetUrl: (asset) =>
|
|
224
|
+
assetUrl: (asset) => servedUrls.get(asset.id)
|
|
202
225
|
});
|
|
203
226
|
}
|
|
204
227
|
function seoContextOf(result, assets = []) {
|
|
@@ -283,6 +306,15 @@ function canvasWrapContext(result, info) {
|
|
|
283
306
|
result
|
|
284
307
|
};
|
|
285
308
|
}
|
|
309
|
+
function canvasBeaconProps(result, info) {
|
|
310
|
+
const document = result.document;
|
|
311
|
+
return {
|
|
312
|
+
path: info.url,
|
|
313
|
+
status: 200,
|
|
314
|
+
locale: result.tags.locale,
|
|
315
|
+
...document ? { collection: document.collection, documentId: document.id } : {}
|
|
316
|
+
};
|
|
317
|
+
}
|
|
286
318
|
function pathFromSlug(slug) {
|
|
287
319
|
return "/" + (slug ?? []).join("/");
|
|
288
320
|
}
|
|
@@ -399,7 +431,7 @@ async function resolveCanvasPage(source, url, perspective, options = {}) {
|
|
|
399
431
|
}
|
|
400
432
|
const presentation = presentationFor(source, result);
|
|
401
433
|
const body = pageBodyOf(result, presentation);
|
|
402
|
-
const allAssets = source.assets?.() ?? [];
|
|
434
|
+
const allAssets = source.assets?.(locale) ?? [];
|
|
403
435
|
const bakedAssets = options.registry ? scopeAssetsToPage(
|
|
404
436
|
allAssets,
|
|
405
437
|
options.registry,
|
|
@@ -590,8 +622,13 @@ function createCanvasRoute(options) {
|
|
|
590
622
|
presentation: outcome.presentation
|
|
591
623
|
}
|
|
592
624
|
);
|
|
593
|
-
|
|
594
|
-
|
|
625
|
+
const page = options.wrap ? options.wrap(rendered, canvasWrapContext(outcome.result, { url, perspective })) : rendered;
|
|
626
|
+
const Beacon = options.analytics;
|
|
627
|
+
if (!Beacon || perspective !== "published") return page;
|
|
628
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
629
|
+
page,
|
|
630
|
+
/* @__PURE__ */ jsx(Beacon, { ...canvasBeaconProps(outcome.result, { url}) })
|
|
631
|
+
] });
|
|
595
632
|
}
|
|
596
633
|
}
|
|
597
634
|
};
|
|
@@ -758,6 +795,177 @@ function createRevalidateRoute(options) {
|
|
|
758
795
|
}
|
|
759
796
|
};
|
|
760
797
|
}
|
|
798
|
+
function createFixedWindowLimiter(options) {
|
|
799
|
+
const windowMs = options.windowMs ?? 6e4;
|
|
800
|
+
const now = options.now ?? Date.now;
|
|
801
|
+
const windows = /* @__PURE__ */ new Map();
|
|
802
|
+
return {
|
|
803
|
+
hit(key) {
|
|
804
|
+
const at = now();
|
|
805
|
+
for (const [held, window2] of windows) if (at - window2.windowStart >= windowMs) windows.delete(held);
|
|
806
|
+
const window = windows.get(key);
|
|
807
|
+
if (!window) {
|
|
808
|
+
windows.set(key, { windowStart: at, count: 1 });
|
|
809
|
+
return { allowed: true };
|
|
810
|
+
}
|
|
811
|
+
if (window.count >= options.limit) {
|
|
812
|
+
return { allowed: false, retryAfterSeconds: Math.max(1, Math.ceil((window.windowStart + windowMs - at) / 1e3)) };
|
|
813
|
+
}
|
|
814
|
+
window.count += 1;
|
|
815
|
+
return { allowed: true };
|
|
816
|
+
},
|
|
817
|
+
size: () => windows.size
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
function defaultIngestClientKey(request) {
|
|
821
|
+
const forwarded = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim();
|
|
822
|
+
if (forwarded) return forwarded;
|
|
823
|
+
const real = request.headers.get("x-real-ip")?.trim();
|
|
824
|
+
return real || "unknown";
|
|
825
|
+
}
|
|
826
|
+
function isCrossOrigin(request) {
|
|
827
|
+
const site = request.headers.get("sec-fetch-site");
|
|
828
|
+
if (site && site !== "same-origin" && site !== "none") return true;
|
|
829
|
+
const origin = request.headers.get("origin");
|
|
830
|
+
if (!origin) return false;
|
|
831
|
+
try {
|
|
832
|
+
return new URL(origin).origin !== new URL(request.url).origin;
|
|
833
|
+
} catch {
|
|
834
|
+
return true;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
async function evaluateIngestRequest(input) {
|
|
838
|
+
const { request } = input;
|
|
839
|
+
if (request.method !== "POST") {
|
|
840
|
+
return { ok: false, status: 405, body: { error: "Only POST is accepted." }, headers: { allow: "POST" } };
|
|
841
|
+
}
|
|
842
|
+
if (!input.configured) {
|
|
843
|
+
return { ok: false, status: 503, body: { error: "Ingest is not configured on this site." } };
|
|
844
|
+
}
|
|
845
|
+
if (isCrossOrigin(request)) {
|
|
846
|
+
return { ok: false, status: 403, body: { error: "Cross-origin requests are not accepted." } };
|
|
847
|
+
}
|
|
848
|
+
const limited = input.limiter.hit(input.clientKey(request));
|
|
849
|
+
if (!limited.allowed) {
|
|
850
|
+
return {
|
|
851
|
+
ok: false,
|
|
852
|
+
status: 429,
|
|
853
|
+
body: { error: "Too many requests \u2014 try again shortly." },
|
|
854
|
+
headers: { "retry-after": String(limited.retryAfterSeconds) }
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
if (input.rawBody === void 0) {
|
|
858
|
+
return { ok: false, status: 413, body: { error: "The payload is too large." } };
|
|
859
|
+
}
|
|
860
|
+
const read = readIngestPayload(input.rawBody);
|
|
861
|
+
if (!read.ok) {
|
|
862
|
+
return { ok: false, status: 400, body: { error: read.message, ...read.issues ? { issues: read.issues } : {} } };
|
|
863
|
+
}
|
|
864
|
+
if (input.botGate) {
|
|
865
|
+
const verdict = await input.botGate({ request, payload: read.value });
|
|
866
|
+
if (!verdict.allow) {
|
|
867
|
+
return { ok: false, status: 403, body: { error: "Refused by the bot gate.", gate: "bot", reason: verdict.reason } };
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
if (!input.enrich) return { ok: true, payload: read.value };
|
|
871
|
+
return enrichPayload(input.enrich, request, read.value);
|
|
872
|
+
}
|
|
873
|
+
async function enrichPayload(enrich, request, payload) {
|
|
874
|
+
let enriched;
|
|
875
|
+
try {
|
|
876
|
+
enriched = await enrich({ request, payload });
|
|
877
|
+
} catch (error) {
|
|
878
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
879
|
+
return { ok: false, status: 500, body: { error: `The site's ingest enrichment failed: ${message}` } };
|
|
880
|
+
}
|
|
881
|
+
const body = JSON.stringify(enriched ?? null);
|
|
882
|
+
const reread = readIngestPayload(body);
|
|
883
|
+
if (!reread.ok) {
|
|
884
|
+
return {
|
|
885
|
+
ok: false,
|
|
886
|
+
status: 500,
|
|
887
|
+
body: { error: "The site's ingest enrichment returned an invalid payload.", ...reread.issues ? { issues: reread.issues } : {} }
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
if (new TextEncoder().encode(body).byteLength > INGEST_MAX_BODY_BYTES) {
|
|
891
|
+
return { ok: false, status: 500, body: { error: "The site's ingest enrichment grew the payload past the receiver's cap." } };
|
|
892
|
+
}
|
|
893
|
+
return { ok: true, payload: reread.value };
|
|
894
|
+
}
|
|
895
|
+
async function readCappedBody(request, maxBytes = INGEST_MAX_BODY_BYTES) {
|
|
896
|
+
const declared = Number(request.headers.get("content-length"));
|
|
897
|
+
if (Number.isFinite(declared) && declared > maxBytes) return void 0;
|
|
898
|
+
if (!request.body) return "";
|
|
899
|
+
const reader = request.body.getReader();
|
|
900
|
+
const chunks = [];
|
|
901
|
+
let total = 0;
|
|
902
|
+
for (; ; ) {
|
|
903
|
+
const { done, value } = await reader.read();
|
|
904
|
+
if (done) break;
|
|
905
|
+
total += value.byteLength;
|
|
906
|
+
if (total > maxBytes) {
|
|
907
|
+
await reader.cancel().catch(() => void 0);
|
|
908
|
+
return void 0;
|
|
909
|
+
}
|
|
910
|
+
chunks.push(value);
|
|
911
|
+
}
|
|
912
|
+
const bytes = new Uint8Array(total);
|
|
913
|
+
let offset = 0;
|
|
914
|
+
for (const chunk of chunks) {
|
|
915
|
+
bytes.set(chunk, offset);
|
|
916
|
+
offset += chunk.byteLength;
|
|
917
|
+
}
|
|
918
|
+
return new TextDecoder().decode(bytes);
|
|
919
|
+
}
|
|
920
|
+
var NO_STORE = { "cache-control": "no-store" };
|
|
921
|
+
function createIngestRoute(options) {
|
|
922
|
+
const maxBodyBytes = options.maxBodyBytes ?? INGEST_MAX_BODY_BYTES;
|
|
923
|
+
if (maxBodyBytes > INGEST_MAX_BODY_BYTES || maxBodyBytes <= 0) {
|
|
924
|
+
throw new Error(`createIngestRoute: maxBodyBytes must be between 1 and ${INGEST_MAX_BODY_BYTES} (the receiver's cap).`);
|
|
925
|
+
}
|
|
926
|
+
const limiter = createFixedWindowLimiter({
|
|
927
|
+
limit: options.maxRequestsPerMinute ?? 30,
|
|
928
|
+
...options.now ? { now: options.now } : {}
|
|
929
|
+
});
|
|
930
|
+
const clientKey = options.clientKey ?? defaultIngestClientKey;
|
|
931
|
+
const receiver = options.siteUrl ? `${options.siteUrl.replace(/\/+$/, "")}${INGEST_RECEIVER_PATH}` : void 0;
|
|
932
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
933
|
+
return {
|
|
934
|
+
async POST(request) {
|
|
935
|
+
const rawBody = request.method === "POST" ? await readCappedBody(request, maxBodyBytes) : "";
|
|
936
|
+
const evaluation = await evaluateIngestRequest({
|
|
937
|
+
request,
|
|
938
|
+
rawBody,
|
|
939
|
+
configured: options.secret !== void 0 && options.secret.length > 0 && receiver !== void 0,
|
|
940
|
+
limiter,
|
|
941
|
+
clientKey,
|
|
942
|
+
...options.botGate ? { botGate: options.botGate } : {},
|
|
943
|
+
...options.enrich ? { enrich: options.enrich } : {}
|
|
944
|
+
});
|
|
945
|
+
if (!evaluation.ok) {
|
|
946
|
+
return Response.json(evaluation.body, { status: evaluation.status, headers: { ...NO_STORE, ...evaluation.headers } });
|
|
947
|
+
}
|
|
948
|
+
const signed = await signedIngestRequest(evaluation.payload, options.secret);
|
|
949
|
+
let answer;
|
|
950
|
+
try {
|
|
951
|
+
answer = await fetchImpl(receiver, { method: "POST", headers: signed.headers, body: signed.body });
|
|
952
|
+
} catch {
|
|
953
|
+
return Response.json({ error: "The ingest receiver could not be reached." }, { status: 502, headers: NO_STORE });
|
|
954
|
+
}
|
|
955
|
+
if (answer.status === 401) {
|
|
956
|
+
return Response.json(
|
|
957
|
+
{ error: "The ingest receiver refused this site's signature \u2014 INGEST_SECRET must equal the deployment's ELYTRA_INGEST_SECRET." },
|
|
958
|
+
{ status: 502, headers: NO_STORE }
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
const text = await answer.text();
|
|
962
|
+
return new Response(text, {
|
|
963
|
+
status: answer.status,
|
|
964
|
+
headers: { ...NO_STORE, "content-type": answer.headers.get("content-type") ?? "application/json" }
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
}
|
|
761
969
|
var LQIP_MAX_EDGE = 24;
|
|
762
970
|
function fitLqipLongEdge(width, height) {
|
|
763
971
|
const scale = Math.min(1, LQIP_MAX_EDGE / Math.max(width, height));
|
|
@@ -866,7 +1074,8 @@ var ELYTRA_HOST_ENV_KEYS = {
|
|
|
866
1074
|
convexUrl: "ELYTRA_CONVEX_URL",
|
|
867
1075
|
projectId: "ELYTRA_PROJECT_ID",
|
|
868
1076
|
draftToken: "DRAFT_PREVIEW_TOKEN",
|
|
869
|
-
revalidateSecret: "REVALIDATE_SECRET"
|
|
1077
|
+
revalidateSecret: "REVALIDATE_SECRET",
|
|
1078
|
+
ingestSecret: "INGEST_SECRET"
|
|
870
1079
|
};
|
|
871
1080
|
function cleaned(value) {
|
|
872
1081
|
const trimmed = value?.trim();
|
|
@@ -895,17 +1104,11 @@ async function runConvexQuery(fetchImpl, convexUrl, path, args) {
|
|
|
895
1104
|
}
|
|
896
1105
|
return body.value;
|
|
897
1106
|
}
|
|
898
|
-
function parseRedirects(rows) {
|
|
899
|
-
const redirects = [];
|
|
900
|
-
for (const row of rows) {
|
|
901
|
-
const parsed = redirectRecordSchema.safeParse(row);
|
|
902
|
-
if (parsed.success) redirects.push(parsed.data);
|
|
903
|
-
}
|
|
904
|
-
return redirects;
|
|
905
|
-
}
|
|
906
1107
|
function assembleLiveSource(payload, options, perspective) {
|
|
907
1108
|
const locales = options.locales;
|
|
908
|
-
const { documents, issues:
|
|
1109
|
+
const { documents, issues: documentIssues } = parseDeliveredDocuments(payload.documents);
|
|
1110
|
+
const delivered = parseDeliveredRedirects(payload.redirects ?? [], payload.redirectIssues ?? []);
|
|
1111
|
+
const sourceIssues = [...documentIssues, ...delivered.issues];
|
|
909
1112
|
const routes = [...options.routes];
|
|
910
1113
|
const documentSources = perspective === "published" ? documents.map((doc) => ({ draft: doc, published: doc, versions: [] })) : documents;
|
|
911
1114
|
return createStaticContentSource({
|
|
@@ -915,7 +1118,7 @@ function assembleLiveSource(payload, options, perspective) {
|
|
|
915
1118
|
// Redirects are editorial DATA (EC-303): the ENABLED ones ship in the
|
|
916
1119
|
// snapshot, so they change dynamically (no redeploy). Native next.config
|
|
917
1120
|
// redirects run before this.
|
|
918
|
-
redirects:
|
|
1121
|
+
redirects: delivered.redirects,
|
|
919
1122
|
collections: [...options.collections],
|
|
920
1123
|
documents: documentSources,
|
|
921
1124
|
assets: payload.assets,
|
|
@@ -971,7 +1174,7 @@ function resolveGlobals(source, options = {}) {
|
|
|
971
1174
|
const navigationCollection = conventions.navigationCollection ?? "navigation";
|
|
972
1175
|
const itemsFields = conventions.navigationItemsFields ?? DEFAULT_ITEMS_FIELDS;
|
|
973
1176
|
const client = source.client({ perspective, ...options.locale ? { locale: options.locale } : {} });
|
|
974
|
-
const assets = source.assets?.() ?? [];
|
|
1177
|
+
const assets = source.assets?.(options.locale) ?? [];
|
|
975
1178
|
const assetById = new Map(assets.map((asset) => [asset.id, asset]));
|
|
976
1179
|
const listDocs = (collection) => {
|
|
977
1180
|
const result = client.listDocuments(collection);
|
|
@@ -1136,8 +1339,6 @@ function createPreviewDisableRoute() {
|
|
|
1136
1339
|
}
|
|
1137
1340
|
};
|
|
1138
1341
|
}
|
|
1139
|
-
|
|
1140
|
-
// src/host-kit.ts
|
|
1141
1342
|
function resolveHostProject(entry) {
|
|
1142
1343
|
const result = resolveProject(entry);
|
|
1143
1344
|
if (result.ok) return result.project;
|
|
@@ -1238,6 +1439,11 @@ function createElytraHost(options) {
|
|
|
1238
1439
|
});
|
|
1239
1440
|
const previewRoute = () => createPreviewRoute({ token: env.draftToken });
|
|
1240
1441
|
const previewDisableRoute = () => createPreviewDisableRoute();
|
|
1442
|
+
const ingestRoute = (overrides = {}) => createIngestRoute({
|
|
1443
|
+
...overrides,
|
|
1444
|
+
secret: env.ingestSecret,
|
|
1445
|
+
siteUrl: "siteUrl" in overrides ? overrides.siteUrl : convexSiteUrl(env.convexUrl)
|
|
1446
|
+
});
|
|
1241
1447
|
const pageOpensFullBleed = async (slug) => {
|
|
1242
1448
|
const perspective = await requestPerspective();
|
|
1243
1449
|
const source = await loadContent({ perspective });
|
|
@@ -1263,11 +1469,12 @@ function createElytraHost(options) {
|
|
|
1263
1469
|
revalidateRoute,
|
|
1264
1470
|
previewRoute,
|
|
1265
1471
|
previewDisableRoute,
|
|
1472
|
+
ingestRoute,
|
|
1266
1473
|
pageOpensFullBleed
|
|
1267
1474
|
};
|
|
1268
1475
|
}
|
|
1269
1476
|
var PACKAGE = "@elytracms/next";
|
|
1270
1477
|
|
|
1271
|
-
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 };
|
|
1478
|
+
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 };
|
|
1272
1479
|
//# sourceMappingURL=index.js.map
|
|
1273
1480
|
//# sourceMappingURL=index.js.map
|