@vercel/microfrontends 2.3.2 → 2.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @vercel/microfrontends
2
2
 
3
+ ## 2.3.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 9440654: Convert file level JSDoc to a regular block comment in schema types to prevent OpenAPI generators from incorrectly associating it with the `Config` interface declaration.
8
+ - d9589cc: Disable prerender speculation rules on Chromium 147 to avoid a [browser crash](https://chromium-review.googlesource.com/c/chromium/src/+/7761927) triggered during cross-zone navigation. Prefetch rules are unaffected. The guard auto-resolves on Chrome 148+, which ships the upstream fix.
9
+
3
10
  ## 2.3.2
4
11
 
5
12
  ### Patch Changes
package/dist/bin/cli.cjs CHANGED
@@ -30,7 +30,7 @@ var import_commander = require("commander");
30
30
  // package.json
31
31
  var package_default = {
32
32
  name: "@vercel/microfrontends",
33
- version: "2.3.2",
33
+ version: "2.3.3",
34
34
  private: false,
35
35
  description: "Defines configuration and utilities for microfrontends development",
36
36
  keywords: [
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- "use strict";var I=Object.create;var C=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,B=Object.prototype.hasOwnProperty;var V=(t,e)=>{for(var n in e)C(t,n,{get:e[n],enumerable:!0})},v=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of D(e))!B.call(t,o)&&o!==n&&C(t,o,{get:()=>e[o],enumerable:!(i=z(e,o))||i.enumerable});return t};var _=(t,e,n)=>(n=t!=null?I(J(t)):{},v(e||!t||!t.__esModule?C(n,"default",{value:t,enumerable:!0}):n,t)),U=t=>v(C({},"__esModule",{value:!0}),t);var K={};V(K,{Link:()=>A,PrefetchCrossZoneLinks:()=>q,PrefetchCrossZoneLinksContext:()=>P,PrefetchCrossZoneLinksProvider:()=>G,useZoneForHref:()=>T});module.exports=U(K);var H=_(require("next/link.js"),1),g=require("react");var h=require("react");var N=require("path-to-regexp"),R=new Map,W=t=>{let e=R.get(t);if(e)return e;let n=(0,N.pathToRegexp)(t);return R.set(t,n),n},u=class{constructor(e,n){this.pathCache={};this.hasFlaggedPaths=e.hasFlaggedPaths??!1;for(let i of Object.values(e.applications))if(i.routing){i.routing.some(r=>r.flag)&&(this.hasFlaggedPaths=!0);let o=[],s=[];for(let r of i.routing)if(r.flag){if(n?.removeFlaggedPaths)continue;r.group&&delete r.group,o.push(r)}else s.push(...r.paths);s.length>0&&o.push({paths:s}),i.routing=o}this.serialized=e,this.hasFlaggedPaths&&(this.serialized.hasFlaggedPaths=this.hasFlaggedPaths),this.applications=e.applications}static fromEnv(e){if(!e)throw new Error("Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy");return new u(JSON.parse(e))}isEqual(e){return this===e||JSON.stringify(this.applications)===JSON.stringify(e.applications)}getApplicationNameForPath(e){if(!e.startsWith("/"))throw new Error("Path must start with a /");if(this.pathCache[e])return this.pathCache[e];let n=new URL(e,"https://example.com").pathname;for(let[o,s]of Object.entries(this.applications))if(s.routing){for(let r of s.routing)for(let a of r.paths)if(W(a).test(n))return this.pathCache[e]=o,o}let i=Object.entries(this.applications).find(([,o])=>o.default);return i?(this.pathCache[e]=i[0],i[0]):null}serialize(){return this.serialized}};var S=new Map,b=new Map,k=t=>{let e=S.get(t||"");if(e)return e;let n=u.fromEnv(t);return S.set(t||"",n),n},E=null,L=null;async function X(){try{let t=await fetch("/.well-known/vercel/microfrontends/client-config");if(t.status!==200)return null;let e=await t.json(),n=new u(e.config);return L=n,n}catch{return null}}function m(t){let[e,n]=(0,h.useState)(()=>L??k(t)),i=(0,h.useMemo)(()=>{if(process.env.NODE_ENV==="test"&&process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER!=="1"||L)return!1;let r=b.get(t||"");if(r!==void 0)return r;let f=k(t).hasFlaggedPaths;return b.set(t||"",f),!!f},[t]),[o,s]=(0,h.useState)(i);return(0,h.useEffect)(()=>{i&&(E||(E=X()),E.then(r=>{r&&n(a=>a.isEqual(r)?a:r)}).finally(()=>{s(!1)}))},[i]),{clientConfig:e,isLoading:o}}var O=_(require("next/script.js"),1),d=require("react");var w=require("react/jsx-runtime"),y="data-prefetch",l={anyZone:"[data-zone]",external:'[data-zone="null"]',sameZone:'[data-zone="same"]',prefetch:`[${y}]`},M={and:[{href_matches:"/*"},{selector_matches:l.anyZone},{not:{selector_matches:l.sameZone}},{not:{selector_matches:l.external}}]},$={and:[{href_matches:"/*"},{selector_matches:l.anyZone},{not:{selector_matches:l.sameZone}},{not:{selector_matches:l.external}},{selector_matches:l.prefetch}]};function F(t){if(!t)return!0;if("checkVisibility"in t)return t.checkVisibility({opacityProperty:!0});let e=t,n=window.getComputedStyle(e);return n.display==="none"||n.visibility==="hidden"||n.opacity==="0"?!1:F(e.parentElement)}function q({prerenderEagerness:t="conservative"}){let{isLoading:e}=m(process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG),[n,i]=(0,d.useState)([]);return(0,d.useEffect)(()=>{if(e)return;let s=new IntersectionObserver(r=>{r.forEach(a=>{a.isIntersecting&&!a.target.hasAttribute(y)&&F(a.target)&&a.target.setAttribute(y,"true")})},{root:null,rootMargin:"0px",threshold:.1});return n.forEach(r=>s.observe(r)),()=>{s.disconnect()}},[e,n]),(0,d.useEffect)(()=>{if(e)return;let s=new MutationObserver(r=>{r.some(f=>f.type==="childList"&&f.addedNodes.length>0||f.type==="attributes"&&f.attributeName==="href")&&i(Array.from(document.querySelectorAll(`a${l.anyZone}:not(${l.prefetch}):not(${l.sameZone}):not(${l.external})`)))});return s.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["href"]}),()=>{s.disconnect()}},[e]),e?null:(0,w.jsx)(O.default,{dangerouslySetInnerHTML:{__html:JSON.stringify({prefetch:[{eagerness:"moderate",where:M},{eagerness:"immediate",where:$}],prerender:[{eagerness:t,where:M}]})},id:"prefetch-zones-links",type:"speculationrules"})}var c=require("react"),p=require("react/jsx-runtime"),P=(0,c.createContext)({prefetchHref:()=>{}});function G({children:t}){let[e,n]=(0,c.useState)(new Set),i=(0,c.useRef)(typeof navigator<"u"&&(navigator.userAgent.includes("Firefox")||navigator.userAgent.includes("Safari")&&!navigator.userAgent.includes("Chrome"))),o=(0,c.useCallback)(r=>{(0,c.startTransition)(()=>{n(a=>a.has(r)?a:new Set(a).add(r))})},[]),s=(0,c.useMemo)(()=>({prefetchHref:o}),[o]);return i.current?(0,p.jsxs)(P.Provider,{value:s,children:[t,[...e].map(r=>(0,p.jsx)("link",{as:"fetch",href:r,rel:"preload"},r))]}):(0,p.jsx)(p.Fragment,{children:t})}var x=require("react/jsx-runtime"),j=process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH;function T(t){let{clientConfig:e,isLoading:n}=m(process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG),{isRelativePath:i,zoneOfHref:o}=(0,g.useMemo)(()=>{let r=typeof t=="string"&&t.startsWith("/");return{isRelativePath:r,zoneOfHref:r?e.getApplicationNameForPath(t):null}},[e,t]);return typeof t=="string"&&!t.length?{zoneOfHref:null,isDifferentZone:!1,isLoading:!1}:{zoneOfHref:o,isDifferentZone:!i||(o?j!==o:!1),isLoading:n}}var A=(0,g.forwardRef)(({children:t,...e},n)=>{let{prefetchHref:i}=(0,g.useContext)(P),{zoneOfHref:o,isDifferentZone:s,isLoading:r}=T(e.href);function a(){e.href&&i(e.href)}if(s&&o!==null){let{prefetch:f,...Z}=e;return(0,x.jsx)("a",{...Z,"data-zone":o,onFocus:e.prefetch!==!1?a:void 0,onMouseOver:e.prefetch!==!1?a:void 0,children:t})}return(0,x.jsx)(H.default,{...e,"data-zone":o?"same":"null",prefetch:e.prefetch??(r?!1:void 0),ref:n,children:t})});A.displayName="MicrofrontendsLink";0&&(module.exports={Link,PrefetchCrossZoneLinks,PrefetchCrossZoneLinksContext,PrefetchCrossZoneLinksProvider,useZoneForHref});
2
+ "use strict";var I=Object.create;var m=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,B=Object.prototype.hasOwnProperty;var V=(t,e)=>{for(var n in e)m(t,n,{get:e[n],enumerable:!0})},_=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of D(e))!B.call(t,o)&&o!==n&&m(t,o,{get:()=>e[o],enumerable:!(i=z(e,o))||i.enumerable});return t};var b=(t,e,n)=>(n=t!=null?I(J(t)):{},_(e||!t||!t.__esModule?m(n,"default",{value:t,enumerable:!0}):n,t)),U=t=>_(m({},"__esModule",{value:!0}),t);var Q={};V(Q,{Link:()=>Z,PrefetchCrossZoneLinks:()=>q,PrefetchCrossZoneLinksContext:()=>E,PrefetchCrossZoneLinksProvider:()=>j,useZoneForHref:()=>A});module.exports=U(Q);var T=b(require("next/link.js"),1),C=require("react");var h=require("react");var R=require("path-to-regexp"),N=new Map,W=t=>{let e=N.get(t);if(e)return e;let n=(0,R.pathToRegexp)(t);return N.set(t,n),n},u=class{constructor(e,n){this.pathCache={};this.hasFlaggedPaths=e.hasFlaggedPaths??!1;for(let i of Object.values(e.applications))if(i.routing){i.routing.some(r=>r.flag)&&(this.hasFlaggedPaths=!0);let o=[],a=[];for(let r of i.routing)if(r.flag){if(n?.removeFlaggedPaths)continue;r.group&&delete r.group,o.push(r)}else a.push(...r.paths);a.length>0&&o.push({paths:a}),i.routing=o}this.serialized=e,this.hasFlaggedPaths&&(this.serialized.hasFlaggedPaths=this.hasFlaggedPaths),this.applications=e.applications}static fromEnv(e){if(!e)throw new Error("Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy");return new u(JSON.parse(e))}isEqual(e){return this===e||JSON.stringify(this.applications)===JSON.stringify(e.applications)}getApplicationNameForPath(e){if(!e.startsWith("/"))throw new Error("Path must start with a /");if(this.pathCache[e])return this.pathCache[e];let n=new URL(e,"https://example.com").pathname;for(let[o,a]of Object.entries(this.applications))if(a.routing){for(let r of a.routing)for(let s of r.paths)if(W(s).test(n))return this.pathCache[e]=o,o}let i=Object.entries(this.applications).find(([,o])=>o.default);return i?(this.pathCache[e]=i[0],i[0]):null}serialize(){return this.serialized}};var S=new Map,k=new Map,M=t=>{let e=S.get(t||"");if(e)return e;let n=u.fromEnv(t);return S.set(t||"",n),n},y=null,L=null;async function X(){try{let t=await fetch("/.well-known/vercel/microfrontends/client-config");if(t.status!==200)return null;let e=await t.json(),n=new u(e.config);return L=n,n}catch{return null}}function P(t){let[e,n]=(0,h.useState)(()=>L??M(t)),i=(0,h.useMemo)(()=>{if(process.env.NODE_ENV==="test"&&process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER!=="1"||L)return!1;let r=k.get(t||"");if(r!==void 0)return r;let f=M(t).hasFlaggedPaths;return k.set(t||"",f),!!f},[t]),[o,a]=(0,h.useState)(i);return(0,h.useEffect)(()=>{i&&(y||(y=X()),y.then(r=>{r&&n(s=>s.isEqual(r)?s:r)}).finally(()=>{a(!1)}))},[i]),{clientConfig:e,isLoading:o}}var F=b(require("next/script.js"),1),p=require("react");var H=require("react/jsx-runtime"),v="data-prefetch",l={anyZone:"[data-zone]",external:'[data-zone="null"]',sameZone:'[data-zone="same"]',prefetch:`[${v}]`},O={and:[{href_matches:"/*"},{selector_matches:l.anyZone},{not:{selector_matches:l.sameZone}},{not:{selector_matches:l.external}}]},$={and:[{href_matches:"/*"},{selector_matches:l.anyZone},{not:{selector_matches:l.sameZone}},{not:{selector_matches:l.external}},{selector_matches:l.prefetch}]};function w(t){if(!t)return!0;if("checkVisibility"in t)return t.checkVisibility({opacityProperty:!0});let e=t,n=window.getComputedStyle(e);return n.display==="none"||n.visibility==="hidden"||n.opacity==="0"?!1:w(e.parentElement)}function q({prerenderEagerness:t="conservative"}){let{isLoading:e}=P(process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG),[n,i]=(0,p.useState)([]),o=(0,p.useRef)(G());if((0,p.useEffect)(()=>{if(e)return;let r=new IntersectionObserver(s=>{s.forEach(f=>{f.isIntersecting&&!f.target.hasAttribute(v)&&w(f.target)&&f.target.setAttribute(v,"true")})},{root:null,rootMargin:"0px",threshold:.1});return n.forEach(s=>r.observe(s)),()=>{r.disconnect()}},[e,n]),(0,p.useEffect)(()=>{if(e)return;let r=new MutationObserver(s=>{s.some(g=>g.type==="childList"&&g.addedNodes.length>0||g.type==="attributes"&&g.attributeName==="href")&&i(Array.from(document.querySelectorAll(`a${l.anyZone}:not(${l.prefetch}):not(${l.sameZone}):not(${l.external})`)))});return r.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["href"]}),()=>{r.disconnect()}},[e]),e)return null;let a={prefetch:[{eagerness:"moderate",where:O},{eagerness:"immediate",where:$}],...o.current?{prerender:[{eagerness:t,where:O}]}:{}};return(0,H.jsx)(F.default,{dangerouslySetInnerHTML:{__html:JSON.stringify(a)},id:"prefetch-zones-links",type:"speculationrules"})}function G(){if(typeof navigator>"u")return!0;let t=navigator.userAgent.match(/Chrome\/(\d+)/);return t?Number(t[1])!==147:!0}var c=require("react"),d=require("react/jsx-runtime"),E=(0,c.createContext)({prefetchHref:()=>{}});function j({children:t}){let[e,n]=(0,c.useState)(new Set),i=(0,c.useRef)(typeof navigator<"u"&&(navigator.userAgent.includes("Firefox")||navigator.userAgent.includes("Safari")&&!navigator.userAgent.includes("Chrome"))),o=(0,c.useCallback)(r=>{(0,c.startTransition)(()=>{n(s=>s.has(r)?s:new Set(s).add(r))})},[]),a=(0,c.useMemo)(()=>({prefetchHref:o}),[o]);return i.current?(0,d.jsxs)(E.Provider,{value:a,children:[t,[...e].map(r=>(0,d.jsx)("link",{as:"fetch",href:r,rel:"preload"},r))]}):(0,d.jsx)(d.Fragment,{children:t})}var x=require("react/jsx-runtime"),K=process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH;function A(t){let{clientConfig:e,isLoading:n}=P(process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG),{isRelativePath:i,zoneOfHref:o}=(0,C.useMemo)(()=>{let r=typeof t=="string"&&t.startsWith("/");return{isRelativePath:r,zoneOfHref:r?e.getApplicationNameForPath(t):null}},[e,t]);return typeof t=="string"&&!t.length?{zoneOfHref:null,isDifferentZone:!1,isLoading:!1}:{zoneOfHref:o,isDifferentZone:!i||(o?K!==o:!1),isLoading:n}}var Z=(0,C.forwardRef)(({children:t,...e},n)=>{let{prefetchHref:i}=(0,C.useContext)(E),{zoneOfHref:o,isDifferentZone:a,isLoading:r}=A(e.href);function s(){e.href&&i(e.href)}if(a&&o!==null){let{prefetch:f,...g}=e;return(0,x.jsx)("a",{...g,"data-zone":o,onFocus:e.prefetch!==!1?s:void 0,onMouseOver:e.prefetch!==!1?s:void 0,children:t})}return(0,x.jsx)(T.default,{...e,"data-zone":o?"same":"null",prefetch:e.prefetch??(r?!1:void 0),ref:n,children:t})});Z.displayName="MicrofrontendsLink";0&&(module.exports={Link,PrefetchCrossZoneLinks,PrefetchCrossZoneLinksContext,PrefetchCrossZoneLinksProvider,useZoneForHref});
3
3
  //# sourceMappingURL=client.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/client/index.ts","../../src/next/client/link/microfrontends-link.tsx","../../src/config/react/use-client-config.ts","../../src/config/microfrontends-config/client/index.ts","../../src/next/client/prefetch/prefetch-cross-zone-links.tsx","../../src/next/client/prefetch/prefetch-cross-zone-links-context.tsx"],"sourcesContent":["export * from './link';\nexport * from './prefetch';\n","import NextLink, {\n type LinkProps as ExternalNextLinkProps,\n} from 'next/link.js';\nimport type { AnchorHTMLAttributes } from 'react';\nimport { forwardRef, useContext, useMemo } from 'react';\nimport { useClientConfig } from '../../../config/react/use-client-config';\nimport { PrefetchCrossZoneLinksContext } from '../prefetch';\n\ninterface BaseProps {\n children: React.ReactNode;\n href: string;\n}\n\n// fix for tsc inlining LinkProps from next\n// https://github.com/microsoft/TypeScript/issues/37151#issuecomment-756232934\ninterface NextLinkProps extends ExternalNextLinkProps {}\nexport type LinkProps = BaseProps &\n Omit<NextLinkProps, keyof BaseProps> &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>;\n\nconst CURRENT_ZONE_HASH = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH;\n\nexport function useZoneForHref(href: LinkProps['href'] | undefined): {\n zoneOfHref: string | null;\n isDifferentZone: boolean;\n isLoading: boolean;\n} {\n const { clientConfig, isLoading } = useClientConfig(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n );\n const { isRelativePath, zoneOfHref } = useMemo(() => {\n const isRelative = typeof href === 'string' && href.startsWith('/');\n return {\n isRelativePath: isRelative,\n zoneOfHref: isRelative\n ? clientConfig.getApplicationNameForPath(href)\n : null,\n };\n }, [clientConfig, href]);\n\n if (typeof href === 'string' && !href.length) {\n return {\n zoneOfHref: null,\n isDifferentZone: false,\n isLoading: false,\n };\n }\n const isDifferentZone =\n !isRelativePath || (zoneOfHref ? CURRENT_ZONE_HASH !== zoneOfHref : false);\n return { zoneOfHref, isDifferentZone, isLoading };\n}\n\n/**\n * A Link component that works with microfrontend set-ups and will prefetch the\n * cross zone links automatically.\n */\nexport const Link = forwardRef<HTMLAnchorElement, LinkProps>(\n ({ children, ...props }, ref): JSX.Element => {\n const { prefetchHref } = useContext(PrefetchCrossZoneLinksContext);\n const { zoneOfHref, isDifferentZone, isLoading } = useZoneForHref(\n props.href,\n );\n\n function onHoverPrefetch(): void {\n if (!props.href) {\n return;\n }\n prefetchHref(props.href);\n }\n\n if (isDifferentZone && zoneOfHref !== null) {\n const { prefetch: _, ...rest } = props;\n return (\n <a\n {...rest}\n data-zone={zoneOfHref}\n onFocus={props.prefetch !== false ? onHoverPrefetch : undefined}\n onMouseOver={props.prefetch !== false ? onHoverPrefetch : undefined}\n >\n {children}\n </a>\n );\n }\n\n return (\n <NextLink\n {...props}\n data-zone={!zoneOfHref ? 'null' : 'same'}\n prefetch={props.prefetch ?? (isLoading ? false : undefined)}\n ref={ref}\n >\n {children}\n </NextLink>\n );\n },\n);\nLink.displayName = 'MicrofrontendsLink';\n","'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\nimport { MicrofrontendConfigClient } from '../microfrontends-config/client';\nimport type { WellKnownClientData } from '../well-known/types';\n\nconst clientCache = new Map<string, MicrofrontendConfigClient>();\nconst cachedHasDynamicPaths = new Map<string, boolean>();\n\nconst getClient = (config: string | undefined) => {\n const existing = clientCache.get(config || '');\n if (existing) {\n return existing;\n }\n\n const client = MicrofrontendConfigClient.fromEnv(config);\n clientCache.set(config || '', client);\n return client;\n};\n\nlet cachedServerClientConfigPromise: Promise<MicrofrontendConfigClient | null> | null =\n null;\n\nlet cachedServerClient: MicrofrontendConfigClient | null = null;\n\nasync function fetchClientConfigFromServer(): Promise<MicrofrontendConfigClient | null> {\n try {\n const response = await fetch(\n '/.well-known/vercel/microfrontends/client-config',\n );\n if (response.status !== 200) {\n return null;\n }\n const responseJson = (await response.json()) as WellKnownClientData;\n const client = new MicrofrontendConfigClient(responseJson.config);\n cachedServerClient = client;\n return client;\n } catch {\n return null;\n }\n}\n\n/**\n * Hook to use the client microfrontends configuration. This hook will resolve\n * dynamic paths by fetching the configuration from the server if necessary,\n * allowing the server to specify the values for dynamic paths.\n */\nexport function useClientConfig(config: string | undefined): {\n clientConfig: MicrofrontendConfigClient;\n isLoading: boolean;\n} {\n const [clientConfig, setClientConfig] = useState<MicrofrontendConfigClient>(\n () => cachedServerClient ?? getClient(config),\n );\n const canLoad = useMemo(() => {\n if (\n process.env.NODE_ENV === 'test' &&\n process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER !== '1'\n ) {\n return false;\n }\n // If we've already fetched the server config and it's resolved, we don't need\n // to enter the loading state at all\n if (cachedServerClient) return false;\n // If we've already checked this config for dynamic paths, we can use the\n // cached result from before instead of reevaluating.\n const existing = cachedHasDynamicPaths.get(config || '');\n if (existing !== undefined) return existing;\n // Get the original client config to determine if the config has any\n // dynamic paths.\n const originalClientConfig = getClient(config);\n // As an optimization, only fetch the config from the server if the\n // microfrontends configuration has any dynamic paths. If it doesn't,\n // then the server won't return any different values.\n const hasDynamicPaths = originalClientConfig.hasFlaggedPaths;\n cachedHasDynamicPaths.set(config || '', hasDynamicPaths);\n if (!hasDynamicPaths) {\n return false;\n }\n return true;\n }, [config]);\n const [isLoading, setIsLoading] = useState(canLoad);\n useEffect(() => {\n if (!canLoad) return;\n if (!cachedServerClientConfigPromise) {\n cachedServerClientConfigPromise = fetchClientConfigFromServer();\n }\n void cachedServerClientConfigPromise\n .then((newConfig) => {\n if (newConfig) {\n setClientConfig((prevConfig) => {\n return prevConfig.isEqual(newConfig) ? prevConfig : newConfig;\n });\n }\n })\n .finally(() => {\n setIsLoading(false);\n });\n }, [canLoad]);\n\n return { clientConfig, isLoading };\n}\n\nexport function resetCachedServerClientConfigPromise(): void {\n cachedServerClientConfigPromise = null;\n}\n","import { pathToRegexp } from 'path-to-regexp';\nimport type { ClientConfig } from './types';\n\nexport interface MicrofrontendConfigClientOptions {\n removeFlaggedPaths?: boolean;\n}\n\nconst regexpCache = new Map<string, RegExp>();\nconst getRegexp = (path: string): RegExp => {\n const existing = regexpCache.get(path);\n if (existing) {\n return existing;\n }\n\n const regexp = pathToRegexp(path);\n regexpCache.set(path, regexp);\n return regexp;\n};\n\nexport class MicrofrontendConfigClient {\n applications: ClientConfig['applications'];\n hasFlaggedPaths: boolean;\n pathCache: Record<string, string> = {};\n private readonly serialized: ClientConfig;\n\n constructor(config: ClientConfig, opts?: MicrofrontendConfigClientOptions) {\n this.hasFlaggedPaths = config.hasFlaggedPaths ?? false;\n for (const app of Object.values(config.applications)) {\n if (app.routing) {\n if (app.routing.some((match) => match.flag)) {\n this.hasFlaggedPaths = true;\n }\n const newRouting = [];\n const pathsWithoutFlags = [];\n for (const group of app.routing) {\n if (group.flag) {\n if (opts?.removeFlaggedPaths) {\n continue;\n }\n if (group.group) {\n delete group.group;\n }\n newRouting.push(group);\n } else {\n pathsWithoutFlags.push(...group.paths);\n }\n }\n if (pathsWithoutFlags.length > 0) {\n newRouting.push({ paths: pathsWithoutFlags });\n }\n app.routing = newRouting;\n }\n }\n this.serialized = config;\n if (this.hasFlaggedPaths) {\n this.serialized.hasFlaggedPaths = this.hasFlaggedPaths;\n }\n this.applications = config.applications;\n }\n\n /**\n * Create a new `MicrofrontendConfigClient` from a JSON string.\n * Config must be passed in to remain framework agnostic\n */\n static fromEnv(config: string | undefined): MicrofrontendConfigClient {\n if (!config) {\n throw new Error(\n 'Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy',\n );\n }\n return new MicrofrontendConfigClient(JSON.parse(config) as ClientConfig);\n }\n\n isEqual(other: MicrofrontendConfigClient): boolean {\n return (\n this === other ||\n JSON.stringify(this.applications) === JSON.stringify(other.applications)\n );\n }\n\n getApplicationNameForPath(path: string): string | null {\n if (!path.startsWith('/')) {\n throw new Error(`Path must start with a /`);\n }\n\n if (this.pathCache[path]) {\n return this.pathCache[path];\n }\n\n const pathname = new URL(path, 'https://example.com').pathname;\n for (const [name, application] of Object.entries(this.applications)) {\n if (application.routing) {\n for (const group of application.routing) {\n for (const childPath of group.paths) {\n const regexp = getRegexp(childPath);\n if (regexp.test(pathname)) {\n this.pathCache[path] = name;\n return name;\n }\n }\n }\n }\n }\n const defaultApplication = Object.entries(this.applications).find(\n ([, application]) => application.default,\n );\n if (!defaultApplication) {\n return null;\n }\n\n this.pathCache[path] = defaultApplication[0];\n return defaultApplication[0];\n }\n\n serialize(): ClientConfig {\n return this.serialized;\n }\n}\n","import Script from 'next/script.js';\nimport { useEffect, useState } from 'react';\nimport { useClientConfig } from '../../../config/react/use-client-config';\n\nconst PREFETCH_ATTR = 'data-prefetch';\nconst DATA_ATTR_SELECTORS = {\n anyZone: '[data-zone]',\n external: '[data-zone=\"null\"]',\n sameZone: '[data-zone=\"same\"]',\n prefetch: `[${PREFETCH_ATTR}]`,\n} as const;\n\nconst PREFETCH_ON_HOVER_PREDICATES = {\n and: [\n { href_matches: '/*' },\n { selector_matches: DATA_ATTR_SELECTORS.anyZone },\n { not: { selector_matches: DATA_ATTR_SELECTORS.sameZone } },\n { not: { selector_matches: DATA_ATTR_SELECTORS.external } },\n ],\n};\n\nconst PREFETCH_WHEN_VISIBLE_PREDICATES = {\n and: [\n { href_matches: '/*' },\n { selector_matches: DATA_ATTR_SELECTORS.anyZone },\n { not: { selector_matches: DATA_ATTR_SELECTORS.sameZone } },\n { not: { selector_matches: DATA_ATTR_SELECTORS.external } },\n { selector_matches: DATA_ATTR_SELECTORS.prefetch },\n ],\n};\n\nfunction checkVisibility(element: Element | null): boolean {\n if (!element) return true;\n\n if ('checkVisibility' in element) {\n return element.checkVisibility({ opacityProperty: true });\n }\n\n // hack to get around TS thinking element is never;\n const el = element as Element;\n const style = window.getComputedStyle(el);\n\n if (\n style.display === 'none' ||\n style.visibility === 'hidden' ||\n style.opacity === '0'\n ) {\n return false;\n }\n\n return checkVisibility(el.parentElement);\n}\n\ninterface PrefetchCrossZoneLinksProps {\n /**\n * This attributes controls how eager the browser should be in prerendering\n * cross-zone links. Prerendering downloads the HTML and subresources of the page\n * and starts to render the page in the background. This consumes more resources\n * but provides a faster user experience if the user decides to visit that page.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/speculationrules#eagerness\n * for more information.\n *\n * Default value is 'conservative'.\n */\n prerenderEagerness?: 'immediate' | 'eager' | 'moderate' | 'conservative';\n}\n\nexport function PrefetchCrossZoneLinks({\n prerenderEagerness = 'conservative',\n}: PrefetchCrossZoneLinksProps): JSX.Element | null {\n const { isLoading } = useClientConfig(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n );\n const [links, setLinks] = useState<HTMLAnchorElement[]>([]);\n\n useEffect(() => {\n if (isLoading) {\n return;\n }\n\n /**\n * Intersection observer to add the data-prefetch attribute to cross-zone\n * links that have yet to be prefetched and are visible.\n */\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (\n entry.isIntersecting &&\n !entry.target.hasAttribute(PREFETCH_ATTR) &&\n // lazy perform the visibility check for nodes that are intersecting the viewport\n // and have not been prefetched.\n checkVisibility(entry.target)\n ) {\n entry.target.setAttribute(PREFETCH_ATTR, 'true');\n }\n });\n },\n {\n root: null,\n rootMargin: '0px',\n threshold: 0.1,\n },\n );\n\n links.forEach((link) => observer.observe(link));\n\n return () => {\n observer.disconnect();\n };\n }, [isLoading, links]);\n\n useEffect(() => {\n if (isLoading) {\n return;\n }\n\n /**\n * Mutation observer to notify when new nodes have entered/exited the document\n * or an href has changed.\n */\n const observer = new MutationObserver((mutations) => {\n const hasChanged = mutations.some((mutation) => {\n return (\n (mutation.type === 'childList' && mutation.addedNodes.length > 0) ||\n (mutation.type === 'attributes' && mutation.attributeName === 'href')\n );\n });\n\n if (hasChanged) {\n // Whenever there's a change, add all cross-zone links that haven't been\n // prefetched.\n setLinks(\n Array.from(\n document.querySelectorAll<HTMLAnchorElement>(\n `a${DATA_ATTR_SELECTORS.anyZone}:not(${DATA_ATTR_SELECTORS.prefetch}):not(${DATA_ATTR_SELECTORS.sameZone}):not(${DATA_ATTR_SELECTORS.external})`,\n ),\n ),\n );\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['href'],\n });\n\n return () => {\n observer.disconnect();\n };\n }, [isLoading]);\n\n // Wait till the zone-config loads to take into consideration any\n // flagged routes.\n if (isLoading) {\n return null;\n }\n\n // Prefetch links with moderate eagerness by default, immediately when marked \"data-prefetch\".\n // Prerender links with conservative eagerness by default, immediately when marked \"data-prefetch\".\n const speculationRules = {\n prefetch: [\n {\n eagerness: 'moderate',\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n {\n eagerness: 'immediate',\n where: PREFETCH_WHEN_VISIBLE_PREDICATES,\n },\n ],\n prerender: [\n {\n eagerness: prerenderEagerness,\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n ],\n };\n\n return (\n <Script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Safe - injecting JSON speculation rules, not HTML\n dangerouslySetInnerHTML={{\n __html: JSON.stringify(speculationRules),\n }}\n id=\"prefetch-zones-links\"\n type=\"speculationrules\"\n />\n );\n}\n","import type React from 'react';\nimport {\n createContext,\n startTransition,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nexport interface PrefetchCrossZoneLinksContext {\n prefetchHref: (href: string) => void;\n}\n\nexport const PrefetchCrossZoneLinksContext =\n createContext<PrefetchCrossZoneLinksContext>({\n prefetchHref: () => {},\n });\n\nexport function PrefetchCrossZoneLinksProvider({\n children,\n}: {\n children: React.ReactNode;\n}): JSX.Element | null {\n const [seenHrefs, setSeenHrefs] = useState(new Set<string>());\n const isSafariOrFirefox = useRef(\n typeof navigator !== 'undefined' &&\n (navigator.userAgent.includes('Firefox') ||\n (navigator.userAgent.includes('Safari') &&\n !navigator.userAgent.includes('Chrome'))),\n );\n\n // This useCallback must not have any dependencies because if it changes\n // its value, every component that uses this context will rerender.\n const prefetchHref = useCallback((href: string): void => {\n // It's not critical that we render the new preload `<link>` elements\n // immediately. We want to batch together `prefetchHref` calls that\n // occur in one synchronous pass and only render once after they've all\n // called this callback.\n startTransition(() => {\n setSeenHrefs((prevHrefs) => {\n if (prevHrefs.has(href)) return prevHrefs;\n return new Set(prevHrefs).add(href);\n });\n });\n }, []);\n\n const value = useMemo(() => ({ prefetchHref }), [prefetchHref]);\n\n if (!isSafariOrFirefox.current) {\n return <>{children}</>;\n }\n\n return (\n <PrefetchCrossZoneLinksContext.Provider value={value}>\n {children}\n {[...seenHrefs].map((href) => (\n <link as=\"fetch\" href={href} key={href} rel=\"preload\" />\n ))}\n </PrefetchCrossZoneLinksContext.Provider>\n );\n}\n"],"mappings":"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","names":["client_exports","__export","Link","PrefetchCrossZoneLinks","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","useZoneForHref","__toCommonJS","import_link","import_react","import_react","import_path_to_regexp","regexpCache","getRegexp","path","existing","regexp","MicrofrontendConfigClient","config","opts","app","match","newRouting","pathsWithoutFlags","group","other","pathname","name","application","childPath","defaultApplication","clientCache","cachedHasDynamicPaths","getClient","config","existing","client","MicrofrontendConfigClient","cachedServerClientConfigPromise","cachedServerClient","fetchClientConfigFromServer","response","responseJson","useClientConfig","clientConfig","setClientConfig","canLoad","hasDynamicPaths","isLoading","setIsLoading","newConfig","prevConfig","import_script","import_react","import_jsx_runtime","PREFETCH_ATTR","DATA_ATTR_SELECTORS","PREFETCH_ON_HOVER_PREDICATES","PREFETCH_WHEN_VISIBLE_PREDICATES","checkVisibility","element","el","style","PrefetchCrossZoneLinks","prerenderEagerness","isLoading","useClientConfig","links","setLinks","observer","entries","entry","link","mutations","mutation","Script","import_react","import_jsx_runtime","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","children","seenHrefs","setSeenHrefs","isSafariOrFirefox","prefetchHref","href","prevHrefs","value","import_jsx_runtime","CURRENT_ZONE_HASH","useZoneForHref","href","clientConfig","isLoading","useClientConfig","isRelativePath","zoneOfHref","isRelative","Link","children","props","ref","prefetchHref","PrefetchCrossZoneLinksContext","isDifferentZone","onHoverPrefetch","_","rest","NextLink"]}
