remote-components 0.0.50 → 0.0.51

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.
Files changed (71) hide show
  1. package/dist/html/host.cjs +330 -166
  2. package/dist/html/host.cjs.map +1 -1
  3. package/dist/html/host.js +330 -166
  4. package/dist/html/host.js.map +1 -1
  5. package/dist/html/remote.cjs +114 -20
  6. package/dist/html/remote.cjs.map +1 -1
  7. package/dist/html/remote.js +114 -20
  8. package/dist/html/remote.js.map +1 -1
  9. package/dist/internal/shared/{ssr → client}/fetch-with-protected-rc-fallback.cjs +9 -6
  10. package/dist/internal/shared/client/fetch-with-protected-rc-fallback.cjs.map +1 -0
  11. package/dist/internal/shared/{ssr → client}/fetch-with-protected-rc-fallback.d.ts +1 -2
  12. package/dist/internal/shared/{ssr → client}/fetch-with-protected-rc-fallback.js +10 -6
  13. package/dist/internal/shared/client/fetch-with-protected-rc-fallback.js.map +1 -0
  14. package/dist/internal/shared/client/protected-rc-fallback.cjs +32 -0
  15. package/dist/internal/shared/client/protected-rc-fallback.cjs.map +1 -0
  16. package/dist/internal/shared/client/protected-rc-fallback.d.ts +6 -0
  17. package/dist/internal/shared/client/protected-rc-fallback.js +8 -0
  18. package/dist/internal/shared/client/protected-rc-fallback.js.map +1 -0
  19. package/dist/internal/shared/client/remote-component.cjs +190 -100
  20. package/dist/internal/shared/client/remote-component.cjs.map +1 -1
  21. package/dist/internal/shared/client/remote-component.js +190 -100
  22. package/dist/internal/shared/client/remote-component.js.map +1 -1
  23. package/dist/internal/shared/constants.cjs +29 -0
  24. package/dist/internal/shared/constants.cjs.map +1 -0
  25. package/dist/internal/shared/constants.d.ts +3 -0
  26. package/dist/internal/shared/constants.js +5 -0
  27. package/dist/internal/shared/constants.js.map +1 -0
  28. package/dist/internal/shared/ssr/fetch-remote-component.cjs +1 -2
  29. package/dist/internal/shared/ssr/fetch-remote-component.cjs.map +1 -1
  30. package/dist/internal/shared/ssr/fetch-remote-component.js +1 -2
  31. package/dist/internal/shared/ssr/fetch-remote-component.js.map +1 -1
  32. package/dist/internal/shared/ssr/fetch-with-hooks.cjs +1 -1
  33. package/dist/internal/shared/ssr/fetch-with-hooks.cjs.map +1 -1
  34. package/dist/internal/shared/ssr/fetch-with-hooks.js +1 -1
  35. package/dist/internal/shared/ssr/fetch-with-hooks.js.map +1 -1
  36. package/dist/internal/shared/utils/logger.cjs.map +1 -1
  37. package/dist/internal/shared/utils/logger.d.ts +1 -1
  38. package/dist/internal/shared/utils/logger.js.map +1 -1
  39. package/dist/next/config.cjs.map +1 -1
  40. package/dist/next/config.js.map +1 -1
  41. package/dist/next/host/client/index.cjs +202 -107
  42. package/dist/next/host/client/index.cjs.map +1 -1
  43. package/dist/next/host/client/index.js +202 -107
  44. package/dist/next/host/client/index.js.map +1 -1
  45. package/dist/next/host/pages-router-server.cjs +1 -2
  46. package/dist/next/host/pages-router-server.cjs.map +1 -1
  47. package/dist/next/host/pages-router-server.d.ts +5 -0
  48. package/dist/next/host/pages-router-server.js +1 -2
  49. package/dist/next/host/pages-router-server.js.map +1 -1
  50. package/dist/next/proxy.cjs +36 -24
  51. package/dist/next/proxy.cjs.map +1 -1
  52. package/dist/next/proxy.d.ts +0 -1
  53. package/dist/next/proxy.js +36 -24
  54. package/dist/next/proxy.js.map +1 -1
  55. package/dist/react/index.cjs +202 -107
  56. package/dist/react/index.cjs.map +1 -1
  57. package/dist/react/index.js +202 -107
  58. package/dist/react/index.js.map +1 -1
  59. package/dist/shared/host/proxy.cjs +23 -20
  60. package/dist/shared/host/proxy.cjs.map +1 -1
  61. package/dist/shared/host/proxy.d.ts +0 -3
  62. package/dist/shared/host/proxy.js +23 -19
  63. package/dist/shared/host/proxy.js.map +1 -1
  64. package/dist/shared/remote/proxy.cjs +9 -1
  65. package/dist/shared/remote/proxy.cjs.map +1 -1
  66. package/dist/shared/remote/proxy.d.ts +7 -1
  67. package/dist/shared/remote/proxy.js +8 -1
  68. package/dist/shared/remote/proxy.js.map +1 -1
  69. package/package.json +1 -1
  70. package/dist/internal/shared/ssr/fetch-with-protected-rc-fallback.cjs.map +0 -1
  71. package/dist/internal/shared/ssr/fetch-with-protected-rc-fallback.js.map +0 -1
@@ -189,8 +189,7 @@ async function getRemoteComponentProps(src, options) {
189
189
  remoteShared
190
190
  } = await (0, import_fetch_remote_component.fetchRemoteComponent)(src, {
191
191
  rsc: true,
192
- onRequest: options?.onRequest,
193
- onResponse: options?.onResponse
192
+ ...options
194
193
  });
