@vercel/microfrontends 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +5 -5
  2. package/dist/bin/cli.cjs +346 -90
  3. package/dist/config/client.d.ts +1 -1
  4. package/dist/config/edge.d.ts +2 -2
  5. package/dist/config.cjs +1 -6
  6. package/dist/config.cjs.map +1 -1
  7. package/dist/config.d.ts +3 -3
  8. package/dist/config.js +1 -6
  9. package/dist/config.js.map +1 -1
  10. package/dist/{index-05742bef.d.ts → index-a99d5459.d.ts} +2 -2
  11. package/dist/{microfrontend-config-2425db74.d.ts → microfrontend-config-983a5139.d.ts} +1 -1
  12. package/dist/next/client.cjs +1 -1
  13. package/dist/next/client.cjs.map +1 -1
  14. package/dist/next/client.js +1 -1
  15. package/dist/next/client.js.map +1 -1
  16. package/dist/next/config.cjs +13 -11
  17. package/dist/next/config.cjs.map +1 -1
  18. package/dist/next/config.js +13 -11
  19. package/dist/next/config.js.map +1 -1
  20. package/dist/next/middleware.cjs +22 -8
  21. package/dist/next/middleware.cjs.map +1 -1
  22. package/dist/next/middleware.js +22 -8
  23. package/dist/next/middleware.js.map +1 -1
  24. package/dist/next/testing.cjs +1 -6
  25. package/dist/next/testing.cjs.map +1 -1
  26. package/dist/next/testing.d.ts +2 -2
  27. package/dist/next/testing.js +1 -6
  28. package/dist/next/testing.js.map +1 -1
  29. package/dist/overrides.d.ts +2 -2
  30. package/dist/{schema-83a75e61.d.ts → schema-2922d49e.d.ts} +1 -7
  31. package/dist/{types-4fd1c7c6.d.ts → types-0030abae.d.ts} +1 -8
  32. package/dist/{types-13f3e535.d.ts → types-c777c2f5.d.ts} +1 -1
  33. package/dist/v2/config.cjs +1 -1
  34. package/dist/v2/config.cjs.map +1 -1
  35. package/dist/v2/config.d.ts +3 -3
  36. package/dist/v2/config.js +1 -1
  37. package/dist/v2/config.js.map +1 -1
  38. package/dist/v2/microfrontends/server.cjs +296 -36
  39. package/dist/v2/microfrontends/server.cjs.map +1 -1
  40. package/dist/v2/microfrontends/server.d.ts +26 -4
  41. package/dist/v2/microfrontends/server.js +296 -36
  42. package/dist/v2/microfrontends/server.js.map +1 -1
  43. package/dist/v2/microfrontends.cjs +1 -1
  44. package/dist/v2/microfrontends.cjs.map +1 -1
  45. package/dist/v2/microfrontends.d.ts +3 -3
  46. package/dist/v2/microfrontends.js +1 -1
  47. package/dist/v2/microfrontends.js.map +1 -1
  48. package/dist/v2/next/client.cjs +1 -1
  49. package/dist/v2/next/client.cjs.map +1 -1
  50. package/dist/v2/next/client.js +1 -1
  51. package/dist/v2/next/client.js.map +1 -1
  52. package/dist/v2/next/config.cjs +331 -68
  53. package/dist/v2/next/config.cjs.map +1 -1
  54. package/dist/v2/next/config.js +331 -68
  55. package/dist/v2/next/config.js.map +1 -1
  56. package/dist/v2/next/endpoints.cjs +5 -2
  57. package/dist/v2/next/endpoints.cjs.map +1 -1
  58. package/dist/v2/next/endpoints.d.ts +1 -1
  59. package/dist/v2/next/endpoints.js +5 -2
  60. package/dist/v2/next/endpoints.js.map +1 -1
  61. package/dist/v2/next/middleware.cjs +108 -88
  62. package/dist/v2/next/middleware.cjs.map +1 -1
  63. package/dist/v2/next/middleware.js +108 -88
  64. package/dist/v2/next/middleware.js.map +1 -1
  65. package/dist/v2/overrides.cjs +1 -1
  66. package/dist/v2/overrides.cjs.map +1 -1
  67. package/dist/v2/overrides.d.ts +3 -3
  68. package/dist/v2/overrides.js +1 -1
  69. package/dist/v2/overrides.js.map +1 -1
  70. package/dist/v2/schema.cjs.map +1 -1
  71. package/dist/v2/schema.d.ts +1 -1
  72. package/dist/validation.cjs +0 -11
  73. package/dist/validation.cjs.map +1 -1
  74. package/dist/validation.d.ts +2 -9
  75. package/dist/validation.js +0 -11
  76. package/dist/validation.js.map +1 -1
  77. package/package.json +8 -7
  78. package/schema/schema-v2.json +0 -7
  79. package/schema/schema.json +0 -4
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/client/link/multi-zones-link.tsx","../../src/next/client/prefetch/prefetch-cross-zone-links-context.tsx","../../src/next/client/prefetch/prefetch-cross-zone-links.tsx","../../src/config/client/use-client-config.ts","../../src/config/client/client-config.ts","../../src/config/client/paths.ts"],"sourcesContent":["import type { AnchorHTMLAttributes } from 'react';\nimport { forwardRef, useContext } from 'react';\nimport NextLink, { type LinkProps as NextLinkProps } from 'next/link';\nimport { PrefetchCrossZoneLinksContext } from '../prefetch';\nimport { getApplicationNameForPath } from '../../../config/client/paths';\nimport { useClientConfig } from '../../../config/client/use-client-config';\n\ninterface BaseProps {\n children: React.ReactNode;\n href: string;\n}\nexport type LinkProps = BaseProps &\n Omit<NextLinkProps, keyof BaseProps> &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>;\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\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 removeFlaggedPathsFromDefault: true,\n });\n const isRelativePath =\n typeof href === 'string' && (href as string).startsWith('/');\n const zoneOfHref = isRelativePath\n ? getApplicationNameForPath(clientConfig, href as string)\n : null;\n const isDifferentZone =\n !isRelativePath || (zoneOfHref ? CURRENT_ZONE !== zoneOfHref : false);\n return { zoneOfHref, isDifferentZone, isLoading };\n}\n\n/**\n * A Link component that works with Multi-Zones 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 return (\n // biome-ignore lint/a11y/useKeyWithMouseEvents: Only used for prefetching.\n // biome-ignore lint/nursery/noStaticElementInteractions: Ignored using `--suppress`\n <a\n {...props}\n data-zone={zoneOfHref}\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 = 'MultiZonesLink';\n","import React, {\n createContext,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\n\nexport interface PrefetchCrossZoneLinksContext {\n prefetchHref: (href: string) => void;\n}\n\nexport const PrefetchCrossZoneLinksContext =\n createContext<PrefetchCrossZoneLinksContext>({\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n prefetchHref: () => {},\n });\n\nexport function PrefetchCrossZoneLinksProvider({\n children,\n}: { children: React.ReactNode }): JSX.Element | null {\n const [seenHrefs, setSeenHrefs] = useState(new Set<string>());\n const [isSafariOrFirefox, setIsSafariOrFirefox] = useState(false);\n\n useEffect(() => {\n setIsSafariOrFirefox(\n typeof navigator !== 'undefined' &&\n (navigator.userAgent.includes('Firefox') ||\n (navigator.userAgent.includes('Safari') &&\n !navigator.userAgent.includes('Chrome'))),\n );\n }, []);\n\n const prefetchHref = useCallback(\n (href: string): void => {\n if (!seenHrefs.has(href)) {\n setSeenHrefs(new Set(seenHrefs).add(href));\n }\n },\n [seenHrefs],\n );\n\n const value = useMemo(() => ({ prefetchHref }), [prefetchHref]);\n\n if (!isSafariOrFirefox) {\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","import { useEffect, useState } from 'react';\nimport Script from 'next/script';\nimport { useClientConfig } from '../../../config/client/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\nexport function PrefetchCrossZoneLinks(): JSX.Element | null {\n const { isLoading } = useClientConfig();\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: 'conservative',\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n ],\n };\n\n return (\n <Script\n dangerouslySetInnerHTML={{\n __html: `${JSON.stringify(speculationRules)}`,\n }}\n id=\"prefetch-zones-links\"\n type=\"speculationrules\"\n />\n );\n}\n","'use client';\n\nimport { useState, useEffect } from 'react';\nimport type { ClientConfig, WellKnownClientData } from './types';\nimport { getClientConfigFromEnv } from './client-config';\n\nlet cachedServerClientConfigPromise: Promise<ClientConfig | null> | null = null;\n\nasync function fetchClientConfigFromServer(): Promise<ClientConfig | null> {\n try {\n const response = await fetch(\n '/.well-known/vercel/micro-frontends/client-config',\n );\n if (response.status !== 200) {\n return null;\n }\n const responseJson = (await response.json()) as WellKnownClientData;\n return responseJson.config;\n } catch (err) {\n return null;\n }\n}\n\nfunction isEqual(config1: ClientConfig, config2: ClientConfig): boolean {\n return JSON.stringify(config1) === JSON.stringify(config2);\n}\n\nfunction removeFlaggedPaths(config: ClientConfig): ClientConfig {\n for (const app of Object.values(config.applications)) {\n if (app.routing) {\n app.routing.matches = app.routing.matches.filter(\n (match) => !match.options?.flag,\n );\n }\n }\n return config;\n}\n\n/**\n * Hook to use the client micro-frontends 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({\n removeFlaggedPathsFromDefault,\n}: {\n removeFlaggedPathsFromDefault?: boolean;\n} = {}): {\n clientConfig: ClientConfig;\n isLoading: boolean;\n} {\n const [clientConfig, setClientConfig] = useState<ClientConfig>(\n removeFlaggedPathsFromDefault\n ? removeFlaggedPaths(getClientConfigFromEnv())\n : getClientConfigFromEnv(),\n );\n const [isLoading, setIsLoading] = useState(true);\n useEffect(() => {\n if (\n process.env.NODE_ENV === 'test' &&\n process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER !== '1'\n ) {\n setIsLoading(false);\n return;\n }\n // Since we may remove flagged paths from the client config above, we need\n // to use the original client config to determine if the config has any\n // dynamic paths.\n const originalClientConfig = getClientConfigFromEnv();\n // As an optimization, only fetch the config from the server if the\n // micro-frontends configuration has any dynamic paths. If it doesn't,\n // then the server won't return any different values.\n const hasDynamicPaths = Object.values(\n originalClientConfig.applications,\n ).some((app) =>\n app.routing?.matches.some(\n (group) =>\n group.options?.flag || group.options?.routeToDefaultApplication,\n ),\n );\n if (!hasDynamicPaths) {\n setIsLoading(false);\n return;\n }\n if (!cachedServerClientConfigPromise) {\n cachedServerClientConfigPromise = fetchClientConfigFromServer();\n }\n void cachedServerClientConfigPromise\n .then((newConfig) => {\n if (newConfig) {\n setClientConfig((prevConfig) => {\n return isEqual(prevConfig, newConfig) ? prevConfig : newConfig;\n });\n }\n })\n .finally(() => {\n setIsLoading(false);\n });\n }, [clientConfig.applications]);\n\n return { clientConfig, isLoading };\n}\n\nexport function resetCachedServerClientConfigPromise(): void {\n cachedServerClientConfigPromise = null;\n}\n","import type { Config } from '../schema';\nimport type { ClientConfig, WellKnownClientData } from './types';\n\n/**\n * Given a micro-frontends configuration, this returns a version that is\n * acceptable to be used client-side without leaking any information.\n */\nexport function getConfigForClient(config: Config): ClientConfig {\n return {\n applications: Object.fromEntries(\n Object.entries(config.applications).map(([name, application]) => [\n name,\n {\n default: application.default,\n routing: application.routing,\n },\n ]),\n ),\n };\n}\n\nexport function getClientConfigFromEnv(): ClientConfig {\n // This has to use the actual name rather than the constant variable in order\n // for the env var to be replaced during the build correctly.\n const clientConfigJson = process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG;\n if (!clientConfigJson) {\n throw new Error(\n 'Could not find client micro-frontends config in the environment',\n );\n }\n return JSON.parse(clientConfigJson) as ClientConfig;\n}\n\nexport type { ClientConfig, WellKnownClientData };\n","import { pathToRegexp } from 'path-to-regexp';\nimport type { ClientConfig } from './types';\n\nconst weakCache = new WeakMap<ClientConfig, Record<string, string>>();\n/**\n * Given a relative path, this function returns the name of the micro-frontend\n * that serves the path.\n */\nexport function getApplicationNameForPath(\n config: ClientConfig,\n path: string,\n): string {\n if (!path.startsWith('/')) {\n throw new Error(`Path must start with a /`);\n }\n const cache = weakCache.get(config) ?? {};\n if (cache[path]) {\n return cache[path];\n }\n if (!weakCache.has(config)) {\n weakCache.set(config, cache);\n }\n const pathname = new URL(path, 'https://example.com').pathname;\n\n for (const [name, application] of Object.entries(config.applications)) {\n if (application.routing) {\n for (const group of application.routing.matches) {\n for (const childPath of group.paths) {\n const regexp = pathToRegexp(childPath);\n if (regexp.test(pathname)) {\n cache[path] = name;\n return name;\n }\n }\n }\n }\n }\n const defaultApplication = Object.entries(config.applications).find(\n ([, application]) => application.default,\n );\n if (!defaultApplication) {\n throw new Error(\n 'No default application found. A default application must be configured in the micro-frontends configuration.',\n );\n }\n cache[path] = defaultApplication[0];\n return defaultApplication[0];\n}\n"],"mappings":"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","names":["forwardRef","useContext","NextLink","createContext","useCallback","useEffect","useMemo","useState","Fragment","jsx","jsxs","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","children","seenHrefs","setSeenHrefs","isSafariOrFirefox","setIsSafariOrFirefox","prefetchHref","href","value","useEffect","useState","Script","useState","useEffect","getClientConfigFromEnv","clientConfigJson","cachedServerClientConfigPromise","fetchClientConfigFromServer","response","isEqual","config1","config2","removeFlaggedPaths","config","app","match","_a","useClientConfig","removeFlaggedPathsFromDefault","clientConfig","setClientConfig","useState","getClientConfigFromEnv","isLoading","setIsLoading","useEffect","originalClientConfig","group","_b","newConfig","prevConfig","jsx","PREFETCH_ATTR","DATA_ATTR_SELECTORS","PREFETCH_ON_HOVER_PREDICATES","PREFETCH_WHEN_VISIBLE_PREDICATES","checkVisibility","element","el","style","PrefetchCrossZoneLinks","isLoading","useClientConfig","links","setLinks","useState","useEffect","observer","entries","entry","link","mutations","mutation","Script","pathToRegexp","weakCache","getApplicationNameForPath","config","path","cache","pathname","name","application","group","childPath","defaultApplication","jsx","CURRENT_ZONE","useZoneForHref","href","clientConfig","isLoading","useClientConfig","isRelativePath","zoneOfHref","getApplicationNameForPath","Link","forwardRef","children","props","ref","prefetchHref","useContext","PrefetchCrossZoneLinksContext","isDifferentZone","onHoverPrefetch","NextLink"]}
1
+ {"version":3,"sources":["../../src/next/client/link/multi-zones-link.tsx","../../src/next/client/prefetch/prefetch-cross-zone-links-context.tsx","../../src/next/client/prefetch/prefetch-cross-zone-links.tsx","../../src/config/client/use-client-config.ts","../../src/config/client/client-config.ts","../../src/config/client/paths.ts"],"sourcesContent":["import type { AnchorHTMLAttributes } from 'react';\nimport { forwardRef, useContext } from 'react';\nimport NextLink, { type LinkProps as NextLinkProps } from 'next/link';\nimport { PrefetchCrossZoneLinksContext } from '../prefetch';\nimport { getApplicationNameForPath } from '../../../config/client/paths';\nimport { useClientConfig } from '../../../config/client/use-client-config';\n\ninterface BaseProps {\n children: React.ReactNode;\n href: string;\n}\nexport type LinkProps = BaseProps &\n Omit<NextLinkProps, keyof BaseProps> &\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>;\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\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 removeFlaggedPathsFromDefault: true,\n });\n const isRelativePath =\n typeof href === 'string' && (href as string).startsWith('/');\n const zoneOfHref = isRelativePath\n ? getApplicationNameForPath(clientConfig, href as string)\n : null;\n const isDifferentZone =\n !isRelativePath || (zoneOfHref ? CURRENT_ZONE !== zoneOfHref : false);\n return { zoneOfHref, isDifferentZone, isLoading };\n}\n\n/**\n * A Link component that works with Multi-Zones 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 // biome-ignore lint/a11y/useKeyWithMouseEvents: Only used for prefetching.\n // biome-ignore lint/nursery/noStaticElementInteractions: Ignored using `--suppress`\n <a\n {...rest}\n data-zone={zoneOfHref}\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 = 'MultiZonesLink';\n","import React, {\n createContext,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\n\nexport interface PrefetchCrossZoneLinksContext {\n prefetchHref: (href: string) => void;\n}\n\nexport const PrefetchCrossZoneLinksContext =\n createContext<PrefetchCrossZoneLinksContext>({\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n prefetchHref: () => {},\n });\n\nexport function PrefetchCrossZoneLinksProvider({\n children,\n}: { children: React.ReactNode }): JSX.Element | null {\n const [seenHrefs, setSeenHrefs] = useState(new Set<string>());\n const [isSafariOrFirefox, setIsSafariOrFirefox] = useState(false);\n\n useEffect(() => {\n setIsSafariOrFirefox(\n typeof navigator !== 'undefined' &&\n (navigator.userAgent.includes('Firefox') ||\n (navigator.userAgent.includes('Safari') &&\n !navigator.userAgent.includes('Chrome'))),\n );\n }, []);\n\n const prefetchHref = useCallback(\n (href: string): void => {\n if (!seenHrefs.has(href)) {\n setSeenHrefs(new Set(seenHrefs).add(href));\n }\n },\n [seenHrefs],\n );\n\n const value = useMemo(() => ({ prefetchHref }), [prefetchHref]);\n\n if (!isSafariOrFirefox) {\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","import { useEffect, useState } from 'react';\nimport Script from 'next/script';\nimport { useClientConfig } from '../../../config/client/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\nexport function PrefetchCrossZoneLinks(): JSX.Element | null {\n const { isLoading } = useClientConfig();\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: 'conservative',\n where: PREFETCH_ON_HOVER_PREDICATES,\n },\n ],\n };\n\n return (\n <Script\n dangerouslySetInnerHTML={{\n __html: `${JSON.stringify(speculationRules)}`,\n }}\n id=\"prefetch-zones-links\"\n type=\"speculationrules\"\n />\n );\n}\n","'use client';\n\nimport { useState, useEffect } from 'react';\nimport type { ClientConfig, WellKnownClientData } from './types';\nimport { getClientConfigFromEnv } from './client-config';\n\nlet cachedServerClientConfigPromise: Promise<ClientConfig | null> | null = null;\n\nasync function fetchClientConfigFromServer(): Promise<ClientConfig | null> {\n try {\n const response = await fetch(\n '/.well-known/vercel/micro-frontends/client-config',\n );\n if (response.status !== 200) {\n return null;\n }\n const responseJson = (await response.json()) as WellKnownClientData;\n return responseJson.config;\n } catch (err) {\n return null;\n }\n}\n\nfunction isEqual(config1: ClientConfig, config2: ClientConfig): boolean {\n return JSON.stringify(config1) === JSON.stringify(config2);\n}\n\nfunction removeFlaggedPaths(config: ClientConfig): ClientConfig {\n for (const app of Object.values(config.applications)) {\n if (app.routing) {\n app.routing.matches = app.routing.matches.filter(\n (match) => !match.options?.flag,\n );\n }\n }\n return config;\n}\n\n/**\n * Hook to use the client micro-frontends 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({\n removeFlaggedPathsFromDefault,\n}: {\n removeFlaggedPathsFromDefault?: boolean;\n} = {}): {\n clientConfig: ClientConfig;\n isLoading: boolean;\n} {\n const [clientConfig, setClientConfig] = useState<ClientConfig>(\n removeFlaggedPathsFromDefault\n ? removeFlaggedPaths(getClientConfigFromEnv())\n : getClientConfigFromEnv(),\n );\n const [isLoading, setIsLoading] = useState(true);\n useEffect(() => {\n if (\n process.env.NODE_ENV === 'test' &&\n process.env.MFE_FORCE_CLIENT_CONFIG_FROM_SERVER !== '1'\n ) {\n setIsLoading(false);\n return;\n }\n // Since we may remove flagged paths from the client config above, we need\n // to use the original client config to determine if the config has any\n // dynamic paths.\n const originalClientConfig = getClientConfigFromEnv();\n // As an optimization, only fetch the config from the server if the\n // micro-frontends configuration has any dynamic paths. If it doesn't,\n // then the server won't return any different values.\n const hasDynamicPaths = Object.values(\n originalClientConfig.applications,\n ).some((app) => app.routing?.matches.some((group) => group.options?.flag));\n if (!hasDynamicPaths) {\n setIsLoading(false);\n return;\n }\n if (!cachedServerClientConfigPromise) {\n cachedServerClientConfigPromise = fetchClientConfigFromServer();\n }\n void cachedServerClientConfigPromise\n .then((newConfig) => {\n if (newConfig) {\n setClientConfig((prevConfig) => {\n return isEqual(prevConfig, newConfig) ? prevConfig : newConfig;\n });\n }\n })\n .finally(() => {\n setIsLoading(false);\n });\n }, [clientConfig.applications]);\n\n return { clientConfig, isLoading };\n}\n\nexport function resetCachedServerClientConfigPromise(): void {\n cachedServerClientConfigPromise = null;\n}\n","import type { Config } from '../schema';\nimport type { ClientConfig, WellKnownClientData } from './types';\n\n/**\n * Given a micro-frontends configuration, this returns a version that is\n * acceptable to be used client-side without leaking any information.\n */\nexport function getConfigForClient(config: Config): ClientConfig {\n return {\n applications: Object.fromEntries(\n Object.entries(config.applications).map(([name, application]) => [\n name,\n {\n default: application.default,\n routing: application.routing,\n },\n ]),\n ),\n };\n}\n\nexport function getClientConfigFromEnv(): ClientConfig {\n // This has to use the actual name rather than the constant variable in order\n // for the env var to be replaced during the build correctly.\n const clientConfigJson = process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG;\n if (!clientConfigJson) {\n throw new Error(\n 'Could not find client micro-frontends config in the environment',\n );\n }\n return JSON.parse(clientConfigJson) as ClientConfig;\n}\n\nexport type { ClientConfig, WellKnownClientData };\n","import { pathToRegexp } from 'path-to-regexp';\nimport type { ClientConfig } from './types';\n\nconst weakCache = new WeakMap<ClientConfig, Record<string, string>>();\n/**\n * Given a relative path, this function returns the name of the micro-frontend\n * that serves the path.\n */\nexport function getApplicationNameForPath(\n config: ClientConfig,\n path: string,\n): string {\n if (!path.startsWith('/')) {\n throw new Error(`Path must start with a /`);\n }\n const cache = weakCache.get(config) ?? {};\n if (cache[path]) {\n return cache[path];\n }\n if (!weakCache.has(config)) {\n weakCache.set(config, cache);\n }\n const pathname = new URL(path, 'https://example.com').pathname;\n\n for (const [name, application] of Object.entries(config.applications)) {\n if (application.routing) {\n for (const group of application.routing.matches) {\n for (const childPath of group.paths) {\n const regexp = pathToRegexp(childPath);\n if (regexp.test(pathname)) {\n cache[path] = name;\n return name;\n }\n }\n }\n }\n }\n const defaultApplication = Object.entries(config.applications).find(\n ([, application]) => application.default,\n );\n if (!defaultApplication) {\n throw new Error(\n 'No default application found. A default application must be configured in the micro-frontends configuration.',\n );\n }\n cache[path] = defaultApplication[0];\n return defaultApplication[0];\n}\n"],"mappings":"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","names":["forwardRef","useContext","NextLink","createContext","useCallback","useEffect","useMemo","useState","Fragment","jsx","jsxs","PrefetchCrossZoneLinksContext","PrefetchCrossZoneLinksProvider","children","seenHrefs","setSeenHrefs","isSafariOrFirefox","setIsSafariOrFirefox","prefetchHref","href","value","useEffect","useState","Script","useState","useEffect","getClientConfigFromEnv","clientConfigJson","cachedServerClientConfigPromise","fetchClientConfigFromServer","response","isEqual","config1","config2","removeFlaggedPaths","config","app","match","_a","useClientConfig","removeFlaggedPathsFromDefault","clientConfig","setClientConfig","useState","getClientConfigFromEnv","isLoading","setIsLoading","useEffect","originalClientConfig","group","newConfig","prevConfig","jsx","PREFETCH_ATTR","DATA_ATTR_SELECTORS","PREFETCH_ON_HOVER_PREDICATES","PREFETCH_WHEN_VISIBLE_PREDICATES","checkVisibility","element","el","style","PrefetchCrossZoneLinks","isLoading","useClientConfig","links","setLinks","useState","useEffect","observer","entries","entry","link","mutations","mutation","Script","pathToRegexp","weakCache","getApplicationNameForPath","config","path","cache","pathname","name","application","group","childPath","defaultApplication","jsx","CURRENT_ZONE","useZoneForHref","href","clientConfig","isLoading","useClientConfig","isRelativePath","zoneOfHref","getApplicationNameForPath","Link","forwardRef","children","props","ref","prefetchHref","useContext","PrefetchCrossZoneLinksContext","isDifferentZone","onHoverPrefetch","_","rest","NextLink"]}
@@ -701,10 +701,6 @@ var schema_default = {
701
701
  flag: {
702
702
  type: "string",
703
703
  description: "flag name that can be used to enable/disable all paths in the group"
704
- },
705
- routeToDefaultApplication: {
706
- type: "boolean",
707
- description: "True to route the request to the default application for this micro-frontends set-up. This must be `true` when using `flag` or when you want to use custom logic to make the routing decision for this group of paths."
708
704
  }
709
705
  }
710
706
  },
