@elytracms/next 0.0.87 → 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 CHANGED
@@ -10,7 +10,7 @@ import { LocaleConfig, Locale, RouteRecord, CollectionDef, RedirectRecord } from
10
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';
11
11
  import { AssetRecord, PersistenceAdapter } from '@elytracms/core/persistence';
12
12
  export { AssetRecord } from '@elytracms/core/persistence';
13
- import { Metadata, MetadataRoute } from 'next';
13
+ import { ResolvingMetadata, Metadata, ResolvedMetadata, MetadataRoute } from 'next';
14
14
  import { z } from 'zod';
15
15
  export { z } from 'zod';
16
16
  import { ImageLoader } from 'next/image';
@@ -538,13 +538,53 @@ interface CanvasRouteProps {
538
538
  interface CanvasRoute {
539
539
  /** The page component: `export default route.Page`. */
540
540
  Page: (props: CanvasRouteProps) => Promise<ReactNode>;
541
- /** Next metadata hook: `export const generateMetadata = route.generateMetadata`. */
542
- generateMetadata: (props: CanvasRouteProps) => Promise<Metadata>;
541
+ /**
542
+ * Next metadata hook: `export const generateMetadata = route.generateMetadata`.
543
+ * Next passes the parent segment's resolving metadata as the second argument;
544
+ * the route merges the page's tags over it (EC-610). A caller that invokes this
545
+ * by hand (the scaffold's `app/page.tsx`) forwards its own `parent`.
546
+ */
547
+ generateMetadata: (props: CanvasRouteProps, parent?: ResolvingMetadata) => Promise<Metadata>;
543
548
  }
544
549
  /** Create the `{ Page, generateMetadata }` pair for `app/[[...slug]]/page.tsx`. */
545
550
  declare function createCanvasRoute(options: CanvasRouteOptions): CanvasRoute;
546
551
 
552
+ /**
553
+ * EC-531, narrowed by EC-610 (owner ruling 3, 2026-10-23): `og:locale` ONLY in the
554
+ * shape Open Graph specifies — `language_TERRITORY` with an underscore (`de_DE`),
555
+ * where BCP 47 writes `de-DE`. The value is the page's RESOLVED locale
556
+ * (`result.tags.locale`, the same fact the router used to pick the content). A
557
+ * bare language code (`de`) is outside that format, so the kit emits nothing for
558
+ * it and the layout's own value stands through the merge: inventing a territory
559
+ * (`de` → `de_DE`) would be wrong exactly where it matters (Austria, the UK,
560
+ * Brazil, Latin America), and a wrong valid value looks deliberate.
561
+ */
562
+ declare function openGraphLocale(locale: string): string | null;
547
563
  declare function canvasPageMetadata(result: ResolvePageResult, assets?: readonly ResolvedAsset[]): Metadata;
564
+ /**
565
+ * EC-610 (owner ruling 1, 2026-10-13): **the site provides the default tags, and
566
+ * the page's tags are merged on top** — a value the page states overrides the
567
+ * site's, a value the page leaves empty falls back to the site's.
568
+ *
569
+ * Next merges metadata SHALLOWLY (read: `generate-metadata.md`, "Merging"): a
570
+ * page segment's `openGraph` object REPLACES the layout's whole `openGraph`, so a
571
+ * site-wide `type`, `siteName`, site image or `locale` declared once in the root
572
+ * layout never reached a canvas page, and both real sites hand-rolled around it
573
+ * differently (NK-039's page hook, Konditorei's post bypass). This is the one
574
+ * merge, applied by the canvas route to whatever builds the page's metadata (the
575
+ * kit's default or the host's `metadata` option): every object-valued key the
576
+ * page sets (`openGraph`, `twitter`, `robots`, `alternates`, …) is merged one
577
+ * level deep over the parent's RESOLVED value; a key the page does not set is left
578
+ * to Next, which inherits it from the parent as before; a scalar or array the
579
+ * page sets wins outright.
580
+ *
581
+ * The parent's resolved shapes spread back cleanly: Next hands `generateMetadata`
582
+ * a `ResolvedMetadata` whose URLs are already strings, whose `images` are
583
+ * descriptor objects and whose titles are `{ absolute, template }` — all of which
584
+ * `Metadata` accepts (Next's own "extend rather than replace" example spreads
585
+ * `(await parent).openGraph?.images`).
586
+ */
587
+ declare function mergeMetadataOverParent(parent: ResolvedMetadata | null | undefined, page: Metadata): Metadata;
548
588
 
549
589
  /**
550
590
  * `sitemap.xml` from published routes (EC-171): walk the same route records
@@ -1668,4 +1708,4 @@ declare function evaluatePreviewRequest(requestUrl: string | URL, expectedToken:
1668
1708
  */
1669
1709
  declare const PACKAGE = "@elytracms/next";
1670
1710
 
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, REVALIDATE_SIGNATURE_HEADER, type ResolveCanvasPageOptions, type ResolveGlobalsOptions, type ResolvedLink, type ResolvedNavEntry, type ResolvedNavLink, type RevalidateEvaluation, type RevalidatePayload, type RevalidateRouteHandlers, type RevalidateRouteOptions, 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, nextCacheTags, nextImagePrimitive, pathFromSlug, projectSweepTag, resolveCanvasPage, resolveGlobals, resolveHostEnv, revalidatePayloadSchema, routeCacheTag, safeRedirectTarget, scopeCacheTag, signRevalidateBody };
1711
+ 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, REVALIDATE_SIGNATURE_HEADER, type ResolveCanvasPageOptions, type ResolveGlobalsOptions, type ResolvedLink, type ResolvedNavEntry, type ResolvedNavLink, type RevalidateEvaluation, type RevalidatePayload, type RevalidateRouteHandlers, type RevalidateRouteOptions, 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, revalidatePayloadSchema, routeCacheTag, safeRedirectTarget, scopeCacheTag, signRevalidateBody };
package/dist/index.js CHANGED
@@ -467,7 +467,7 @@ function resolveOgImage(value, assets) {
467
467
  return assets.find((asset) => asset.id === value)?.url ?? null;
468
468
  }
