@flyo/nitro-next 2.0.1 → 2.1.0
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/client.d.mts +50 -2
- package/dist/client.d.ts +50 -2
- package/dist/client.js +28 -0
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +27 -0
- package/dist/client.mjs.map +1 -1
- package/dist/proxy.d.mts +2 -2
- package/dist/proxy.d.ts +2 -2
- package/dist/proxy.js +8 -2
- package/dist/proxy.js.map +1 -1
- package/dist/proxy.mjs +8 -2
- package/dist/proxy.mjs.map +1 -1
- package/dist/server.d.mts +74 -5
- package/dist/server.d.ts +74 -5
- package/dist/server.js +82 -8
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +81 -8
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
package/dist/server.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.tsx"],"sourcesContent":["import { cache } from 'react';\nimport type { Metadata, MetadataRoute } from 'next';\nimport { notFound } from 'next/navigation';\nimport {\n Page,\n Block,\n Entity,\n ConfigApi,\n ConfigResponse,\n Configuration,\n PagesApi,\n EntitiesApi,\n SitemapApi,\n SearchApi\n} from '@flyo/nitro-typescript';\n\n/**\n * Read-only configuration state\n */\nexport interface NitroState {\n readonly accessToken: string;\n readonly lang: string | null;\n readonly baseUrl: string | null;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly components: Record<string, any>;\n readonly showMissingComponentAlert: boolean;\n readonly liveEdit: boolean;\n readonly serverCacheTtl: number;\n readonly clientCacheTtl: number;\n}\n\n/**\n * Route params type for Next.js catch-all routes\n */\ntype RouteParams = {\n params: Promise<{ slug?: string[] }>;\n};\n\n/**\n * Generic route params type for entity routes\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype EntityRouteParams<T = any> = {\n params: Promise<T>;\n};\n\n/**\n * Entity resolver function type\n * Users provide this to resolve entities from their route params\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type EntityResolver<T = any> = (params: Promise<T>) => Promise<Entity>;\n\n/**\n * The Flyo instance returned by initNitro().\n * Carries all API access methods and configuration state.\n */\nexport interface FlyoInstance {\n /** Read-only configuration state */\n readonly state: NitroState;\n /** Fetch and cache the Nitro CMS configuration (React-cached per request) */\n getNitroConfig(): Promise<ConfigResponse>;\n /** Create a PagesApi client */\n getNitroPages(): PagesApi;\n /** Create an EntitiesApi client */\n getNitroEntities(): EntitiesApi;\n /** Create a SitemapApi client */\n getNitroSitemap(): SitemapApi;\n /** Create a SearchApi client */\n getNitroSearch(): SearchApi;\n /** Resolve a page from catch-all route params (React-cached per request) */\n pageResolveRoute(props: RouteParams): Promise<{ page: Page; path: string; cfg: ConfigResponse }>;\n /** Generate a Next.js sitemap from Flyo Nitro content */\n sitemap(): Promise<MetadataRoute.Sitemap>;\n}\n\n/**\n * Initialize and return a FlyoInstance.\n *\n * Call once in your flyo.config.tsx and export the result.\n * The returned object carries all methods bound to the configuration,\n * eliminating the need for global state.\n *\n * @example\n * ```tsx\n * // flyo.config.tsx\n * import { initNitro } from '@flyo/nitro-next/server';\n *\n * export const flyo = initNitro({\n * accessToken: process.env.FLYO_ACCESS_TOKEN || '',\n * lang: 'en',\n * baseUrl: process.env.SITE_URL || 'http://localhost:3000',\n * liveEdit: process.env.FLYO_LIVE_EDIT === 'true',\n * components: { HeroBanner, Text },\n * });\n * ```\n */\nexport function initNitro({\n accessToken,\n lang,\n baseUrl,\n components,\n showMissingComponentAlert,\n liveEdit,\n serverCacheTtl,\n clientCacheTtl,\n}: {\n accessToken: string;\n lang?: string;\n baseUrl?: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n components?: Record<string, any>;\n showMissingComponentAlert?: boolean;\n liveEdit?: boolean;\n serverCacheTtl?: number;\n clientCacheTtl?: number;\n}): FlyoInstance {\n const configuration = new Configuration({ apiKey: accessToken });\n\n const state: NitroState = {\n accessToken,\n lang: lang ?? null,\n baseUrl: baseUrl ?? null,\n components: components ?? {},\n showMissingComponentAlert: showMissingComponentAlert ?? liveEdit ?? false,\n liveEdit: liveEdit ?? false,\n serverCacheTtl: serverCacheTtl ?? 1200,\n clientCacheTtl: clientCacheTtl ?? 900,\n };\n\n const getNitroConfig = cache(async (): Promise<ConfigResponse> => {\n const configApi = new ConfigApi(configuration);\n const useLang = state.lang ?? undefined;\n return configApi.config({ lang: useLang });\n });\n\n const pageResolveRoute = cache(async ({ params }: RouteParams) => {\n const { slug } = await params;\n const path = slug?.join('/') ?? '';\n\n const cfg = await getNitroConfig();\n\n if (!cfg.pages?.includes(path)) {\n notFound();\n }\n\n const page = await new PagesApi(configuration)\n .page({ slug: path })\n .catch((error: unknown) => {\n console.error('Error fetching page:', path, error);\n notFound();\n });\n\n if (!page) {\n notFound();\n }\n\n return { page, path, cfg };\n });\n\n const sitemap = async (): Promise<MetadataRoute.Sitemap> => {\n const sitemapApi = new SitemapApi(configuration);\n const sitemapLang = state.lang ?? undefined;\n\n if (!state.baseUrl) {\n throw new Error('baseUrl is not configured. Please set it in initNitro().');\n }\n\n const items = await sitemapApi.sitemap({ lang: sitemapLang });\n const cleanBaseUrl = state.baseUrl.endsWith('/') ? state.baseUrl.slice(0, -1) : state.baseUrl;\n\n return items.map((item) => {\n let path = '';\n\n if (item.routes && typeof item.routes === 'object') {\n const routeValues = Object.values(item.routes);\n if (routeValues.length > 0) {\n path = routeValues[0];\n }\n }\n\n if (!path && item.entity_slug) {\n path = item.entity_slug;\n }\n\n const cleanPath = path && !path.startsWith('/') ? `/${path}` : path;\n\n return {\n url: `${cleanBaseUrl}${cleanPath}`,\n lastModified: new Date(),\n };\n });\n };\n\n return {\n state,\n getNitroConfig,\n getNitroPages: () => new PagesApi(configuration),\n getNitroEntities: () => new EntitiesApi(configuration),\n getNitroSitemap: () => new SitemapApi(configuration),\n getNitroSearch: () => new SearchApi(configuration),\n pageResolveRoute,\n sitemap,\n };\n}\n\n// ─── Helper ──────────────────────────────────────────────────────────────────\n\n/**\n * Helper function to read environment variables with fallback\n */\nconst readEnv = (key: string, fallback = \"\"): string => {\n const value = process.env[key];\n if (value !== undefined && value !== \"\") {\n return String(value);\n }\n return fallback;\n};\n\n/**\n * Internal helper to wrap and cache entity resolvers\n */\nfunction createCachedEntityResolver<T>(\n resolver: EntityResolver<T>\n): (props: EntityRouteParams<T>) => Promise<Entity> {\n return cache(async ({ params }: EntityRouteParams<T>) => {\n const entity = await resolver(params);\n\n if (!entity) {\n notFound();\n }\n\n return entity;\n });\n}\n\n// ─── Server Components ───────────────────────────────────────────────────────\n\n/**\n * NitroDebugInfo Component\n *\n * Async server component that outputs debug information as an HTML comment.\n * Resolves config internally from the flyo instance.\n *\n * @example\n * ```tsx\n * <NitroDebugInfo flyo={flyo} />\n * ```\n */\nexport async function NitroDebugInfo({ flyo }: { flyo: FlyoInstance }) {\n try {\n const config = await flyo.getNitroConfig();\n const { state } = flyo;\n\n const mode = readEnv(\"NODE_ENV\", \"-\");\n const vercelDeploymentId = readEnv(\"VERCEL_DEPLOYMENT_ID\", \"-\");\n const vercelGitCommitSha = readEnv(\"VERCEL_GIT_COMMIT_SHA\", \"-\");\n const version = readEnv(\"VERSION\", \"\");\n\n const tokenValue = state.accessToken || \"\";\n const token = typeof tokenValue === \"string\" ? tokenValue : \"\";\n const tokenType = token.startsWith(\"p-\")\n ? \"production\"\n : token.startsWith(\"d-\")\n ? \"develop\"\n : \"unknown\";\n\n const debug = state.liveEdit;\n\n const apiVersion = config.nitro?.version?.toString() || \"-\";\n const apiLastUpdate = config.nitro?.updated_at\n ? new Date(config.nitro.updated_at * 1000).toLocaleString(\"de-CH\", {\n day: \"2-digit\",\n month: \"2-digit\",\n year: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n })\n : \"-\";\n\n const debugInfoParts = [\n `liveedit:${debug}`,\n `env:${mode}`,\n `version:${apiVersion}`,\n `versiondate:${apiLastUpdate}`,\n `tokentype:${tokenType}`,\n `did:${vercelDeploymentId}`,\n `csha:${vercelGitCommitSha}`,\n ];\n\n if (version) {\n debugInfoParts.push(`release:${version}`);\n }\n\n const debugInfo = debugInfoParts.join(\" | \");\n\n return (\n <template dangerouslySetInnerHTML={{ __html: `<!-- ${debugInfo} -->` }} suppressHydrationWarning />\n );\n } catch {\n return <template dangerouslySetInnerHTML={{ __html: `<!-- nitro-debug: not initialized -->` }} suppressHydrationWarning />;\n }\n}\n\n/**\n * Renders a JSON-LD structured data script tag from an Entity's jsonld field.\n * Safely escapes HTML to prevent XSS.\n */\nexport function NitroEntityJsonLd({ entity }: { entity: Entity }) {\n if (!entity?.jsonld) {\n return null;\n }\n\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{\n __html: JSON.stringify(entity.jsonld).replace(/</g, '\\\\u003c'),\n }}\n />\n );\n}\n\n/**\n * NitroPage renders all blocks from a Flyo page.\n */\nexport function NitroPage({\n page,\n flyo,\n}: {\n page: Page;\n flyo: FlyoInstance;\n}) {\n if (!page?.json || !Array.isArray(page.json)) {\n return null;\n }\n\n return (\n <>\n {page.json.map((block: Block, index: number) => (\n <NitroBlock\n key={block.uid || index}\n block={block}\n flyo={flyo}\n />\n ))}\n </>\n );\n}\n\n/**\n * NitroBlock renders a single block using the registered component.\n */\nexport function NitroBlock({\n block,\n flyo,\n}: {\n block: Block;\n flyo: FlyoInstance;\n}) {\n if (!block) {\n return null;\n }\n\n const Component = block.component ? flyo.state.components[block.component] : undefined;\n\n if (Component) {\n return <Component block={block} />;\n }\n\n if (flyo.state.showMissingComponentAlert) {\n return (\n <div style={{ border: '1px solid #fff', padding: '1rem', marginBottom: '1rem', backgroundColor: 'red' }}>\n Component <b>{block.component}</b> not found.\n </div>\n );\n }\n\n return null;\n}\n\n/**\n * NitroSlot renders nested blocks from a slot.\n * Used for recursive block rendering when blocks contain slots.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { NitroSlot } from '@flyo/nitro-next/server';\n *\n * export function MyComponent({ block }) {\n * return (\n * <div>\n * <NitroSlot slot={block.slots?.content} flyo={flyo} />\n * </div>\n * );\n * }\n * ```\n */\nexport function NitroSlot({\n slot,\n flyo,\n}: {\n slot?: {\n content?: Block[];\n };\n flyo: FlyoInstance;\n}) {\n if (!slot?.content || !Array.isArray(slot.content)) {\n return null;\n }\n\n return (\n <>\n {slot.content.map((block: Block, index: number) => (\n <NitroBlock\n key={block.uid || index}\n block={block}\n flyo={flyo}\n />\n ))}\n </>\n );\n}\n\n// ─── Factory Functions ───────────────────────────────────────────────────────\n\n/**\n * Create a page route handler for Nitro pages.\n * Returns a Next.js page component function.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { nitroPageRoute } from '@flyo/nitro-next/server';\n *\n * export default nitroPageRoute(flyo);\n * ```\n */\nexport function nitroPageRoute(flyo: FlyoInstance) {\n async function pageRoute(props: RouteParams) {\n const { page } = await flyo.pageResolveRoute(props);\n return <NitroPage page={page} flyo={flyo} />;\n }\n return pageRoute;\n}\n\n/**\n * Create a metadata generator for Nitro pages.\n * Returns a Next.js generateMetadata function.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { nitroPageGenerateMetadata } from '@flyo/nitro-next/server';\n *\n * export const generateMetadata = nitroPageGenerateMetadata(flyo);\n * ```\n */\nexport function nitroPageGenerateMetadata(flyo: FlyoInstance) {\n return async (props: RouteParams): Promise<Metadata> => {\n const { page } = await flyo.pageResolveRoute(props);\n\n const meta = page.meta_json;\n const title = meta?.title || page.title || '';\n const description = meta?.description ?? '';\n const image = meta?.image ?? '';\n\n const ogImage = image ? `${image}/thumb/1200x630?format=jpg` : undefined;\n const twImage = image ? `${image}/thumb/1200x600?format=jpg` : undefined;\n\n return {\n title,\n description,\n openGraph: {\n title,\n description,\n images: ogImage ? [ogImage] : [],\n type: 'website',\n },\n twitter: {\n card: 'summary_large_image',\n title,\n description,\n images: twImage ? [twImage] : [],\n },\n };\n };\n}\n\n/**\n * Create a static params generator for Nitro pages.\n * Returns a Next.js generateStaticParams function.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { nitroPageGenerateStaticParams } from '@flyo/nitro-next/server';\n *\n * export const generateStaticParams = nitroPageGenerateStaticParams(flyo);\n * ```\n */\nexport function nitroPageGenerateStaticParams(flyo: FlyoInstance) {\n return async () => {\n const cfg = await flyo.getNitroConfig();\n const pages = cfg.pages ?? [];\n\n return pages.map((path: string) => ({\n slug: path === '' ? undefined : path.split('/'),\n }));\n };\n}\n\n/**\n * Create an entity route handler with a custom resolver.\n * Returns a Next.js page component function.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { nitroEntityRoute } from '@flyo/nitro-next/server';\n *\n * const resolver = async (params) => {\n * const { slug } = await params;\n * return flyo.getNitroEntities().entityBySlug({ slug, typeId: 123 });\n * };\n *\n * export default nitroEntityRoute(flyo, {\n * resolver,\n * render: (entity) => <h1>{entity.entity?.entity_title}</h1>,\n * });\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function nitroEntityRoute<T = any>(\n flyo: FlyoInstance,\n options: {\n resolver: EntityResolver<T>;\n render?: (entity: Entity) => React.ReactNode;\n }\n) {\n const cachedResolver = createCachedEntityResolver(options.resolver);\n\n async function entityRoute(props: EntityRouteParams<T>) {\n const entity = await cachedResolver(props);\n\n if (options.render) {\n return options.render(entity);\n }\n\n return <div>{entity.entity?.entity_title}</div>;\n }\n return entityRoute;\n}\n\n/**\n * Create a metadata generator for entity detail pages.\n * Returns a Next.js generateMetadata function.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { nitroEntityGenerateMetadata } from '@flyo/nitro-next/server';\n *\n * export const generateMetadata = nitroEntityGenerateMetadata(flyo, { resolver });\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function nitroEntityGenerateMetadata<T = any>(\n flyo: FlyoInstance,\n options: {\n resolver: EntityResolver<T>;\n }\n) {\n const cachedResolver = createCachedEntityResolver(options.resolver);\n\n return async (props: EntityRouteParams<T>): Promise<Metadata> => {\n const entity = await cachedResolver(props);\n\n const title = entity.entity?.entity_title || '';\n const description = entity.entity?.entity_teaser ?? '';\n const image = entity.entity?.entity_image ?? '';\n\n const ogImage = image ? `${image}/thumb/1200x630?format=jpg` : undefined;\n const twImage = image ? `${image}/thumb/1200x600?format=jpg` : undefined;\n\n return {\n title,\n description,\n openGraph: {\n title,\n description,\n images: ogImage ? [ogImage] : [],\n type: 'website',\n },\n twitter: {\n card: 'summary_large_image',\n title,\n description,\n images: twImage ? [twImage] : [],\n },\n };\n };\n}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.tsx","../src/i18n.ts"],"sourcesContent":["import { cache } from 'react';\nimport type { Metadata, MetadataRoute } from 'next';\nimport { notFound } from 'next/navigation';\nimport { headers } from 'next/headers';\nimport {\n Page,\n Block,\n Entity,\n ConfigApi,\n ConfigResponse,\n Configuration,\n PagesApi,\n EntitiesApi,\n SitemapApi,\n SearchApi,\n type Translation\n} from '@flyo/nitro-typescript';\n\n// Re-export the framework-agnostic language-links helper + types so they are\n// available from `@flyo/nitro-next/server` alongside the rest of the server API.\nexport { getLanguageLinks } from './i18n';\nexport type { FlyoLanguageLink } from './i18n';\nexport type { Translation } from '@flyo/nitro-typescript';\n\n/**\n * Read-only configuration state\n */\nexport interface NitroState {\n readonly accessToken: string;\n /** The single default language (back-compat). Prefer `defaultLocale` for i18n. */\n readonly lang: string | null;\n /** All locale shortcodes the site supports, e.g. `['de', 'en']`. Empty for single-language setups. */\n readonly locales: string[];\n /** The primary/default locale (no URL prefix). Matches `config.nitro.primary_language`. */\n readonly defaultLocale: string | null;\n readonly baseUrl: string | null;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly components: Record<string, any>;\n readonly showMissingComponentAlert: boolean;\n readonly liveEdit: boolean;\n readonly serverCacheTtl: number;\n readonly clientCacheTtl: number;\n}\n\n/**\n * Route params type for Next.js catch-all routes\n */\ntype RouteParams = {\n params: Promise<{ slug?: string[] }>;\n};\n\n/**\n * Generic route params type for entity routes\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype EntityRouteParams<T = any> = {\n params: Promise<T>;\n};\n\n/**\n * Entity resolver function type\n * Users provide this to resolve entities from their route params\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type EntityResolver<T = any> = (params: Promise<T>) => Promise<Entity>;\n\n/**\n * The Flyo instance returned by initNitro().\n * Carries all API access methods and configuration state.\n */\nexport interface FlyoInstance {\n /** Read-only configuration state */\n readonly state: NitroState;\n /**\n * Fetch and cache the Nitro CMS configuration (React-cached per requested locale).\n * When `lang` is omitted, the active request locale is used (from the `x-flyo-locale`\n * header set by the proxy), falling back to `defaultLocale`.\n */\n getNitroConfig(lang?: string): Promise<ConfigResponse>;\n /** Create a PagesApi client */\n getNitroPages(): PagesApi;\n /** Create an EntitiesApi client */\n getNitroEntities(): EntitiesApi;\n /** Create a SitemapApi client */\n getNitroSitemap(): SitemapApi;\n /** Create a SearchApi client */\n getNitroSearch(): SearchApi;\n /**\n * Resolve the active request locale from the `x-flyo-locale` header (set by the\n * proxy middleware), falling back to `defaultLocale`. Use it inside entity\n * resolvers or to set `<html lang>`.\n */\n getRequestLocale(): Promise<string | undefined>;\n /** Resolve a page from catch-all route params (React-cached per request) */\n pageResolveRoute(props: RouteParams): Promise<{ page: Page; path: string; lang: string | undefined; cfg: ConfigResponse }>;\n /** Generate a Next.js sitemap from Flyo Nitro content */\n sitemap(): Promise<MetadataRoute.Sitemap>;\n}\n\n/**\n * Initialize and return a FlyoInstance.\n *\n * Call once in your flyo.config.tsx and export the result.\n * The returned object carries all methods bound to the configuration,\n * eliminating the need for global state.\n *\n * @example\n * ```tsx\n * // flyo.config.tsx\n * import { initNitro } from '@flyo/nitro-next/server';\n *\n * export const flyo = initNitro({\n * accessToken: process.env.FLYO_ACCESS_TOKEN || '',\n * lang: 'en',\n * baseUrl: process.env.SITE_URL || 'http://localhost:3000',\n * liveEdit: process.env.FLYO_LIVE_EDIT === 'true',\n * components: { HeroBanner, Text },\n * });\n * ```\n */\nexport function initNitro({\n accessToken,\n lang,\n locales,\n defaultLocale,\n baseUrl,\n components,\n showMissingComponentAlert,\n liveEdit,\n serverCacheTtl,\n clientCacheTtl,\n}: {\n accessToken: string;\n lang?: string;\n /** All supported locale shortcodes, e.g. `['de', 'en']`. Enables per-request i18n. */\n locales?: string[];\n /** The primary/default locale (no URL prefix). Defaults to `lang`. Should match `config.nitro.primary_language`. */\n defaultLocale?: string;\n baseUrl?: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n components?: Record<string, any>;\n showMissingComponentAlert?: boolean;\n liveEdit?: boolean;\n serverCacheTtl?: number;\n clientCacheTtl?: number;\n}): FlyoInstance {\n const configuration = new Configuration({ apiKey: accessToken });\n\n const resolvedDefaultLocale = defaultLocale ?? lang ?? null;\n const resolvedLocales = locales ?? (resolvedDefaultLocale ? [resolvedDefaultLocale] : []);\n\n const state: NitroState = {\n accessToken,\n lang: lang ?? null,\n locales: resolvedLocales,\n defaultLocale: resolvedDefaultLocale,\n baseUrl: baseUrl ?? null,\n components: components ?? {},\n showMissingComponentAlert: showMissingComponentAlert ?? liveEdit ?? false,\n liveEdit: liveEdit ?? false,\n serverCacheTtl: serverCacheTtl ?? 1200,\n clientCacheTtl: clientCacheTtl ?? 900,\n };\n\n const getRequestLocale = async (): Promise<string | undefined> => {\n try {\n const requestHeaders = await headers();\n const headerLocale = requestHeaders.get('x-flyo-locale');\n if (headerLocale) {\n return headerLocale;\n }\n } catch {\n // `headers()` throws outside a request scope (build time, tests, …) — fall back below.\n }\n return state.defaultLocale ?? state.lang ?? undefined;\n };\n\n // React-cached per requested locale, so different languages don't collide on\n // one memoized value and the same locale is only fetched once per request.\n const fetchConfig = cache(async (lang?: string): Promise<ConfigResponse> => {\n const configApi = new ConfigApi(configuration);\n return configApi.config({ lang: lang ?? undefined });\n });\n\n const getNitroConfig = async (lang?: string): Promise<ConfigResponse> => {\n const useLang = lang ?? (await getRequestLocale());\n return fetchConfig(useLang);\n };\n\n const pageResolveRoute = cache(async ({ params }: RouteParams) => {\n const { slug } = await params;\n const segments = slug ?? [];\n // Pages are addressed by their full, globally-unique slug (locale prefix\n // included), which is exactly what the catch-all captures — pass it through.\n const path = segments.join('/');\n\n // Derive the active locale from the leading path segment when it is a\n // configured locale (matches the Laravel adapter's `request()->segment(1)`).\n const firstSegment = segments[0];\n const lang = firstSegment && state.locales.includes(firstSegment)\n ? firstSegment\n : (state.defaultLocale ?? undefined);\n\n const cfg = await getNitroConfig(lang);\n\n if (!cfg.pages?.includes(path)) {\n notFound();\n }\n\n const page = await new PagesApi(configuration)\n .page({ slug: path, lang })\n .catch((error: unknown) => {\n console.error('Error fetching page:', path, error);\n notFound();\n });\n\n if (!page) {\n notFound();\n }\n\n return { page, path, lang, cfg };\n });\n\n const sitemap = async (): Promise<MetadataRoute.Sitemap> => {\n const sitemapApi = new SitemapApi(configuration);\n const sitemapLang = state.lang ?? undefined;\n\n if (!state.baseUrl) {\n throw new Error('baseUrl is not configured. Please set it in initNitro().');\n }\n\n const items = await sitemapApi.sitemap({ lang: sitemapLang });\n const cleanBaseUrl = state.baseUrl.endsWith('/') ? state.baseUrl.slice(0, -1) : state.baseUrl;\n\n return items.map((item) => {\n let path = '';\n\n if (item.routes && typeof item.routes === 'object') {\n const routeValues = Object.values(item.routes);\n if (routeValues.length > 0) {\n path = routeValues[0];\n }\n }\n\n if (!path && item.entity_slug) {\n path = item.entity_slug;\n }\n\n const cleanPath = path && !path.startsWith('/') ? `/${path}` : path;\n\n return {\n url: `${cleanBaseUrl}${cleanPath}`,\n lastModified: new Date(),\n };\n });\n };\n\n return {\n state,\n getNitroConfig,\n getRequestLocale,\n getNitroPages: () => new PagesApi(configuration),\n getNitroEntities: () => new EntitiesApi(configuration),\n getNitroSitemap: () => new SitemapApi(configuration),\n getNitroSearch: () => new SearchApi(configuration),\n pageResolveRoute,\n sitemap,\n };\n}\n\n// ─── Helper ──────────────────────────────────────────────────────────────────\n\n/**\n * Helper function to read environment variables with fallback\n */\nconst readEnv = (key: string, fallback = \"\"): string => {\n const value = process.env[key];\n if (value !== undefined && value !== \"\") {\n return String(value);\n }\n return fallback;\n};\n\n/**\n * Build Next.js `alternates` (hreflang + canonical) from a page's or entity's\n * `translation[]`. Returns `undefined` when there are no linked translations.\n */\nfunction buildLanguageAlternates(\n translations: Translation[] | undefined,\n currentLang?: string,\n): Metadata['alternates'] | undefined {\n const languages: Record<string, string> = {};\n let canonical: string | undefined;\n\n for (const t of translations ?? []) {\n const shortcode = t.language?.shortcode;\n if (shortcode && t.href) {\n languages[shortcode] = t.href;\n if (currentLang && shortcode === currentLang) {\n canonical = t.href;\n }\n }\n }\n\n if (Object.keys(languages).length === 0) {\n return undefined;\n }\n\n return canonical ? { canonical, languages } : { languages };\n}\n\n/**\n * Internal helper to wrap and cache entity resolvers\n */\nfunction createCachedEntityResolver<T>(\n resolver: EntityResolver<T>\n): (props: EntityRouteParams<T>) => Promise<Entity> {\n return cache(async ({ params }: EntityRouteParams<T>) => {\n const entity = await resolver(params);\n\n if (!entity) {\n notFound();\n }\n\n return entity;\n });\n}\n\n// ─── Server Components ───────────────────────────────────────────────────────\n\n/**\n * NitroDebugInfo Component\n *\n * Async server component that outputs debug information as an HTML comment.\n * Resolves config internally from the flyo instance.\n *\n * @example\n * ```tsx\n * <NitroDebugInfo flyo={flyo} />\n * ```\n */\nexport async function NitroDebugInfo({ flyo }: { flyo: FlyoInstance }) {\n try {\n const config = await flyo.getNitroConfig();\n const { state } = flyo;\n\n const mode = readEnv(\"NODE_ENV\", \"-\");\n const vercelDeploymentId = readEnv(\"VERCEL_DEPLOYMENT_ID\", \"-\");\n const vercelGitCommitSha = readEnv(\"VERCEL_GIT_COMMIT_SHA\", \"-\");\n const version = readEnv(\"VERSION\", \"\");\n\n const tokenValue = state.accessToken || \"\";\n const token = typeof tokenValue === \"string\" ? tokenValue : \"\";\n const tokenType = token.startsWith(\"p-\")\n ? \"production\"\n : token.startsWith(\"d-\")\n ? \"develop\"\n : \"unknown\";\n\n const debug = state.liveEdit;\n\n const apiVersion = config.nitro?.version?.toString() || \"-\";\n const apiLastUpdate = config.nitro?.updated_at\n ? new Date(config.nitro.updated_at * 1000).toLocaleString(\"de-CH\", {\n day: \"2-digit\",\n month: \"2-digit\",\n year: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n })\n : \"-\";\n\n const debugInfoParts = [\n `liveedit:${debug}`,\n `env:${mode}`,\n `version:${apiVersion}`,\n `versiondate:${apiLastUpdate}`,\n `tokentype:${tokenType}`,\n `did:${vercelDeploymentId}`,\n `csha:${vercelGitCommitSha}`,\n ];\n\n if (version) {\n debugInfoParts.push(`release:${version}`);\n }\n\n const debugInfo = debugInfoParts.join(\" | \");\n\n return (\n <template dangerouslySetInnerHTML={{ __html: `<!-- ${debugInfo} -->` }} suppressHydrationWarning />\n );\n } catch {\n return <template dangerouslySetInnerHTML={{ __html: `<!-- nitro-debug: not initialized -->` }} suppressHydrationWarning />;\n }\n}\n\n/**\n * Renders a JSON-LD structured data script tag from an Entity's jsonld field.\n * Safely escapes HTML to prevent XSS.\n */\nexport function NitroEntityJsonLd({ entity }: { entity: Entity }) {\n if (!entity?.jsonld) {\n return null;\n }\n\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{\n __html: JSON.stringify(entity.jsonld).replace(/</g, '\\\\u003c'),\n }}\n />\n );\n}\n\n/**\n * NitroPage renders all blocks from a Flyo page.\n */\nexport function NitroPage({\n page,\n flyo,\n}: {\n page: Page;\n flyo: FlyoInstance;\n}) {\n if (!page?.json || !Array.isArray(page.json)) {\n return null;\n }\n\n return (\n <>\n {page.json.map((block: Block, index: number) => (\n <NitroBlock\n key={block.uid || index}\n block={block}\n flyo={flyo}\n />\n ))}\n </>\n );\n}\n\n/**\n * NitroBlock renders a single block using the registered component.\n */\nexport function NitroBlock({\n block,\n flyo,\n}: {\n block: Block;\n flyo: FlyoInstance;\n}) {\n if (!block) {\n return null;\n }\n\n const Component = block.component ? flyo.state.components[block.component] : undefined;\n\n if (Component) {\n return <Component block={block} />;\n }\n\n if (flyo.state.showMissingComponentAlert) {\n return (\n <div style={{ border: '1px solid #fff', padding: '1rem', marginBottom: '1rem', backgroundColor: 'red' }}>\n Component <b>{block.component}</b> not found.\n </div>\n );\n }\n\n return null;\n}\n\n/**\n * NitroSlot renders nested blocks from a slot.\n * Used for recursive block rendering when blocks contain slots.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { NitroSlot } from '@flyo/nitro-next/server';\n *\n * export function MyComponent({ block }) {\n * return (\n * <div>\n * <NitroSlot slot={block.slots?.content} flyo={flyo} />\n * </div>\n * );\n * }\n * ```\n */\nexport function NitroSlot({\n slot,\n flyo,\n}: {\n slot?: {\n content?: Block[];\n };\n flyo: FlyoInstance;\n}) {\n if (!slot?.content || !Array.isArray(slot.content)) {\n return null;\n }\n\n return (\n <>\n {slot.content.map((block: Block, index: number) => (\n <NitroBlock\n key={block.uid || index}\n block={block}\n flyo={flyo}\n />\n ))}\n </>\n );\n}\n\n// ─── Factory Functions ───────────────────────────────────────────────────────\n\n/**\n * Create a page route handler for Nitro pages.\n * Returns a Next.js page component function.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { nitroPageRoute } from '@flyo/nitro-next/server';\n *\n * export default nitroPageRoute(flyo);\n * ```\n */\nexport function nitroPageRoute(flyo: FlyoInstance) {\n async function pageRoute(props: RouteParams) {\n const { page } = await flyo.pageResolveRoute(props);\n return <NitroPage page={page} flyo={flyo} />;\n }\n return pageRoute;\n}\n\n/**\n * Create a metadata generator for Nitro pages.\n * Returns a Next.js generateMetadata function.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { nitroPageGenerateMetadata } from '@flyo/nitro-next/server';\n *\n * export const generateMetadata = nitroPageGenerateMetadata(flyo);\n * ```\n */\nexport function nitroPageGenerateMetadata(flyo: FlyoInstance) {\n return async (props: RouteParams): Promise<Metadata> => {\n const { page, lang } = await flyo.pageResolveRoute(props);\n\n const meta = page.meta_json;\n const title = meta?.title || page.title || '';\n const description = meta?.description ?? '';\n const image = meta?.image ?? '';\n\n const ogImage = image ? `${image}/thumb/1200x630?format=jpg` : undefined;\n const twImage = image ? `${image}/thumb/1200x600?format=jpg` : undefined;\n\n const alternates = buildLanguageAlternates(page.translation, lang);\n\n return {\n title,\n description,\n ...(alternates ? { alternates } : {}),\n openGraph: {\n title,\n description,\n images: ogImage ? [ogImage] : [],\n type: 'website',\n },\n twitter: {\n card: 'summary_large_image',\n title,\n description,\n images: twImage ? [twImage] : [],\n },\n };\n };\n}\n\n/**\n * Create a static params generator for Nitro pages.\n * Returns a Next.js generateStaticParams function.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { nitroPageGenerateStaticParams } from '@flyo/nitro-next/server';\n *\n * export const generateStaticParams = nitroPageGenerateStaticParams(flyo);\n * ```\n */\nexport function nitroPageGenerateStaticParams(flyo: FlyoInstance) {\n return async () => {\n const cfg = await flyo.getNitroConfig();\n const pages = cfg.pages ?? [];\n\n return pages.map((path: string) => ({\n slug: path === '' ? undefined : path.split('/'),\n }));\n };\n}\n\n/**\n * Create an entity route handler with a custom resolver.\n * Returns a Next.js page component function.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { nitroEntityRoute } from '@flyo/nitro-next/server';\n *\n * const resolver = async (params) => {\n * const { slug } = await params;\n * return flyo.getNitroEntities().entityBySlug({ slug, typeId: 123 });\n * };\n *\n * export default nitroEntityRoute(flyo, {\n * resolver,\n * render: (entity) => <h1>{entity.entity?.entity_title}</h1>,\n * });\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function nitroEntityRoute<T = any>(\n flyo: FlyoInstance,\n options: {\n resolver: EntityResolver<T>;\n render?: (entity: Entity) => React.ReactNode;\n }\n) {\n const cachedResolver = createCachedEntityResolver(options.resolver);\n\n async function entityRoute(props: EntityRouteParams<T>) {\n const entity = await cachedResolver(props);\n\n if (options.render) {\n return options.render(entity);\n }\n\n return <div>{entity.entity?.entity_title}</div>;\n }\n return entityRoute;\n}\n\n/**\n * Create a metadata generator for entity detail pages.\n * Returns a Next.js generateMetadata function.\n *\n * @example\n * ```tsx\n * import { flyo } from '@/flyo.config';\n * import { nitroEntityGenerateMetadata } from '@flyo/nitro-next/server';\n *\n * export const generateMetadata = nitroEntityGenerateMetadata(flyo, { resolver });\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function nitroEntityGenerateMetadata<T = any>(\n flyo: FlyoInstance,\n options: {\n resolver: EntityResolver<T>;\n }\n) {\n const cachedResolver = createCachedEntityResolver(options.resolver);\n\n return async (props: EntityRouteParams<T>): Promise<Metadata> => {\n const entity = await cachedResolver(props);\n\n const title = entity.entity?.entity_title || '';\n const description = entity.entity?.entity_teaser ?? '';\n const image = entity.entity?.entity_image ?? '';\n\n const ogImage = image ? `${image}/thumb/1200x630?format=jpg` : undefined;\n const twImage = image ? `${image}/thumb/1200x600?format=jpg` : undefined;\n\n const alternates = buildLanguageAlternates(entity.translation, entity.language);\n\n return {\n title,\n description,\n ...(alternates ? { alternates } : {}),\n openGraph: {\n title,\n description,\n images: ogImage ? [ogImage] : [],\n type: 'website',\n },\n twitter: {\n card: 'summary_large_image',\n title,\n description,\n images: twImage ? [twImage] : [],\n },\n };\n };\n}","import type { Translation } from '@flyo/nitro-typescript';\n\n/**\n * A single language option for a language switcher, derived from a page's or\n * entity's `translation[]`. Framework-agnostic plain data — render it however\n * you like.\n */\nexport interface FlyoLanguageLink {\n /** Locale shortcode, e.g. `\"de\"`. */\n shortcode: string;\n /** Full language name from the CMS translation, e.g. `\"Deutsch\"` (when available). */\n name?: string;\n /** Fully-resolved localized href, or `null` when this locale has no linked translation. */\n href: string | null;\n /** Localized page/entity title, when available. */\n title?: string;\n /** `true` when this entry is the currently active locale. */\n isCurrent: boolean;\n /** `true` when a linked translation actually exists for this locale. */\n exists: boolean;\n}\n\n/**\n * Map a page's or entity's `translation[]` into a switcher-ready array of typed\n * links — the building block for a language switcher.\n *\n * Flyo only returns `translation` entries for languages that actually have a\n * translation. Pass `options.locales` (e.g. `flyo.state.locales`) to also get an\n * entry for every configured locale that is *missing* a translation — those come\n * back as `{ href: null, exists: false }` so you can render a fallback (a disabled\n * item, a link to the home page, …).\n *\n * Pure — no React or server-only APIs, so it is safe to call from server *or*\n * client components.\n *\n * @example\n * ```tsx\n * const links = getLanguageLinks(page.translation, {\n * currentLang: lang,\n * locales: flyo.state.locales,\n * });\n * // links.map(l => l.exists\n * // ? <a key={l.shortcode} href={l.href!} aria-current={l.isCurrent || undefined}>{l.name ?? l.shortcode}</a>\n * // : <span key={l.shortcode} aria-disabled>{l.shortcode}</span>)\n * ```\n */\nexport function getLanguageLinks(\n translations: Translation[] | undefined,\n options?: { currentLang?: string; locales?: string[] },\n): FlyoLanguageLink[] {\n const currentLang = options?.currentLang;\n\n const byShortcode = new Map<string, Translation>();\n for (const t of translations ?? []) {\n const shortcode = t.language?.shortcode;\n if (shortcode) {\n byShortcode.set(shortcode, t);\n }\n }\n\n const toLink = (shortcode: string, t: Translation | undefined): FlyoLanguageLink => ({\n shortcode,\n name: t?.language?.name,\n href: t?.href ?? null,\n title: t?.title,\n isCurrent: shortcode === currentLang,\n exists: t?.href != null,\n });\n\n // When the full set of locales is known, emit an entry for every one so\n // callers can render fallbacks for languages that have no translation.\n if (options?.locales && options.locales.length > 0) {\n return options.locales.map((shortcode) => toLink(shortcode, byShortcode.get(shortcode)));\n }\n\n // Otherwise, just surface the translations that exist.\n return (translations ?? [])\n .filter((t) => t.language?.shortcode)\n .map((t) => toLink(t.language!.shortcode!, t));\n}\n"],"mappings":"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","names":["lang"]}
|