@@ -1065,11 +1061,10 @@ var validateOptions = (options) => {
1065
1061
  // src/config/utils/convert.ts
1066
1062
  function convertV1RoutingToV2Routing(routing) {
1067
1063
  return routing.matches.map((group) => {
1068
- var _a, _b;
1064
+ var _a;
1069
1065
  return {
1070
1066
  group: group.group,
1071
1067
  flag: (_a = group.options) == null ? void 0 : _a.flag,
1072
- routeToDefaultApplication: (_b = group.options) == null ? void 0 : _b.routeToDefaultApplication,
1073
1068
  paths: group.paths
1074
1069
  };
1075
1070
  });
@@ -1441,7 +1436,16 @@ function rewritesMapToArr(rewrites) {
1441
1436
  return [
1442
1437
  {
1443
1438
  source,
1444
- destination
1439
+ destination,
1440
+ missing: [
1441
+ // if this header is present, the proxy has performed the rewrite.
1442
+ // once the proxy routing is fully rolled out, this package should
1443
+ // be updated to not perform any rewrites when deployed.
1444
+ {
1445
+ type: "header",
1446
+ key: "x-vercel-mfe-host"
1447
+ }
1448
+ ]
1445
1449
  }
1446
1450
  ];
1447
1451
  });
@@ -1449,7 +1453,7 @@ function rewritesMapToArr(rewrites) {
1449
1453
  function transform4(args) {
1450
1454
  const { next, microfrontend, zone } = args;
1451
1455
  const buildBeforeFiles = () => {
1452
- var _a, _b, _c, _d, _e;
1456
+ var _a, _b, _c, _d;
1453
1457
  const rewrites = /* @__PURE__ */ new Map();
1454
1458
  if ((_a = zone.routing) == null ? void 0 : _a.assetPrefix) {
1455
1459
  rewrites.set(`/${zone.routing.assetPrefix}/_next/:path+`, {
@@ -1482,9 +1486,7 @@ function transform4(args) {
1482
1486
  });
1483
1487
  }
1484
1488
  for (const group of routing.matches) {
1485
- if ((_d = group.options) == null ? void 0 : _d.routeToDefaultApplication) {
1486
- continue;
1487
- } else if ((_e = group.options) == null ? void 0 : _e.flag) {
1489
+ if ((_d = group.options) == null ? void 0 : _d.flag) {
1488
1490
  continue;
1489
1491
  } else {
1490
1492
  for (const source of group.paths) {