469
469
  function openGraphLocale(locale) {
470
- return locale.replace(/-/g, "_");
470
+ return locale.includes("-") ? locale.replace(/-/g, "_") : null;
471
471
  }
472
472
  function canvasPageMetadata(result, assets = []) {
473
473
  const doc = result.document;
@@ -484,17 +484,32 @@ function canvasPageMetadata(result, assets = []) {
484
484
  if (seoDescription) metadata.description = seoDescription;
485
485
  const ogTitle = seoOgTitle ?? title;
486
486
  if (doc) {
487
+ const ogLocale = openGraphLocale(result.tags.locale);
487
488
  metadata.openGraph = {
488
489
  ...ogTitle !== null ? { title: ogTitle } : {},
489
490
  ...seoDescription ? { description: seoDescription } : {},
490
491
  ...seoOgImage ? { images: [{ url: seoOgImage }] } : {},
491
- locale: openGraphLocale(result.tags.locale)
492
+ ...ogLocale !== null ? { locale: ogLocale } : {}
492
493
  };
493
494
  }
494
495
  if (seoNoindex) metadata.robots = { index: false };
495
496
  if (seoCanonical) metadata.alternates = { canonical: seoCanonical };
496
497
  return metadata;
497
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
+ }
498
513
  function defaultMetadata(result, assets) {
499
514
  return canvasPageMetadata(result, assets);
500
515
  }
@@ -555,7 +570,7 @@ function createCanvasRoute(options) {
555
570
  }
556
571
  }
557
572
  };
558
- const generateMetadata = async (props) => {
573
+ const pageMetadata = async (props) => {
559
574
  const { slug } = await props.params;
560
575
  const url = pathFromSlug(slug);
561
576
  const perspective = await requestPerspective();
@@ -569,6 +584,10 @@ function createCanvasRoute(options) {
569
584
  if (outcome.kind !== "render") return {};
570
585
  return options.metadata ? options.metadata(outcome.result, { url, perspective }) : defaultMetadata(outcome.result, outcome.assets);
571
586
  };
587
+ const generateMetadata = async (props, parent) => {
588
+ const [page, resolvedParent] = await Promise.all([pageMetadata(props), parent ?? null]);
589
+ return mergeMetadataOverParent(resolvedParent, page);
590
+ };
572
591
  return { Page, generateMetadata };
573
592
  }
574
593
  function paramNamesOf(pattern) {
@@ -1265,6 +1284,6 @@ function createElytraHost(options) {
1265
1284
  }
1266
1285
  var PACKAGE = "@elytracms/next";
1267
1286
 
1268
- 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, nextCacheTags, nextImagePrimitive, pathFromSlug, projectSweepTag, resolveCanvasPage, resolveGlobals, resolveHostEnv, revalidatePayloadSchema, routeCacheTag, safeRedirectTarget, scopeCacheTag, signRevalidateBody };
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 };
1269
1288
  //# sourceMappingURL=index.js.map
1270
1289
  //# sourceMappingURL=index.js.map