/** * `next/link` shim — environment-aware Link component. * * Defaults to a plain `` so non-Next hosts (Vite, CRA, esbuild) * work out of the box. Next.js hosts can opt into the REAL `next/link` * (with client-router prefetching) by calling {@link registerLink} * ONCE at app init: * * // hub: lib/embed-shim-registration.ts * import NextLink from 'next/link' * import { registerLink } from '@flamingo-stack/openframe-frontend-core/embed-shims' * registerLink(NextLink) * * After registration, every lib component that renders this shim * delegates to `NextLink` — prefetch, replace, scroll, locale, etc. * all work as expected. Without registration, the shim falls through * to the plain `` path that drops Next-specific props. * * Lib internals import this shim directly (relative path); hub-side * code goes through the barrel (`@flamingo-stack/.../embed-shims`). */ 'use client'; import { forwardRef, type AnchorHTMLAttributes, type ComponentType, type ReactNode, type Ref } from 'react'; type LinkProps = Omit, 'href'> & { href?: string | { pathname?: string; query?: Record }; prefetch?: boolean | null; replace?: boolean; scroll?: boolean; shallow?: boolean; passHref?: boolean; legacyBehavior?: boolean; locale?: string | false; children?: ReactNode; }; /** What the shim renders once a host has registered `next/link`. */ type RegisteredLink = ComponentType }>; let impl: RegisteredLink | null = null; /** * Register the real `next/link` so this shim delegates to it instead * of rendering a plain ``. Call ONCE at app init in a Next.js host. */ export function registerLink

(component: ComponentType

): void { // The registration contract IS the assertion: the host states that its // component handles the props this shim forwards (`next/link` does). One // narrow assertion here, instead of `any` on the slot and on this // signature, which erased the type for every caller. impl = component as RegisteredLink; } const Link = forwardRef(function NextLinkShim(props, ref) { // Real impl path — registered by the host. Hand off untouched so // every Next-specific prop (prefetch, replace, scroll, locale…) // reaches the real component intact. if (impl) { const Real = impl; return ; } // Fallback path — plain . Drops Next-only props and reduces // `UrlObject` href to its pathname. const { href, children, prefetch: _prefetch, replace: _replace, scroll: _scroll, shallow: _shallow, passHref: _passHref, legacyBehavior: _legacyBehavior, locale: _locale, ...rest } = props; const hrefStr = typeof href === 'string' ? href : href?.pathname ? href.pathname : undefined; return ( {children} ); }); export default Link;