@elytracms/next 0.0.87 → 0.0.90
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 +200 -114
- package/dist/index.js +72 -68
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -6,15 +6,16 @@ export { ComponentImplementations, RenderAsset } from '@elytracms/core/runtime-r
|
|
|
6
6
|
import { HostComponentEntry } from '@elytracms/core/studio-core';
|
|
7
7
|
import { ResolvePageResult, ResolvedAsset, Perspective, ContentClient, CacheTags, ContentDocumentSource, ResolvedDocument } from '@elytracms/core/content';
|
|
8
8
|
export { Perspective } from '@elytracms/core/content';
|
|
9
|
-
import { LocaleConfig, Locale, RouteRecord, CollectionDef, RedirectRecord } from '@elytracms/core/cms-core';
|
|
10
|
-
export { CmsDocument, CollectionDef, FieldDef, Locale, LocaleConfig, LocaleRequest, LocaleStrategy, LocaleUrlPath, RedirectRecord, RouteRecord, documentSchema, fallbackHomeRoutes, localeConfigSchema, localeUrlPath, mergeRouteRecords, redirectRecordSchema, resolveLocale, routeRecordSchema, splitLocalePath, textDirection } from '@elytracms/core/cms-core';
|
|
9
|
+
import { LocaleConfig, Locale, RouteRecord, CollectionDef, RedirectRecord, LocaleConfigInput } from '@elytracms/core/cms-core';
|
|
10
|
+
export { CmsDocument, CollectionDef, FieldDef, Locale, LocaleConfig, LocaleConfigInput, LocaleRequest, LocaleStrategy, LocaleUrlPath, RedirectRecord, RouteRecord, documentSchema, fallbackHomeRoutes, localeConfigSchema, localeUrlPath, mergeRouteRecords, parseLocaleConfig, redirectRecordSchema, resolveLocale, routeRecordSchema, splitLocalePath, textDirection } from '@elytracms/core/cms-core';
|
|
11
11
|
import { AssetRecord, PersistenceAdapter } from '@elytracms/core/persistence';
|
|
12
12
|
export { AssetRecord } from '@elytracms/core/persistence';
|
|
13
|
-
import { Metadata, MetadataRoute } from 'next';
|
|
14
|
-
import {
|
|
15
|
-
export {
|
|
13
|
+
import { ResolvingMetadata, Metadata, ResolvedMetadata, MetadataRoute } from 'next';
|
|
14
|
+
import { RevalidationEvent } from '@elytracms/core/revalidation';
|
|
15
|
+
export { REVALIDATE_SIGNATURE_HEADER, RevalidationEvent, RevalidationEventKind, revalidationEventSchema, signRevalidationBody, signedRevalidationRequest } from '@elytracms/core/revalidation';
|
|
16
16
|
import { ImageLoader } from 'next/image';
|
|
17
17
|
import { HostImageRenderer } from './host-image.js';
|
|
18
|
+
export { z } from 'zod';
|
|
18
19
|
export { ComponentNode } from '@elytracms/core/project-graph';
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -259,9 +260,9 @@ interface ContentSnapshotOptions {
|
|
|
259
260
|
* and EC-519 collapsed the two declarations into one; this was the last place
|
|
260
261
|
* in the chain still inventing the value, and it sits in the package that
|
|
261
262
|
* resolves the URLs. Hand it the project's `LocaleConfig` — the one the
|
|
262
|
-
* config declares.
|
|
263
|
+
* config declares. Parsed here (EC-580): a `default` outside `locales` throws.
|
|
263
264
|
*/
|
|
264
|
-
locales:
|
|
265
|
+
locales: LocaleConfigInput;
|
|
265
266
|
}
|
|
266
267
|
interface ContentSnapshot extends ContentSource {
|
|
267
268
|
readonly projectId: string;
|
|
@@ -275,8 +276,8 @@ interface ContentSnapshot extends ContentSource {
|
|
|
275
276
|
*/
|
|
276
277
|
interface StaticContentData {
|
|
277
278
|
projectId: string;
|
|
278
|
-
/** Locale configuration — **required**, never defaulted (see EC-530 above). */
|
|
279
|
-
locales:
|
|
279
|
+
/** Locale configuration — **required**, never defaulted (see EC-530 above); parsed (EC-580). */
|
|
280
|
+
locales: LocaleConfigInput;
|
|
280
281
|
/** Route records served by `resolvePage` (EC-019). */
|
|
281
282
|
routes?: readonly RouteRecord[];
|
|
282
283
|
/** Redirect records served by `resolvePage` (EC-019/020). */
|
|
@@ -443,9 +444,9 @@ declare function resolveCanvasPage(source: ContentSource, url: string, perspecti
|
|
|
443
444
|
|
|
444
445
|
interface CanvasCacheOptions {
|
|
445
446
|
/**
|
|
446
|
-
* Project scope prefix for every tag (`p:<scope>:…`) — must be the
|
|
447
|
-
*
|
|
448
|
-
*
|
|
447
|
+
* Project scope prefix for every tag (`p:<scope>:…`) — must be the SAME scope
|
|
448
|
+
* the revalidate receiver derives its tags with (`createRevalidateRoute`'s
|
|
449
|
+
* `scope`; the delivery kit hands its one `contentScope` to both, EC-579).
|
|
449
450
|
*/
|
|
450
451
|
scope?: string;
|
|
451
452
|
/**
|
|
@@ -538,13 +539,59 @@ interface CanvasRouteProps {
|
|
|
538
539
|
interface CanvasRoute {
|
|
539
540
|
/** The page component: `export default route.Page`. */
|
|
540
541
|
Page: (props: CanvasRouteProps) => Promise<ReactNode>;
|
|
541
|
-
/**
|
|
542
|
-
|
|
542
|
+
/**
|
|
543
|
+
* Next metadata hook: `export const generateMetadata = route.generateMetadata`.
|
|
544
|
+
* Next passes the parent segment's resolving metadata as the second argument;
|
|
545
|
+
* the route merges the page's tags over it (EC-610). A caller that invokes this
|
|
546
|
+
* by hand (the scaffold's `app/page.tsx`) forwards its own `parent`.
|
|
547
|
+
*
|
|
548
|
+
* `parent` is REQUIRED, not optional (EC-614): Next's webpack build validates a
|
|
549
|
+
* page's `generateMetadata` with `SecondArg<…> extends ResolvingMetadata`, and an
|
|
550
|
+
* optional parameter reads as `ResolvingMetadata | undefined`, which fails that
|
|
551
|
+
* constraint — a consumer re-exporting this hook could not `next build --webpack`
|
|
552
|
+
* on 0.0.88 (the Turbopack validator is lenient, which is how the gate missed it).
|
|
553
|
+
*/
|
|
554
|
+
generateMetadata: (props: CanvasRouteProps, parent: ResolvingMetadata) => Promise<Metadata>;
|
|
543
555
|
}
|
|
544
556
|
/** Create the `{ Page, generateMetadata }` pair for `app/[[...slug]]/page.tsx`. */
|
|
545
557
|
declare function createCanvasRoute(options: CanvasRouteOptions): CanvasRoute;
|
|
546
558
|
|
|
559
|
+
/**
|
|
560
|
+
* EC-531, narrowed by EC-610 (owner ruling 3, 2026-10-23): `og:locale` ONLY in the
|
|
561
|
+
* shape Open Graph specifies — `language_TERRITORY` with an underscore (`de_DE`),
|
|
562
|
+
* where BCP 47 writes `de-DE`. The value is the page's RESOLVED locale
|
|
563
|
+
* (`result.tags.locale`, the same fact the router used to pick the content). A
|
|
564
|
+
* bare language code (`de`) is outside that format, so the kit emits nothing for
|
|
565
|
+
* it and the layout's own value stands through the merge: inventing a territory
|
|
566
|
+
* (`de` → `de_DE`) would be wrong exactly where it matters (Austria, the UK,
|
|
567
|
+
* Brazil, Latin America), and a wrong valid value looks deliberate.
|
|
568
|
+
*/
|
|
569
|
+
declare function openGraphLocale(locale: string): string | null;
|
|
547
570
|
declare function canvasPageMetadata(result: ResolvePageResult, assets?: readonly ResolvedAsset[]): Metadata;
|
|
571
|
+
/**
|
|
572
|
+
* EC-610 (owner ruling 1, 2026-10-13): **the site provides the default tags, and
|
|
573
|
+
* the page's tags are merged on top** — a value the page states overrides the
|
|
574
|
+
* site's, a value the page leaves empty falls back to the site's.
|
|
575
|
+
*
|
|
576
|
+
* Next merges metadata SHALLOWLY (read: `generate-metadata.md`, "Merging"): a
|
|
577
|
+
* page segment's `openGraph` object REPLACES the layout's whole `openGraph`, so a
|
|
578
|
+
* site-wide `type`, `siteName`, site image or `locale` declared once in the root
|
|
579
|
+
* layout never reached a canvas page, and both real sites hand-rolled around it
|
|
580
|
+
* differently (NK-039's page hook, Konditorei's post bypass). This is the one
|
|
581
|
+
* merge, applied by the canvas route to whatever builds the page's metadata (the
|
|
582
|
+
* kit's default or the host's `metadata` option): every object-valued key the
|
|
583
|
+
* page sets (`openGraph`, `twitter`, `robots`, `alternates`, …) is merged one
|
|
584
|
+
* level deep over the parent's RESOLVED value; a key the page does not set is left
|
|
585
|
+
* to Next, which inherits it from the parent as before; a scalar or array the
|
|
586
|
+
* page sets wins outright.
|
|
587
|
+
*
|
|
588
|
+
* The parent's resolved shapes spread back cleanly: Next hands `generateMetadata`
|
|
589
|
+
* a `ResolvedMetadata` whose URLs are already strings, whose `images` are
|
|
590
|
+
* descriptor objects and whose titles are `{ absolute, template }` — all of which
|
|
591
|
+
* `Metadata` accepts (Next's own "extend rather than replace" example spreads
|
|
592
|
+
* `(await parent).openGraph?.images`).
|
|
593
|
+
*/
|
|
594
|
+
declare function mergeMetadataOverParent(parent: ResolvedMetadata | null | undefined, page: Metadata): Metadata;
|
|
548
595
|
|
|
549
596
|
/**
|
|
550
597
|
* `sitemap.xml` from published routes (EC-171): walk the same route records
|
|
@@ -648,10 +695,17 @@ declare function createCanvasSitemap(options: CreateCanvasSitemapOptions): () =>
|
|
|
648
695
|
* Cache-tag pipeline (EC-146, vision AD-5) — the pure half.
|
|
649
696
|
*
|
|
650
697
|
* This module defines (a) the **canonical Next.js tag string format** mapping
|
|
651
|
-
* the accessor-level `CacheTags` (EC-143) onto `revalidateTag` keys,
|
|
652
|
-
*
|
|
653
|
-
*
|
|
654
|
-
*
|
|
698
|
+
* the accessor-level `CacheTags` (EC-143) onto `revalidateTag` keys, (b) the
|
|
699
|
+
* **tag derivation for a revalidation event** ({@link revalidationTags}, EC-579 —
|
|
700
|
+
* the receiver's half of the one emitter), and (c) the **tag-discovery caching
|
|
701
|
+
* logic** the catch-all route helper runs inside `unstable_cache`. No Next.js
|
|
702
|
+
* imports — everything here is unit-testable; `route.tsx` injects the real
|
|
703
|
+
* `unstable_cache` via {@link CacheWrapper}.
|
|
704
|
+
*
|
|
705
|
+
* This is the ONE tag module: the entries are tagged here and the invalidations
|
|
706
|
+
* are derived here, from the same formatters and the same scope. Before EC-579 the
|
|
707
|
+
* format was mirrored twice more (the studio's browser dispatcher and an inline
|
|
708
|
+
* literal in the Convex emitter), and each emitter brought its own scope.
|
|
655
709
|
*
|
|
656
710
|
* ## Tag format
|
|
657
711
|
*
|
|
@@ -669,8 +723,8 @@ declare function createCanvasSitemap(options: CreateCanvasSitemapOptions): () =>
|
|
|
669
723
|
* (alt/dimensions/re-upload) drops exactly the pages that baked it. Not
|
|
670
724
|
* locale-scoped — an asset record is locale-agnostic.
|
|
671
725
|
* - `project` — the whole-project sweep tag attached to **every** cached
|
|
672
|
-
* entry;
|
|
673
|
-
*
|
|
726
|
+
* entry; every revalidation event that changes live output invalidates it
|
|
727
|
+
* (see {@link revalidationTags} for why, until EC-515).
|
|
674
728
|
*
|
|
675
729
|
* ## Project scope
|
|
676
730
|
*
|
|
@@ -678,16 +732,11 @@ declare function createCanvasSitemap(options: CreateCanvasSitemapOptions): () =>
|
|
|
678
732
|
* `p:prj_aurora:doc:post:post-1`). The cost is a few bytes per tag and it
|
|
679
733
|
* keeps multi-project hosts (one Next app serving several builder projects)
|
|
680
734
|
* from cross-invalidating, so the prefix is **on whenever the host knows its
|
|
681
|
-
* project id** — decision: include it.
|
|
682
|
-
*
|
|
683
|
-
*
|
|
684
|
-
*
|
|
685
|
-
*
|
|
686
|
-
*
|
|
687
|
-
* The studio-side emitter cannot depend on this package (it would drag the
|
|
688
|
-
* `next` peer dependency into the studio bundle), so
|
|
689
|
-
* `packages/studio/src/lib/webhooks/tags.ts` mirrors these formatters; tests on
|
|
690
|
-
* both sides pin the exact literal strings to keep them in lock-step.
|
|
735
|
+
* project id** — decision: include it. Since EC-579 the scope has ONE owner: the
|
|
736
|
+
* host. The webhook carries an event, not tags, and the receiver derives the
|
|
737
|
+
* tags with the same scope its cache entries are written under (the delivery
|
|
738
|
+
* kit hands both the one `contentScope`), so emitter and host can no longer
|
|
739
|
+
* disagree about it.
|
|
691
740
|
*/
|
|
692
741
|
/** Apply the optional multi-project scope prefix to one tag. */
|
|
693
742
|
declare function scopeCacheTag(tag: string, scope?: string): string;
|
|
@@ -701,9 +750,8 @@ declare function collectionCacheTag(collection: string, locale: string, scope?:
|
|
|
701
750
|
/** Tag for one resolved route path (or redirect-chain hop) in one locale. */
|
|
702
751
|
declare function routeCacheTag(pattern: string, locale: string, scope?: string): string;
|
|
703
752
|
/**
|
|
704
|
-
* The whole-project sweep tag. Attached to every cached page entry;
|
|
705
|
-
*
|
|
706
|
-
* change's precise tag set is unknowable).
|
|
753
|
+
* The whole-project sweep tag. Attached to every cached page entry; derived for
|
|
754
|
+
* every revalidation event that changes live output ({@link revalidationTags}).
|
|
707
755
|
*/
|
|
708
756
|
declare function projectSweepTag(scope?: string): string;
|
|
709
757
|
/**
|
|
@@ -712,6 +760,38 @@ declare function projectSweepTag(scope?: string): string;
|
|
|
712
760
|
* cache entry — including the project sweep tag.
|
|
713
761
|
*/
|
|
714
762
|
declare function nextCacheTags(tags: CacheTags, scope?: string): string[];
|
|
763
|
+
/** The receiver's own project — what its cached entries are tagged with. */
|
|
764
|
+
interface RevalidationProject {
|
|
765
|
+
/** The tag scope of this host's cache entries (`p:<scope>:…`), if any. */
|
|
766
|
+
scope?: string | undefined;
|
|
767
|
+
/** Every locale this host serves — one `collection:` tag per locale. */
|
|
768
|
+
locales: readonly string[];
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* The tags one revalidation event invalidates on this host — sorted, deduplicated,
|
|
772
|
+
* scoped with the receiver's OWN scope, so the emitter never needs to know it.
|
|
773
|
+
*
|
|
774
|
+
* - `publish` / `unpublish` / `delete` → `doc:<collection>:<id>` (its detail page and
|
|
775
|
+
* every cached list that contained it) + `collection:<collection>:<locale>` for
|
|
776
|
+
* EVERY locale this host serves (list membership may have changed) + the sweep;
|
|
777
|
+
* - `redirects` → the sweep (routing can change any URL; see the `redirects` trigger
|
|
778
|
+
* in the studio's `convex/triggers.ts` for why a targeted `route:` tag is wrong);
|
|
779
|
+
* - `asset` → `asset:<id>` + the sweep (a page reading the asset through a document
|
|
780
|
+
* asset FIELD carries no `asset:` tag — EC-566 D16, EC-589);
|
|
781
|
+
* - `probe` → nothing (the doctor's no-op ping).
|
|
782
|
+
*
|
|
783
|
+
* **Why every live-output event also carries the project sweep — a deliberate,
|
|
784
|
+
* measured deviation from the EC-579 ruling's text, which lists the sweep only for
|
|
785
|
+
* redirects and asset removes.** A self-hosted Next host honours ONLY the baseline
|
|
786
|
+
* tags of a cache read (measured in EC-515 on Next 16.2.10 — see
|
|
787
|
+
* {@link loadCachedEntry}): a discovered `doc:` / `collection:` / `asset:` tag
|
|
788
|
+
* invalidates nothing there, silently, and Vercel is unmeasured. Until EC-515's
|
|
789
|
+
* option D makes the precise tags honoured, the precise tags alone would turn
|
|
790
|
+
* "revalidates coarsely" into "never revalidates" on every self-hosted host — so the
|
|
791
|
+
* sweep stays on every event, exactly as the Convex path sent it before. Dropping it
|
|
792
|
+
* from a publish is EC-515's job, together with the read that makes it safe.
|
|
793
|
+
*/
|
|
794
|
+
declare function revalidationTags(event: RevalidationEvent, project: RevalidationProject): string[];
|
|
715
795
|
/**
|
|
716
796
|
* The slice of `unstable_cache` the discovery logic needs, injectable so the
|
|
717
797
|
* logic is testable against a faithful in-memory model (and so the test model
|
|
@@ -809,11 +889,11 @@ interface CachedEntry<T> {
|
|
|
809
889
|
* webhook are all correct — the read simply never consults them.
|
|
810
890
|
*
|
|
811
891
|
* Consequence for a self-hosted deployment: invalidation granularity collapses
|
|
812
|
-
* to whatever is in `baselineTags`.
|
|
813
|
-
*
|
|
814
|
-
* coarsely;
|
|
815
|
-
*
|
|
816
|
-
*
|
|
892
|
+
* to whatever is in `baselineTags`. {@link revalidationTags} therefore adds the
|
|
893
|
+
* project sweep to every event that changes live output (EC-579), so instant
|
|
894
|
+
* publish still *works* there, coarsely; its targeted `doc:`/`collection:`/`asset:`
|
|
895
|
+
* tags do not reach a self-hosted host's entries at all. Vercel-hosted deployments
|
|
896
|
+
* are unaffected in practice — both consumer
|
|
817
897
|
* sites were measured healthy on 2026-08-01 — which is consistent with Vercel's
|
|
818
898
|
* cache handler purging by the entry's stored tags, though that has **not** been
|
|
819
899
|
* measured here.
|
|
@@ -826,90 +906,80 @@ interface CachedEntry<T> {
|
|
|
826
906
|
declare function loadCachedEntry<T>(cache: CacheWrapper, keyParts: string[], baselineTags: readonly string[], load: () => Promise<CachedEntry<T>>): Promise<CachedEntry<T>>;
|
|
827
907
|
|
|
828
908
|
/**
|
|
829
|
-
* Pure
|
|
830
|
-
*
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
836
|
-
*
|
|
909
|
+
* Pure request logic of the revalidation webhook receiver (EC-146), kept free of
|
|
910
|
+
* Next.js imports so it is unit-testable. `revalidate.ts` wires it into a route
|
|
911
|
+
* handler with `revalidateTag`.
|
|
912
|
+
*
|
|
913
|
+
* The wire — event schema, signer, header — is NOT declared here: it is the one
|
|
914
|
+
* module `@elytracms/core/revalidation`, shared with the emitter (the studio's
|
|
915
|
+
* Convex write chokepoint) and the doctor probe (EC-579). This file only decides
|
|
916
|
+
* the HTTP answer: auth before parsing, then the event.
|
|
837
917
|
*/
|
|
838
|
-
/** Header carrying the HMAC signature. */
|
|
839
|
-
declare const REVALIDATE_SIGNATURE_HEADER = "x-elytra-signature";
|
|
840
|
-
/** The webhook body: which tags to revalidate, and why. */
|
|
841
|
-
declare const revalidatePayloadSchema: z.ZodObject<{
|
|
842
|
-
projectId: z.ZodString;
|
|
843
|
-
event: z.ZodString;
|
|
844
|
-
tags: z.ZodArray<z.ZodString>;
|
|
845
|
-
timestamp: z.ZodString;
|
|
846
|
-
}, z.core.$strip>;
|
|
847
|
-
type RevalidatePayload = z.infer<typeof revalidatePayloadSchema>;
|
|
848
|
-
/** Compute the signature header value for a raw body (emitter/test side). */
|
|
849
|
-
declare function signRevalidateBody(rawBody: string, secret: string): string;
|
|
850
918
|
type RevalidateEvaluation = {
|
|
851
919
|
ok: true;
|
|
852
|
-
|
|
920
|
+
event: RevalidationEvent;
|
|
853
921
|
} | {
|
|
854
922
|
ok: false;
|
|
855
923
|
status: 400 | 401;
|
|
856
924
|
message: string;
|
|
857
925
|
};
|
|
858
926
|
/**
|
|
859
|
-
* Evaluate one webhook request: verify the HMAC first (auth before parsing),
|
|
860
|
-
*
|
|
861
|
-
*
|
|
862
|
-
* - unconfigured (empty) secret → 401 for every request: the endpoint stays
|
|
863
|
-
*
|
|
864
|
-
* - missing/malformed/mismatched signature → 401 (
|
|
865
|
-
* - unparseable or schema-invalid body → 400
|
|
927
|
+
* Evaluate one webhook request: verify the HMAC first (auth before parsing), then
|
|
928
|
+
* read the event.
|
|
929
|
+
*
|
|
930
|
+
* - unconfigured (empty) secret → 401 for every request: the endpoint stays closed
|
|
931
|
+
* until a secret is deliberately configured (mirrors EC-144 preview);
|
|
932
|
+
* - missing/malformed/mismatched signature → 401 (constant-time verify);
|
|
933
|
+
* - unparseable or schema-invalid body → 400, nothing revalidated; a body in the
|
|
934
|
+
* pre-EC-579 tag-list shape gets its own named 400 (an older Elytra emitter —
|
|
935
|
+
* deploy the matching studio functions), because a version skew between the
|
|
936
|
+
* Convex deployment and this host is the one way a correctly signed body can
|
|
937
|
+
* arrive in the wrong shape.
|
|
866
938
|
*/
|
|
867
|
-
declare function evaluateRevalidateRequest(rawBody: string, signature: string | null | undefined, secret: string | undefined): RevalidateEvaluation
|
|
939
|
+
declare function evaluateRevalidateRequest(rawBody: string, signature: string | null | undefined, secret: string | undefined): Promise<RevalidateEvaluation>;
|
|
868
940
|
|
|
869
941
|
/**
|
|
870
942
|
* Revalidation webhook route handler (EC-146): the receiving end of the
|
|
871
|
-
* publish → live-in-seconds pipeline. The studio's
|
|
872
|
-
* `{
|
|
873
|
-
* HMAC over the raw body
|
|
874
|
-
*
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
*
|
|
943
|
+
* publish → live-in-seconds pipeline. The studio's Convex write chokepoint — the ONE
|
|
944
|
+
* emitter (EC-579) — POSTs a signed EVENT (`{ kind, collection?, id? }`, the wire in
|
|
945
|
+
* `@elytracms/core/revalidation`); this handler verifies the HMAC over the raw body,
|
|
946
|
+
* derives the tags from the event with this host's OWN scope and locales
|
|
947
|
+
* (`revalidationTags`, `cache.ts`) and calls `revalidateTag` for each, dropping the
|
|
948
|
+
* cached entries whose fetch-time tag sets contained them.
|
|
949
|
+
*
|
|
950
|
+
* Server-to-server only: the emitter runs in Convex, so there is no CORS and no
|
|
951
|
+
* `OPTIONS` preflight (the studio's browser dispatcher that needed them is deleted).
|
|
952
|
+
* Server-only by construction — consumed from an App Router route handler file,
|
|
953
|
+
* never from client bundles.
|
|
878
954
|
*/
|
|
879
|
-
interface RevalidateRouteOptions {
|
|
955
|
+
interface RevalidateRouteOptions extends RevalidationProject {
|
|
880
956
|
/**
|
|
881
957
|
* Shared HMAC secret. An empty/undefined secret rejects every request with
|
|
882
958
|
* 401 — the endpoint stays closed until deliberately configured.
|
|
883
959
|
*/
|
|
884
960
|
secret: string | undefined;
|
|
885
|
-
/**
|
|
886
|
-
* CORS origin allowed to call this endpoint (e.g. the studio's origin).
|
|
887
|
-
* The v1 dispatcher is a browser `fetch` from the studio, and the custom
|
|
888
|
-
* signature header always triggers a CORS preflight — so cross-origin
|
|
889
|
-
* studio → host dispatch only works when this is set. Server-side emitters
|
|
890
|
-
* (the Convex action variant, CLIs, CI) need no CORS and may leave it
|
|
891
|
-
* unset. Never use `*` with a real secret-bearing deployment unless you
|
|
892
|
-
* accept that any origin may *attempt* deliveries (they still need the
|
|
893
|
-
* secret to have any effect).
|
|
894
|
-
*/
|
|
895
|
-
allowOrigin?: string;
|
|
896
961
|
}
|
|
897
962
|
interface RevalidateRouteHandlers {
|
|
898
963
|
POST(request: Request): Promise<Response>;
|
|
899
|
-
OPTIONS(): Promise<Response>;
|
|
900
964
|
}
|
|
901
965
|
/**
|
|
902
|
-
* Create the webhook route
|
|
966
|
+
* Create the webhook route handler for `app/api/revalidate/route.ts`. Through the
|
|
967
|
+
* delivery kit it is `export const { POST } = elytra.revalidateRoute()`, which hands
|
|
968
|
+
* it the kit's own scope and locales — the ones its cached entries carry. Wiring it
|
|
969
|
+
* by hand, pass the SAME scope your canvas route caches under:
|
|
903
970
|
*
|
|
904
971
|
* ```ts
|
|
905
|
-
* export const { POST
|
|
972
|
+
* export const { POST } = createRevalidateRoute({
|
|
906
973
|
* secret: process.env.REVALIDATE_SECRET,
|
|
974
|
+
* scope: process.env.ELYTRA_PROJECT_ID,
|
|
975
|
+
* locales: ['en', 'de'],
|
|
907
976
|
* })
|
|
908
977
|
* ```
|
|
909
978
|
*
|
|
910
|
-
* Unsigned/invalid requests are rejected with 401 (
|
|
911
|
-
*
|
|
912
|
-
*
|
|
979
|
+
* Unsigned/invalid requests are rejected with 401 (constant-time HMAC verify, see
|
|
980
|
+
* `evaluateRevalidateRequest`), signed-but-unreadable ones with 400; a valid event
|
|
981
|
+
* revalidates its derived tags and answers `{ revalidated: tags }` (`[]` for the
|
|
982
|
+
* doctor's `probe`).
|
|
913
983
|
*/
|
|
914
984
|
declare function createRevalidateRoute(options: RevalidateRouteOptions): RevalidateRouteHandlers;
|
|
915
985
|
|
|
@@ -1112,7 +1182,9 @@ declare function elytraHostImage(options?: ElytraHostImageOptions): HostImageRen
|
|
|
1112
1182
|
* | | equal the Convex deployment's own env var) |
|
|
1113
1183
|
* | `REVALIDATE_SECRET` | Instant-publish webhook HMAC secret (must equal the |
|
|
1114
1184
|
* | | deployment's `CANVAS_REVALIDATE_SECRET`) |
|
|
1115
|
-
*
|
|
1185
|
+
*
|
|
1186
|
+
* EC-579 retired `STUDIO_ORIGIN` (the CORS origin of the studio's deleted browser
|
|
1187
|
+
* dispatcher): the one emitter is the Convex deployment, server to server.
|
|
1116
1188
|
*/
|
|
1117
1189
|
/** The kit's documented env names (single source of truth, also used in docs/tests). */
|
|
1118
1190
|
declare const ELYTRA_HOST_ENV_KEYS: {
|
|
@@ -1120,14 +1192,12 @@ declare const ELYTRA_HOST_ENV_KEYS: {
|
|
|
1120
1192
|
readonly projectId: "ELYTRA_PROJECT_ID";
|
|
1121
1193
|
readonly draftToken: "DRAFT_PREVIEW_TOKEN";
|
|
1122
1194
|
readonly revalidateSecret: "REVALIDATE_SECRET";
|
|
1123
|
-
readonly studioOrigin: "STUDIO_ORIGIN";
|
|
1124
1195
|
};
|
|
1125
1196
|
interface ElytraHostEnv {
|
|
1126
1197
|
convexUrl?: string;
|
|
1127
1198
|
projectId?: string;
|
|
1128
1199
|
draftToken?: string;
|
|
1129
1200
|
revalidateSecret?: string;
|
|
1130
|
-
studioOrigin?: string;
|
|
1131
1201
|
}
|
|
1132
1202
|
/**
|
|
1133
1203
|
* Resolve the kit's env bag from a raw env record (`process.env` by default at
|
|
@@ -1298,7 +1368,7 @@ declare function createPreviewDisableRoute(): PreviewRouteHandlers;
|
|
|
1298
1368
|
* export const generateMetadata = route.generateMetadata
|
|
1299
1369
|
*
|
|
1300
1370
|
* // app/api/revalidate/route.ts
|
|
1301
|
-
* export const { POST
|
|
1371
|
+
* export const { POST } = elytra.revalidateRoute()
|
|
1302
1372
|
*
|
|
1303
1373
|
* // app/api/draft/route.ts — GET /api/draft?token=…&redirect=/path
|
|
1304
1374
|
* export const { GET } = elytra.previewRoute()
|
|
@@ -1324,7 +1394,7 @@ interface ElytraHostOptions {
|
|
|
1324
1394
|
* Locale config — config-owned (AD-11), never read from the snapshot.
|
|
1325
1395
|
*
|
|
1326
1396
|
* **Import it, do not restate it** (EC-519). This is the *same*
|
|
1327
|
-
* {@link
|
|
1397
|
+
* {@link LocaleConfigInput} `elytra.config.ts` takes, so a project declares its
|
|
1328
1398
|
* locales once and hands that one binding to both mounts. It stays a host
|
|
1329
1399
|
* option because the engine cannot know where a repo keeps the value — but it
|
|
1330
1400
|
* is no longer an option you have to *retype*: since EC-532 the delivery
|
|
@@ -1336,8 +1406,14 @@ interface ElytraHostOptions {
|
|
|
1336
1406
|
* fact `locales` + `defaultLocale`, and in our own demo the two drifted — the
|
|
1337
1407
|
* host said `['en']`, the config said `['en','de']`, and every `/de` URL 404'd
|
|
1338
1408
|
* silently for weeks (EC-512).
|
|
1409
|
+
*
|
|
1410
|
+
* The DECLARED shape (EC-580): the kit parses it into the engine's `LocaleConfig`
|
|
1411
|
+
* where every content source is assembled (`createStaticContentSource` — the
|
|
1412
|
+
* empty source is built at boot), so a `default` outside `locales` stops the host
|
|
1413
|
+
* with a located message instead of serving — the same rule the studio's
|
|
1414
|
+
* workspace gate applies to the same value.
|
|
1339
1415
|
*/
|
|
1340
|
-
locales:
|
|
1416
|
+
locales: LocaleConfigInput;
|
|
1341
1417
|
/** CMS collection schema — config-owned (AD-11). */
|
|
1342
1418
|
collections: readonly CollectionDef[];
|
|
1343
1419
|
/** Routes as code (AD-3/AD-11): records, or the `(collections) => records` derive. */
|
|
@@ -1384,7 +1460,7 @@ interface ElytraHostOptions {
|
|
|
1384
1460
|
/**
|
|
1385
1461
|
* Env bag; defaults to `process.env`. Documented names (see `host-env.ts`):
|
|
1386
1462
|
* `ELYTRA_CONVEX_URL`, `ELYTRA_PROJECT_ID`, `DRAFT_PREVIEW_TOKEN`,
|
|
1387
|
-
* `REVALIDATE_SECRET
|
|
1463
|
+
* `REVALIDATE_SECRET`.
|
|
1388
1464
|
*/
|
|
1389
1465
|
env?: Record<string, string | undefined>;
|
|
1390
1466
|
/**
|
|
@@ -1415,13 +1491,17 @@ interface HostCanvasRouteOptions {
|
|
|
1415
1491
|
*/
|
|
1416
1492
|
wrap?: (children: ReactNode, ctx: CanvasWrapContext) => ReactNode;
|
|
1417
1493
|
/**
|
|
1418
|
-
* Tagged data cache (instant publish).
|
|
1419
|
-
*
|
|
1420
|
-
*
|
|
1421
|
-
*
|
|
1422
|
-
*
|
|
1494
|
+
* Tagged data cache (instant publish). Always on and always scoped to the kit's
|
|
1495
|
+
* {@link ElytraHost.contentScope}, with the kit's
|
|
1496
|
+
* {@link ElytraHost.contentMode} in the entry key (EC-543). `false` disables
|
|
1497
|
+
* caching entirely.
|
|
1498
|
+
*
|
|
1499
|
+
* There is no scope override (EC-579): the kit's revalidate receiver derives its
|
|
1500
|
+
* tags with that same `contentScope`, so one value scopes both the entries and
|
|
1501
|
+
* their invalidation. An override here was the one way left for the two to
|
|
1502
|
+
* disagree — and a disagreement drops nothing, silently.
|
|
1423
1503
|
*/
|
|
1424
|
-
cache?:
|
|
1504
|
+
cache?: false;
|
|
1425
1505
|
}
|
|
1426
1506
|
interface LoadGlobalsOptions {
|
|
1427
1507
|
/** Perspective of the chrome read. Default `'published'` (live is published-only). */
|
|
@@ -1458,8 +1538,9 @@ type ElytraContentMode = 'live' | 'fallback' | 'empty';
|
|
|
1458
1538
|
/** The delivery host handle {@link createElytraHost} yields. */
|
|
1459
1539
|
interface ElytraHost {
|
|
1460
1540
|
/**
|
|
1461
|
-
* The cache-tag scope: `ELYTRA_PROJECT_ID` in live mode
|
|
1462
|
-
*
|
|
1541
|
+
* The cache-tag scope: `ELYTRA_PROJECT_ID` in live mode, else the config slug.
|
|
1542
|
+
* The ONE scope of this host (EC-579): its cache entries are written under it and
|
|
1543
|
+
* its revalidate receiver derives every invalidation tag with it.
|
|
1463
1544
|
*/
|
|
1464
1545
|
contentScope: string;
|
|
1465
1546
|
/**
|
|
@@ -1492,11 +1573,13 @@ interface ElytraHost {
|
|
|
1492
1573
|
/** The catch-all canvas route pair for `app/[[...slug]]/page.tsx`. */
|
|
1493
1574
|
canvasRoute(options?: HostCanvasRouteOptions): CanvasRoute;
|
|
1494
1575
|
/**
|
|
1495
|
-
* The instant-publish webhook
|
|
1496
|
-
*
|
|
1497
|
-
*
|
|
1576
|
+
* The instant-publish webhook handler for `app/api/revalidate/route.ts`, wired to
|
|
1577
|
+
* `REVALIDATE_SECRET` and to this host's own project — {@link contentScope} and the
|
|
1578
|
+
* config locales — which it derives the tags of every event with (EC-579). Unset
|
|
1579
|
+
* secret = endpoint closed (401), never a default secret. Only the secret can be
|
|
1580
|
+
* overridden: the scope and locales are the ones this kit's cache entries carry.
|
|
1498
1581
|
*/
|
|
1499
|
-
revalidateRoute(overrides?: Partial<RevalidateRouteOptions
|
|
1582
|
+
revalidateRoute(overrides?: Partial<Pick<RevalidateRouteOptions, 'secret'>>): RevalidateRouteHandlers;
|
|
1500
1583
|
/**
|
|
1501
1584
|
* The draft-mode ENABLE route for e.g. `app/api/draft/route.ts` —
|
|
1502
1585
|
* `GET /api/draft?token=…&redirect=/path`, gated by the ONE
|
|
@@ -1542,8 +1625,11 @@ interface LiveContentOptions {
|
|
|
1542
1625
|
/** Convex deployment URL, e.g. `https://your-deployment-123.convex.cloud`. */
|
|
1543
1626
|
convexUrl: string;
|
|
1544
1627
|
projectId: string;
|
|
1545
|
-
/**
|
|
1546
|
-
|
|
1628
|
+
/**
|
|
1629
|
+
* Project locale config — config-owned (AD-11), never read from the snapshot.
|
|
1630
|
+
* The declared shape; parsed where the source is assembled (EC-580).
|
|
1631
|
+
*/
|
|
1632
|
+
locales: LocaleConfigInput;
|
|
1547
1633
|
/** CMS collection schema — config-owned (AD-11); drives depth-1 relation population. */
|
|
1548
1634
|
collections: readonly CollectionDef[];
|
|
1549
1635
|
/** Route records — config-owned (AD-3/AD-11); merged with the home fallback. */
|
|
@@ -1668,4 +1754,4 @@ declare function evaluatePreviewRequest(requestUrl: string | URL, expectedToken:
|
|
|
1668
1754
|
*/
|
|
1669
1755
|
declare const PACKAGE = "@elytracms/next";
|
|
1670
1756
|
|
|
1671
|
-
export { type AssetImageLoaderOptions, type CacheWrapper, type CacheWrapperOptions, type CachedEntry, type CanvasCacheOptions, type CanvasContentRequest, type CanvasPageOutcome, CanvasRenderer, type CanvasRendererProps, type CanvasRequestInfo, type CanvasRoute, type CanvasRouteOptions, type CanvasRouteProps, type CanvasSitemapOptions, type CanvasSitemapResult, type CanvasWrapContext, type ContentSnapshot, type ContentSnapshotOptions, type ContentSource, type CreateCanvasSitemapOptions, type DefineHostComponentsOptions, type DeliveryPresentation, ELYTRA_HOST_ENV_KEYS, type ElytraContentMode, type ElytraHost, type ElytraHostEnv, type ElytraHostImageOptions, type ElytraHostOptions, ElytraImage, type ElytraImageAsset, type ElytraImageProps, type GlobalsConventions, type HostCanvasRouteOptions, type HostComponent, type HostComponents, type LiveContentOptions, type LoadGlobalsOptions, type NextImagePrimitiveOptions, PACKAGE, type PreviewEvaluation, type PreviewRouteHandlers, type PreviewRouteOptions,
|
|
1757
|
+
export { type AssetImageLoaderOptions, type CacheWrapper, type CacheWrapperOptions, type CachedEntry, type CanvasCacheOptions, type CanvasContentRequest, type CanvasPageOutcome, CanvasRenderer, type CanvasRendererProps, type CanvasRequestInfo, type CanvasRoute, type CanvasRouteOptions, type CanvasRouteProps, type CanvasSitemapOptions, type CanvasSitemapResult, type CanvasWrapContext, type ContentSnapshot, type ContentSnapshotOptions, type ContentSource, type CreateCanvasSitemapOptions, type DefineHostComponentsOptions, type DeliveryPresentation, ELYTRA_HOST_ENV_KEYS, type ElytraContentMode, type ElytraHost, type ElytraHostEnv, type ElytraHostImageOptions, type ElytraHostOptions, ElytraImage, type ElytraImageAsset, type ElytraImageProps, type GlobalsConventions, type HostCanvasRouteOptions, type HostComponent, type HostComponents, type LiveContentOptions, type LoadGlobalsOptions, type NextImagePrimitiveOptions, PACKAGE, type PreviewEvaluation, type PreviewRouteHandlers, type PreviewRouteOptions, type ResolveCanvasPageOptions, type ResolveGlobalsOptions, type ResolvedLink, type ResolvedNavEntry, type ResolvedNavLink, type RevalidateEvaluation, type RevalidateRouteHandlers, type RevalidateRouteOptions, type RevalidationProject, type SiteGlobals, type SitemapParamsProvider, type SitemapRouteContext, type SitemapSkippedRoute, type StaticContentData, 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, revalidationTags, routeCacheTag, safeRedirectTarget, scopeCacheTag };
|