1
+ {"version":3,"sources":["../../src/next/client/index.ts","../../src/next/client/link/microfrontends-link.tsx","../../src/config/react/use-client-config.ts","../../src/config/microfrontends-config/client/index.ts","../../src/next/client/prefetch/prefetch-cross-zone-links.tsx","../../src/next/client/prefetch/prefetch-cross-zone-links-context.tsx"],"sourcesContent":["export * from './link';\nexport * from './prefetch';\n","import NextLink, {\n type LinkProps as ExternalNextLinkProps,\n} from 'next/link.js';\nimport type { AnchorHTMLAttributes } from 'react';\nimport { forwardRef, useContext, useMemo } from 'react';\nimport { useClientConfig } from '../../../config/react/use-client-config';\nimport { PrefetchCrossZoneLinksContext } from '../prefetch';\n\ninterface BaseProps {\n children: React.ReactNode;\n href: string;\n}\n\n// fix for tsc inlining LinkProps from next\n// https://github.com/microsoft/TypeScript/issues/37151#issuecomment-756232934\ninterface NextLinkProps extends ExternalNextLinkProps {}\nexport type LinkProps = BaseProps &\n Omit<NextLinkProps, keyof BaseProps> &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>;\n\nconst CURRENT_ZONE_HASH = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH;\n\nexport function useZoneForHref(href: LinkProps['href'] | undefined): {\n zoneOfHref: string | null;\n isDifferentZone: boolean;\n isLoading: boolean;\n} {\n const { clientConfig, isLoading } = useClientConfig(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n );\n const { isRelativePath, zoneOfHref } = useMemo(() => {\n const isRelative = typeof href === 'string' && href.startsWith('/');\n return {\n isRelativePath: isRelative,\n zoneOfHref: isRelative\n ? clientConfig.getApplicationNameForPath(href)\n : null,\n };\n }, [clientConfig, href]);\n\n if (typeof href === 'string' && !href.length) {\n return {\n zoneOfHref: null,\n isDifferentZone: false,\n isLoading: false,\n };\n }\n const isDifferentZone =\n !isRelativePath || (zoneOfHref ? CURRENT_ZONE_HASH !== zoneOfHref : false);\n return { zoneOfHref, isDifferentZone, isLoading };\n}\n\n/**\n * A Link component that works with microfrontend set-ups and will prefetch the\n * cross zone links automatically.\n */\nexport const Link = forwardRef<HTMLAnchorElement, LinkProps>(\n ({ children, ...props }, ref): JSX.Element => {\n const { prefetchHref } = useContext(PrefetchCrossZoneLinksContext);\n const { zoneOfHref, isDifferentZone, isLoading } = useZoneForHref(\n props.href,\n );\n\n function onHoverPrefetch(): void {\n if (!props.href) {\n return;\n }\n prefetchHref(props.href);\n }\n\n if (isDifferentZone && zoneOfHref !== null) {\n const { prefetch: _, ...rest } = props;\n return (\n <a\n {...rest}\n data-zone={zoneOfHref}\n onFocus={props.prefetch !== false ? onHoverPrefetch : undefined}\n onMouseOver={props.prefetch !== false ? onHoverPrefetch : undefined}\n >\n {children}\n </a>\n );\n }\n\n return (\n <NextLink\n {...props}\n data-zone={!zoneOfHref ? 'null' : 'same'}\n prefetch={props.prefetch ?? (isLoading ? false : undefined)}\n ref={ref}\n >\n {children}\n </NextLink>\n );\n },\n);\nLink.displayName = 'MicrofrontendsLink';\n","'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\nimport { MicrofrontendConfigClient } from '../microfrontends-config/client';\nimport type { WellKnownClientData } from '../well-known/types';\n\nconst clientCache = new Map<string, MicrofrontendConfigClient>();\nconst cachedHasDynamicPaths = new Map<string, boolean>();\n\nconst getClient = (config: string | undefined) => {\n const existing = clientCache.get(config || '');\n if (existing) {\n return existing;\n }\n\n const client = MicrofrontendConfigClient.fromEnv(config);\n clientCache.set(config || '', client);\n return client;\n};\n\nlet cachedServerClientConfigPromise: Promise<MicrofrontendConfigClient | null> | null =\n null;\n\nlet cachedServerClient: MicrofrontendConfigClient | null = null;\n\nasync function fetchClientConfigFromServer(): Promise<MicrofrontendConfigClient | null> {\n try {\n const response = await fetch(\n '/.well-known/vercel/microfrontends/client-config',\n );\n if (response.status !== 200) {\n return null;\n }\n const responseJson = (await response.json()) as WellKnownClientData;\n const client = new MicrofrontendConfigClient(responseJson.config);\n cachedServerClient = client;\n return client;\n } catch {\n return null;\n }\n}\n\n/**\n * Hook to use the client microfrontends configuration. This hook will resolve\n * dynamic paths by fetching the configuration from the server if necessary,\n * allowing the server to specify the values for dynamic paths.\n */\nexport function useClientConfig(config: string | undefined): {\n clientConfig: MicrofrontendConfigClient;\n isLoading: boolean;\n} {\n const [clientConfig, setClientConfig] = useState<MicrofrontendConfigClient>(\n () => cachedServerClient ?? getClient(config),\n );\n const canLoad = useMemo(() => {\n if (\n process.env.NODE_ENV === 'test' &&\n process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER !== '1'\n ) {\n return false;\n }\n // If we've already fetched the server config and it's resolved, we don't need\n // to enter the loading state at all\n if (cachedServerClient) return false;\n // If we've already checked this config for dynamic paths, we can use the\n // cached result from before instead of reevaluating.\n const existing = cachedHasDynamicPaths.get(config || '');\n if (existing !== undefined) return existing;\n // Get the original client config to determine if the config has any\n // dynamic paths.\n const originalClientConfig = getClient(config);\n // As an optimization, only fetch the config from the server if the\n // microfrontends configuration has any dynamic paths. If it doesn't,\n // then the server won't return any different values.\n const hasDynamicPaths = originalClientConfig.hasFlaggedPaths;\n cachedHasDynamicPaths.set(config || '', hasDynamicPaths);\n if (!hasDynamicPaths) {\n return false;\n }\n return true;\n }, [config]);\n const [isLoading, setIsLoading] = useState(canLoad);\n useEffect(() => {\n if (!canLoad) return;\n if (!cachedServerClientConfigPromise) {\n cachedServerClientConfigPromise = fetchClientConfigFromServer();\n }\n void cachedServerClientConfigPromise\n .then((newConfig) => {\n if (newConfig) {\n setClientConfig((prevConfig) => {\n return prevConfig.isEqual(newConfig) ? prevConfig : newConfig;\n });\n }\n })\n .finally(() => {\n setIsLoading(false);\n });\n }, [canLoad]);\n\n return { clientConfig, isLoading };\n}\n\nexport function resetCachedServerClientConfigPromise(): void {\n cachedServerClientConfigPromise = null;\n}\n","import { pathToRegexp } from 'path-to-regexp';\nimport type { ClientConfig } from './types';\n\nexport interface MicrofrontendConfigClientOptions {\n removeFlaggedPaths?: boolean;\n}\n\nconst regexpCache = new Map<string, RegExp>();\nconst getRegexp = (path: string): RegExp => {\n const existing = regexpCache.get(path);\n if (existing) {\n return existing;\n }\n\n const regexp = pathToRegexp(path);\n regexpCache.set(path, regexp);\n return regexp;\n};\n\nexport class MicrofrontendConfigClient {\n applications: ClientConfig['applications'];\n hasFlaggedPaths: boolean;\n pathCache: Record<string, string> = {};\n private readonly serialized: ClientConfig;\n\n constructor(config: ClientConfig, opts?: MicrofrontendConfigClientOptions) {\n this.hasFlaggedPaths = config.hasFlaggedPaths ?? false;\n for (const app of Object.values(config.applications)) {\n if (app.routing) {\n if (app.routing.some((match) => match.flag)) {\n this.hasFlaggedPaths = true;\n }\n const newRouting = [];\n const pathsWithoutFlags = [];\n for (const group of app.routing) {\n if (group.flag) {\n if (opts?.removeFlaggedPaths) {\n continue;\n }\n if (group.group) {\n delete group.group;\n }\n newRouting.push(group);\n } else {\n pathsWithoutFlags.push(...group.paths);\n }\n }\n if (pathsWithoutFlags.length > 0) {\n newRouting.push({ paths: pathsWithoutFlags });\n }\n app.routing = newRouting;\n }\n }\n this.serialized = config;\n if (this.hasFlaggedPaths) {\n this.serialized.hasFlaggedPaths = this.hasFlaggedPaths;\n }\n this.applications = config.applications;\n }\n\n /**\n * Create a new `MicrofrontendConfigClient` from a JSON string.\n * Config must be passed in to remain framework agnostic\n */\n static fromEnv(config: string | undefined): MicrofrontendConfigClient {\n if (!config) {\n throw new Error(\n 'Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy',\n );\n }\n return new MicrofrontendConfigClient(JSON.parse(config) as ClientConfig);\n }\n\n isEqual(other: MicrofrontendConfigClient): boolean {\n return (\n this === other ||\n JSON.stringify(this.applications) === JSON.stringify(other.applications)\n );\n }\n\n getApplicationNameForPath(path: string): string | null {\n if (!path.startsWith('/')) {\n throw new Error(`Path must start with a /`);\n }\n\n if (this.pathCache[path]) {\n return this.pathCache[path];\n }\n\n const pathname = new URL(path, 'https://example.com').pathname;\n for (const [name, application] of Object.entries(this.applications)) {\n if (application.routing) {\n for (const group of application.routing) {\n for (const childPath of group.paths) {\n const regexp = getRegexp(childPath);\n if (regexp.test(pathname)) {\n this.pathCache[path] = name;\n return name;\n }\n }\n }\n }\n }\n const defaultApplication = Object.entries(this.applications).find(\n ([, application]) => application.default,\n );\n if (!defaultApplication) {\n return null;\n }\n\n this.pathCache[path] = defaultApplication[0];\n return defaultApplication[0];\n }\n\n serialize(): ClientConfig {\n return this.serialized;\n }\n}\n","import Script from 'next/script.js';\nimport { useEffect, useRef, useState } from 'react';\nimport { useClientConfig } from '../../../config/react/use-client-config';\n\nconst PREFETCH_ATTR = 'data-prefetch';\nconst DATA_ATTR_SELECTORS = {\n anyZone: '[data-zone]',\n external: '[data-zone=\"null\"]',\n sameZone: '[data-zone=\"same\"]',\n prefetch: `[${PREFETCH_ATTR}]`,\n} as const;\n\nconst PREFETCH_ON_HOVER_PREDICATES = {\n and: [\n { href_matches: '/*' },\n { selector_matches: DATA_ATTR_SELECTORS.anyZone },\n { not: { selector_matches: DATA_ATTR_SELECTORS.sameZone } },\n { not: { selector_matches: DATA_ATTR_SELECTORS.external } },\n ],\n};\n\nconst PREFETCH_WHEN_VISIBLE_PREDICATES = {\n and: [\n { href_matches: '/*' },\n { selector_matches: DATA_ATTR_SELECTORS.anyZone },\n { not: { selector_matches: DATA_ATTR_SELECTORS.sameZone } },\n { not: { selector_matches: DATA_ATTR_SELECTORS.external } },\n { selector_matches: DATA_ATTR_SELECTORS.prefetch },\n ],\n};\n\nfunction checkVisibility(element: Element | null): boolean {\n if (!element) return true;\n\n if ('checkVisibility' in element) {\n return element.checkVisibility({ opacityProperty: true });\n }\n\n // hack to get around TS thinking element is never;\n const el = element as Element;\n const style = window.getComputedStyle(el);\n\n if (\n style.display === 'none' ||\n style.visibility === 'hidden' ||\n style.opacity === '0'\n ) {\n return false;\n }\n\n return checkVisibility(el.parentElement);\n}\n\ninterface PrefetchCrossZoneLinksProps {\n /**\n * This attributes controls how eager the browser should be in prerendering\n * cross-zone links. Prerendering downloads the HTML and subresources of the page\n * and starts to render the page in the background. This consumes more resources\n * but provides a faster user experience if the user decides to visit that page.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/speculationrules#eagerness\n * for more information.\n *\n * Default value is 'conservative'.\n */\n prerenderEagerness?: 'immediate' | 'eager' | 'moderate' | 'conservative';\n}\n\nexport function PrefetchCrossZoneLinks({\n prerenderEagerness = 'conservative',\n}: PrefetchCrossZoneLinksProps): JSX.Element | null {\n const { isLoading } = useClientConfig(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n );\n const [links, setLinks] = useState<HTMLAnchorElement[]>([]);\n const canPrerender = useRef(isPrerenderSafe());\n\n useEffect(() => {\n if (isLoading) {\n return;\n }\n\n /**\n * Intersection observer to add the data-prefetch attribute to cross-zone\n * links that have yet to be prefetched and are visible.\n */\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (\n entry.isIntersecting &&\n !entry.target.hasAttribute(PREFETCH_ATTR) &&\n // lazy perform the visibility check for nodes that are intersecting the viewport\n // and have not been prefetched.\n checkVisibility(entry.target)\n ) {\n entry.target.setAttribute(PREFETCH_ATTR, 'true');\n }\n });\n },\n {\n root: null,\n rootMargin: '0px',\n threshold: 0.1,\n },\n );\n\n links.forEach((link) => observer.observe(link));\n\n return () => {\n observer.disconnect();\n };\n }, [isLoading, links]);\n\n useEffect(() => {\n if (isLoading) {\n return;\n }\n\n /**\n * Mutation observer to notify when new nodes have entered/exited the document\n * or an href has changed.\n */\n const observer = new MutationObserver((mutations) => {\n const hasChanged = mutations.some((mutation) => {\n return (\n (mutation.type === 'childList' && mutation.addedNodes.length > 0) ||\n (mutation.type === 'attributes' && mutation.attributeName === 'href')\n );\n });\n\n if (hasChanged) {\n // Whenever there's a change, add all cross-zone links that haven't been\n // prefetched.\n setLinks(\n Array.from(\n document.querySelectorAll<HTMLAnchorElement>(\n `a${DATA_ATTR_SELECTORS.anyZone}:not(${DATA_ATTR_SELECTORS.prefetch}):not(${DATA_ATTR_SELECTORS.sameZone}):not(${DATA_ATTR_SELECTORS.external})`,\n ),\n ),\n );\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['href'],\n });\n\n return () => {\n observer.disconnect();\n };\n }, [isLoading]);\n\n // Wait till the zone-config loads to take into consideration any\n // flagged routes.\n if (isLoading) {\n return null;\n }\n\n // Prefetch links with moderate eagerness by default, immediately when marked \"data-prefetch\".\n // Prerender links with conservative eagerness by default, immediately when marked \"data-prefetch\".\n const speculationRules = {\n prefetch: [\n {\n eagerness: 'moderate',\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n {\n eagerness: 'immediate',\n where: PREFETCH_WHEN_VISIBLE_PREDICATES,\n },\n ],\n ...(canPrerender.current\n ? {\n prerender: [\n {\n eagerness: prerenderEagerness,\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n ],\n }\n : {}),\n };\n\n return (\n <Script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Safe - injecting JSON speculation rules, not HTML\n dangerouslySetInnerHTML={{\n __html: JSON.stringify(speculationRules),\n }}\n id=\"prefetch-zones-links\"\n type=\"speculationrules\"\n />\n );\n}\n\n/**\n * Chromium 147 ships a resurrected CHECK in MarkPrerenderMatchedWithPrefetch\n * (CL 7641455) that crashes the browser process when prerender + prefetch\n * matching is invoked more than once per URL — which happens on cross-zone\n * MFE navigations. The CHECK was removed in CL 7761927 and the fix\n * cherry-picked into M148 (148.0.7778.43). No other major version is\n * affected: < 147 never had the resurrected CHECK, >= 148 has the fix.\n *\n * Non-Chromium browsers never process `<script type=\"speculationrules\">`\n * at all, so the prerender entry is inert for them — no need to exclude.\n *\n * On the server (navigator undefined) we return `true` because the\n * `next/script` afterInteractive strategy defers injection to the client;\n * the server-side value is never materialised into HTML.\n *\n * @see https://chromium-review.googlesource.com/c/chromium/src/+/7761927\n * @see https://vercel.slack.com/archives/C0B1DHDQ3S7 (inc-6387)\n *\n * TODO: Remove once Chrome 147 usage drops below the long-tail threshold.\n */\nfunction isPrerenderSafe(): boolean {\n if (typeof navigator === 'undefined') return true;\n const match = navigator.userAgent.match(/Chrome\\/(\\d+)/);\n if (!match) return true;\n return Number(match[1]) !== 147;\n}\n","import type React from 'react';\nimport {\n createContext,\n startTransition,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nexport interface PrefetchCrossZoneLinksContext {\n prefetchHref: (href: string) => void;\n}\n\nexport const PrefetchCrossZoneLinksContext =\n createContext<PrefetchCrossZoneLinksContext>({\n prefetchHref: () => {},\n });\n\nexport function PrefetchCrossZoneLinksProvider({\n children,\n}: {\n children: React.ReactNode;\n}): JSX.Element | null {\n const [seenHrefs, setSeenHrefs] = useState(new Set<string>());\n const isSafariOrFirefox = useRef(\n typeof navigator !== 'undefined' &&\n (navigator.userAgent.includes('Firefox') ||\n (navigator.userAgent.includes('Safari') &&\n !navigator.userAgent.includes('Chrome'))),\n );\n\n // This useCallback must not have any dependencies because if it changes\n // its value, every component that uses this context will rerender.\n const prefetchHref = useCallback((href: string): void => {\n // It's not critical that we render the new preload `<link>` elements\n // immediately. We want to batch together `prefetchHref` calls that\n // occur in one synchronous pass and only render once after they've all\n // called this callback.\n startTransition(() => {\n setSeenHrefs((prevHrefs) => {\n if (prevHrefs.has(href)) return prevHrefs;\n return new Set(prevHrefs).add(href);\n });\n });\n }, []);\n\n const value = useMemo(() => ({ prefetchHref }), [prefetchHref]);\n\n if (!isSafariOrFirefox.current) {\n return <>{children}</>;\n }\n\n return (\n <PrefetchCrossZoneLinksContext.Provider value={value}>\n {children}\n {[...seenHrefs].map((href) => (\n <link as=\"fetch\" href={href} key={href} rel=\"preload\" />\n ))}\n </PrefetchCrossZoneLinksContext.Provider>\n );\n}\n"],"mappings":"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","names":["client_exports","__export","Link","PrefetchCrossZoneLinks","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","useZoneForHref","__toCommonJS","import_link","import_react","import_react","import_path_to_regexp","regexpCache","getRegexp","path","existing","regexp","MicrofrontendConfigClient","config","opts","app","match","newRouting","pathsWithoutFlags","group","other","pathname","name","application","childPath","defaultApplication","clientCache","cachedHasDynamicPaths","getClient","config","existing","client","MicrofrontendConfigClient","cachedServerClientConfigPromise","cachedServerClient","fetchClientConfigFromServer","response","responseJson","useClientConfig","clientConfig","setClientConfig","canLoad","hasDynamicPaths","isLoading","setIsLoading","newConfig","prevConfig","import_script","import_react","import_jsx_runtime","PREFETCH_ATTR","DATA_ATTR_SELECTORS","PREFETCH_ON_HOVER_PREDICATES","PREFETCH_WHEN_VISIBLE_PREDICATES","checkVisibility","element","el","style","PrefetchCrossZoneLinks","prerenderEagerness","isLoading","useClientConfig","links","setLinks","canPrerender","isPrerenderSafe","observer","entries","entry","link","mutations","mutation","speculationRules","Script","match","import_react","import_jsx_runtime","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","children","seenHrefs","setSeenHrefs","isSafariOrFirefox","prefetchHref","href","prevHrefs","value","import_jsx_runtime","CURRENT_ZONE_HASH","useZoneForHref","href","clientConfig","isLoading","useClientConfig","isRelativePath","zoneOfHref","isRelative","Link","children","props","ref","prefetchHref","PrefetchCrossZoneLinksContext","isDifferentZone","onHoverPrefetch","_","rest","NextLink"]}
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- import U from"next/link.js";import{forwardRef as W,useContext as X,useMemo as $}from"react";import{useEffect as k,useMemo as M,useState as m}from"react";import{pathToRegexp as S}from"path-to-regexp";var C=new Map,b=t=>{let e=C.get(t);if(e)return e;let r=S(t);return C.set(t,r),r},f=class{constructor(e,r){this.pathCache={};this.hasFlaggedPaths=e.hasFlaggedPaths??!1;for(let i of Object.values(e.applications))if(i.routing){i.routing.some(n=>n.flag)&&(this.hasFlaggedPaths=!0);let o=[],s=[];for(let n of i.routing)if(n.flag){if(r?.removeFlaggedPaths)continue;n.group&&delete n.group,o.push(n)}else s.push(...n.paths);s.length>0&&o.push({paths:s}),i.routing=o}this.serialized=e,this.hasFlaggedPaths&&(this.serialized.hasFlaggedPaths=this.hasFlaggedPaths),this.applications=e.applications}static fromEnv(e){if(!e)throw new Error("Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy");return new f(JSON.parse(e))}isEqual(e){return this===e||JSON.stringify(this.applications)===JSON.stringify(e.applications)}getApplicationNameForPath(e){if(!e.startsWith("/"))throw new Error("Path must start with a /");if(this.pathCache[e])return this.pathCache[e];let r=new URL(e,"https://example.com").pathname;for(let[o,s]of Object.entries(this.applications))if(s.routing){for(let n of s.routing)for(let a of n.paths)if(b(a).test(r))return this.pathCache[e]=o,o}let i=Object.entries(this.applications).find(([,o])=>o.default);return i?(this.pathCache[e]=i[0],i[0]):null}serialize(){return this.serialized}};var P=new Map,E=new Map,L=t=>{let e=P.get(t||"");if(e)return e;let r=f.fromEnv(t);return P.set(t||"",r),r},h=null,p=null;async function O(){try{let t=await fetch("/.well-known/vercel/microfrontends/client-config");if(t.status!==200)return null;let e=await t.json(),r=new f(e.config);return p=r,r}catch{return null}}function u(t){let[e,r]=m(()=>p??L(t)),i=M(()=>{if(process.env.NODE_ENV==="test"&&process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER!=="1"||p)return!1;let n=E.get(t||"");if(n!==void 0)return n;let l=L(t).hasFlaggedPaths;return E.set(t||"",l),!!l},[t]),[o,s]=m(i);return k(()=>{i&&(h||(h=O()),h.then(n=>{n&&r(a=>a.isEqual(n)?a:n)}).finally(()=>{s(!1)}))},[i]),{clientConfig:e,isLoading:o}}import F from"next/script.js";import{useEffect as y,useState as w}from"react";import{jsx as T}from"react/jsx-runtime";var g="data-prefetch",c={anyZone:"[data-zone]",external:'[data-zone="null"]',sameZone:'[data-zone="same"]',prefetch:`[${g}]`},x={and:[{href_matches:"/*"},{selector_matches:c.anyZone},{not:{selector_matches:c.sameZone}},{not:{selector_matches:c.external}}]},H={and:[{href_matches:"/*"},{selector_matches:c.anyZone},{not:{selector_matches:c.sameZone}},{not:{selector_matches:c.external}},{selector_matches:c.prefetch}]};function v(t){if(!t)return!0;if("checkVisibility"in t)return t.checkVisibility({opacityProperty:!0});let e=t,r=window.getComputedStyle(e);return r.display==="none"||r.visibility==="hidden"||r.opacity==="0"?!1:v(e.parentElement)}function ie({prerenderEagerness:t="conservative"}){let{isLoading:e}=u(process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG),[r,i]=w([]);return y(()=>{if(e)return;let s=new IntersectionObserver(n=>{n.forEach(a=>{a.isIntersecting&&!a.target.hasAttribute(g)&&v(a.target)&&a.target.setAttribute(g,"true")})},{root:null,rootMargin:"0px",threshold:.1});return r.forEach(n=>s.observe(n)),()=>{s.disconnect()}},[e,r]),y(()=>{if(e)return;let s=new MutationObserver(n=>{n.some(l=>l.type==="childList"&&l.addedNodes.length>0||l.type==="attributes"&&l.attributeName==="href")&&i(Array.from(document.querySelectorAll(`a${c.anyZone}:not(${c.prefetch}):not(${c.sameZone}):not(${c.external})`)))});return s.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["href"]}),()=>{s.disconnect()}},[e]),e?null:T(F,{dangerouslySetInnerHTML:{__html:JSON.stringify({prefetch:[{eagerness:"moderate",where:x},{eagerness:"immediate",where:H}],prerender:[{eagerness:t,where:x}]})},id:"prefetch-zones-links",type:"speculationrules"})}import{createContext as A,startTransition as Z,useCallback as I,useMemo as z,useRef as D,useState as J}from"react";import{Fragment as B,jsx as _,jsxs as V}from"react/jsx-runtime";var d=A({prefetchHref:()=>{}});function le({children:t}){let[e,r]=J(new Set),i=D(typeof navigator<"u"&&(navigator.userAgent.includes("Firefox")||navigator.userAgent.includes("Safari")&&!navigator.userAgent.includes("Chrome"))),o=I(n=>{Z(()=>{r(a=>a.has(n)?a:new Set(a).add(n))})},[]),s=z(()=>({prefetchHref:o}),[o]);return i.current?V(d.Provider,{value:s,children:[t,[...e].map(n=>_("link",{as:"fetch",href:n,rel:"preload"},n))]}):_(B,{children:t})}import{jsx as R}from"react/jsx-runtime";var q=process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH;function G(t){let{clientConfig:e,isLoading:r}=u(process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG),{isRelativePath:i,zoneOfHref:o}=$(()=>{let n=typeof t=="string"&&t.startsWith("/");return{isRelativePath:n,zoneOfHref:n?e.getApplicationNameForPath(t):null}},[e,t]);return typeof t=="string"&&!t.length?{zoneOfHref:null,isDifferentZone:!1,isLoading:!1}:{zoneOfHref:o,isDifferentZone:!i||(o?q!==o:!1),isLoading:r}}var j=W(({children:t,...e},r)=>{let{prefetchHref:i}=X(d),{zoneOfHref:o,isDifferentZone:s,isLoading:n}=G(e.href);function a(){e.href&&i(e.href)}if(s&&o!==null){let{prefetch:l,...N}=e;return R("a",{...N,"data-zone":o,onFocus:e.prefetch!==!1?a:void 0,onMouseOver:e.prefetch!==!1?a:void 0,children:t})}return R(U,{...e,"data-zone":o?"same":"null",prefetch:e.prefetch??(n?!1:void 0),ref:r,children:t})});j.displayName="MicrofrontendsLink";export{j as Link,ie as PrefetchCrossZoneLinks,d as PrefetchCrossZoneLinksContext,le as PrefetchCrossZoneLinksProvider,G as useZoneForHref};
2
+ import X from"next/link.js";import{forwardRef as $,useContext as q,useMemo as G}from"react";import{useEffect as k,useMemo as M,useState as P}from"react";import{pathToRegexp as R}from"path-to-regexp";var m=new Map,S=t=>{let e=m.get(t);if(e)return e;let r=R(t);return m.set(t,r),r},f=class{constructor(e,r){this.pathCache={};this.hasFlaggedPaths=e.hasFlaggedPaths??!1;for(let i of Object.values(e.applications))if(i.routing){i.routing.some(n=>n.flag)&&(this.hasFlaggedPaths=!0);let o=[],a=[];for(let n of i.routing)if(n.flag){if(r?.removeFlaggedPaths)continue;n.group&&delete n.group,o.push(n)}else a.push(...n.paths);a.length>0&&o.push({paths:a}),i.routing=o}this.serialized=e,this.hasFlaggedPaths&&(this.serialized.hasFlaggedPaths=this.hasFlaggedPaths),this.applications=e.applications}static fromEnv(e){if(!e)throw new Error("Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy");return new f(JSON.parse(e))}isEqual(e){return this===e||JSON.stringify(this.applications)===JSON.stringify(e.applications)}getApplicationNameForPath(e){if(!e.startsWith("/"))throw new Error("Path must start with a /");if(this.pathCache[e])return this.pathCache[e];let r=new URL(e,"https://example.com").pathname;for(let[o,a]of Object.entries(this.applications))if(a.routing){for(let n of a.routing)for(let s of n.paths)if(S(s).test(r))return this.pathCache[e]=o,o}let i=Object.entries(this.applications).find(([,o])=>o.default);return i?(this.pathCache[e]=i[0],i[0]):null}serialize(){return this.serialized}};var E=new Map,y=new Map,L=t=>{let e=E.get(t||"");if(e)return e;let r=f.fromEnv(t);return E.set(t||"",r),r},p=null,d=null;async function O(){try{let t=await fetch("/.well-known/vercel/microfrontends/client-config");if(t.status!==200)return null;let e=await t.json(),r=new f(e.config);return d=r,r}catch{return null}}function h(t){let[e,r]=P(()=>d??L(t)),i=M(()=>{if(process.env.NODE_ENV==="test"&&process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER!=="1"||d)return!1;let n=y.get(t||"");if(n!==void 0)return n;let l=L(t).hasFlaggedPaths;return y.set(t||"",l),!!l},[t]),[o,a]=P(i);return k(()=>{i&&(p||(p=O()),p.then(n=>{n&&r(s=>s.isEqual(n)?s:n)}).finally(()=>{a(!1)}))},[i]),{clientConfig:e,isLoading:o}}import F from"next/script.js";import{useEffect as v,useRef as w,useState as H}from"react";import{jsx as Z}from"react/jsx-runtime";var g="data-prefetch",c={anyZone:"[data-zone]",external:'[data-zone="null"]',sameZone:'[data-zone="same"]',prefetch:`[${g}]`},x={and:[{href_matches:"/*"},{selector_matches:c.anyZone},{not:{selector_matches:c.sameZone}},{not:{selector_matches:c.external}}]},T={and:[{href_matches:"/*"},{selector_matches:c.anyZone},{not:{selector_matches:c.sameZone}},{not:{selector_matches:c.external}},{selector_matches:c.prefetch}]};function _(t){if(!t)return!0;if("checkVisibility"in t)return t.checkVisibility({opacityProperty:!0});let e=t,r=window.getComputedStyle(e);return r.display==="none"||r.visibility==="hidden"||r.opacity==="0"?!1:_(e.parentElement)}function ae({prerenderEagerness:t="conservative"}){let{isLoading:e}=h(process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG),[r,i]=H([]),o=w(A());if(v(()=>{if(e)return;let n=new IntersectionObserver(s=>{s.forEach(l=>{l.isIntersecting&&!l.target.hasAttribute(g)&&_(l.target)&&l.target.setAttribute(g,"true")})},{root:null,rootMargin:"0px",threshold:.1});return r.forEach(s=>n.observe(s)),()=>{n.disconnect()}},[e,r]),v(()=>{if(e)return;let n=new MutationObserver(s=>{s.some(u=>u.type==="childList"&&u.addedNodes.length>0||u.type==="attributes"&&u.attributeName==="href")&&i(Array.from(document.querySelectorAll(`a${c.anyZone}:not(${c.prefetch}):not(${c.sameZone}):not(${c.external})`)))});return n.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["href"]}),()=>{n.disconnect()}},[e]),e)return null;let a={prefetch:[{eagerness:"moderate",where:x},{eagerness:"immediate",where:T}],...o.current?{prerender:[{eagerness:t,where:x}]}:{}};return Z(F,{dangerouslySetInnerHTML:{__html:JSON.stringify(a)},id:"prefetch-zones-links",type:"speculationrules"})}function A(){if(typeof navigator>"u")return!0;let t=navigator.userAgent.match(/Chrome\/(\d+)/);return t?Number(t[1])!==147:!0}import{createContext as I,startTransition as z,useCallback as D,useMemo as J,useRef as B,useState as V}from"react";import{Fragment as U,jsx as b,jsxs as W}from"react/jsx-runtime";var C=I({prefetchHref:()=>{}});function ue({children:t}){let[e,r]=V(new Set),i=B(typeof navigator<"u"&&(navigator.userAgent.includes("Firefox")||navigator.userAgent.includes("Safari")&&!navigator.userAgent.includes("Chrome"))),o=D(n=>{z(()=>{r(s=>s.has(n)?s:new Set(s).add(n))})},[]),a=J(()=>({prefetchHref:o}),[o]);return i.current?W(C.Provider,{value:a,children:[t,[...e].map(n=>b("link",{as:"fetch",href:n,rel:"preload"},n))]}):b(U,{children:t})}import{jsx as N}from"react/jsx-runtime";var j=process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH;function K(t){let{clientConfig:e,isLoading:r}=h(process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG),{isRelativePath:i,zoneOfHref:o}=G(()=>{let n=typeof t=="string"&&t.startsWith("/");return{isRelativePath:n,zoneOfHref:n?e.getApplicationNameForPath(t):null}},[e,t]);return typeof t=="string"&&!t.length?{zoneOfHref:null,isDifferentZone:!1,isLoading:!1}:{zoneOfHref:o,isDifferentZone:!i||(o?j!==o:!1),isLoading:r}}var Q=$(({children:t,...e},r)=>{let{prefetchHref:i}=q(C),{zoneOfHref:o,isDifferentZone:a,isLoading:n}=K(e.href);function s(){e.href&&i(e.href)}if(a&&o!==null){let{prefetch:l,...u}=e;return N("a",{...u,"data-zone":o,onFocus:e.prefetch!==!1?s:void 0,onMouseOver:e.prefetch!==!1?s:void 0,children:t})}return N(X,{...e,"data-zone":o?"same":"null",prefetch:e.prefetch??(n?!1:void 0),ref:r,children:t})});Q.displayName="MicrofrontendsLink";export{Q as Link,ae as PrefetchCrossZoneLinks,C as PrefetchCrossZoneLinksContext,ue as PrefetchCrossZoneLinksProvider,K as useZoneForHref};
3
3
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/client/link/microfrontends-link.tsx","../../src/config/react/use-client-config.ts","../../src/config/microfrontends-config/client/index.ts","../../src/next/client/prefetch/prefetch-cross-zone-links.tsx","../../src/next/client/prefetch/prefetch-cross-zone-links-context.tsx"],"sourcesContent":["import NextLink, {\n type LinkProps as ExternalNextLinkProps,\n} from 'next/link.js';\nimport type { AnchorHTMLAttributes } from 'react';\nimport { forwardRef, useContext, useMemo } from 'react';\nimport { useClientConfig } from '../../../config/react/use-client-config';\nimport { PrefetchCrossZoneLinksContext } from '../prefetch';\n\ninterface BaseProps {\n children: React.ReactNode;\n href: string;\n}\n\n// fix for tsc inlining LinkProps from next\n// https://github.com/microsoft/TypeScript/issues/37151#issuecomment-756232934\ninterface NextLinkProps extends ExternalNextLinkProps {}\nexport type LinkProps = BaseProps &\n Omit<NextLinkProps, keyof BaseProps> &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>;\n\nconst CURRENT_ZONE_HASH = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH;\n\nexport function useZoneForHref(href: LinkProps['href'] | undefined): {\n zoneOfHref: string | null;\n isDifferentZone: boolean;\n isLoading: boolean;\n} {\n const { clientConfig, isLoading } = useClientConfig(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n );\n const { isRelativePath, zoneOfHref } = useMemo(() => {\n const isRelative = typeof href === 'string' && href.startsWith('/');\n return {\n isRelativePath: isRelative,\n zoneOfHref: isRelative\n ? clientConfig.getApplicationNameForPath(href)\n : null,\n };\n }, [clientConfig, href]);\n\n if (typeof href === 'string' && !href.length) {\n return {\n zoneOfHref: null,\n isDifferentZone: false,\n isLoading: false,\n };\n }\n const isDifferentZone =\n !isRelativePath || (zoneOfHref ? CURRENT_ZONE_HASH !== zoneOfHref : false);\n return { zoneOfHref, isDifferentZone, isLoading };\n}\n\n/**\n * A Link component that works with microfrontend set-ups and will prefetch the\n * cross zone links automatically.\n */\nexport const Link = forwardRef<HTMLAnchorElement, LinkProps>(\n ({ children, ...props }, ref): JSX.Element => {\n const { prefetchHref } = useContext(PrefetchCrossZoneLinksContext);\n const { zoneOfHref, isDifferentZone, isLoading } = useZoneForHref(\n props.href,\n );\n\n function onHoverPrefetch(): void {\n if (!props.href) {\n return;\n }\n prefetchHref(props.href);\n }\n\n if (isDifferentZone && zoneOfHref !== null) {\n const { prefetch: _, ...rest } = props;\n return (\n <a\n {...rest}\n data-zone={zoneOfHref}\n onFocus={props.prefetch !== false ? onHoverPrefetch : undefined}\n onMouseOver={props.prefetch !== false ? onHoverPrefetch : undefined}\n >\n {children}\n </a>\n );\n }\n\n return (\n <NextLink\n {...props}\n data-zone={!zoneOfHref ? 'null' : 'same'}\n prefetch={props.prefetch ?? (isLoading ? false : undefined)}\n ref={ref}\n >\n {children}\n </NextLink>\n );\n },\n);\nLink.displayName = 'MicrofrontendsLink';\n","'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\nimport { MicrofrontendConfigClient } from '../microfrontends-config/client';\nimport type { WellKnownClientData } from '../well-known/types';\n\nconst clientCache = new Map<string, MicrofrontendConfigClient>();\nconst cachedHasDynamicPaths = new Map<string, boolean>();\n\nconst getClient = (config: string | undefined) => {\n const existing = clientCache.get(config || '');\n if (existing) {\n return existing;\n }\n\n const client = MicrofrontendConfigClient.fromEnv(config);\n clientCache.set(config || '', client);\n return client;\n};\n\nlet cachedServerClientConfigPromise: Promise<MicrofrontendConfigClient | null> | null =\n null;\n\nlet cachedServerClient: MicrofrontendConfigClient | null = null;\n\nasync function fetchClientConfigFromServer(): Promise<MicrofrontendConfigClient | null> {\n try {\n const response = await fetch(\n '/.well-known/vercel/microfrontends/client-config',\n );\n if (response.status !== 200) {\n return null;\n }\n const responseJson = (await response.json()) as WellKnownClientData;\n const client = new MicrofrontendConfigClient(responseJson.config);\n cachedServerClient = client;\n return client;\n } catch {\n return null;\n }\n}\n\n/**\n * Hook to use the client microfrontends configuration. This hook will resolve\n * dynamic paths by fetching the configuration from the server if necessary,\n * allowing the server to specify the values for dynamic paths.\n */\nexport function useClientConfig(config: string | undefined): {\n clientConfig: MicrofrontendConfigClient;\n isLoading: boolean;\n} {\n const [clientConfig, setClientConfig] = useState<MicrofrontendConfigClient>(\n () => cachedServerClient ?? getClient(config),\n );\n const canLoad = useMemo(() => {\n if (\n process.env.NODE_ENV === 'test' &&\n process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER !== '1'\n ) {\n return false;\n }\n // If we've already fetched the server config and it's resolved, we don't need\n // to enter the loading state at all\n if (cachedServerClient) return false;\n // If we've already checked this config for dynamic paths, we can use the\n // cached result from before instead of reevaluating.\n const existing = cachedHasDynamicPaths.get(config || '');\n if (existing !== undefined) return existing;\n // Get the original client config to determine if the config has any\n // dynamic paths.\n const originalClientConfig = getClient(config);\n // As an optimization, only fetch the config from the server if the\n // microfrontends configuration has any dynamic paths. If it doesn't,\n // then the server won't return any different values.\n const hasDynamicPaths = originalClientConfig.hasFlaggedPaths;\n cachedHasDynamicPaths.set(config || '', hasDynamicPaths);\n if (!hasDynamicPaths) {\n return false;\n }\n return true;\n }, [config]);\n const [isLoading, setIsLoading] = useState(canLoad);\n useEffect(() => {\n if (!canLoad) return;\n if (!cachedServerClientConfigPromise) {\n cachedServerClientConfigPromise = fetchClientConfigFromServer();\n }\n void cachedServerClientConfigPromise\n .then((newConfig) => {\n if (newConfig) {\n setClientConfig((prevConfig) => {\n return prevConfig.isEqual(newConfig) ? prevConfig : newConfig;\n });\n }\n })\n .finally(() => {\n setIsLoading(false);\n });\n }, [canLoad]);\n\n return { clientConfig, isLoading };\n}\n\nexport function resetCachedServerClientConfigPromise(): void {\n cachedServerClientConfigPromise = null;\n}\n","import { pathToRegexp } from 'path-to-regexp';\nimport type { ClientConfig } from './types';\n\nexport interface MicrofrontendConfigClientOptions {\n removeFlaggedPaths?: boolean;\n}\n\nconst regexpCache = new Map<string, RegExp>();\nconst getRegexp = (path: string): RegExp => {\n const existing = regexpCache.get(path);\n if (existing) {\n return existing;\n }\n\n const regexp = pathToRegexp(path);\n regexpCache.set(path, regexp);\n return regexp;\n};\n\nexport class MicrofrontendConfigClient {\n applications: ClientConfig['applications'];\n hasFlaggedPaths: boolean;\n pathCache: Record<string, string> = {};\n private readonly serialized: ClientConfig;\n\n constructor(config: ClientConfig, opts?: MicrofrontendConfigClientOptions) {\n this.hasFlaggedPaths = config.hasFlaggedPaths ?? false;\n for (const app of Object.values(config.applications)) {\n if (app.routing) {\n if (app.routing.some((match) => match.flag)) {\n this.hasFlaggedPaths = true;\n }\n const newRouting = [];\n const pathsWithoutFlags = [];\n for (const group of app.routing) {\n if (group.flag) {\n if (opts?.removeFlaggedPaths) {\n continue;\n }\n if (group.group) {\n delete group.group;\n }\n newRouting.push(group);\n } else {\n pathsWithoutFlags.push(...group.paths);\n }\n }\n if (pathsWithoutFlags.length > 0) {\n newRouting.push({ paths: pathsWithoutFlags });\n }\n app.routing = newRouting;\n }\n }\n this.serialized = config;\n if (this.hasFlaggedPaths) {\n this.serialized.hasFlaggedPaths = this.hasFlaggedPaths;\n }\n this.applications = config.applications;\n }\n\n /**\n * Create a new `MicrofrontendConfigClient` from a JSON string.\n * Config must be passed in to remain framework agnostic\n */\n static fromEnv(config: string | undefined): MicrofrontendConfigClient {\n if (!config) {\n throw new Error(\n 'Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy',\n );\n }\n return new MicrofrontendConfigClient(JSON.parse(config) as ClientConfig);\n }\n\n isEqual(other: MicrofrontendConfigClient): boolean {\n return (\n this === other ||\n JSON.stringify(this.applications) === JSON.stringify(other.applications)\n );\n }\n\n getApplicationNameForPath(path: string): string | null {\n if (!path.startsWith('/')) {\n throw new Error(`Path must start with a /`);\n }\n\n if (this.pathCache[path]) {\n return this.pathCache[path];\n }\n\n const pathname = new URL(path, 'https://example.com').pathname;\n for (const [name, application] of Object.entries(this.applications)) {\n if (application.routing) {\n for (const group of application.routing) {\n for (const childPath of group.paths) {\n const regexp = getRegexp(childPath);\n if (regexp.test(pathname)) {\n this.pathCache[path] = name;\n return name;\n }\n }\n }\n }\n }\n const defaultApplication = Object.entries(this.applications).find(\n ([, application]) => application.default,\n );\n if (!defaultApplication) {\n return null;\n }\n\n this.pathCache[path] = defaultApplication[0];\n return defaultApplication[0];\n }\n\n serialize(): ClientConfig {\n return this.serialized;\n }\n}\n","import Script from 'next/script.js';\nimport { useEffect, useState } from 'react';\nimport { useClientConfig } from '../../../config/react/use-client-config';\n\nconst PREFETCH_ATTR = 'data-prefetch';\nconst DATA_ATTR_SELECTORS = {\n anyZone: '[data-zone]',\n external: '[data-zone=\"null\"]',\n sameZone: '[data-zone=\"same\"]',\n prefetch: `[${PREFETCH_ATTR}]`,\n} as const;\n\nconst PREFETCH_ON_HOVER_PREDICATES = {\n and: [\n { href_matches: '/*' },\n { selector_matches: DATA_ATTR_SELECTORS.anyZone },\n { not: { selector_matches: DATA_ATTR_SELECTORS.sameZone } },\n { not: { selector_matches: DATA_ATTR_SELECTORS.external } },\n ],\n};\n\nconst PREFETCH_WHEN_VISIBLE_PREDICATES = {\n and: [\n { href_matches: '/*' },\n { selector_matches: DATA_ATTR_SELECTORS.anyZone },\n { not: { selector_matches: DATA_ATTR_SELECTORS.sameZone } },\n { not: { selector_matches: DATA_ATTR_SELECTORS.external } },\n { selector_matches: DATA_ATTR_SELECTORS.prefetch },\n ],\n};\n\nfunction checkVisibility(element: Element | null): boolean {\n if (!element) return true;\n\n if ('checkVisibility' in element) {\n return element.checkVisibility({ opacityProperty: true });\n }\n\n // hack to get around TS thinking element is never;\n const el = element as Element;\n const style = window.getComputedStyle(el);\n\n if (\n style.display === 'none' ||\n style.visibility === 'hidden' ||\n style.opacity === '0'\n ) {\n return false;\n }\n\n return checkVisibility(el.parentElement);\n}\n\ninterface PrefetchCrossZoneLinksProps {\n /**\n * This attributes controls how eager the browser should be in prerendering\n * cross-zone links. Prerendering downloads the HTML and subresources of the page\n * and starts to render the page in the background. This consumes more resources\n * but provides a faster user experience if the user decides to visit that page.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/speculationrules#eagerness\n * for more information.\n *\n * Default value is 'conservative'.\n */\n prerenderEagerness?: 'immediate' | 'eager' | 'moderate' | 'conservative';\n}\n\nexport function PrefetchCrossZoneLinks({\n prerenderEagerness = 'conservative',\n}: PrefetchCrossZoneLinksProps): JSX.Element | null {\n const { isLoading } = useClientConfig(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n );\n const [links, setLinks] = useState<HTMLAnchorElement[]>([]);\n\n useEffect(() => {\n if (isLoading) {\n return;\n }\n\n /**\n * Intersection observer to add the data-prefetch attribute to cross-zone\n * links that have yet to be prefetched and are visible.\n */\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (\n entry.isIntersecting &&\n !entry.target.hasAttribute(PREFETCH_ATTR) &&\n // lazy perform the visibility check for nodes that are intersecting the viewport\n // and have not been prefetched.\n checkVisibility(entry.target)\n ) {\n entry.target.setAttribute(PREFETCH_ATTR, 'true');\n }\n });\n },\n {\n root: null,\n rootMargin: '0px',\n threshold: 0.1,\n },\n );\n\n links.forEach((link) => observer.observe(link));\n\n return () => {\n observer.disconnect();\n };\n }, [isLoading, links]);\n\n useEffect(() => {\n if (isLoading) {\n return;\n }\n\n /**\n * Mutation observer to notify when new nodes have entered/exited the document\n * or an href has changed.\n */\n const observer = new MutationObserver((mutations) => {\n const hasChanged = mutations.some((mutation) => {\n return (\n (mutation.type === 'childList' && mutation.addedNodes.length > 0) ||\n (mutation.type === 'attributes' && mutation.attributeName === 'href')\n );\n });\n\n if (hasChanged) {\n // Whenever there's a change, add all cross-zone links that haven't been\n // prefetched.\n setLinks(\n Array.from(\n document.querySelectorAll<HTMLAnchorElement>(\n `a${DATA_ATTR_SELECTORS.anyZone}:not(${DATA_ATTR_SELECTORS.prefetch}):not(${DATA_ATTR_SELECTORS.sameZone}):not(${DATA_ATTR_SELECTORS.external})`,\n ),\n ),\n );\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['href'],\n });\n\n return () => {\n observer.disconnect();\n };\n }, [isLoading]);\n\n // Wait till the zone-config loads to take into consideration any\n // flagged routes.\n if (isLoading) {\n return null;\n }\n\n // Prefetch links with moderate eagerness by default, immediately when marked \"data-prefetch\".\n // Prerender links with conservative eagerness by default, immediately when marked \"data-prefetch\".\n const speculationRules = {\n prefetch: [\n {\n eagerness: 'moderate',\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n {\n eagerness: 'immediate',\n where: PREFETCH_WHEN_VISIBLE_PREDICATES,\n },\n ],\n prerender: [\n {\n eagerness: prerenderEagerness,\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n ],\n };\n\n return (\n <Script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Safe - injecting JSON speculation rules, not HTML\n dangerouslySetInnerHTML={{\n __html: JSON.stringify(speculationRules),\n }}\n id=\"prefetch-zones-links\"\n type=\"speculationrules\"\n />\n );\n}\n","import type React from 'react';\nimport {\n createContext,\n startTransition,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nexport interface PrefetchCrossZoneLinksContext {\n prefetchHref: (href: string) => void;\n}\n\nexport const PrefetchCrossZoneLinksContext =\n createContext<PrefetchCrossZoneLinksContext>({\n prefetchHref: () => {},\n });\n\nexport function PrefetchCrossZoneLinksProvider({\n children,\n}: {\n children: React.ReactNode;\n}): JSX.Element | null {\n const [seenHrefs, setSeenHrefs] = useState(new Set<string>());\n const isSafariOrFirefox = useRef(\n typeof navigator !== 'undefined' &&\n (navigator.userAgent.includes('Firefox') ||\n (navigator.userAgent.includes('Safari') &&\n !navigator.userAgent.includes('Chrome'))),\n );\n\n // This useCallback must not have any dependencies because if it changes\n // its value, every component that uses this context will rerender.\n const prefetchHref = useCallback((href: string): void => {\n // It's not critical that we render the new preload `<link>` elements\n // immediately. We want to batch together `prefetchHref` calls that\n // occur in one synchronous pass and only render once after they've all\n // called this callback.\n startTransition(() => {\n setSeenHrefs((prevHrefs) => {\n if (prevHrefs.has(href)) return prevHrefs;\n return new Set(prevHrefs).add(href);\n });\n });\n }, []);\n\n const value = useMemo(() => ({ prefetchHref }), [prefetchHref]);\n\n if (!isSafariOrFirefox.current) {\n return <>{children}</>;\n }\n\n return (\n <PrefetchCrossZoneLinksContext.Provider value={value}>\n {children}\n {[...seenHrefs].map((href) => (\n <link as=\"fetch\" href={href} key={href} rel=\"preload\" />\n ))}\n </PrefetchCrossZoneLinksContext.Provider>\n );\n}\n"],"mappings":"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","names":["NextLink","forwardRef","useContext","useMemo","useEffect","useMemo","useState","pathToRegexp","regexpCache","getRegexp","path","existing","regexp","MicrofrontendConfigClient","config","opts","app","match","newRouting","pathsWithoutFlags","group","other","pathname","name","application","childPath","defaultApplication","clientCache","cachedHasDynamicPaths","getClient","config","existing","client","MicrofrontendConfigClient","cachedServerClientConfigPromise","cachedServerClient","fetchClientConfigFromServer","response","responseJson","useClientConfig","clientConfig","setClientConfig","useState","canLoad","useMemo","hasDynamicPaths","isLoading","setIsLoading","useEffect","newConfig","prevConfig","Script","useEffect","useState","jsx","PREFETCH_ATTR","DATA_ATTR_SELECTORS","PREFETCH_ON_HOVER_PREDICATES","PREFETCH_WHEN_VISIBLE_PREDICATES","checkVisibility","element","el","style","PrefetchCrossZoneLinks","prerenderEagerness","isLoading","useClientConfig","links","setLinks","useState","useEffect","observer","entries","entry","link","mutations","mutation","Script","createContext","startTransition","useCallback","useMemo","useRef","useState","Fragment","jsx","jsxs","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","children","seenHrefs","setSeenHrefs","isSafariOrFirefox","prefetchHref","href","prevHrefs","value","jsx","CURRENT_ZONE_HASH","useZoneForHref","href","clientConfig","isLoading","useClientConfig","isRelativePath","zoneOfHref","useMemo","isRelative","Link","forwardRef","children","props","ref","prefetchHref","useContext","PrefetchCrossZoneLinksContext","isDifferentZone","onHoverPrefetch","_","rest","NextLink"]}
1
+ {"version":3,"sources":["../../src/next/client/link/microfrontends-link.tsx","../../src/config/react/use-client-config.ts","../../src/config/microfrontends-config/client/index.ts","../../src/next/client/prefetch/prefetch-cross-zone-links.tsx","../../src/next/client/prefetch/prefetch-cross-zone-links-context.tsx"],"sourcesContent":["import NextLink, {\n type LinkProps as ExternalNextLinkProps,\n} from 'next/link.js';\nimport type { AnchorHTMLAttributes } from 'react';\nimport { forwardRef, useContext, useMemo } from 'react';\nimport { useClientConfig } from '../../../config/react/use-client-config';\nimport { PrefetchCrossZoneLinksContext } from '../prefetch';\n\ninterface BaseProps {\n children: React.ReactNode;\n href: string;\n}\n\n// fix for tsc inlining LinkProps from next\n// https://github.com/microsoft/TypeScript/issues/37151#issuecomment-756232934\ninterface NextLinkProps extends ExternalNextLinkProps {}\nexport type LinkProps = BaseProps &\n Omit<NextLinkProps, keyof BaseProps> &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>;\n\nconst CURRENT_ZONE_HASH = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION_HASH;\n\nexport function useZoneForHref(href: LinkProps['href'] | undefined): {\n zoneOfHref: string | null;\n isDifferentZone: boolean;\n isLoading: boolean;\n} {\n const { clientConfig, isLoading } = useClientConfig(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n );\n const { isRelativePath, zoneOfHref } = useMemo(() => {\n const isRelative = typeof href === 'string' && href.startsWith('/');\n return {\n isRelativePath: isRelative,\n zoneOfHref: isRelative\n ? clientConfig.getApplicationNameForPath(href)\n : null,\n };\n }, [clientConfig, href]);\n\n if (typeof href === 'string' && !href.length) {\n return {\n zoneOfHref: null,\n isDifferentZone: false,\n isLoading: false,\n };\n }\n const isDifferentZone =\n !isRelativePath || (zoneOfHref ? CURRENT_ZONE_HASH !== zoneOfHref : false);\n return { zoneOfHref, isDifferentZone, isLoading };\n}\n\n/**\n * A Link component that works with microfrontend set-ups and will prefetch the\n * cross zone links automatically.\n */\nexport const Link = forwardRef<HTMLAnchorElement, LinkProps>(\n ({ children, ...props }, ref): JSX.Element => {\n const { prefetchHref } = useContext(PrefetchCrossZoneLinksContext);\n const { zoneOfHref, isDifferentZone, isLoading } = useZoneForHref(\n props.href,\n );\n\n function onHoverPrefetch(): void {\n if (!props.href) {\n return;\n }\n prefetchHref(props.href);\n }\n\n if (isDifferentZone && zoneOfHref !== null) {\n const { prefetch: _, ...rest } = props;\n return (\n <a\n {...rest}\n data-zone={zoneOfHref}\n onFocus={props.prefetch !== false ? onHoverPrefetch : undefined}\n onMouseOver={props.prefetch !== false ? onHoverPrefetch : undefined}\n >\n {children}\n </a>\n );\n }\n\n return (\n <NextLink\n {...props}\n data-zone={!zoneOfHref ? 'null' : 'same'}\n prefetch={props.prefetch ?? (isLoading ? false : undefined)}\n ref={ref}\n >\n {children}\n </NextLink>\n );\n },\n);\nLink.displayName = 'MicrofrontendsLink';\n","'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\nimport { MicrofrontendConfigClient } from '../microfrontends-config/client';\nimport type { WellKnownClientData } from '../well-known/types';\n\nconst clientCache = new Map<string, MicrofrontendConfigClient>();\nconst cachedHasDynamicPaths = new Map<string, boolean>();\n\nconst getClient = (config: string | undefined) => {\n const existing = clientCache.get(config || '');\n if (existing) {\n return existing;\n }\n\n const client = MicrofrontendConfigClient.fromEnv(config);\n clientCache.set(config || '', client);\n return client;\n};\n\nlet cachedServerClientConfigPromise: Promise<MicrofrontendConfigClient | null> | null =\n null;\n\nlet cachedServerClient: MicrofrontendConfigClient | null = null;\n\nasync function fetchClientConfigFromServer(): Promise<MicrofrontendConfigClient | null> {\n try {\n const response = await fetch(\n '/.well-known/vercel/microfrontends/client-config',\n );\n if (response.status !== 200) {\n return null;\n }\n const responseJson = (await response.json()) as WellKnownClientData;\n const client = new MicrofrontendConfigClient(responseJson.config);\n cachedServerClient = client;\n return client;\n } catch {\n return null;\n }\n}\n\n/**\n * Hook to use the client microfrontends configuration. This hook will resolve\n * dynamic paths by fetching the configuration from the server if necessary,\n * allowing the server to specify the values for dynamic paths.\n */\nexport function useClientConfig(config: string | undefined): {\n clientConfig: MicrofrontendConfigClient;\n isLoading: boolean;\n} {\n const [clientConfig, setClientConfig] = useState<MicrofrontendConfigClient>(\n () => cachedServerClient ?? getClient(config),\n );\n const canLoad = useMemo(() => {\n if (\n process.env.NODE_ENV === 'test' &&\n process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER !== '1'\n ) {\n return false;\n }\n // If we've already fetched the server config and it's resolved, we don't need\n // to enter the loading state at all\n if (cachedServerClient) return false;\n // If we've already checked this config for dynamic paths, we can use the\n // cached result from before instead of reevaluating.\n const existing = cachedHasDynamicPaths.get(config || '');\n if (existing !== undefined) return existing;\n // Get the original client config to determine if the config has any\n // dynamic paths.\n const originalClientConfig = getClient(config);\n // As an optimization, only fetch the config from the server if the\n // microfrontends configuration has any dynamic paths. If it doesn't,\n // then the server won't return any different values.\n const hasDynamicPaths = originalClientConfig.hasFlaggedPaths;\n cachedHasDynamicPaths.set(config || '', hasDynamicPaths);\n if (!hasDynamicPaths) {\n return false;\n }\n return true;\n }, [config]);\n const [isLoading, setIsLoading] = useState(canLoad);\n useEffect(() => {\n if (!canLoad) return;\n if (!cachedServerClientConfigPromise) {\n cachedServerClientConfigPromise = fetchClientConfigFromServer();\n }\n void cachedServerClientConfigPromise\n .then((newConfig) => {\n if (newConfig) {\n setClientConfig((prevConfig) => {\n return prevConfig.isEqual(newConfig) ? prevConfig : newConfig;\n });\n }\n })\n .finally(() => {\n setIsLoading(false);\n });\n }, [canLoad]);\n\n return { clientConfig, isLoading };\n}\n\nexport function resetCachedServerClientConfigPromise(): void {\n cachedServerClientConfigPromise = null;\n}\n","import { pathToRegexp } from 'path-to-regexp';\nimport type { ClientConfig } from './types';\n\nexport interface MicrofrontendConfigClientOptions {\n removeFlaggedPaths?: boolean;\n}\n\nconst regexpCache = new Map<string, RegExp>();\nconst getRegexp = (path: string): RegExp => {\n const existing = regexpCache.get(path);\n if (existing) {\n return existing;\n }\n\n const regexp = pathToRegexp(path);\n regexpCache.set(path, regexp);\n return regexp;\n};\n\nexport class MicrofrontendConfigClient {\n applications: ClientConfig['applications'];\n hasFlaggedPaths: boolean;\n pathCache: Record<string, string> = {};\n private readonly serialized: ClientConfig;\n\n constructor(config: ClientConfig, opts?: MicrofrontendConfigClientOptions) {\n this.hasFlaggedPaths = config.hasFlaggedPaths ?? false;\n for (const app of Object.values(config.applications)) {\n if (app.routing) {\n if (app.routing.some((match) => match.flag)) {\n this.hasFlaggedPaths = true;\n }\n const newRouting = [];\n const pathsWithoutFlags = [];\n for (const group of app.routing) {\n if (group.flag) {\n if (opts?.removeFlaggedPaths) {\n continue;\n }\n if (group.group) {\n delete group.group;\n }\n newRouting.push(group);\n } else {\n pathsWithoutFlags.push(...group.paths);\n }\n }\n if (pathsWithoutFlags.length > 0) {\n newRouting.push({ paths: pathsWithoutFlags });\n }\n app.routing = newRouting;\n }\n }\n this.serialized = config;\n if (this.hasFlaggedPaths) {\n this.serialized.hasFlaggedPaths = this.hasFlaggedPaths;\n }\n this.applications = config.applications;\n }\n\n /**\n * Create a new `MicrofrontendConfigClient` from a JSON string.\n * Config must be passed in to remain framework agnostic\n */\n static fromEnv(config: string | undefined): MicrofrontendConfigClient {\n if (!config) {\n throw new Error(\n 'Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`? Is the local proxy running and this application is being accessed via the proxy port? See https://vercel.com/docs/microfrontends/local-development#setting-up-microfrontends-proxy',\n );\n }\n return new MicrofrontendConfigClient(JSON.parse(config) as ClientConfig);\n }\n\n isEqual(other: MicrofrontendConfigClient): boolean {\n return (\n this === other ||\n JSON.stringify(this.applications) === JSON.stringify(other.applications)\n );\n }\n\n getApplicationNameForPath(path: string): string | null {\n if (!path.startsWith('/')) {\n throw new Error(`Path must start with a /`);\n }\n\n if (this.pathCache[path]) {\n return this.pathCache[path];\n }\n\n const pathname = new URL(path, 'https://example.com').pathname;\n for (const [name, application] of Object.entries(this.applications)) {\n if (application.routing) {\n for (const group of application.routing) {\n for (const childPath of group.paths) {\n const regexp = getRegexp(childPath);\n if (regexp.test(pathname)) {\n this.pathCache[path] = name;\n return name;\n }\n }\n }\n }\n }\n const defaultApplication = Object.entries(this.applications).find(\n ([, application]) => application.default,\n );\n if (!defaultApplication) {\n return null;\n }\n\n this.pathCache[path] = defaultApplication[0];\n return defaultApplication[0];\n }\n\n serialize(): ClientConfig {\n return this.serialized;\n }\n}\n","import Script from 'next/script.js';\nimport { useEffect, useRef, useState } from 'react';\nimport { useClientConfig } from '../../../config/react/use-client-config';\n\nconst PREFETCH_ATTR = 'data-prefetch';\nconst DATA_ATTR_SELECTORS = {\n anyZone: '[data-zone]',\n external: '[data-zone=\"null\"]',\n sameZone: '[data-zone=\"same\"]',\n prefetch: `[${PREFETCH_ATTR}]`,\n} as const;\n\nconst PREFETCH_ON_HOVER_PREDICATES = {\n and: [\n { href_matches: '/*' },\n { selector_matches: DATA_ATTR_SELECTORS.anyZone },\n { not: { selector_matches: DATA_ATTR_SELECTORS.sameZone } },\n { not: { selector_matches: DATA_ATTR_SELECTORS.external } },\n ],\n};\n\nconst PREFETCH_WHEN_VISIBLE_PREDICATES = {\n and: [\n { href_matches: '/*' },\n { selector_matches: DATA_ATTR_SELECTORS.anyZone },\n { not: { selector_matches: DATA_ATTR_SELECTORS.sameZone } },\n { not: { selector_matches: DATA_ATTR_SELECTORS.external } },\n { selector_matches: DATA_ATTR_SELECTORS.prefetch },\n ],\n};\n\nfunction checkVisibility(element: Element | null): boolean {\n if (!element) return true;\n\n if ('checkVisibility' in element) {\n return element.checkVisibility({ opacityProperty: true });\n }\n\n // hack to get around TS thinking element is never;\n const el = element as Element;\n const style = window.getComputedStyle(el);\n\n if (\n style.display === 'none' ||\n style.visibility === 'hidden' ||\n style.opacity === '0'\n ) {\n return false;\n }\n\n return checkVisibility(el.parentElement);\n}\n\ninterface PrefetchCrossZoneLinksProps {\n /**\n * This attributes controls how eager the browser should be in prerendering\n * cross-zone links. Prerendering downloads the HTML and subresources of the page\n * and starts to render the page in the background. This consumes more resources\n * but provides a faster user experience if the user decides to visit that page.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/speculationrules#eagerness\n * for more information.\n *\n * Default value is 'conservative'.\n */\n prerenderEagerness?: 'immediate' | 'eager' | 'moderate' | 'conservative';\n}\n\nexport function PrefetchCrossZoneLinks({\n prerenderEagerness = 'conservative',\n}: PrefetchCrossZoneLinksProps): JSX.Element | null {\n const { isLoading } = useClientConfig(\n process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG,\n );\n const [links, setLinks] = useState<HTMLAnchorElement[]>([]);\n const canPrerender = useRef(isPrerenderSafe());\n\n useEffect(() => {\n if (isLoading) {\n return;\n }\n\n /**\n * Intersection observer to add the data-prefetch attribute to cross-zone\n * links that have yet to be prefetched and are visible.\n */\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (\n entry.isIntersecting &&\n !entry.target.hasAttribute(PREFETCH_ATTR) &&\n // lazy perform the visibility check for nodes that are intersecting the viewport\n // and have not been prefetched.\n checkVisibility(entry.target)\n ) {\n entry.target.setAttribute(PREFETCH_ATTR, 'true');\n }\n });\n },\n {\n root: null,\n rootMargin: '0px',\n threshold: 0.1,\n },\n );\n\n links.forEach((link) => observer.observe(link));\n\n return () => {\n observer.disconnect();\n };\n }, [isLoading, links]);\n\n useEffect(() => {\n if (isLoading) {\n return;\n }\n\n /**\n * Mutation observer to notify when new nodes have entered/exited the document\n * or an href has changed.\n */\n const observer = new MutationObserver((mutations) => {\n const hasChanged = mutations.some((mutation) => {\n return (\n (mutation.type === 'childList' && mutation.addedNodes.length > 0) ||\n (mutation.type === 'attributes' && mutation.attributeName === 'href')\n );\n });\n\n if (hasChanged) {\n // Whenever there's a change, add all cross-zone links that haven't been\n // prefetched.\n setLinks(\n Array.from(\n document.querySelectorAll<HTMLAnchorElement>(\n `a${DATA_ATTR_SELECTORS.anyZone}:not(${DATA_ATTR_SELECTORS.prefetch}):not(${DATA_ATTR_SELECTORS.sameZone}):not(${DATA_ATTR_SELECTORS.external})`,\n ),\n ),\n );\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['href'],\n });\n\n return () => {\n observer.disconnect();\n };\n }, [isLoading]);\n\n // Wait till the zone-config loads to take into consideration any\n // flagged routes.\n if (isLoading) {\n return null;\n }\n\n // Prefetch links with moderate eagerness by default, immediately when marked \"data-prefetch\".\n // Prerender links with conservative eagerness by default, immediately when marked \"data-prefetch\".\n const speculationRules = {\n prefetch: [\n {\n eagerness: 'moderate',\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n {\n eagerness: 'immediate',\n where: PREFETCH_WHEN_VISIBLE_PREDICATES,\n },\n ],\n ...(canPrerender.current\n ? {\n prerender: [\n {\n eagerness: prerenderEagerness,\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n ],\n }\n : {}),\n };\n\n return (\n <Script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Safe - injecting JSON speculation rules, not HTML\n dangerouslySetInnerHTML={{\n __html: JSON.stringify(speculationRules),\n }}\n id=\"prefetch-zones-links\"\n type=\"speculationrules\"\n />\n );\n}\n\n/**\n * Chromium 147 ships a resurrected CHECK in MarkPrerenderMatchedWithPrefetch\n * (CL 7641455) that crashes the browser process when prerender + prefetch\n * matching is invoked more than once per URL — which happens on cross-zone\n * MFE navigations. The CHECK was removed in CL 7761927 and the fix\n * cherry-picked into M148 (148.0.7778.43). No other major version is\n * affected: < 147 never had the resurrected CHECK, >= 148 has the fix.\n *\n * Non-Chromium browsers never process `<script type=\"speculationrules\">`\n * at all, so the prerender entry is inert for them — no need to exclude.\n *\n * On the server (navigator undefined) we return `true` because the\n * `next/script` afterInteractive strategy defers injection to the client;\n * the server-side value is never materialised into HTML.\n *\n * @see https://chromium-review.googlesource.com/c/chromium/src/+/7761927\n * @see https://vercel.slack.com/archives/C0B1DHDQ3S7 (inc-6387)\n *\n * TODO: Remove once Chrome 147 usage drops below the long-tail threshold.\n */\nfunction isPrerenderSafe(): boolean {\n if (typeof navigator === 'undefined') return true;\n const match = navigator.userAgent.match(/Chrome\\/(\\d+)/);\n if (!match) return true;\n return Number(match[1]) !== 147;\n}\n","import type React from 'react';\nimport {\n createContext,\n startTransition,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nexport interface PrefetchCrossZoneLinksContext {\n prefetchHref: (href: string) => void;\n}\n\nexport const PrefetchCrossZoneLinksContext =\n createContext<PrefetchCrossZoneLinksContext>({\n prefetchHref: () => {},\n });\n\nexport function PrefetchCrossZoneLinksProvider({\n children,\n}: {\n children: React.ReactNode;\n}): JSX.Element | null {\n const [seenHrefs, setSeenHrefs] = useState(new Set<string>());\n const isSafariOrFirefox = useRef(\n typeof navigator !== 'undefined' &&\n (navigator.userAgent.includes('Firefox') ||\n (navigator.userAgent.includes('Safari') &&\n !navigator.userAgent.includes('Chrome'))),\n );\n\n // This useCallback must not have any dependencies because if it changes\n // its value, every component that uses this context will rerender.\n const prefetchHref = useCallback((href: string): void => {\n // It's not critical that we render the new preload `<link>` elements\n // immediately. We want to batch together `prefetchHref` calls that\n // occur in one synchronous pass and only render once after they've all\n // called this callback.\n startTransition(() => {\n setSeenHrefs((prevHrefs) => {\n if (prevHrefs.has(href)) return prevHrefs;\n return new Set(prevHrefs).add(href);\n });\n });\n }, []);\n\n const value = useMemo(() => ({ prefetchHref }), [prefetchHref]);\n\n if (!isSafariOrFirefox.current) {\n return <>{children}</>;\n }\n\n return (\n <PrefetchCrossZoneLinksContext.Provider value={value}>\n {children}\n {[...seenHrefs].map((href) => (\n <link as=\"fetch\" href={href} key={href} rel=\"preload\" />\n ))}\n </PrefetchCrossZoneLinksContext.Provider>\n );\n}\n"],"mappings":"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","names":["NextLink","forwardRef","useContext","useMemo","useEffect","useMemo","useState","pathToRegexp","regexpCache","getRegexp","path","existing","regexp","MicrofrontendConfigClient","config","opts","app","match","newRouting","pathsWithoutFlags","group","other","pathname","name","application","childPath","defaultApplication","clientCache","cachedHasDynamicPaths","getClient","config","existing","client","MicrofrontendConfigClient","cachedServerClientConfigPromise","cachedServerClient","fetchClientConfigFromServer","response","responseJson","useClientConfig","clientConfig","setClientConfig","useState","canLoad","useMemo","hasDynamicPaths","isLoading","setIsLoading","useEffect","newConfig","prevConfig","Script","useEffect","useRef","useState","jsx","PREFETCH_ATTR","DATA_ATTR_SELECTORS","PREFETCH_ON_HOVER_PREDICATES","PREFETCH_WHEN_VISIBLE_PREDICATES","checkVisibility","element","el","style","PrefetchCrossZoneLinks","prerenderEagerness","isLoading","useClientConfig","links","setLinks","useState","canPrerender","useRef","isPrerenderSafe","useEffect","observer","entries","entry","link","mutations","mutation","speculationRules","Script","match","createContext","startTransition","useCallback","useMemo","useRef","useState","Fragment","jsx","jsxs","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","children","seenHrefs","setSeenHrefs","isSafariOrFirefox","prefetchHref","href","prevHrefs","value","jsx","CURRENT_ZONE_HASH","useZoneForHref","href","clientConfig","isLoading","useClientConfig","isRelativePath","zoneOfHref","useMemo","isRelative","Link","forwardRef","children","props","ref","prefetchHref","useContext","PrefetchCrossZoneLinksContext","isDifferentZone","onHoverPrefetch","_","rest","NextLink"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/microfrontends",
3
- "version": "2.3.2",
3
+ "version": "2.3.3",
4
4
  "private": false,
5
5
  "description": "Defines configuration and utilities for microfrontends development",
6
6
  "keywords": [