195
194
  const props = {
196
195
  src,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/next/host/pages-router-server.tsx"],"sourcesContent":["import * as Form from 'next/form';\nimport * as Image from 'next/image';\nimport * as Link from 'next/link';\nimport * as Router from 'next/router';\nimport * as Script from 'next/script';\nimport { useEffect } from 'react';\nimport { shared as _shared } from 'remote-components/shared/host/pages';\nimport { RemoteComponentsError } from '#internal/shared/client/remote-component';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\nimport type { OnRequestHook, OnResponseHook } from '#internal/shared/ssr/types';\nimport { RemoteComponent as RemoteComponentReact } from '#remote-components/react/index';\n\nconst navigationImpl = {\n useRouter: () => Router.useRouter(),\n useSearchParams: () => {\n const router = Router.useRouter();\n return {\n get: (key: string) => router.query[key],\n has: (key: string) => key in router.query,\n };\n },\n};\n\nconst sharedCache = new Map<string, Record<string, () => Promise<unknown>>>();\nconst shared = (bundle: string) => {\n if (sharedCache.has(bundle)) {\n return sharedCache.get(bundle);\n }\n\n const remoteLoader = ({ src, width, quality }: Image.ImageLoaderProps) => {\n const self = globalThis as typeof globalThis & {\n __remote_bundle_url__?: Record<string, URL>;\n };\n const { assetPrefix } =\n /^(?<assetPrefix>.*?)\\/_next\\//.exec(src)?.groups ?? {};\n return `${\n self.__remote_bundle_url__?.[bundle]?.origin ?? ''\n }${assetPrefix}/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${\n quality || 75\n }`;\n };\n\n const imageImpl = (ImageComponent: typeof Image.default) => {\n function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n }\n RemoteImage.default = RemoteImage;\n RemoteImage.getImageProps = Image.getImageProps;\n return RemoteImage;\n };\n\n return {\n // polyfill Next.js App Router client API (minimal)\n // some API methods are not available when using a Next.js Pages Router application as host\n 'next/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/components/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/app-dir/link': () =>\n Promise.resolve(Link.default) as Promise<unknown>,\n 'next/link': () => Promise.resolve(Link.default) as Promise<unknown>,\n 'next/dist/client/app-dir/form': () =>\n Promise.resolve(Form.default) as Promise<unknown>,\n 'next/form': () => Promise.resolve(Form.default) as Promise<unknown>,\n 'next/dist/client/script': () =>\n Promise.resolve(Script.default) as Promise<unknown>,\n 'next/script': () => Promise.resolve(Script.default) as Promise<unknown>,\n 'next/dist/client/image-component': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n Image: imageImpl(Image.default.default ?? Image.default),\n }) as Promise<unknown>,\n 'next/dist/api/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n 'next/router': () => Promise.resolve(Router) as Promise<unknown>,\n ..._shared,\n // always override next/image to use the remote loader\n 'next/image': () =>\n Promise.resolve(\n // @ts-expect-error default.default\n // eslint-disable-next-line\n imageImpl(Image.default.default ?? Image.default),\n ) as Promise<unknown>,\n };\n};\n\n// internal symbols to access global store\nconst REMOTE_COMPONENT_STORE = Symbol('REMOTE_COMPONENT_STORE');\nconst REMOTE_COMPONENT_KEY = '__REMOTE_COMPONENT_KEY__';\n\n// temporary global store for remote component HTML\n// the store is used to save the HTML of remote components for SSR without sending the content to the client\nconst self = globalThis as typeof globalThis & {\n [REMOTE_COMPONENT_STORE]?: Map<string, React.ReactNode>;\n};\n\nfunction getKey({\n bundle,\n route,\n name,\n}: {\n bundle?: string;\n route?: string;\n name?: string;\n}): string {\n return `${bundle ?? '__next'}:${route ?? '/'}:${\n name ?? '__vercel_remote_component'\n }__${crypto.randomUUID()}`;\n}\n\nfunction setComponent(key: string, component: React.ReactNode): void {\n if (!self[REMOTE_COMPONENT_STORE]) {\n self[REMOTE_COMPONENT_STORE] = new Map();\n }\n self[REMOTE_COMPONENT_STORE].set(key, component);\n}\n\nfunction getComponent(key: string): React.ReactNode | undefined {\n const component = self[REMOTE_COMPONENT_STORE]?.get(key);\n // remove the component from the store after retrieving it to prevent memory leaks\n // storing the HTML in the global store is only needed for SSR and it's temporary only used for a single render\n self[REMOTE_COMPONENT_STORE]?.delete(key);\n return component;\n}\n\nexport interface RemoteComponentProps {\n src: string;\n bundle?: string;\n route?: string;\n name?: string;\n isolate?: boolean;\n mode?: 'open' | 'closed';\n reset?: boolean;\n /** Called right before a new remote component load starts. */\n onBeforeLoad?: (src: string | URL) => void;\n /** Called when the remote component has been successfully loaded and mounted. */\n onLoad?: (src: string | URL) => void;\n /** Called when an error occurs while loading or mounting the remote component. */\n onError?: (error: unknown) => void;\n /** Called when a different remote component is loaded into the same wrapper. */\n onChange?: (info: {\n previousSrc: string | URL | null;\n nextSrc: string | URL | null;\n previousName: string | undefined;\n nextName: string | undefined;\n }) => void;\n /**\n * Called when a fetch request is made to retrieve the remote component payload.\n * Can be used to intercept requests, modify headers, or provide a custom response.\n */\n onRequest?: OnRequestHook;\n /**\n * Called after a fetch completes to retrieve the remote component payload.\n * Can be used to inspect the response (e.g., check for redirects) or transform it.\n */\n onResponse?: OnResponseHook;\n [REMOTE_COMPONENT_KEY]?: string;\n children?: React.ReactNode;\n}\n\n/**\n * This component handles the rendering of remote microfrontends.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n */\nexport function RemoteComponent(props: RemoteComponentProps) {\n const remoteComponent =\n typeof document !== 'undefined'\n ? null\n : // retrieve the HTML from the global store\n getComponent(\n props[REMOTE_COMPONENT_KEY] ?? '__vercel_remote_component',\n );\n\n useEffect(() => {\n const clientSelf = globalThis as typeof globalThis & {\n __remote_component_shared__?: Record<string, () => Promise<unknown>>;\n };\n // eslint-disable-next-line camelcase\n clientSelf.__remote_component_shared__ = shared(props.bundle ?? 'default');\n }, [props.bundle]);\n\n if (!props[REMOTE_COMPONENT_KEY]) {\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n onBeforeLoad={props.onBeforeLoad}\n onChange={props.onChange}\n onError={props.onError}\n onLoad={props.onLoad}\n onRequest={props.onRequest}\n onResponse={props.onResponse}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {props.children}\n </RemoteComponentReact>\n );\n }\n\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n onBeforeLoad={props.onBeforeLoad}\n onChange={props.onChange}\n onError={props.onError}\n onLoad={props.onLoad}\n onRequest={props.onRequest}\n onResponse={props.onResponse}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {remoteComponent}\n </RemoteComponentReact>\n );\n}\n\n/**\n * Fetches the remote component properties from the server. You need to pass these properties to the `<RemoteComponent>` component to render the fetched remote component.\n *\n * @param src - The source URL of the remote component. When using the Vercel Microfrontends solution, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param headers - The HTTP headers used for supporting the Vercel Microfrontends proxy.\n * @returns The properties of the remote component.\n *\n * @example\n *\n * ```tsx\n * import { getRemoteComponentProps } from 'remote-components/next/host/pages';\n * import type { GetServerSideProps } from 'next';\n *\n * export const getServerSideProps: GetServerSideProps<PageProps> = async function getServerSideProps({ req }) {\n * const myRemoteComponent = await getRemoteComponentProps(\n * '/nextjs-app-remote/components/header',\n * req.headers,\n * );\n * return {\n * props: {\n * remoteComponents: {\n * myRemoteComponent,\n * },\n * },\n * };\n * }\n * ```\n */\nexport async function getRemoteComponentProps(\n src: string,\n options?: {\n /**\n * Called when a fetch request is made to retrieve the remote component payload.\n * Can be used to intercept requests, modify headers, or provide a custom response.\n */\n onRequest?: OnRequestHook;\n /**\n * Called after a fetch completes to retrieve the remote component payload.\n * Can be used to inspect the response (e.g., check for redirects) or transform it.\n */\n onResponse?: OnResponseHook;\n },\n): Promise<RemoteComponentProps> {\n if (typeof document !== 'undefined') {\n throw new RemoteComponentsError(\n '`getRemoteComponentProps()` can only be used on the server side.',\n );\n }\n\n const {\n metadata: { bundle, route },\n name,\n links,\n component,\n nextData,\n remoteShared,\n } = await fetchRemoteComponent(src, {\n rsc: true,\n onRequest: options?.onRequest,\n onResponse: options?.onResponse,\n });\n\n const props: RemoteComponentProps = {\n src,\n bundle,\n name,\n route,\n };\n\n const key = getKey(props);\n\n // do not render the HTML in development mode when remote is using Next.js Pages Router\n // this behavior is emulating the Next.js Pages Router FOUC as the styles are only applied on the client when running in development mode\n if (nextData?.buildId !== 'development') {\n // store the HTML in a global store\n setComponent(\n key,\n <>\n <script\n data-remote-components-shared=\"\"\n id={`${name}_shared`}\n type=\"application/json\"\n >\n {JSON.stringify(remoteShared)}\n </script>\n {links.map((link) => (\n <link\n key={`${link.as}_${link.href}`}\n {...link}\n precedence={undefined}\n />\n ))}\n {component}\n </>,\n );\n }\n\n return {\n ...props,\n // add remote component key to the props\n [REMOTE_COMPONENT_KEY]: key,\n };\n}\n"],"mappings":"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","names":["import_react","self","_shared","RemoteComponentReact"]}
1
+ {"version":3,"sources":["../../../src/next/host/pages-router-server.tsx"],"sourcesContent":["import * as Form from 'next/form';\nimport * as Image from 'next/image';\nimport * as Link from 'next/link';\nimport * as Router from 'next/router';\nimport * as Script from 'next/script';\nimport { useEffect } from 'react';\nimport { shared as _shared } from 'remote-components/shared/host/pages';\nimport { RemoteComponentsError } from '#internal/shared/client/remote-component';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\nimport type { OnRequestHook, OnResponseHook } from '#internal/shared/ssr/types';\nimport { RemoteComponent as RemoteComponentReact } from '#remote-components/react/index';\n\nconst navigationImpl = {\n useRouter: () => Router.useRouter(),\n useSearchParams: () => {\n const router = Router.useRouter();\n return {\n get: (key: string) => router.query[key],\n has: (key: string) => key in router.query,\n };\n },\n};\n\nconst sharedCache = new Map<string, Record<string, () => Promise<unknown>>>();\nconst shared = (bundle: string) => {\n if (sharedCache.has(bundle)) {\n return sharedCache.get(bundle);\n }\n\n const remoteLoader = ({ src, width, quality }: Image.ImageLoaderProps) => {\n const self = globalThis as typeof globalThis & {\n __remote_bundle_url__?: Record<string, URL>;\n };\n const { assetPrefix } =\n /^(?<assetPrefix>.*?)\\/_next\\//.exec(src)?.groups ?? {};\n return `${\n self.__remote_bundle_url__?.[bundle]?.origin ?? ''\n }${assetPrefix}/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${\n quality || 75\n }`;\n };\n\n const imageImpl = (ImageComponent: typeof Image.default) => {\n function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n }\n RemoteImage.default = RemoteImage;\n RemoteImage.getImageProps = Image.getImageProps;\n return RemoteImage;\n };\n\n return {\n // polyfill Next.js App Router client API (minimal)\n // some API methods are not available when using a Next.js Pages Router application as host\n 'next/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/components/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/app-dir/link': () =>\n Promise.resolve(Link.default) as Promise<unknown>,\n 'next/link': () => Promise.resolve(Link.default) as Promise<unknown>,\n 'next/dist/client/app-dir/form': () =>\n Promise.resolve(Form.default) as Promise<unknown>,\n 'next/form': () => Promise.resolve(Form.default) as Promise<unknown>,\n 'next/dist/client/script': () =>\n Promise.resolve(Script.default) as Promise<unknown>,\n 'next/script': () => Promise.resolve(Script.default) as Promise<unknown>,\n 'next/dist/client/image-component': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n Image: imageImpl(Image.default.default ?? Image.default),\n }) as Promise<unknown>,\n 'next/dist/api/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n 'next/router': () => Promise.resolve(Router) as Promise<unknown>,\n ..._shared,\n // always override next/image to use the remote loader\n 'next/image': () =>\n Promise.resolve(\n // @ts-expect-error default.default\n // eslint-disable-next-line\n imageImpl(Image.default.default ?? Image.default),\n ) as Promise<unknown>,\n };\n};\n\n// internal symbols to access global store\nconst REMOTE_COMPONENT_STORE = Symbol('REMOTE_COMPONENT_STORE');\nconst REMOTE_COMPONENT_KEY = '__REMOTE_COMPONENT_KEY__';\n\n// temporary global store for remote component HTML\n// the store is used to save the HTML of remote components for SSR without sending the content to the client\nconst self = globalThis as typeof globalThis & {\n [REMOTE_COMPONENT_STORE]?: Map<string, React.ReactNode>;\n};\n\nfunction getKey({\n bundle,\n route,\n name,\n}: {\n bundle?: string;\n route?: string;\n name?: string;\n}): string {\n return `${bundle ?? '__next'}:${route ?? '/'}:${\n name ?? '__vercel_remote_component'\n }__${crypto.randomUUID()}`;\n}\n\nfunction setComponent(key: string, component: React.ReactNode): void {\n if (!self[REMOTE_COMPONENT_STORE]) {\n self[REMOTE_COMPONENT_STORE] = new Map();\n }\n self[REMOTE_COMPONENT_STORE].set(key, component);\n}\n\nfunction getComponent(key: string): React.ReactNode | undefined {\n const component = self[REMOTE_COMPONENT_STORE]?.get(key);\n // remove the component from the store after retrieving it to prevent memory leaks\n // storing the HTML in the global store is only needed for SSR and it's temporary only used for a single render\n self[REMOTE_COMPONENT_STORE]?.delete(key);\n return component;\n}\n\nexport interface RemoteComponentProps {\n src: string;\n bundle?: string;\n route?: string;\n name?: string;\n isolate?: boolean;\n mode?: 'open' | 'closed';\n reset?: boolean;\n /** Called right before a new remote component load starts. */\n onBeforeLoad?: (src: string | URL) => void;\n /** Called when the remote component has been successfully loaded and mounted. */\n onLoad?: (src: string | URL) => void;\n /** Called when an error occurs while loading or mounting the remote component. */\n onError?: (error: unknown) => void;\n /** Called when a different remote component is loaded into the same wrapper. */\n onChange?: (info: {\n previousSrc: string | URL | null;\n nextSrc: string | URL | null;\n previousName: string | undefined;\n nextName: string | undefined;\n }) => void;\n /**\n * Called when a fetch request is made to retrieve the remote component payload.\n * Can be used to intercept requests, modify headers, or provide a custom response.\n */\n onRequest?: OnRequestHook;\n /**\n * Called after a fetch completes to retrieve the remote component payload.\n * Can be used to inspect the response (e.g., check for redirects) or transform it.\n */\n onResponse?: OnResponseHook;\n [REMOTE_COMPONENT_KEY]?: string;\n children?: React.ReactNode;\n}\n\n/**\n * This component handles the rendering of remote microfrontends.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n */\nexport function RemoteComponent(props: RemoteComponentProps) {\n const remoteComponent =\n typeof document !== 'undefined'\n ? null\n : // retrieve the HTML from the global store\n getComponent(\n props[REMOTE_COMPONENT_KEY] ?? '__vercel_remote_component',\n );\n\n useEffect(() => {\n const clientSelf = globalThis as typeof globalThis & {\n __remote_component_shared__?: Record<string, () => Promise<unknown>>;\n };\n // eslint-disable-next-line camelcase\n clientSelf.__remote_component_shared__ = shared(props.bundle ?? 'default');\n }, [props.bundle]);\n\n if (!props[REMOTE_COMPONENT_KEY]) {\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n onBeforeLoad={props.onBeforeLoad}\n onChange={props.onChange}\n onError={props.onError}\n onLoad={props.onLoad}\n onRequest={props.onRequest}\n onResponse={props.onResponse}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {props.children}\n </RemoteComponentReact>\n );\n }\n\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n onBeforeLoad={props.onBeforeLoad}\n onChange={props.onChange}\n onError={props.onError}\n onLoad={props.onLoad}\n onRequest={props.onRequest}\n onResponse={props.onResponse}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {remoteComponent}\n </RemoteComponentReact>\n );\n}\n\n/**\n * Fetches the remote component properties from the server. You need to pass these properties to the `<RemoteComponent>` component to render the fetched remote component.\n *\n * @param src - The source URL of the remote component. When using the Vercel Microfrontends solution, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param headers - The HTTP headers used for supporting the Vercel Microfrontends proxy.\n * @returns The properties of the remote component.\n *\n * @example\n *\n * ```tsx\n * import { getRemoteComponentProps } from 'remote-components/next/host/pages';\n * import type { GetServerSideProps } from 'next';\n *\n * export const getServerSideProps: GetServerSideProps<PageProps> = async function getServerSideProps({ req }) {\n * const myRemoteComponent = await getRemoteComponentProps(\n * '/nextjs-app-remote/components/header',\n * req.headers,\n * );\n * return {\n * props: {\n * remoteComponents: {\n * myRemoteComponent,\n * },\n * },\n * };\n * }\n * ```\n */\nexport async function getRemoteComponentProps(\n src: string,\n options?: {\n /**\n * Called when a fetch request is made to retrieve the remote component payload.\n * Can be used to intercept requests, modify headers, or provide a custom response.\n */\n onRequest?: OnRequestHook;\n /**\n * Called after a fetch completes to retrieve the remote component payload.\n * Can be used to inspect the response (e.g., check for redirects) or transform it.\n */\n onResponse?: OnResponseHook;\n /**\n * The name of the exposed remote component. Used to identify the remote component\n * when multiple remote components are exposed on a page.\n */\n name?: string;\n },\n): Promise<RemoteComponentProps> {\n if (typeof document !== 'undefined') {\n throw new RemoteComponentsError(\n '`getRemoteComponentProps()` can only be used on the server side.',\n );\n }\n\n const {\n metadata: { bundle, route },\n name,\n links,\n component,\n nextData,\n remoteShared,\n } = await fetchRemoteComponent(src, {\n rsc: true,\n ...options,\n });\n\n const props: RemoteComponentProps = {\n src,\n bundle,\n name,\n route,\n };\n\n const key = getKey(props);\n\n // do not render the HTML in development mode when remote is using Next.js Pages Router\n // this behavior is emulating the Next.js Pages Router FOUC as the styles are only applied on the client when running in development mode\n if (nextData?.buildId !== 'development') {\n // store the HTML in a global store\n setComponent(\n key,\n <>\n <script\n data-remote-components-shared=\"\"\n id={`${name}_shared`}\n type=\"application/json\"\n >\n {JSON.stringify(remoteShared)}\n </script>\n {links.map((link) => (\n <link\n key={`${link.as}_${link.href}`}\n {...link}\n precedence={undefined}\n />\n ))}\n {component}\n </>,\n );\n }\n\n return {\n ...props,\n // add remote component key to the props\n [REMOTE_COMPONENT_KEY]: key,\n };\n}\n"],"mappings":"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","names":["import_react","self","_shared","RemoteComponentReact"]}
@@ -83,6 +83,11 @@ declare function getRemoteComponentProps(src: string, options?: {
83
83
  * Can be used to inspect the response (e.g., check for redirects) or transform it.
84
84
  */
85
85
  onResponse?: OnResponseHook;
86
+ /**
87
+ * The name of the exposed remote component. Used to identify the remote component
88
+ * when multiple remote components are exposed on a page.
89
+ */
90
+ name?: string;
86
91
  }): Promise<RemoteComponentProps>;
87
92
 
88
93
  export { RemoteComponent, RemoteComponentProps, getRemoteComponentProps };
@@ -155,8 +155,7 @@ async function getRemoteComponentProps(src, options) {
155
155
  remoteShared
156
156
  } = await fetchRemoteComponent(src, {
157
157
  rsc: true,
158
- onRequest: options?.onRequest,
159
- onResponse: options?.onResponse
158
+ ...options
160
159
  });
161
160
  const props = {
162
161
  src,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/next/host/pages-router-server.tsx"],"sourcesContent":["import * as Form from 'next/form';\nimport * as Image from 'next/image';\nimport * as Link from 'next/link';\nimport * as Router from 'next/router';\nimport * as Script from 'next/script';\nimport { useEffect } from 'react';\nimport { shared as _shared } from 'remote-components/shared/host/pages';\nimport { RemoteComponentsError } from '#internal/shared/client/remote-component';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\nimport type { OnRequestHook, OnResponseHook } from '#internal/shared/ssr/types';\nimport { RemoteComponent as RemoteComponentReact } from '#remote-components/react/index';\n\nconst navigationImpl = {\n useRouter: () => Router.useRouter(),\n useSearchParams: () => {\n const router = Router.useRouter();\n return {\n get: (key: string) => router.query[key],\n has: (key: string) => key in router.query,\n };\n },\n};\n\nconst sharedCache = new Map<string, Record<string, () => Promise<unknown>>>();\nconst shared = (bundle: string) => {\n if (sharedCache.has(bundle)) {\n return sharedCache.get(bundle);\n }\n\n const remoteLoader = ({ src, width, quality }: Image.ImageLoaderProps) => {\n const self = globalThis as typeof globalThis & {\n __remote_bundle_url__?: Record<string, URL>;\n };\n const { assetPrefix } =\n /^(?<assetPrefix>.*?)\\/_next\\//.exec(src)?.groups ?? {};\n return `${\n self.__remote_bundle_url__?.[bundle]?.origin ?? ''\n }${assetPrefix}/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${\n quality || 75\n }`;\n };\n\n const imageImpl = (ImageComponent: typeof Image.default) => {\n function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n }\n RemoteImage.default = RemoteImage;\n RemoteImage.getImageProps = Image.getImageProps;\n return RemoteImage;\n };\n\n return {\n // polyfill Next.js App Router client API (minimal)\n // some API methods are not available when using a Next.js Pages Router application as host\n 'next/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/components/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/app-dir/link': () =>\n Promise.resolve(Link.default) as Promise<unknown>,\n 'next/link': () => Promise.resolve(Link.default) as Promise<unknown>,\n 'next/dist/client/app-dir/form': () =>\n Promise.resolve(Form.default) as Promise<unknown>,\n 'next/form': () => Promise.resolve(Form.default) as Promise<unknown>,\n 'next/dist/client/script': () =>\n Promise.resolve(Script.default) as Promise<unknown>,\n 'next/script': () => Promise.resolve(Script.default) as Promise<unknown>,\n 'next/dist/client/image-component': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n Image: imageImpl(Image.default.default ?? Image.default),\n }) as Promise<unknown>,\n 'next/dist/api/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n 'next/router': () => Promise.resolve(Router) as Promise<unknown>,\n ..._shared,\n // always override next/image to use the remote loader\n 'next/image': () =>\n Promise.resolve(\n // @ts-expect-error default.default\n // eslint-disable-next-line\n imageImpl(Image.default.default ?? Image.default),\n ) as Promise<unknown>,\n };\n};\n\n// internal symbols to access global store\nconst REMOTE_COMPONENT_STORE = Symbol('REMOTE_COMPONENT_STORE');\nconst REMOTE_COMPONENT_KEY = '__REMOTE_COMPONENT_KEY__';\n\n// temporary global store for remote component HTML\n// the store is used to save the HTML of remote components for SSR without sending the content to the client\nconst self = globalThis as typeof globalThis & {\n [REMOTE_COMPONENT_STORE]?: Map<string, React.ReactNode>;\n};\n\nfunction getKey({\n bundle,\n route,\n name,\n}: {\n bundle?: string;\n route?: string;\n name?: string;\n}): string {\n return `${bundle ?? '__next'}:${route ?? '/'}:${\n name ?? '__vercel_remote_component'\n }__${crypto.randomUUID()}`;\n}\n\nfunction setComponent(key: string, component: React.ReactNode): void {\n if (!self[REMOTE_COMPONENT_STORE]) {\n self[REMOTE_COMPONENT_STORE] = new Map();\n }\n self[REMOTE_COMPONENT_STORE].set(key, component);\n}\n\nfunction getComponent(key: string): React.ReactNode | undefined {\n const component = self[REMOTE_COMPONENT_STORE]?.get(key);\n // remove the component from the store after retrieving it to prevent memory leaks\n // storing the HTML in the global store is only needed for SSR and it's temporary only used for a single render\n self[REMOTE_COMPONENT_STORE]?.delete(key);\n return component;\n}\n\nexport interface RemoteComponentProps {\n src: string;\n bundle?: string;\n route?: string;\n name?: string;\n isolate?: boolean;\n mode?: 'open' | 'closed';\n reset?: boolean;\n /** Called right before a new remote component load starts. */\n onBeforeLoad?: (src: string | URL) => void;\n /** Called when the remote component has been successfully loaded and mounted. */\n onLoad?: (src: string | URL) => void;\n /** Called when an error occurs while loading or mounting the remote component. */\n onError?: (error: unknown) => void;\n /** Called when a different remote component is loaded into the same wrapper. */\n onChange?: (info: {\n previousSrc: string | URL | null;\n nextSrc: string | URL | null;\n previousName: string | undefined;\n nextName: string | undefined;\n }) => void;\n /**\n * Called when a fetch request is made to retrieve the remote component payload.\n * Can be used to intercept requests, modify headers, or provide a custom response.\n */\n onRequest?: OnRequestHook;\n /**\n * Called after a fetch completes to retrieve the remote component payload.\n * Can be used to inspect the response (e.g., check for redirects) or transform it.\n */\n onResponse?: OnResponseHook;\n [REMOTE_COMPONENT_KEY]?: string;\n children?: React.ReactNode;\n}\n\n/**\n * This component handles the rendering of remote microfrontends.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n */\nexport function RemoteComponent(props: RemoteComponentProps) {\n const remoteComponent =\n typeof document !== 'undefined'\n ? null\n : // retrieve the HTML from the global store\n getComponent(\n props[REMOTE_COMPONENT_KEY] ?? '__vercel_remote_component',\n );\n\n useEffect(() => {\n const clientSelf = globalThis as typeof globalThis & {\n __remote_component_shared__?: Record<string, () => Promise<unknown>>;\n };\n // eslint-disable-next-line camelcase\n clientSelf.__remote_component_shared__ = shared(props.bundle ?? 'default');\n }, [props.bundle]);\n\n if (!props[REMOTE_COMPONENT_KEY]) {\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n onBeforeLoad={props.onBeforeLoad}\n onChange={props.onChange}\n onError={props.onError}\n onLoad={props.onLoad}\n onRequest={props.onRequest}\n onResponse={props.onResponse}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {props.children}\n </RemoteComponentReact>\n );\n }\n\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n onBeforeLoad={props.onBeforeLoad}\n onChange={props.onChange}\n onError={props.onError}\n onLoad={props.onLoad}\n onRequest={props.onRequest}\n onResponse={props.onResponse}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {remoteComponent}\n </RemoteComponentReact>\n );\n}\n\n/**\n * Fetches the remote component properties from the server. You need to pass these properties to the `<RemoteComponent>` component to render the fetched remote component.\n *\n * @param src - The source URL of the remote component. When using the Vercel Microfrontends solution, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param headers - The HTTP headers used for supporting the Vercel Microfrontends proxy.\n * @returns The properties of the remote component.\n *\n * @example\n *\n * ```tsx\n * import { getRemoteComponentProps } from 'remote-components/next/host/pages';\n * import type { GetServerSideProps } from 'next';\n *\n * export const getServerSideProps: GetServerSideProps<PageProps> = async function getServerSideProps({ req }) {\n * const myRemoteComponent = await getRemoteComponentProps(\n * '/nextjs-app-remote/components/header',\n * req.headers,\n * );\n * return {\n * props: {\n * remoteComponents: {\n * myRemoteComponent,\n * },\n * },\n * };\n * }\n * ```\n */\nexport async function getRemoteComponentProps(\n src: string,\n options?: {\n /**\n * Called when a fetch request is made to retrieve the remote component payload.\n * Can be used to intercept requests, modify headers, or provide a custom response.\n */\n onRequest?: OnRequestHook;\n /**\n * Called after a fetch completes to retrieve the remote component payload.\n * Can be used to inspect the response (e.g., check for redirects) or transform it.\n */\n onResponse?: OnResponseHook;\n },\n): Promise<RemoteComponentProps> {\n if (typeof document !== 'undefined') {\n throw new RemoteComponentsError(\n '`getRemoteComponentProps()` can only be used on the server side.',\n );\n }\n\n const {\n metadata: { bundle, route },\n name,\n links,\n component,\n nextData,\n remoteShared,\n } = await fetchRemoteComponent(src, {\n rsc: true,\n onRequest: options?.onRequest,\n onResponse: options?.onResponse,\n });\n\n const props: RemoteComponentProps = {\n src,\n bundle,\n name,\n route,\n };\n\n const key = getKey(props);\n\n // do not render the HTML in development mode when remote is using Next.js Pages Router\n // this behavior is emulating the Next.js Pages Router FOUC as the styles are only applied on the client when running in development mode\n if (nextData?.buildId !== 'development') {\n // store the HTML in a global store\n setComponent(\n key,\n <>\n <script\n data-remote-components-shared=\"\"\n id={`${name}_shared`}\n type=\"application/json\"\n >\n {JSON.stringify(remoteShared)}\n </script>\n {links.map((link) => (\n <link\n key={`${link.as}_${link.href}`}\n {...link}\n precedence={undefined}\n />\n ))}\n {component}\n </>,\n );\n }\n\n return {\n ...props,\n // add remote component key to the props\n [REMOTE_COMPONENT_KEY]: key,\n };\n}\n"],"mappings":"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","names":["self"]}
1
+ {"version":3,"sources":["../../../src/next/host/pages-router-server.tsx"],"sourcesContent":["import * as Form from 'next/form';\nimport * as Image from 'next/image';\nimport * as Link from 'next/link';\nimport * as Router from 'next/router';\nimport * as Script from 'next/script';\nimport { useEffect } from 'react';\nimport { shared as _shared } from 'remote-components/shared/host/pages';\nimport { RemoteComponentsError } from '#internal/shared/client/remote-component';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\nimport type { OnRequestHook, OnResponseHook } from '#internal/shared/ssr/types';\nimport { RemoteComponent as RemoteComponentReact } from '#remote-components/react/index';\n\nconst navigationImpl = {\n useRouter: () => Router.useRouter(),\n useSearchParams: () => {\n const router = Router.useRouter();\n return {\n get: (key: string) => router.query[key],\n has: (key: string) => key in router.query,\n };\n },\n};\n\nconst sharedCache = new Map<string, Record<string, () => Promise<unknown>>>();\nconst shared = (bundle: string) => {\n if (sharedCache.has(bundle)) {\n return sharedCache.get(bundle);\n }\n\n const remoteLoader = ({ src, width, quality }: Image.ImageLoaderProps) => {\n const self = globalThis as typeof globalThis & {\n __remote_bundle_url__?: Record<string, URL>;\n };\n const { assetPrefix } =\n /^(?<assetPrefix>.*?)\\/_next\\//.exec(src)?.groups ?? {};\n return `${\n self.__remote_bundle_url__?.[bundle]?.origin ?? ''\n }${assetPrefix}/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${\n quality || 75\n }`;\n };\n\n const imageImpl = (ImageComponent: typeof Image.default) => {\n function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n }\n RemoteImage.default = RemoteImage;\n RemoteImage.getImageProps = Image.getImageProps;\n return RemoteImage;\n };\n\n return {\n // polyfill Next.js App Router client API (minimal)\n // some API methods are not available when using a Next.js Pages Router application as host\n 'next/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/components/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/app-dir/link': () =>\n Promise.resolve(Link.default) as Promise<unknown>,\n 'next/link': () => Promise.resolve(Link.default) as Promise<unknown>,\n 'next/dist/client/app-dir/form': () =>\n Promise.resolve(Form.default) as Promise<unknown>,\n 'next/form': () => Promise.resolve(Form.default) as Promise<unknown>,\n 'next/dist/client/script': () =>\n Promise.resolve(Script.default) as Promise<unknown>,\n 'next/script': () => Promise.resolve(Script.default) as Promise<unknown>,\n 'next/dist/client/image-component': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n Image: imageImpl(Image.default.default ?? Image.default),\n }) as Promise<unknown>,\n 'next/dist/api/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n 'next/router': () => Promise.resolve(Router) as Promise<unknown>,\n ..._shared,\n // always override next/image to use the remote loader\n 'next/image': () =>\n Promise.resolve(\n // @ts-expect-error default.default\n // eslint-disable-next-line\n imageImpl(Image.default.default ?? Image.default),\n ) as Promise<unknown>,\n };\n};\n\n// internal symbols to access global store\nconst REMOTE_COMPONENT_STORE = Symbol('REMOTE_COMPONENT_STORE');\nconst REMOTE_COMPONENT_KEY = '__REMOTE_COMPONENT_KEY__';\n\n// temporary global store for remote component HTML\n// the store is used to save the HTML of remote components for SSR without sending the content to the client\nconst self = globalThis as typeof globalThis & {\n [REMOTE_COMPONENT_STORE]?: Map<string, React.ReactNode>;\n};\n\nfunction getKey({\n bundle,\n route,\n name,\n}: {\n bundle?: string;\n route?: string;\n name?: string;\n}): string {\n return `${bundle ?? '__next'}:${route ?? '/'}:${\n name ?? '__vercel_remote_component'\n }__${crypto.randomUUID()}`;\n}\n\nfunction setComponent(key: string, component: React.ReactNode): void {\n if (!self[REMOTE_COMPONENT_STORE]) {\n self[REMOTE_COMPONENT_STORE] = new Map();\n }\n self[REMOTE_COMPONENT_STORE].set(key, component);\n}\n\nfunction getComponent(key: string): React.ReactNode | undefined {\n const component = self[REMOTE_COMPONENT_STORE]?.get(key);\n // remove the component from the store after retrieving it to prevent memory leaks\n // storing the HTML in the global store is only needed for SSR and it's temporary only used for a single render\n self[REMOTE_COMPONENT_STORE]?.delete(key);\n return component;\n}\n\nexport interface RemoteComponentProps {\n src: string;\n bundle?: string;\n route?: string;\n name?: string;\n isolate?: boolean;\n mode?: 'open' | 'closed';\n reset?: boolean;\n /** Called right before a new remote component load starts. */\n onBeforeLoad?: (src: string | URL) => void;\n /** Called when the remote component has been successfully loaded and mounted. */\n onLoad?: (src: string | URL) => void;\n /** Called when an error occurs while loading or mounting the remote component. */\n onError?: (error: unknown) => void;\n /** Called when a different remote component is loaded into the same wrapper. */\n onChange?: (info: {\n previousSrc: string | URL | null;\n nextSrc: string | URL | null;\n previousName: string | undefined;\n nextName: string | undefined;\n }) => void;\n /**\n * Called when a fetch request is made to retrieve the remote component payload.\n * Can be used to intercept requests, modify headers, or provide a custom response.\n */\n onRequest?: OnRequestHook;\n /**\n * Called after a fetch completes to retrieve the remote component payload.\n * Can be used to inspect the response (e.g., check for redirects) or transform it.\n */\n onResponse?: OnResponseHook;\n [REMOTE_COMPONENT_KEY]?: string;\n children?: React.ReactNode;\n}\n\n/**\n * This component handles the rendering of remote microfrontends.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n */\nexport function RemoteComponent(props: RemoteComponentProps) {\n const remoteComponent =\n typeof document !== 'undefined'\n ? null\n : // retrieve the HTML from the global store\n getComponent(\n props[REMOTE_COMPONENT_KEY] ?? '__vercel_remote_component',\n );\n\n useEffect(() => {\n const clientSelf = globalThis as typeof globalThis & {\n __remote_component_shared__?: Record<string, () => Promise<unknown>>;\n };\n // eslint-disable-next-line camelcase\n clientSelf.__remote_component_shared__ = shared(props.bundle ?? 'default');\n }, [props.bundle]);\n\n if (!props[REMOTE_COMPONENT_KEY]) {\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n onBeforeLoad={props.onBeforeLoad}\n onChange={props.onChange}\n onError={props.onError}\n onLoad={props.onLoad}\n onRequest={props.onRequest}\n onResponse={props.onResponse}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {props.children}\n </RemoteComponentReact>\n );\n }\n\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n onBeforeLoad={props.onBeforeLoad}\n onChange={props.onChange}\n onError={props.onError}\n onLoad={props.onLoad}\n onRequest={props.onRequest}\n onResponse={props.onResponse}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {remoteComponent}\n </RemoteComponentReact>\n );\n}\n\n/**\n * Fetches the remote component properties from the server. You need to pass these properties to the `<RemoteComponent>` component to render the fetched remote component.\n *\n * @param src - The source URL of the remote component. When using the Vercel Microfrontends solution, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param headers - The HTTP headers used for supporting the Vercel Microfrontends proxy.\n * @returns The properties of the remote component.\n *\n * @example\n *\n * ```tsx\n * import { getRemoteComponentProps } from 'remote-components/next/host/pages';\n * import type { GetServerSideProps } from 'next';\n *\n * export const getServerSideProps: GetServerSideProps<PageProps> = async function getServerSideProps({ req }) {\n * const myRemoteComponent = await getRemoteComponentProps(\n * '/nextjs-app-remote/components/header',\n * req.headers,\n * );\n * return {\n * props: {\n * remoteComponents: {\n * myRemoteComponent,\n * },\n * },\n * };\n * }\n * ```\n */\nexport async function getRemoteComponentProps(\n src: string,\n options?: {\n /**\n * Called when a fetch request is made to retrieve the remote component payload.\n * Can be used to intercept requests, modify headers, or provide a custom response.\n */\n onRequest?: OnRequestHook;\n /**\n * Called after a fetch completes to retrieve the remote component payload.\n * Can be used to inspect the response (e.g., check for redirects) or transform it.\n */\n onResponse?: OnResponseHook;\n /**\n * The name of the exposed remote component. Used to identify the remote component\n * when multiple remote components are exposed on a page.\n */\n name?: string;\n },\n): Promise<RemoteComponentProps> {\n if (typeof document !== 'undefined') {\n throw new RemoteComponentsError(\n '`getRemoteComponentProps()` can only be used on the server side.',\n );\n }\n\n const {\n metadata: { bundle, route },\n name,\n links,\n component,\n nextData,\n remoteShared,\n } = await fetchRemoteComponent(src, {\n rsc: true,\n ...options,\n });\n\n const props: RemoteComponentProps = {\n src,\n bundle,\n name,\n route,\n };\n\n const key = getKey(props);\n\n // do not render the HTML in development mode when remote is using Next.js Pages Router\n // this behavior is emulating the Next.js Pages Router FOUC as the styles are only applied on the client when running in development mode\n if (nextData?.buildId !== 'development') {\n // store the HTML in a global store\n setComponent(\n key,\n <>\n <script\n data-remote-components-shared=\"\"\n id={`${name}_shared`}\n type=\"application/json\"\n >\n {JSON.stringify(remoteShared)}\n </script>\n {links.map((link) => (\n <link\n key={`${link.as}_${link.href}`}\n {...link}\n precedence={undefined}\n />\n ))}\n {component}\n </>,\n );\n }\n\n return {\n ...props,\n // add remote component key to the props\n [REMOTE_COMPONENT_KEY]: key,\n };\n}\n"],"mappings":"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","names":["self"]}
@@ -58,6 +58,12 @@ function getCorsHeaders(options, requestHeaders) {
58
58
  } : {};
59
59
  return CORS_HEADERS;
60
60
  }
61
+ function getSecurityHeaders() {
62
+ return {
63
+ "Content-Security-Policy": "frame-ancestors 'none'",
64
+ "X-Frame-Options": "DENY"
65
+ };
66
+ }
61
67
  function handleCorsPreflightRequest(method, headers, options) {
62
68
  if (method !== "OPTIONS" || options === false) {
63
69
  return null;
@@ -65,7 +71,7 @@ function handleCorsPreflightRequest(method, headers, options) {
65
71
  const corsHeaders = getCorsHeaders(options, headers);
66
72
  return new Response(void 0, {
67
73
  status: 200,
68
- headers: corsHeaders
74
+ headers: { ...corsHeaders, ...getSecurityHeaders() }
69
75
  });
70
76
  }
71
77
 
@@ -87,6 +93,10 @@ function withRemoteComponents(proxy, options) {
87
93
  ([k, v]) => response.headers.set(k, v)
88
94
  );
89
95
  }
96
+ const securityHeaders = getSecurityHeaders();
97
+ Object.entries(securityHeaders).forEach(
98
+ ([k, v]) => response.headers.set(k, v)
99
+ );
90
100
  return response;
91
101
  };
92
102
  }
@@ -94,6 +104,9 @@ function withRemoteComponents(proxy, options) {
94
104
  // src/next/proxy/withRemoteComponentsHost.ts
95
105
  var import_server2 = require("next/server");
96
106
 
107
+ // src/shared/constants.ts
108
+ var RC_PROTECTED_REMOTE_FETCH_PATHNAME = "/rc-fetch-protected-remote";
109
+
97
110
  // src/shared/ssr/fetch-headers.ts
98
111
  function remoteFetchHeaders() {
99
112
  return {
@@ -110,23 +123,32 @@ function remoteFetchHeaders() {
110
123
  };
111
124
  }
112
125
 
113
- // src/shared/utils/logger.ts
114
- var DEBUG = typeof window !== "undefined" && localStorage.getItem("RC_DEBUG") === "true";
115
-
116
- // src/shared/ssr/fetch-with-protected-rc-fallback.ts
117
- var RC_PROTECTED_REMOTE_FETCH_PATHNAME = "/rc-fetch-protected-remote";
118
-
119
126
  // src/shared/host/proxy.ts
120
- function isUrlAllowed(targetUrl, options) {
127
+ async function handleProtectedRemoteFetchRequest(requestUrl, options) {
128
+ const url = new URL(requestUrl, "https://fallback.com");
129
+ if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {
130
+ return null;
131
+ }
132
+ const targetUrl = url.searchParams.get("url");
133
+ if (!targetUrl) {
134
+ return new Response("Bad request, missing url query param", {
135
+ status: 400
136
+ });
137
+ }
121
138
  const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(
122
139
  ","
123
140
  ).map((p) => p.trim());
124
141
  const optionPatterns = options?.allowedProxyUrls;
125
142
  const allowedPatterns = [...optionPatterns || [], ...envPatterns || []];
126
143
  if (allowedPatterns.length === 0) {
127
- return false;
144
+ return new Response(
145
+ `Forbidden: no allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS provided to withRemoteComponentsHost.`,
146
+ {
147
+ status: 403
148
+ }
149
+ );
128
150
  }
129
- return allowedPatterns.some((pattern) => {
151
+ const isUrlAllowed = allowedPatterns.some((pattern) => {
130
152
  try {
131
153
  const regex = new RegExp(pattern);
132
154
  return regex.test(targetUrl);
@@ -138,19 +160,7 @@ function isUrlAllowed(targetUrl, options) {
138
160
  return false;
139
161
  }
140
162
  });
141
- }
142
- async function handleProtectedRemoteFetchRequest(requestUrl, options) {
143
- const url = new URL(requestUrl, "https://fallback.com");
144
- if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {
145
- return null;
146
- }
147
- const targetUrl = url.searchParams.get("url");
148
- if (!targetUrl) {
149
- return new Response("Bad request, missing url query param", {
150
- status: 400
151
- });
152
- }
153
- if (!isUrlAllowed(targetUrl, options)) {
163
+ if (!isUrlAllowed) {
154
164
  return new Response(
155
165
  `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,
156
166
  {
@@ -159,9 +169,11 @@ async function handleProtectedRemoteFetchRequest(requestUrl, options) {
159
169
  );
160
170
  }
161
171
  const response = await fetch(targetUrl, { headers: remoteFetchHeaders() });
172
+ const contentType = response.headers.get("content-type");
162
173
  return new Response(response.body, {
163
174
  status: response.status,
164
- statusText: response.statusText
175
+ statusText: response.statusText,
176
+ headers: contentType ? { "content-type": contentType } : void 0
165
177
  });
166
178
  }
167
179
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/proxy/index.ts","../../src/next/proxy/withRemoteComponents.ts","../../src/shared/remote/proxy.ts","../../src/next/proxy/withRemoteComponentsHost.ts","../../src/shared/ssr/fetch-headers.ts","../../src/shared/utils/logger.ts","../../src/shared/ssr/fetch-with-protected-rc-fallback.ts","../../src/shared/host/proxy.ts"],"sourcesContent":["export {\n type RemoteComponentProxyOptions,\n withRemoteComponents,\n} from './withRemoteComponents';\nexport { withRemoteComponentsHost } from './withRemoteComponentsHost';\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n getCorsHeaders,\n handleCorsPreflightRequest,\n type RemoteComponentProxyOptions,\n} from '#internal/shared/remote/proxy';\n\n/**\n * This proxy is used to handle CORS and other remote component related tasks.\n * It can be used to wrap a Next.js proxy handler function to add CORS headers and handle preflight requests.\n *\n * @param proxy - The Next.js proxy handler function to wrap.\n * @param options - Optional configuration for handling remote components.\n * @returns A Next.js proxy handler function that handles CORS and preflight requests\n */\nexport function withRemoteComponents(\n proxy?: (request: NextRequest) => NextResponse | Promise<NextResponse>,\n options?: RemoteComponentProxyOptions,\n) {\n return async (request: NextRequest) => {\n // Check if this is a CORS preflight request\n const preflightResponse = handleCorsPreflightRequest(\n request.method,\n request.headers,\n options?.cors,\n );\n\n if (preflightResponse) {\n return preflightResponse;\n }\n\n // For all other requests, continue and attach CORS headers\n const response =\n typeof proxy === 'function' ? await proxy(request) : NextResponse.next();\n\n if (options?.cors !== false) {\n const corsHeaders = getCorsHeaders(options?.cors, request.headers);\n Object.entries(corsHeaders).forEach(([k, v]) =>\n response.headers.set(k, v),\n );\n }\n\n return response;\n };\n}\n\nexport type { RemoteComponentProxyOptions };\n","/**\n * Proxy utilities for remote applications that expose components to hosts.\n */\n\nimport type { IncomingHttpHeaders } from 'node:http';\n\nexport interface RemoteComponentProxyOptions {\n cors?:\n | {\n origin?: string | string[];\n method?: string | string[];\n headers?: string | string[];\n credentials?: boolean;\n maxAge?: string;\n }\n | false;\n}\n\n/**\n * Gets a header value from either a Headers object or a plain object.\n */\nexport function getHeader(\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n name: string,\n): string | undefined {\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined;\n }\n const value = headers[name];\n // IncomingHttpHeaders can have string | string[] | undefined\n return Array.isArray(value) ? value[0] : value;\n}\n\n/**\n * Computes CORS headers based on the provided options and request headers.\n *\n * @param options - CORS configuration options\n * @param requestHeaders - Headers from the incoming request (can be a Headers object or a plain object)\n * @returns Object containing CORS headers to be added to the response\n */\nexport function getCorsHeaders(\n options: RemoteComponentProxyOptions['cors'],\n requestHeaders: Record<string, string> | Headers | IncomingHttpHeaders,\n): Record<string, string> {\n if (options === false) {\n return {};\n }\n\n const originHeader = getHeader(requestHeaders, 'origin');\n const refererHeader = getHeader(requestHeaders, 'referer');\n const origin =\n originHeader ?? (refererHeader ? new URL(refererHeader).origin : '*');\n\n const ALLOWED_ORIGINS = (\n process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS ||\n (Array.isArray(options?.origin)\n ? options.origin.join(',')\n : options?.origin) ||\n '*'\n )\n .split(',')\n .map((allowedOrigin) => allowedOrigin.trim());\n\n const isAllowed =\n ALLOWED_ORIGINS.includes('*') || ALLOWED_ORIGINS.includes(origin);\n\n const allowedHeaders =\n process.env.REMOTE_COMPONENTS_ALLOW_HEADERS ||\n (Array.isArray(options?.headers)\n ? options.headers.map((h) => h.trim()).join(',')\n : options?.headers) ||\n getHeader(requestHeaders, 'access-control-request-headers');\n\n const CORS_HEADERS = (\n isAllowed\n ? {\n 'Access-Control-Allow-Origin': origin,\n 'Access-Control-Allow-Methods':\n process.env.REMOTE_COMPONENTS_ALLOW_METHODS ||\n (Array.isArray(options?.method)\n ? options.method.map((m) => m.trim()).join(',')\n : options?.method) ||\n 'GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS',\n ...(allowedHeaders\n ? { 'Access-Control-Allow-Headers': allowedHeaders }\n : {}),\n ...(process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS ||\n options?.credentials\n ? {\n 'Access-Control-Allow-Credentials':\n process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || 'true',\n }\n : {}),\n 'Access-Control-Max-Age': options?.maxAge || '600',\n Vary: 'Origin',\n }\n : {}\n ) as Record<string, string>;\n\n return CORS_HEADERS;\n}\n\n/**\n * Handles CORS preflight OPTIONS requests.\n *\n * @param method - The HTTP method of the incoming request\n * @param headers - Headers from the incoming request (can be a Headers object or a plain object)\n * @param options - CORS configuration options\n * @returns Response object for the preflight request, or null if not a preflight\n */\nexport function handleCorsPreflightRequest(\n method: string,\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n options?: RemoteComponentProxyOptions['cors'],\n): Response | null {\n if (method !== 'OPTIONS' || options === false) {\n return null;\n }\n\n const corsHeaders = getCorsHeaders(options, headers);\n\n return new Response(undefined, {\n status: 200,\n headers: corsHeaders,\n });\n}\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n type HostProxyOptions,\n handleProtectedRemoteFetchRequest,\n} from '#internal/shared/host/proxy';\n\n/**\n * Only needed when accessing a remote component in a preview environment that\n * is protected with vercel deployment protection. If the remote component fetch\n * errors, it will attempt to fetch via this proxy which adds the protection\n * bypass header to the request.\n *\n * To use this proxy, the path `/rc-fetch-protected-remote` must be added to the\n * proxy/middleware matchers.\n *\n * @param proxy - Optional Next.js middleware function to run after checking for protected remote fetch\n * @param options - Host proxy configuration options for SSRF protection\n */\nexport function withRemoteComponentsHost(\n proxy?: (request: NextRequest) => NextResponse | Promise<NextResponse>,\n options?: HostProxyOptions,\n) {\n return async (request: NextRequest) => {\n // Check if this is a protected remote fetch request\n const protectedFetchResponse = await handleProtectedRemoteFetchRequest(\n request.url,\n options,\n );\n\n if (protectedFetchResponse) {\n return protectedFetchResponse;\n }\n\n return typeof proxy === 'function'\n ? await proxy(request)\n : NextResponse.next();\n };\n}\n\nexport type { HostProxyOptions };\n","/**\n * The headers to use when fetching the remote component.\n */\nexport function remoteFetchHeaders() {\n return {\n /**\n * Authenticates deployment protection for the remote. Needed for SSR and SSG clients.\n * If the remote component uses vercel deployment protection, ensure the host and remote vercel\n * projects share a common automation bypass secret, and the shared secret is used as the\n * VERCEL_AUTOMATION_BYPASS_SECRET env var in the host project.\n */\n ...(typeof process === 'object' &&\n typeof process.env === 'object' &&\n typeof process.env.VERCEL_AUTOMATION_BYPASS_SECRET === 'string'\n ? {\n 'x-vercel-protection-bypass':\n process.env.VERCEL_AUTOMATION_BYPASS_SECRET,\n }\n : {}),\n Accept: 'text/html',\n };\n}\n","import { RemoteComponentsError } from '#internal/shared/error';\n\ntype LogLocation =\n | 'ChunkLoader'\n | 'ComponentLoader'\n | 'SharedModules'\n | 'WebpackAdapter'\n | 'TurbopackModule'\n | 'StaticLoader'\n | 'Polyfill'\n | 'HtmlRemote'\n | 'HtmlHost'\n | 'Config'\n | 'NextAppRouter'\n | 'NextAppRouterCompat'\n | 'FetchRemoteComponent';\n\nconst PREFIX = 'remote-components';\nconst DEBUG =\n typeof window !== 'undefined' && localStorage.getItem('RC_DEBUG') === 'true';\n\nexport function logDebug(location: LogLocation, message: string) {\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.debug(`[${PREFIX}:${location}]: ${message}`);\n }\n}\n\nexport function logInfo(location: LogLocation, message: string) {\n // eslint-disable-next-line no-console\n console.info(`[${PREFIX}:${location}]: ${message}`);\n}\n\nexport function logWarn(location: LogLocation, message: string) {\n // eslint-disable-next-line no-console\n console.warn(`[${PREFIX}:${location}]: ${message}`);\n}\n\nexport function logError(\n location: LogLocation,\n message: string,\n cause?: unknown,\n) {\n // eslint-disable-next-line no-console\n console.error(\n new RemoteComponentsError(`[${PREFIX}:${location}]: ${message}`, {\n cause,\n }),\n );\n}\n","import { isAbortError } from '#internal/shared/utils/abort';\nimport { logError, logInfo } from '#internal/shared/utils/logger';\n\nexport const RC_PROTECTED_REMOTE_FETCH_PATHNAME = '/rc-fetch-protected-remote';\n\n/**\n * When a vercel host preview fetches a vercel protected remote preview on the\n * client, the request will reject as it's not possible to authenticate for the\n * protected deployment in a client side fetch - cookies cannot be shared across\n * domains. To enable previews, this request is proxied via the host where the\n * process.env.VERCEL_AUTOMATION_BYPASS_SECRET will be added.\n *\n * @param url - The URL to fetch\n * @param init - Fetch init options (should include signal for abort support)\n */\nexport async function fetchWithProtectedRcFallback(\n url: URL | string,\n init?: RequestInit,\n): Promise<Response> {\n try {\n const res = await fetch(url, init);\n return res;\n } catch (error) {\n // Re-throw AbortError immediately - don't try fallback for cancelled requests\n if (isAbortError(error)) {\n throw error;\n }\n\n if (\n typeof document === 'object' &&\n typeof document.location === 'object' &&\n document.location.origin !== new URL(url).origin\n ) {\n logInfo(\n 'FetchRemoteComponent',\n 'Request failed due to CORS, attempting to fetch it via the withRemoteComponentsHost proxy.',\n );\n // Pass signal to proxy fetch as well so it can be cancelled\n const proxiedRes = await fetch(\n `${RC_PROTECTED_REMOTE_FETCH_PATHNAME}?url=${url}`,\n init?.signal ? { signal: init.signal } : undefined,\n );\n if (proxiedRes.status === 200) {\n return proxiedRes;\n } else {\n logError(\n 'FetchRemoteComponent',\n `Could not proxy remote: [response status ${\n proxiedRes.status\n }] ${await proxiedRes.text()}`,\n );\n }\n }\n\n throw error;\n }\n}\n","/**\n * Proxy utilities for host applications that consume remote components.\n *\n * Hosts do NOT handle CORS - that's the remote's responsibility.\n * Hosts only handle protected fetch proxying.\n */\n\nimport { remoteFetchHeaders } from '#internal/shared/ssr/fetch-headers';\nimport { RC_PROTECTED_REMOTE_FETCH_PATHNAME } from '#internal/shared/ssr/fetch-with-protected-rc-fallback';\n\nexport interface HostProxyOptions {\n /**\n * List of allowed URL patterns (as regex strings) that can be proxied.\n * These patterns are combined with REMOTE_COMPONENTS_ALLOWED_PROXY_URLS env var if both are set.\n * If neither is set, all URLs are blocked.\n */\n allowedProxyUrls?: string[];\n}\n\n/**\n * Validates if a URL is allowed to be proxied based on the allowed patterns.\n *\n * @param targetUrl - The URL to validate\n * @param options - Host proxy configuration options\n * @returns true if the URL is allowed, false otherwise\n */\nfunction isUrlAllowed(targetUrl: string, options?: HostProxyOptions): boolean {\n const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(\n ',',\n ).map((p) => p.trim());\n const optionPatterns = options?.allowedProxyUrls;\n\n // Combine both sources if both are specified\n const allowedPatterns = [...(optionPatterns || []), ...(envPatterns || [])];\n\n if (allowedPatterns.length === 0) {\n return false;\n }\n\n // Check if the URL matches any of the allowed patterns\n return allowedPatterns.some((pattern) => {\n try {\n const regex = new RegExp(pattern);\n return regex.test(targetUrl);\n } catch (error) {\n console.error(\n `Invalid regex pattern in allowedProxyUrls: ${pattern}`,\n error,\n );\n return false;\n }\n });\n}\n\n/**\n * Handles protected remote component fetch requests by proxying them with\n * authentication headers. This is needed for accessing Vercel-protected remote\n * component deployments from client-side code.\n *\n * @param requestUrl - The full request URL\n * @param options - Host proxy configuration options\n * @returns Response object if this is a protected fetch request, or null if not\n */\nexport async function handleProtectedRemoteFetchRequest(\n requestUrl: string,\n options?: HostProxyOptions,\n): Promise<Response | null> {\n const url = new URL(requestUrl, 'https://fallback.com');\n\n if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {\n return null;\n }\n\n const targetUrl = url.searchParams.get('url');\n if (!targetUrl) {\n return new Response('Bad request, missing url query param', {\n status: 400,\n });\n }\n\n // Validate URL against allowed patterns to prevent SSRF attacks\n if (!isUrlAllowed(targetUrl, options)) {\n return new Response(\n `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Fetch the remote resource\n const response = await fetch(targetUrl, { headers: remoteFetchHeaders() });\n\n // Create new headers without content-encoding to avoid decoding errors\n // (Node.js fetch auto-decodes but keeps the header, causing browser issues)\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n });\n}\n\nexport { RC_PROTECTED_REMOTE_FETCH_PATHNAME };\n"],"mappings":"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","names":["import_server"]}
1
+ {"version":3,"sources":["../../src/next/proxy/index.ts","../../src/next/proxy/withRemoteComponents.ts","../../src/shared/remote/proxy.ts","../../src/next/proxy/withRemoteComponentsHost.ts","../../src/shared/constants.ts","../../src/shared/ssr/fetch-headers.ts","../../src/shared/host/proxy.ts"],"sourcesContent":["export {\n type RemoteComponentProxyOptions,\n withRemoteComponents,\n} from './withRemoteComponents';\nexport { withRemoteComponentsHost } from './withRemoteComponentsHost';\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n getCorsHeaders,\n getSecurityHeaders,\n handleCorsPreflightRequest,\n type RemoteComponentProxyOptions,\n} from '#internal/shared/remote/proxy';\n\n/**\n * This proxy is used to handle CORS and other remote component related tasks.\n * It can be used to wrap a Next.js proxy handler function to add CORS headers and handle preflight requests.\n *\n * @param proxy - The Next.js proxy handler function to wrap.\n * @param options - Optional configuration for handling remote components.\n * @returns A Next.js proxy handler function that handles CORS and preflight requests\n */\nexport function withRemoteComponents(\n proxy?: (request: NextRequest) => NextResponse | Promise<NextResponse>,\n options?: RemoteComponentProxyOptions,\n) {\n return async (request: NextRequest) => {\n // Check if this is a CORS preflight request\n const preflightResponse = handleCorsPreflightRequest(\n request.method,\n request.headers,\n options?.cors,\n );\n\n if (preflightResponse) {\n return preflightResponse;\n }\n\n // For all other requests, continue and attach CORS headers\n const response =\n typeof proxy === 'function' ? await proxy(request) : NextResponse.next();\n\n if (options?.cors !== false) {\n const corsHeaders = getCorsHeaders(options?.cors, request.headers);\n Object.entries(corsHeaders).forEach(([k, v]) =>\n response.headers.set(k, v),\n );\n }\n\n const securityHeaders = getSecurityHeaders();\n Object.entries(securityHeaders).forEach(([k, v]) =>\n response.headers.set(k, v),\n );\n\n return response;\n };\n}\n\nexport type { RemoteComponentProxyOptions };\n","/**\n * Proxy utilities for remote applications that expose components to hosts.\n */\n\nimport type { IncomingHttpHeaders } from 'node:http';\n\nexport interface RemoteComponentProxyOptions {\n cors?:\n | {\n origin?: string | string[];\n method?: string | string[];\n headers?: string | string[];\n credentials?: boolean;\n maxAge?: string;\n }\n | false;\n}\n\n/**\n * Gets a header value from either a Headers object or a plain object.\n */\nexport function getHeader(\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n name: string,\n): string | undefined {\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined;\n }\n const value = headers[name];\n // IncomingHttpHeaders can have string | string[] | undefined\n return Array.isArray(value) ? value[0] : value;\n}\n\n/**\n * Computes CORS headers based on the provided options and request headers.\n *\n * @param options - CORS configuration options\n * @param requestHeaders - Headers from the incoming request (can be a Headers object or a plain object)\n * @returns Object containing CORS headers to be added to the response\n */\nexport function getCorsHeaders(\n options: RemoteComponentProxyOptions['cors'],\n requestHeaders: Record<string, string> | Headers | IncomingHttpHeaders,\n): Record<string, string> {\n if (options === false) {\n return {};\n }\n\n const originHeader = getHeader(requestHeaders, 'origin');\n const refererHeader = getHeader(requestHeaders, 'referer');\n const origin =\n originHeader ?? (refererHeader ? new URL(refererHeader).origin : '*');\n\n const ALLOWED_ORIGINS = (\n process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS ||\n (Array.isArray(options?.origin)\n ? options.origin.join(',')\n : options?.origin) ||\n '*'\n )\n .split(',')\n .map((allowedOrigin) => allowedOrigin.trim());\n\n const isAllowed =\n ALLOWED_ORIGINS.includes('*') || ALLOWED_ORIGINS.includes(origin);\n\n const allowedHeaders =\n process.env.REMOTE_COMPONENTS_ALLOW_HEADERS ||\n (Array.isArray(options?.headers)\n ? options.headers.map((h) => h.trim()).join(',')\n : options?.headers) ||\n getHeader(requestHeaders, 'access-control-request-headers');\n\n const CORS_HEADERS = (\n isAllowed\n ? {\n 'Access-Control-Allow-Origin': origin,\n 'Access-Control-Allow-Methods':\n process.env.REMOTE_COMPONENTS_ALLOW_METHODS ||\n (Array.isArray(options?.method)\n ? options.method.map((m) => m.trim()).join(',')\n : options?.method) ||\n 'GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS',\n ...(allowedHeaders\n ? { 'Access-Control-Allow-Headers': allowedHeaders }\n : {}),\n ...(process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS ||\n options?.credentials\n ? {\n 'Access-Control-Allow-Credentials':\n process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || 'true',\n }\n : {}),\n 'Access-Control-Max-Age': options?.maxAge || '600',\n Vary: 'Origin',\n }\n : {}\n ) as Record<string, string>;\n\n return CORS_HEADERS;\n}\n\n/**\n * Returns security headers that prevent the remote component pages from being\n * embedded in frames. Remote components are fetched via HTTP and rendered\n * directly into the host's DOM — they should never be loaded in an iframe.\n */\nexport function getSecurityHeaders(): Record<string, string> {\n return {\n 'Content-Security-Policy': \"frame-ancestors 'none'\",\n 'X-Frame-Options': 'DENY',\n };\n}\n\n/**\n * Handles CORS preflight OPTIONS requests.\n *\n * @param method - The HTTP method of the incoming request\n * @param headers - Headers from the incoming request (can be a Headers object or a plain object)\n * @param options - CORS configuration options\n * @returns Response object for the preflight request, or null if not a preflight\n */\nexport function handleCorsPreflightRequest(\n method: string,\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n options?: RemoteComponentProxyOptions['cors'],\n): Response | null {\n if (method !== 'OPTIONS' || options === false) {\n return null;\n }\n\n const corsHeaders = getCorsHeaders(options, headers);\n\n return new Response(undefined, {\n status: 200,\n headers: { ...corsHeaders, ...getSecurityHeaders() },\n });\n}\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n type HostProxyOptions,\n handleProtectedRemoteFetchRequest,\n} from '#internal/shared/host/proxy';\n\n/**\n * Only needed when accessing a remote component in a preview environment that\n * is protected with vercel deployment protection. If the remote component fetch\n * errors, it will attempt to fetch via this proxy which adds the protection\n * bypass header to the request.\n *\n * To use this proxy, the path `/rc-fetch-protected-remote` must be added to the\n * proxy/middleware matchers.\n *\n * @param proxy - Optional Next.js middleware function to run after checking for protected remote fetch\n * @param options - Host proxy configuration options for SSRF protection\n */\nexport function withRemoteComponentsHost(\n proxy?: (request: NextRequest) => NextResponse | Promise<NextResponse>,\n options?: HostProxyOptions,\n) {\n return async (request: NextRequest) => {\n // Check if this is a protected remote fetch request\n const protectedFetchResponse = await handleProtectedRemoteFetchRequest(\n request.url,\n options,\n );\n\n if (protectedFetchResponse) {\n return protectedFetchResponse;\n }\n\n return typeof proxy === 'function'\n ? await proxy(request)\n : NextResponse.next();\n };\n}\n\nexport type { HostProxyOptions };\n","export const RC_PROTECTED_REMOTE_FETCH_PATHNAME = '/rc-fetch-protected-remote';\n","/**\n * The headers to use when fetching the remote component.\n */\nexport function remoteFetchHeaders() {\n return {\n /**\n * Authenticates deployment protection for the remote. Needed for SSR and SSG clients.\n * If the remote component uses vercel deployment protection, ensure the host and remote vercel\n * projects share a common automation bypass secret, and the shared secret is used as the\n * VERCEL_AUTOMATION_BYPASS_SECRET env var in the host project.\n */\n ...(typeof process === 'object' &&\n typeof process.env === 'object' &&\n typeof process.env.VERCEL_AUTOMATION_BYPASS_SECRET === 'string'\n ? {\n 'x-vercel-protection-bypass':\n process.env.VERCEL_AUTOMATION_BYPASS_SECRET,\n }\n : {}),\n Accept: 'text/html',\n };\n}\n","/**\n * Proxy utilities for host applications that consume remote components.\n *\n * Hosts do NOT handle CORS - that's the remote's responsibility.\n * Hosts only handle protected fetch proxying.\n */\n\nimport { RC_PROTECTED_REMOTE_FETCH_PATHNAME } from '#internal/shared/constants';\nimport { remoteFetchHeaders } from '#internal/shared/ssr/fetch-headers';\n\nexport interface HostProxyOptions {\n /**\n * List of allowed URL patterns (as regex strings) that can be proxied.\n * These patterns are combined with REMOTE_COMPONENTS_ALLOWED_PROXY_URLS env var if both are set.\n * If neither is set, all URLs are blocked.\n */\n allowedProxyUrls?: string[];\n}\n\n/**\n * Handles protected remote component fetch requests by proxying them with\n * authentication headers. This is needed for accessing Vercel-protected remote\n * component deployments from client-side code.\n *\n * @param requestUrl - The full request URL\n * @param options - Host proxy configuration options\n * @returns Response object if this is a protected fetch request, or null if not\n */\nexport async function handleProtectedRemoteFetchRequest(\n requestUrl: string,\n options?: HostProxyOptions,\n): Promise<Response | null> {\n const url = new URL(requestUrl, 'https://fallback.com');\n\n if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {\n return null;\n }\n\n const targetUrl = url.searchParams.get('url');\n if (!targetUrl) {\n return new Response('Bad request, missing url query param', {\n status: 400,\n });\n }\n\n const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(\n ',',\n ).map((p) => p.trim());\n const optionPatterns = options?.allowedProxyUrls;\n\n // Combine both sources if both are specified\n const allowedPatterns = [...(optionPatterns || []), ...(envPatterns || [])];\n\n if (allowedPatterns.length === 0) {\n return new Response(\n `Forbidden: no allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS provided to withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Validate URL against allowed patterns to prevent SSRF attacks\n const isUrlAllowed = allowedPatterns.some((pattern) => {\n try {\n const regex = new RegExp(pattern);\n return regex.test(targetUrl);\n } catch (error) {\n console.error(\n `Invalid regex pattern in allowedProxyUrls: ${pattern}`,\n error,\n );\n return false;\n }\n });\n\n if (!isUrlAllowed) {\n return new Response(\n `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Fetch the remote resource\n const response = await fetch(targetUrl, { headers: remoteFetchHeaders() });\n\n const contentType = response.headers.get('content-type');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: contentType ? { 'content-type': contentType } : undefined,\n });\n}\n"],"mappings":"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","names":["import_server"]}
@@ -30,7 +30,6 @@ declare function withRemoteComponents(proxy?: (request: NextRequest) => NextResp
30
30
  * Hosts do NOT handle CORS - that's the remote's responsibility.
31
31
  * Hosts only handle protected fetch proxying.
32
32
  */
33
-
34
33
  interface HostProxyOptions {
35
34
  /**
36
35
  * List of allowed URL patterns (as regex strings) that can be proxied.
@@ -31,6 +31,12 @@ function getCorsHeaders(options, requestHeaders) {
31
31
  } : {};
32
32
  return CORS_HEADERS;
33
33
  }
34
+ function getSecurityHeaders() {
35
+ return {
36
+ "Content-Security-Policy": "frame-ancestors 'none'",
37
+ "X-Frame-Options": "DENY"
38
+ };
39
+ }
34
40
  function handleCorsPreflightRequest(method, headers, options) {
35
41
  if (method !== "OPTIONS" || options === false) {
36
42
  return null;
@@ -38,7 +44,7 @@ function handleCorsPreflightRequest(method, headers, options) {
38
44
  const corsHeaders = getCorsHeaders(options, headers);
39
45
  return new Response(void 0, {
40
46
  status: 200,
41
- headers: corsHeaders
47
+ headers: { ...corsHeaders, ...getSecurityHeaders() }
42
48
  });
43
49
  }
44
50
 
@@ -60,6 +66,10 @@ function withRemoteComponents(proxy, options) {
60
66
  ([k, v]) => response.headers.set(k, v)
61
67
  );
62
68
  }
69
+ const securityHeaders = getSecurityHeaders();
70
+ Object.entries(securityHeaders).forEach(
71
+ ([k, v]) => response.headers.set(k, v)
72
+ );
63
73
  return response;
64
74
  };
65
75
  }
@@ -67,6 +77,9 @@ function withRemoteComponents(proxy, options) {
67
77
  // src/next/proxy/withRemoteComponentsHost.ts
68
78
  import { NextResponse as NextResponse2 } from "next/server";
69
79
 
80
+ // src/shared/constants.ts
81
+ var RC_PROTECTED_REMOTE_FETCH_PATHNAME = "/rc-fetch-protected-remote";
82
+
70
83
  // src/shared/ssr/fetch-headers.ts
71
84
  function remoteFetchHeaders() {
72
85
  return {
@@ -83,23 +96,32 @@ function remoteFetchHeaders() {
83
96
  };
84
97
  }
85
98
 
86
- // src/shared/utils/logger.ts
87
- var DEBUG = typeof window !== "undefined" && localStorage.getItem("RC_DEBUG") === "true";
88
-
89
- // src/shared/ssr/fetch-with-protected-rc-fallback.ts
90
- var RC_PROTECTED_REMOTE_FETCH_PATHNAME = "/rc-fetch-protected-remote";
91
-
92
99
  // src/shared/host/proxy.ts
93
- function isUrlAllowed(targetUrl, options) {
100
+ async function handleProtectedRemoteFetchRequest(requestUrl, options) {
101
+ const url = new URL(requestUrl, "https://fallback.com");
102
+ if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {
103
+ return null;
104
+ }
105
+ const targetUrl = url.searchParams.get("url");
106
+ if (!targetUrl) {
107
+ return new Response("Bad request, missing url query param", {
108
+ status: 400
109
+ });
110
+ }
94
111
  const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(
95
112
  ","
96
113
  ).map((p) => p.trim());
97
114
  const optionPatterns = options?.allowedProxyUrls;
98
115
  const allowedPatterns = [...optionPatterns || [], ...envPatterns || []];
99
116
  if (allowedPatterns.length === 0) {
100
- return false;
117
+ return new Response(
118
+ `Forbidden: no allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS provided to withRemoteComponentsHost.`,
119
+ {
120
+ status: 403
121
+ }
122
+ );
101
123
  }
102
- return allowedPatterns.some((pattern) => {
124
+ const isUrlAllowed = allowedPatterns.some((pattern) => {
103
125
  try {
104
126
  const regex = new RegExp(pattern);
105
127
  return regex.test(targetUrl);
@@ -111,19 +133,7 @@ function isUrlAllowed(targetUrl, options) {
111
133
  return false;
112
134
  }
113
135
  });
114
- }
115
- async function handleProtectedRemoteFetchRequest(requestUrl, options) {
116
- const url = new URL(requestUrl, "https://fallback.com");
117
- if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {
118
- return null;
119
- }
120
- const targetUrl = url.searchParams.get("url");
121
- if (!targetUrl) {
122
- return new Response("Bad request, missing url query param", {
123
- status: 400
124
- });
125
- }
126
- if (!isUrlAllowed(targetUrl, options)) {
136
+ if (!isUrlAllowed) {
127
137
  return new Response(
128
138
  `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,
129
139
  {
@@ -132,9 +142,11 @@ async function handleProtectedRemoteFetchRequest(requestUrl, options) {
132
142
  );
133
143
  }
134
144
  const response = await fetch(targetUrl, { headers: remoteFetchHeaders() });
145
+ const contentType = response.headers.get("content-type");
135
146
  return new Response(response.body, {
136
147
  status: response.status,
137
- statusText: response.statusText
148
+ statusText: response.statusText,
149
+ headers: contentType ? { "content-type": contentType } : void 0
138
150
  });
139
151
  }
140
152
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/proxy/withRemoteComponents.ts","../../src/shared/remote/proxy.ts","../../src/next/proxy/withRemoteComponentsHost.ts","../../src/shared/ssr/fetch-headers.ts","../../src/shared/utils/logger.ts","../../src/shared/ssr/fetch-with-protected-rc-fallback.ts","../../src/shared/host/proxy.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\nimport {\n getCorsHeaders,\n handleCorsPreflightRequest,\n type RemoteComponentProxyOptions,\n} from '#internal/shared/remote/proxy';\n\n/**\n * This proxy is used to handle CORS and other remote component related tasks.\n * It can be used to wrap a Next.js proxy handler function to add CORS headers and handle preflight requests.\n *\n * @param proxy - The Next.js proxy handler function to wrap.\n * @param options - Optional configuration for handling remote components.\n * @returns A Next.js proxy handler function that handles CORS and preflight requests\n */\nexport function withRemoteComponents(\n proxy?: (request: NextRequest) => NextResponse | Promise<NextResponse>,\n options?: RemoteComponentProxyOptions,\n) {\n return async (request: NextRequest) => {\n // Check if this is a CORS preflight request\n const preflightResponse = handleCorsPreflightRequest(\n request.method,\n request.headers,\n options?.cors,\n );\n\n if (preflightResponse) {\n return preflightResponse;\n }\n\n // For all other requests, continue and attach CORS headers\n const response =\n typeof proxy === 'function' ? await proxy(request) : NextResponse.next();\n\n if (options?.cors !== false) {\n const corsHeaders = getCorsHeaders(options?.cors, request.headers);\n Object.entries(corsHeaders).forEach(([k, v]) =>\n response.headers.set(k, v),\n );\n }\n\n return response;\n };\n}\n\nexport type { RemoteComponentProxyOptions };\n","/**\n * Proxy utilities for remote applications that expose components to hosts.\n */\n\nimport type { IncomingHttpHeaders } from 'node:http';\n\nexport interface RemoteComponentProxyOptions {\n cors?:\n | {\n origin?: string | string[];\n method?: string | string[];\n headers?: string | string[];\n credentials?: boolean;\n maxAge?: string;\n }\n | false;\n}\n\n/**\n * Gets a header value from either a Headers object or a plain object.\n */\nexport function getHeader(\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n name: string,\n): string | undefined {\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined;\n }\n const value = headers[name];\n // IncomingHttpHeaders can have string | string[] | undefined\n return Array.isArray(value) ? value[0] : value;\n}\n\n/**\n * Computes CORS headers based on the provided options and request headers.\n *\n * @param options - CORS configuration options\n * @param requestHeaders - Headers from the incoming request (can be a Headers object or a plain object)\n * @returns Object containing CORS headers to be added to the response\n */\nexport function getCorsHeaders(\n options: RemoteComponentProxyOptions['cors'],\n requestHeaders: Record<string, string> | Headers | IncomingHttpHeaders,\n): Record<string, string> {\n if (options === false) {\n return {};\n }\n\n const originHeader = getHeader(requestHeaders, 'origin');\n const refererHeader = getHeader(requestHeaders, 'referer');\n const origin =\n originHeader ?? (refererHeader ? new URL(refererHeader).origin : '*');\n\n const ALLOWED_ORIGINS = (\n process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS ||\n (Array.isArray(options?.origin)\n ? options.origin.join(',')\n : options?.origin) ||\n '*'\n )\n .split(',')\n .map((allowedOrigin) => allowedOrigin.trim());\n\n const isAllowed =\n ALLOWED_ORIGINS.includes('*') || ALLOWED_ORIGINS.includes(origin);\n\n const allowedHeaders =\n process.env.REMOTE_COMPONENTS_ALLOW_HEADERS ||\n (Array.isArray(options?.headers)\n ? options.headers.map((h) => h.trim()).join(',')\n : options?.headers) ||\n getHeader(requestHeaders, 'access-control-request-headers');\n\n const CORS_HEADERS = (\n isAllowed\n ? {\n 'Access-Control-Allow-Origin': origin,\n 'Access-Control-Allow-Methods':\n process.env.REMOTE_COMPONENTS_ALLOW_METHODS ||\n (Array.isArray(options?.method)\n ? options.method.map((m) => m.trim()).join(',')\n : options?.method) ||\n 'GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS',\n ...(allowedHeaders\n ? { 'Access-Control-Allow-Headers': allowedHeaders }\n : {}),\n ...(process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS ||\n options?.credentials\n ? {\n 'Access-Control-Allow-Credentials':\n process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || 'true',\n }\n : {}),\n 'Access-Control-Max-Age': options?.maxAge || '600',\n Vary: 'Origin',\n }\n : {}\n ) as Record<string, string>;\n\n return CORS_HEADERS;\n}\n\n/**\n * Handles CORS preflight OPTIONS requests.\n *\n * @param method - The HTTP method of the incoming request\n * @param headers - Headers from the incoming request (can be a Headers object or a plain object)\n * @param options - CORS configuration options\n * @returns Response object for the preflight request, or null if not a preflight\n */\nexport function handleCorsPreflightRequest(\n method: string,\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n options?: RemoteComponentProxyOptions['cors'],\n): Response | null {\n if (method !== 'OPTIONS' || options === false) {\n return null;\n }\n\n const corsHeaders = getCorsHeaders(options, headers);\n\n return new Response(undefined, {\n status: 200,\n headers: corsHeaders,\n });\n}\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n type HostProxyOptions,\n handleProtectedRemoteFetchRequest,\n} from '#internal/shared/host/proxy';\n\n/**\n * Only needed when accessing a remote component in a preview environment that\n * is protected with vercel deployment protection. If the remote component fetch\n * errors, it will attempt to fetch via this proxy which adds the protection\n * bypass header to the request.\n *\n * To use this proxy, the path `/rc-fetch-protected-remote` must be added to the\n * proxy/middleware matchers.\n *\n * @param proxy - Optional Next.js middleware function to run after checking for protected remote fetch\n * @param options - Host proxy configuration options for SSRF protection\n */\nexport function withRemoteComponentsHost(\n proxy?: (request: NextRequest) => NextResponse | Promise<NextResponse>,\n options?: HostProxyOptions,\n) {\n return async (request: NextRequest) => {\n // Check if this is a protected remote fetch request\n const protectedFetchResponse = await handleProtectedRemoteFetchRequest(\n request.url,\n options,\n );\n\n if (protectedFetchResponse) {\n return protectedFetchResponse;\n }\n\n return typeof proxy === 'function'\n ? await proxy(request)\n : NextResponse.next();\n };\n}\n\nexport type { HostProxyOptions };\n","/**\n * The headers to use when fetching the remote component.\n */\nexport function remoteFetchHeaders() {\n return {\n /**\n * Authenticates deployment protection for the remote. Needed for SSR and SSG clients.\n * If the remote component uses vercel deployment protection, ensure the host and remote vercel\n * projects share a common automation bypass secret, and the shared secret is used as the\n * VERCEL_AUTOMATION_BYPASS_SECRET env var in the host project.\n */\n ...(typeof process === 'object' &&\n typeof process.env === 'object' &&\n typeof process.env.VERCEL_AUTOMATION_BYPASS_SECRET === 'string'\n ? {\n 'x-vercel-protection-bypass':\n process.env.VERCEL_AUTOMATION_BYPASS_SECRET,\n }\n : {}),\n Accept: 'text/html',\n };\n}\n","import { RemoteComponentsError } from '#internal/shared/error';\n\ntype LogLocation =\n | 'ChunkLoader'\n | 'ComponentLoader'\n | 'SharedModules'\n | 'WebpackAdapter'\n | 'TurbopackModule'\n | 'StaticLoader'\n | 'Polyfill'\n | 'HtmlRemote'\n | 'HtmlHost'\n | 'Config'\n | 'NextAppRouter'\n | 'NextAppRouterCompat'\n | 'FetchRemoteComponent';\n\nconst PREFIX = 'remote-components';\nconst DEBUG =\n typeof window !== 'undefined' && localStorage.getItem('RC_DEBUG') === 'true';\n\nexport function logDebug(location: LogLocation, message: string) {\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.debug(`[${PREFIX}:${location}]: ${message}`);\n }\n}\n\nexport function logInfo(location: LogLocation, message: string) {\n // eslint-disable-next-line no-console\n console.info(`[${PREFIX}:${location}]: ${message}`);\n}\n\nexport function logWarn(location: LogLocation, message: string) {\n // eslint-disable-next-line no-console\n console.warn(`[${PREFIX}:${location}]: ${message}`);\n}\n\nexport function logError(\n location: LogLocation,\n message: string,\n cause?: unknown,\n) {\n // eslint-disable-next-line no-console\n console.error(\n new RemoteComponentsError(`[${PREFIX}:${location}]: ${message}`, {\n cause,\n }),\n );\n}\n","import { isAbortError } from '#internal/shared/utils/abort';\nimport { logError, logInfo } from '#internal/shared/utils/logger';\n\nexport const RC_PROTECTED_REMOTE_FETCH_PATHNAME = '/rc-fetch-protected-remote';\n\n/**\n * When a vercel host preview fetches a vercel protected remote preview on the\n * client, the request will reject as it's not possible to authenticate for the\n * protected deployment in a client side fetch - cookies cannot be shared across\n * domains. To enable previews, this request is proxied via the host where the\n * process.env.VERCEL_AUTOMATION_BYPASS_SECRET will be added.\n *\n * @param url - The URL to fetch\n * @param init - Fetch init options (should include signal for abort support)\n */\nexport async function fetchWithProtectedRcFallback(\n url: URL | string,\n init?: RequestInit,\n): Promise<Response> {\n try {\n const res = await fetch(url, init);\n return res;\n } catch (error) {\n // Re-throw AbortError immediately - don't try fallback for cancelled requests\n if (isAbortError(error)) {\n throw error;\n }\n\n if (\n typeof document === 'object' &&\n typeof document.location === 'object' &&\n document.location.origin !== new URL(url).origin\n ) {\n logInfo(\n 'FetchRemoteComponent',\n 'Request failed due to CORS, attempting to fetch it via the withRemoteComponentsHost proxy.',\n );\n // Pass signal to proxy fetch as well so it can be cancelled\n const proxiedRes = await fetch(\n `${RC_PROTECTED_REMOTE_FETCH_PATHNAME}?url=${url}`,\n init?.signal ? { signal: init.signal } : undefined,\n );\n if (proxiedRes.status === 200) {\n return proxiedRes;\n } else {\n logError(\n 'FetchRemoteComponent',\n `Could not proxy remote: [response status ${\n proxiedRes.status\n }] ${await proxiedRes.text()}`,\n );\n }\n }\n\n throw error;\n }\n}\n","/**\n * Proxy utilities for host applications that consume remote components.\n *\n * Hosts do NOT handle CORS - that's the remote's responsibility.\n * Hosts only handle protected fetch proxying.\n */\n\nimport { remoteFetchHeaders } from '#internal/shared/ssr/fetch-headers';\nimport { RC_PROTECTED_REMOTE_FETCH_PATHNAME } from '#internal/shared/ssr/fetch-with-protected-rc-fallback';\n\nexport interface HostProxyOptions {\n /**\n * List of allowed URL patterns (as regex strings) that can be proxied.\n * These patterns are combined with REMOTE_COMPONENTS_ALLOWED_PROXY_URLS env var if both are set.\n * If neither is set, all URLs are blocked.\n */\n allowedProxyUrls?: string[];\n}\n\n/**\n * Validates if a URL is allowed to be proxied based on the allowed patterns.\n *\n * @param targetUrl - The URL to validate\n * @param options - Host proxy configuration options\n * @returns true if the URL is allowed, false otherwise\n */\nfunction isUrlAllowed(targetUrl: string, options?: HostProxyOptions): boolean {\n const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(\n ',',\n ).map((p) => p.trim());\n const optionPatterns = options?.allowedProxyUrls;\n\n // Combine both sources if both are specified\n const allowedPatterns = [...(optionPatterns || []), ...(envPatterns || [])];\n\n if (allowedPatterns.length === 0) {\n return false;\n }\n\n // Check if the URL matches any of the allowed patterns\n return allowedPatterns.some((pattern) => {\n try {\n const regex = new RegExp(pattern);\n return regex.test(targetUrl);\n } catch (error) {\n console.error(\n `Invalid regex pattern in allowedProxyUrls: ${pattern}`,\n error,\n );\n return false;\n }\n });\n}\n\n/**\n * Handles protected remote component fetch requests by proxying them with\n * authentication headers. This is needed for accessing Vercel-protected remote\n * component deployments from client-side code.\n *\n * @param requestUrl - The full request URL\n * @param options - Host proxy configuration options\n * @returns Response object if this is a protected fetch request, or null if not\n */\nexport async function handleProtectedRemoteFetchRequest(\n requestUrl: string,\n options?: HostProxyOptions,\n): Promise<Response | null> {\n const url = new URL(requestUrl, 'https://fallback.com');\n\n if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {\n return null;\n }\n\n const targetUrl = url.searchParams.get('url');\n if (!targetUrl) {\n return new Response('Bad request, missing url query param', {\n status: 400,\n });\n }\n\n // Validate URL against allowed patterns to prevent SSRF attacks\n if (!isUrlAllowed(targetUrl, options)) {\n return new Response(\n `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Fetch the remote resource\n const response = await fetch(targetUrl, { headers: remoteFetchHeaders() });\n\n // Create new headers without content-encoding to avoid decoding errors\n // (Node.js fetch auto-decodes but keeps the header, causing browser issues)\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n });\n}\n\nexport { RC_PROTECTED_REMOTE_FETCH_PATHNAME };\n"],"mappings":"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","names":["NextResponse","NextResponse"]}
1
+ {"version":3,"sources":["../../src/next/proxy/withRemoteComponents.ts","../../src/shared/remote/proxy.ts","../../src/next/proxy/withRemoteComponentsHost.ts","../../src/shared/constants.ts","../../src/shared/ssr/fetch-headers.ts","../../src/shared/host/proxy.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\nimport {\n getCorsHeaders,\n getSecurityHeaders,\n handleCorsPreflightRequest,\n type RemoteComponentProxyOptions,\n} from '#internal/shared/remote/proxy';\n\n/**\n * This proxy is used to handle CORS and other remote component related tasks.\n * It can be used to wrap a Next.js proxy handler function to add CORS headers and handle preflight requests.\n *\n * @param proxy - The Next.js proxy handler function to wrap.\n * @param options - Optional configuration for handling remote components.\n * @returns A Next.js proxy handler function that handles CORS and preflight requests\n */\nexport function withRemoteComponents(\n proxy?: (request: NextRequest) => NextResponse | Promise<NextResponse>,\n options?: RemoteComponentProxyOptions,\n) {\n return async (request: NextRequest) => {\n // Check if this is a CORS preflight request\n const preflightResponse = handleCorsPreflightRequest(\n request.method,\n request.headers,\n options?.cors,\n );\n\n if (preflightResponse) {\n return preflightResponse;\n }\n\n // For all other requests, continue and attach CORS headers\n const response =\n typeof proxy === 'function' ? await proxy(request) : NextResponse.next();\n\n if (options?.cors !== false) {\n const corsHeaders = getCorsHeaders(options?.cors, request.headers);\n Object.entries(corsHeaders).forEach(([k, v]) =>\n response.headers.set(k, v),\n );\n }\n\n const securityHeaders = getSecurityHeaders();\n Object.entries(securityHeaders).forEach(([k, v]) =>\n response.headers.set(k, v),\n );\n\n return response;\n };\n}\n\nexport type { RemoteComponentProxyOptions };\n","/**\n * Proxy utilities for remote applications that expose components to hosts.\n */\n\nimport type { IncomingHttpHeaders } from 'node:http';\n\nexport interface RemoteComponentProxyOptions {\n cors?:\n | {\n origin?: string | string[];\n method?: string | string[];\n headers?: string | string[];\n credentials?: boolean;\n maxAge?: string;\n }\n | false;\n}\n\n/**\n * Gets a header value from either a Headers object or a plain object.\n */\nexport function getHeader(\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n name: string,\n): string | undefined {\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined;\n }\n const value = headers[name];\n // IncomingHttpHeaders can have string | string[] | undefined\n return Array.isArray(value) ? value[0] : value;\n}\n\n/**\n * Computes CORS headers based on the provided options and request headers.\n *\n * @param options - CORS configuration options\n * @param requestHeaders - Headers from the incoming request (can be a Headers object or a plain object)\n * @returns Object containing CORS headers to be added to the response\n */\nexport function getCorsHeaders(\n options: RemoteComponentProxyOptions['cors'],\n requestHeaders: Record<string, string> | Headers | IncomingHttpHeaders,\n): Record<string, string> {\n if (options === false) {\n return {};\n }\n\n const originHeader = getHeader(requestHeaders, 'origin');\n const refererHeader = getHeader(requestHeaders, 'referer');\n const origin =\n originHeader ?? (refererHeader ? new URL(refererHeader).origin : '*');\n\n const ALLOWED_ORIGINS = (\n process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS ||\n (Array.isArray(options?.origin)\n ? options.origin.join(',')\n : options?.origin) ||\n '*'\n )\n .split(',')\n .map((allowedOrigin) => allowedOrigin.trim());\n\n const isAllowed =\n ALLOWED_ORIGINS.includes('*') || ALLOWED_ORIGINS.includes(origin);\n\n const allowedHeaders =\n process.env.REMOTE_COMPONENTS_ALLOW_HEADERS ||\n (Array.isArray(options?.headers)\n ? options.headers.map((h) => h.trim()).join(',')\n : options?.headers) ||\n getHeader(requestHeaders, 'access-control-request-headers');\n\n const CORS_HEADERS = (\n isAllowed\n ? {\n 'Access-Control-Allow-Origin': origin,\n 'Access-Control-Allow-Methods':\n process.env.REMOTE_COMPONENTS_ALLOW_METHODS ||\n (Array.isArray(options?.method)\n ? options.method.map((m) => m.trim()).join(',')\n : options?.method) ||\n 'GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS',\n ...(allowedHeaders\n ? { 'Access-Control-Allow-Headers': allowedHeaders }\n : {}),\n ...(process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS ||\n options?.credentials\n ? {\n 'Access-Control-Allow-Credentials':\n process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || 'true',\n }\n : {}),\n 'Access-Control-Max-Age': options?.maxAge || '600',\n Vary: 'Origin',\n }\n : {}\n ) as Record<string, string>;\n\n return CORS_HEADERS;\n}\n\n/**\n * Returns security headers that prevent the remote component pages from being\n * embedded in frames. Remote components are fetched via HTTP and rendered\n * directly into the host's DOM — they should never be loaded in an iframe.\n */\nexport function getSecurityHeaders(): Record<string, string> {\n return {\n 'Content-Security-Policy': \"frame-ancestors 'none'\",\n 'X-Frame-Options': 'DENY',\n };\n}\n\n/**\n * Handles CORS preflight OPTIONS requests.\n *\n * @param method - The HTTP method of the incoming request\n * @param headers - Headers from the incoming request (can be a Headers object or a plain object)\n * @param options - CORS configuration options\n * @returns Response object for the preflight request, or null if not a preflight\n */\nexport function handleCorsPreflightRequest(\n method: string,\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n options?: RemoteComponentProxyOptions['cors'],\n): Response | null {\n if (method !== 'OPTIONS' || options === false) {\n return null;\n }\n\n const corsHeaders = getCorsHeaders(options, headers);\n\n return new Response(undefined, {\n status: 200,\n headers: { ...corsHeaders, ...getSecurityHeaders() },\n });\n}\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n type HostProxyOptions,\n handleProtectedRemoteFetchRequest,\n} from '#internal/shared/host/proxy';\n\n/**\n * Only needed when accessing a remote component in a preview environment that\n * is protected with vercel deployment protection. If the remote component fetch\n * errors, it will attempt to fetch via this proxy which adds the protection\n * bypass header to the request.\n *\n * To use this proxy, the path `/rc-fetch-protected-remote` must be added to the\n * proxy/middleware matchers.\n *\n * @param proxy - Optional Next.js middleware function to run after checking for protected remote fetch\n * @param options - Host proxy configuration options for SSRF protection\n */\nexport function withRemoteComponentsHost(\n proxy?: (request: NextRequest) => NextResponse | Promise<NextResponse>,\n options?: HostProxyOptions,\n) {\n return async (request: NextRequest) => {\n // Check if this is a protected remote fetch request\n const protectedFetchResponse = await handleProtectedRemoteFetchRequest(\n request.url,\n options,\n );\n\n if (protectedFetchResponse) {\n return protectedFetchResponse;\n }\n\n return typeof proxy === 'function'\n ? await proxy(request)\n : NextResponse.next();\n };\n}\n\nexport type { HostProxyOptions };\n","export const RC_PROTECTED_REMOTE_FETCH_PATHNAME = '/rc-fetch-protected-remote';\n","/**\n * The headers to use when fetching the remote component.\n */\nexport function remoteFetchHeaders() {\n return {\n /**\n * Authenticates deployment protection for the remote. Needed for SSR and SSG clients.\n * If the remote component uses vercel deployment protection, ensure the host and remote vercel\n * projects share a common automation bypass secret, and the shared secret is used as the\n * VERCEL_AUTOMATION_BYPASS_SECRET env var in the host project.\n */\n ...(typeof process === 'object' &&\n typeof process.env === 'object' &&\n typeof process.env.VERCEL_AUTOMATION_BYPASS_SECRET === 'string'\n ? {\n 'x-vercel-protection-bypass':\n process.env.VERCEL_AUTOMATION_BYPASS_SECRET,\n }\n : {}),\n Accept: 'text/html',\n };\n}\n","/**\n * Proxy utilities for host applications that consume remote components.\n *\n * Hosts do NOT handle CORS - that's the remote's responsibility.\n * Hosts only handle protected fetch proxying.\n */\n\nimport { RC_PROTECTED_REMOTE_FETCH_PATHNAME } from '#internal/shared/constants';\nimport { remoteFetchHeaders } from '#internal/shared/ssr/fetch-headers';\n\nexport interface HostProxyOptions {\n /**\n * List of allowed URL patterns (as regex strings) that can be proxied.\n * These patterns are combined with REMOTE_COMPONENTS_ALLOWED_PROXY_URLS env var if both are set.\n * If neither is set, all URLs are blocked.\n */\n allowedProxyUrls?: string[];\n}\n\n/**\n * Handles protected remote component fetch requests by proxying them with\n * authentication headers. This is needed for accessing Vercel-protected remote\n * component deployments from client-side code.\n *\n * @param requestUrl - The full request URL\n * @param options - Host proxy configuration options\n * @returns Response object if this is a protected fetch request, or null if not\n */\nexport async function handleProtectedRemoteFetchRequest(\n requestUrl: string,\n options?: HostProxyOptions,\n): Promise<Response | null> {\n const url = new URL(requestUrl, 'https://fallback.com');\n\n if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {\n return null;\n }\n\n const targetUrl = url.searchParams.get('url');\n if (!targetUrl) {\n return new Response('Bad request, missing url query param', {\n status: 400,\n });\n }\n\n const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(\n ',',\n ).map((p) => p.trim());\n const optionPatterns = options?.allowedProxyUrls;\n\n // Combine both sources if both are specified\n const allowedPatterns = [...(optionPatterns || []), ...(envPatterns || [])];\n\n if (allowedPatterns.length === 0) {\n return new Response(\n `Forbidden: no allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS provided to withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Validate URL against allowed patterns to prevent SSRF attacks\n const isUrlAllowed = allowedPatterns.some((pattern) => {\n try {\n const regex = new RegExp(pattern);\n return regex.test(targetUrl);\n } catch (error) {\n console.error(\n `Invalid regex pattern in allowedProxyUrls: ${pattern}`,\n error,\n );\n return false;\n }\n });\n\n if (!isUrlAllowed) {\n return new Response(\n `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Fetch the remote resource\n const response = await fetch(targetUrl, { headers: remoteFetchHeaders() });\n\n const contentType = response.headers.get('content-type');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: contentType ? { 'content-type': contentType } : undefined,\n });\n}\n"],"mappings":"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","names":["NextResponse","NextResponse"]}