commit d4fe4800e638f3972fa316a11505ee5fe3cb569c Author: Varun Shah Date: Tue Jan 20 14:34:18 2026 +0800 Extract anyclip diff --git a/README.md b/README.md new file mode 100644 index 0000000..941046b --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# AnyClip Video Manager - Extracted Source + +Source code extracted from sourcemaps of `videomanager.anyclip.com`. + +## Overview + +Next.js application for video content management, analytics, and publishing. + +## Structure + +``` +├── src/ +│ ├── modules/ # Feature modules (business logic) +│ ├── pages/ # Next.js page components +│ ├── client/ # Client-side utilities +│ ├── shared/ # Shared libraries +│ └── assets/ +├── pages/ # Root Next.js pages (_app.tsx, _error.tsx) +├── client/ # Next.js client runtime +├── vendor/ # Bundled node_modules +└── sourcemaps/ # Original .map files +``` + +## Modules (`src/modules/`) + +| Module | Description | +|--------|-------------| +| `analytics/` | Dashboards - monetization, video performance, custom reports | +| `editorial/` | Video editing - tagging, search, bulk actions, video details | +| `publishing/` | Content publishing and destination management | +| `marketplace/` | Marketplace accounts and dashboard | +| `xRay/` | X-Ray - campaigns, creatives, line items | +| `hubs/` | Content hubs management | +| `users/` | User management | +| `invitations/` | User invitation system | +| `forms/` | Form builder/management | +| `uploaderNew/` | Video upload functionality | +| `userRulesSettings/` | User rules and settings | +| `layout/` | App layout and Redux state | +| `common/` | Shared components - forms, tables, lists, tag selectors | + +## Pages (`src/pages/`) + +- `/analytics` - Analytics dashboard +- `/studio` - Studio interface +- `/personal-settings` - User settings +- `/hubs`, `/users`, `/invitations`, `/forms` - Management pages +- `/x-ray/*` - Campaign, creative, and line item analytics + +## Tech Stack + +- Next.js, React, TypeScript +- Redux (state management) +- Material-UI (components) +- Victory/D3 (charts) diff --git a/client/add-base-path.ts b/client/add-base-path.ts new file mode 100644 index 0000000..015fb2e --- /dev/null +++ b/client/add-base-path.ts @@ -0,0 +1,12 @@ +import { addPathPrefix } from '../shared/lib/router/utils/add-path-prefix' +import { normalizePathTrailingSlash } from './normalize-trailing-slash' + +const basePath = (process.env.__NEXT_ROUTER_BASEPATH as string) || '' + +export function addBasePath(path: string, required?: boolean): string { + return normalizePathTrailingSlash( + process.env.__NEXT_MANUAL_CLIENT_BASE_PATH && !required + ? path + : addPathPrefix(path, basePath) + ) +} diff --git a/client/add-locale.ts b/client/add-locale.ts new file mode 100644 index 0000000..c8cf11a --- /dev/null +++ b/client/add-locale.ts @@ -0,0 +1,13 @@ +import type { addLocale as Fn } from '../shared/lib/router/utils/add-locale' +import { normalizePathTrailingSlash } from './normalize-trailing-slash' + +export const addLocale: typeof Fn = (path, ...args) => { + if (process.env.__NEXT_I18N_SUPPORT) { + return normalizePathTrailingSlash( + ( + require('../shared/lib/router/utils/add-locale') as typeof import('../shared/lib/router/utils/add-locale') + ).addLocale(path, ...args) + ) + } + return path +} diff --git a/client/detect-domain-locale.ts b/client/detect-domain-locale.ts new file mode 100644 index 0000000..9c8204b --- /dev/null +++ b/client/detect-domain-locale.ts @@ -0,0 +1,9 @@ +import type { detectDomainLocale as Fn } from '../shared/lib/i18n/detect-domain-locale' + +export const detectDomainLocale: typeof Fn = (...args) => { + if (process.env.__NEXT_I18N_SUPPORT) { + return ( + require('../shared/lib/i18n/detect-domain-locale') as typeof import('../shared/lib/i18n/detect-domain-locale') + ).detectDomainLocale(...args) + } +} diff --git a/client/has-base-path.ts b/client/has-base-path.ts new file mode 100644 index 0000000..e41ba39 --- /dev/null +++ b/client/has-base-path.ts @@ -0,0 +1,7 @@ +import { pathHasPrefix } from '../shared/lib/router/utils/path-has-prefix' + +const basePath = (process.env.__NEXT_ROUTER_BASEPATH as string) || '' + +export function hasBasePath(path: string): boolean { + return pathHasPrefix(path, basePath) +} diff --git a/client/head-manager.ts b/client/head-manager.ts new file mode 100644 index 0000000..a80acac --- /dev/null +++ b/client/head-manager.ts @@ -0,0 +1,150 @@ +import { setAttributesFromProps } from './set-attributes-from-props' + +import type { JSX } from 'react' + +function reactElementToDOM({ type, props }: JSX.Element): HTMLElement { + const el: HTMLElement = document.createElement(type) + setAttributesFromProps(el, props) + + const { children, dangerouslySetInnerHTML } = props + if (dangerouslySetInnerHTML) { + el.innerHTML = dangerouslySetInnerHTML.__html || '' + } else if (children) { + el.textContent = + typeof children === 'string' + ? children + : Array.isArray(children) + ? children.join('') + : '' + } + return el +} + +/** + * When a `nonce` is present on an element, browsers such as Chrome and Firefox strip it out of the + * actual HTML attributes for security reasons *when the element is added to the document*. Thus, + * given two equivalent elements that have nonces, `Element,isEqualNode()` will return false if one + * of those elements gets added to the document. Although the `element.nonce` property will be the + * same for both elements, the one that was added to the document will return an empty string for + * its nonce HTML attribute value. + * + * This custom `isEqualNode()` function therefore removes the nonce value from the `newTag` before + * comparing it to `oldTag`, restoring it afterwards. + * + * For more information, see: + * https://bugs.chromium.org/p/chromium/issues/detail?id=1211471#c12 + */ +export function isEqualNode(oldTag: Element, newTag: Element) { + if (oldTag instanceof HTMLElement && newTag instanceof HTMLElement) { + const nonce = newTag.getAttribute('nonce') + // Only strip the nonce if `oldTag` has had it stripped. An element's nonce attribute will not + // be stripped if there is no content security policy response header that includes a nonce. + if (nonce && !oldTag.getAttribute('nonce')) { + const cloneTag = newTag.cloneNode(true) as typeof newTag + cloneTag.setAttribute('nonce', '') + cloneTag.nonce = nonce + return nonce === oldTag.nonce && oldTag.isEqualNode(cloneTag) + } + } + + return oldTag.isEqualNode(newTag) +} + +function updateElements(type: string, components: JSX.Element[]) { + const headEl = document.querySelector('head') + if (!headEl) return + + const oldTags = new Set(headEl.querySelectorAll(`${type}[data-next-head]`)) + + if (type === 'meta') { + const metaCharset = headEl.querySelector('meta[charset]') + if (metaCharset !== null) { + oldTags.add(metaCharset) + } + } + + const newTags: Element[] = [] + for (let i = 0; i < components.length; i++) { + const component = components[i] + const newTag = reactElementToDOM(component) + newTag.setAttribute('data-next-head', '') + + let isNew = true + for (const oldTag of oldTags) { + if (isEqualNode(oldTag, newTag)) { + oldTags.delete(oldTag) + isNew = false + break + } + } + + if (isNew) { + newTags.push(newTag) + } + } + + for (const oldTag of oldTags) { + oldTag.parentNode?.removeChild(oldTag) + } + + for (const newTag of newTags) { + // meta[charset] must be first element so special case + if ( + newTag.tagName.toLowerCase() === 'meta' && + newTag.getAttribute('charset') !== null + ) { + headEl.prepend(newTag) + } + headEl.appendChild(newTag) + } +} + +export default function initHeadManager(): { + mountedInstances: Set + updateHead: (head: JSX.Element[]) => void +} { + return { + mountedInstances: new Set(), + updateHead: (head: JSX.Element[]) => { + const tags: Record = {} + + head.forEach((h) => { + if ( + // If the font tag is loaded only on client navigation + // it won't be inlined. In this case revert to the original behavior + h.type === 'link' && + h.props['data-optimized-fonts'] + ) { + if ( + document.querySelector(`style[data-href="${h.props['data-href']}"]`) + ) { + return + } else { + h.props.href = h.props['data-href'] + h.props['data-href'] = undefined + } + } + + const components = tags[h.type] || [] + components.push(h) + tags[h.type] = components + }) + + const titleComponent = tags.title ? tags.title[0] : null + let title = '' + if (titleComponent) { + const { children } = titleComponent.props + title = + typeof children === 'string' + ? children + : Array.isArray(children) + ? children.join('') + : '' + } + if (title !== document.title) document.title = title + ;['meta', 'base', 'link', 'style', 'script'].forEach((type) => { + updateElements(type, tags[type] || []) + }) + }, + } +} diff --git a/client/index.tsx b/client/index.tsx new file mode 100644 index 0000000..d63875b --- /dev/null +++ b/client/index.tsx @@ -0,0 +1,1008 @@ +/* global location */ +// imports polyfill from `@next/polyfill-module` after build. +import '../build/polyfills/polyfill-module' +import type Router from '../shared/lib/router/router' +import type { + AppComponent, + AppProps, + PrivateRouteInfo, +} from '../shared/lib/router/router' + +import React, { type JSX } from 'react' +import ReactDOM from 'react-dom/client' +import { HeadManagerContext } from '../shared/lib/head-manager-context.shared-runtime' +import mitt from '../shared/lib/mitt' +import type { MittEmitter } from '../shared/lib/mitt' +import { RouterContext } from '../shared/lib/router-context.shared-runtime' +import { disableSmoothScrollDuringRouteTransition } from '../shared/lib/router/utils/disable-smooth-scroll' +import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic' +import { + urlQueryToSearchParams, + assign, +} from '../shared/lib/router/utils/querystring' +import { setConfig } from '../shared/lib/runtime-config.external' +import { getURL, loadGetInitialProps, ST } from '../shared/lib/utils' +import type { NextWebVitalsMetric, NEXT_DATA } from '../shared/lib/utils' +import { Portal } from './portal' +import initHeadManager from './head-manager' +import PageLoader from './page-loader' +import type { StyleSheetTuple } from './page-loader' +import { RouteAnnouncer } from './route-announcer' +import { createRouter, makePublicRouterInstance } from './router' +import { getProperError } from '../lib/is-error' +import { ImageConfigContext } from '../shared/lib/image-config-context.shared-runtime' +import type { ImageConfigComplete } from '../shared/lib/image-config' +import { removeBasePath } from './remove-base-path' +import { hasBasePath } from './has-base-path' +import { AppRouterContext } from '../shared/lib/app-router-context.shared-runtime' +import { + adaptForAppRouterInstance, + adaptForPathParams, + adaptForSearchParams, + PathnameContextProviderAdapter, +} from '../shared/lib/router/adapters' +import { + SearchParamsContext, + PathParamsContext, +} from '../shared/lib/hooks-client-context.shared-runtime' +import { onRecoverableError } from './react-client-callbacks/on-recoverable-error' +import tracer from './tracing/tracer' +import { isNextRouterError } from './components/is-next-router-error' + +/// + +declare global { + interface Window { + /* test fns */ + __NEXT_HYDRATED?: boolean + __NEXT_HYDRATED_AT?: number + __NEXT_HYDRATED_CB?: () => void + + /* prod */ + __NEXT_DATA__: NEXT_DATA + __NEXT_P: any[] + } +} +type RenderRouteInfo = PrivateRouteInfo & { + App: AppComponent + scroll?: { x: number; y: number } | null + isHydratePass?: boolean +} +type RenderErrorProps = Omit +type RegisterFn = (input: [string, () => void]) => void + +export const version = process.env.__NEXT_VERSION +export let router: Router +export const emitter: MittEmitter = mitt() + +const looseToArray = (input: any): T[] => [].slice.call(input) + +let initialData: NEXT_DATA +let defaultLocale: string | undefined = undefined +let asPath: string +let pageLoader: PageLoader +let appElement: HTMLElement | null +let headManager: { + mountedInstances: Set + updateHead: (head: JSX.Element[]) => void + getIsSsr?: () => boolean +} +let initialMatchesMiddleware = false +let lastAppProps: AppProps + +let lastRenderReject: (() => void) | null +let devClient: any + +let CachedApp: AppComponent, onPerfEntry: (metric: any) => void +let CachedComponent: React.ComponentType + +class Container extends React.Component<{ + children?: React.ReactNode + fn: (err: Error, info?: any) => void +}> { + componentDidCatch(componentErr: Error, info: any) { + this.props.fn(componentErr, info) + } + + componentDidMount() { + this.scrollToHash() + + // We need to replace the router state if: + // - the page was (auto) exported and has a query string or search (hash) + // - it was auto exported and is a dynamic route (to provide params) + // - if it is a client-side skeleton (fallback render) + // - if middleware matches the current page (may have rewrite params) + // - if rewrites in next.config.js match (may have rewrite params) + if ( + router.isSsr && + (initialData.isFallback || + (initialData.nextExport && + (isDynamicRoute(router.pathname) || + location.search || + process.env.__NEXT_HAS_REWRITES || + initialMatchesMiddleware)) || + (initialData.props && + initialData.props.__N_SSG && + (location.search || + process.env.__NEXT_HAS_REWRITES || + initialMatchesMiddleware))) + ) { + // update query on mount for exported pages + router + .replace( + router.pathname + + '?' + + String( + assign( + urlQueryToSearchParams(router.query), + new URLSearchParams(location.search) + ) + ), + asPath, + { + // @ts-ignore + // WARNING: `_h` is an internal option for handing Next.js + // client-side hydration. Your app should _never_ use this property. + // It may change at any time without notice. + _h: 1, + // Fallback pages must trigger the data fetch, so the transition is + // not shallow. + // Other pages (strictly updating query) happens shallowly, as data + // requirements would already be present. + shallow: !initialData.isFallback && !initialMatchesMiddleware, + } + ) + .catch((err) => { + if (!err.cancelled) throw err + }) + } + } + + componentDidUpdate() { + this.scrollToHash() + } + + scrollToHash() { + let { hash } = location + hash = hash && hash.substring(1) + if (!hash) return + + const el: HTMLElement | null = document.getElementById(hash) + if (!el) return + + // If we call scrollIntoView() in here without a setTimeout + // it won't scroll properly. + setTimeout(() => el.scrollIntoView(), 0) + } + + render() { + if (process.env.NODE_ENV === 'production') { + return this.props.children + } else { + const { PagesDevOverlayBridge } = + require('../next-devtools/userspace/pages/pages-dev-overlay-setup') as typeof import('../next-devtools/userspace/pages/pages-dev-overlay-setup') + return ( + {this.props.children} + ) + } + } +} + +export async function initialize(opts: { devClient?: any } = {}): Promise<{ + assetPrefix: string +}> { + // This makes sure this specific lines are removed in production + if (process.env.NODE_ENV === 'development') { + tracer.onSpanEnd( + ( + require('./tracing/report-to-socket') as typeof import('./tracing/report-to-socket') + ).default + ) + devClient = opts.devClient + } + + initialData = JSON.parse( + document.getElementById('__NEXT_DATA__')!.textContent! + ) + window.__NEXT_DATA__ = initialData + + defaultLocale = initialData.defaultLocale + const prefix: string = initialData.assetPrefix || '' + // With dynamic assetPrefix it's no longer possible to set assetPrefix at the build time + // So, this is how we do it in the client side at runtime + ;(self as any).__next_set_public_path__(`${prefix}/_next/`) //eslint-disable-line + + // Initialize next/config with the environment configuration + setConfig({ + serverRuntimeConfig: {}, + publicRuntimeConfig: initialData.runtimeConfig || {}, + }) + + asPath = getURL() + + // make sure not to attempt stripping basePath for 404s + if (hasBasePath(asPath)) { + asPath = removeBasePath(asPath) + } + + if (process.env.__NEXT_I18N_SUPPORT) { + const { normalizeLocalePath } = + require('../shared/lib/i18n/normalize-locale-path') as typeof import('../shared/lib/i18n/normalize-locale-path') + + const { detectDomainLocale } = + require('../shared/lib/i18n/detect-domain-locale') as typeof import('../shared/lib/i18n/detect-domain-locale') + + const { parseRelativeUrl } = + require('../shared/lib/router/utils/parse-relative-url') as typeof import('../shared/lib/router/utils/parse-relative-url') + + const { formatUrl } = + require('../shared/lib/router/utils/format-url') as typeof import('../shared/lib/router/utils/format-url') + + if (initialData.locales) { + const parsedAs = parseRelativeUrl(asPath) + const localePathResult = normalizeLocalePath( + parsedAs.pathname, + initialData.locales + ) + + if (localePathResult.detectedLocale) { + parsedAs.pathname = localePathResult.pathname + asPath = formatUrl(parsedAs) + } else { + // derive the default locale if it wasn't detected in the asPath + // since we don't prerender static pages with all possible default + // locales + defaultLocale = initialData.locale + } + + // attempt detecting default locale based on hostname + const detectedDomain = detectDomainLocale( + process.env.__NEXT_I18N_DOMAINS as any, + window.location.hostname + ) + + // TODO: investigate if defaultLocale needs to be populated after + // hydration to prevent mismatched renders + if (detectedDomain) { + defaultLocale = detectedDomain.defaultLocale + } + } + } + + if (initialData.scriptLoader) { + const { initScriptLoader } = + require('./script') as typeof import('./script') + initScriptLoader(initialData.scriptLoader) + } + + pageLoader = new PageLoader(initialData.buildId, prefix) + + const register: RegisterFn = ([r, f]) => + pageLoader.routeLoader.onEntrypoint(r, f) + if (window.__NEXT_P) { + // Defer page registration for another tick. This will increase the overall + // latency in hydrating the page, but reduce the total blocking time. + window.__NEXT_P.map((p) => setTimeout(() => register(p), 0)) + } + window.__NEXT_P = [] + ;(window.__NEXT_P as any).push = register + + headManager = initHeadManager() + headManager.getIsSsr = () => { + return router.isSsr + } + + appElement = document.getElementById('__next') + return { assetPrefix: prefix } +} + +function renderApp(App: AppComponent, appProps: AppProps) { + return +} + +function AppContainer({ + children, +}: React.PropsWithChildren<{}>): React.ReactElement { + // Create a memoized value for next/navigation router context. + const adaptedForAppRouter = React.useMemo(() => { + return adaptForAppRouterInstance(router) + }, []) + return ( + + // TODO: Fix disabled eslint rule + // eslint-disable-next-line @typescript-eslint/no-use-before-define + renderError({ App: CachedApp, err: error }).catch((err) => + console.error('Error rendering page: ', err) + ) + } + > + + + + + + + + {children} + + + + + + + + + ) +} + +const wrapApp = + (App: AppComponent) => + (wrappedAppProps: Record): JSX.Element => { + const appProps: AppProps = { + ...wrappedAppProps, + Component: CachedComponent, + err: initialData.err, + router, + } + return {renderApp(App, appProps)} + } + +// This method handles all runtime and debug errors. +// 404 and 500 errors are special kind of errors +// and they are still handle via the main render method. +function renderError(renderErrorProps: RenderErrorProps): Promise { + let { App, err } = renderErrorProps + + // In development runtime errors are caught by our overlay + // In production we catch runtime errors using componentDidCatch which will trigger renderError + if (process.env.NODE_ENV !== 'production') { + // A Next.js rendering runtime error is always unrecoverable + // FIXME: let's make this recoverable (error in GIP client-transition) + devClient.onUnrecoverableError() + + // We need to render an empty so that the `` can + // render itself. + // TODO: Fix disabled eslint rule + // eslint-disable-next-line @typescript-eslint/no-use-before-define + return doRender({ + App: () => null, + props: {}, + Component: () => null, + styleSheets: [], + }) + } + + // Make sure we log the error to the console, otherwise users can't track down issues. + console.error(err) + console.error( + `A client-side exception has occurred, see here for more info: https://nextjs.org/docs/messages/client-side-exception-occurred` + ) + + return pageLoader + .loadPage('/_error') + .then(({ page: ErrorComponent, styleSheets }) => { + return lastAppProps?.Component === ErrorComponent + ? import('../pages/_error') + .then((errorModule) => { + return import('../pages/_app').then((appModule) => { + App = appModule.default as any as AppComponent + renderErrorProps.App = App + return errorModule + }) + }) + .then((m) => ({ + ErrorComponent: m.default as React.ComponentType<{}>, + styleSheets: [], + })) + : { ErrorComponent, styleSheets } + }) + .then(({ ErrorComponent, styleSheets }) => { + // In production we do a normal render with the `ErrorComponent` as component. + // If we've gotten here upon initial render, we can use the props from the server. + // Otherwise, we need to call `getInitialProps` on `App` before mounting. + const AppTree = wrapApp(App) + const appCtx = { + Component: ErrorComponent, + AppTree, + router, + ctx: { + err, + pathname: initialData.page, + query: initialData.query, + asPath, + AppTree, + }, + } + return Promise.resolve( + renderErrorProps.props?.err + ? renderErrorProps.props + : loadGetInitialProps(App, appCtx) + ).then((initProps) => + // TODO: Fix disabled eslint rule + // eslint-disable-next-line @typescript-eslint/no-use-before-define + doRender({ + ...renderErrorProps, + err, + Component: ErrorComponent, + styleSheets, + props: initProps, + }) + ) + }) +} + +// Dummy component that we render as a child of Root so that we can +// toggle the correct styles before the page is rendered. +function Head({ callback }: { callback: () => void }): null { + // We use `useLayoutEffect` to guarantee the callback is executed + // as soon as React flushes the update. + React.useLayoutEffect(() => callback(), [callback]) + return null +} + +const performanceMarks = { + navigationStart: 'navigationStart', + beforeRender: 'beforeRender', + afterRender: 'afterRender', + afterHydrate: 'afterHydrate', + routeChange: 'routeChange', +} as const + +const performanceMeasures = { + hydration: 'Next.js-hydration', + beforeHydration: 'Next.js-before-hydration', + routeChangeToRender: 'Next.js-route-change-to-render', + render: 'Next.js-render', +} as const + +let reactRoot: any = null +// On initial render a hydrate should always happen +let shouldHydrate: boolean = true + +function clearMarks(): void { + ;[ + performanceMarks.beforeRender, + performanceMarks.afterHydrate, + performanceMarks.afterRender, + performanceMarks.routeChange, + ].forEach((mark) => performance.clearMarks(mark)) +} + +function markHydrateComplete(): void { + if (!ST) return + + performance.mark(performanceMarks.afterHydrate) // mark end of hydration + + const hasBeforeRenderMark = performance.getEntriesByName( + performanceMarks.beforeRender, + 'mark' + ).length + if (hasBeforeRenderMark) { + const beforeHydrationMeasure = performance.measure( + performanceMeasures.beforeHydration, + performanceMarks.navigationStart, + performanceMarks.beforeRender + ) + + const hydrationMeasure = performance.measure( + performanceMeasures.hydration, + performanceMarks.beforeRender, + performanceMarks.afterHydrate + ) + + if ( + process.env.NODE_ENV === 'development' && + // Old versions of Safari don't return `PerformanceMeasure`s from `performance.measure()` + beforeHydrationMeasure && + hydrationMeasure + ) { + tracer + .startSpan('navigation-to-hydration', { + startTime: performance.timeOrigin + beforeHydrationMeasure.startTime, + attributes: { + pathname: location.pathname, + query: location.search, + }, + }) + .end( + performance.timeOrigin + + hydrationMeasure.startTime + + hydrationMeasure.duration + ) + } + } + + if (onPerfEntry) { + performance + .getEntriesByName(performanceMeasures.hydration) + .forEach(onPerfEntry) + } + clearMarks() +} + +function markRenderComplete(): void { + if (!ST) return + + performance.mark(performanceMarks.afterRender) // mark end of render + const navStartEntries: PerformanceEntryList = performance.getEntriesByName( + performanceMarks.routeChange, + 'mark' + ) + + if (!navStartEntries.length) return + + const hasBeforeRenderMark = performance.getEntriesByName( + performanceMarks.beforeRender, + 'mark' + ).length + + if (hasBeforeRenderMark) { + performance.measure( + performanceMeasures.routeChangeToRender, + navStartEntries[0].name, + performanceMarks.beforeRender + ) + performance.measure( + performanceMeasures.render, + performanceMarks.beforeRender, + performanceMarks.afterRender + ) + if (onPerfEntry) { + performance + .getEntriesByName(performanceMeasures.render) + .forEach(onPerfEntry) + performance + .getEntriesByName(performanceMeasures.routeChangeToRender) + .forEach(onPerfEntry) + } + } + + clearMarks() + ;[ + performanceMeasures.routeChangeToRender, + performanceMeasures.render, + ].forEach((measure) => performance.clearMeasures(measure)) +} + +function renderReactElement( + domEl: HTMLElement, + fn: (cb: () => void) => JSX.Element +): void { + // mark start of hydrate/render + if (ST) { + performance.mark(performanceMarks.beforeRender) + } + + const reactEl = fn(shouldHydrate ? markHydrateComplete : markRenderComplete) + if (!reactRoot) { + // Unlike with createRoot, you don't need a separate root.render() call here + reactRoot = ReactDOM.hydrateRoot(domEl, reactEl, { + onRecoverableError, + }) + // TODO: Remove shouldHydrate variable when React 18 is stable as it can depend on `reactRoot` existing + shouldHydrate = false + } else { + const startTransition = (React as any).startTransition + startTransition(() => { + reactRoot.render(reactEl) + }) + } +} + +function Root({ + callbacks, + children, +}: React.PropsWithChildren<{ + callbacks: Array<() => void> +}>): React.ReactElement { + // We use `useLayoutEffect` to guarantee the callbacks are executed + // as soon as React flushes the update + React.useLayoutEffect( + () => callbacks.forEach((callback) => callback()), + [callbacks] + ) + + if (process.env.__NEXT_TEST_MODE) { + // eslint-disable-next-line react-hooks/rules-of-hooks + React.useEffect(() => { + window.__NEXT_HYDRATED = true + window.__NEXT_HYDRATED_AT = performance.now() + + if (window.__NEXT_HYDRATED_CB) { + window.__NEXT_HYDRATED_CB() + } + }, []) + } + + return children as React.ReactElement +} + +function doRender(input: RenderRouteInfo): Promise { + let { App, Component, props, err }: RenderRouteInfo = input + let styleSheets: StyleSheetTuple[] | undefined = + 'initial' in input ? undefined : input.styleSheets + Component = Component || lastAppProps.Component + props = props || lastAppProps.props + + const appProps: AppProps = { + ...props, + Component, + err, + router, + } + // lastAppProps has to be set before ReactDom.render to account for ReactDom throwing an error. + lastAppProps = appProps + + let canceled: boolean = false + let resolvePromise: () => void + const renderPromise = new Promise((resolve, reject) => { + if (lastRenderReject) { + lastRenderReject() + } + resolvePromise = () => { + lastRenderReject = null + resolve() + } + lastRenderReject = () => { + canceled = true + lastRenderReject = null + + const error: any = new Error('Cancel rendering route') + error.cancelled = true + reject(error) + } + }) + + // This function has a return type to ensure it doesn't start returning a + // Promise. It should remain synchronous. + function onStart(): boolean { + if ( + !styleSheets || + // We use `style-loader` in development, so we don't need to do anything + // unless we're in production: + process.env.NODE_ENV !== 'production' + ) { + return false + } + + const currentStyleTags: HTMLStyleElement[] = looseToArray( + document.querySelectorAll('style[data-n-href]') + ) + const currentHrefs: Set = new Set( + currentStyleTags.map((tag) => tag.getAttribute('data-n-href')) + ) + + const noscript: Element | null = document.querySelector( + 'noscript[data-n-css]' + ) + const nonce: string | null | undefined = + noscript?.getAttribute('data-n-css') + + styleSheets.forEach(({ href, text }: { href: string; text: any }) => { + if (!currentHrefs.has(href)) { + const styleTag = document.createElement('style') + styleTag.setAttribute('data-n-href', href) + styleTag.setAttribute('media', 'x') + + if (nonce) { + styleTag.setAttribute('nonce', nonce) + } + + document.head.appendChild(styleTag) + styleTag.appendChild(document.createTextNode(text)) + } + }) + return true + } + + function onHeadCommit(): void { + if ( + // Turbopack has it's own css injection handling, this code ends up removing the CSS. + !process.env.TURBOPACK && + // We use `style-loader` in development, so we don't need to do anything + // unless we're in production: + process.env.NODE_ENV === 'production' && + // We can skip this during hydration. Running it wont cause any harm, but + // we may as well save the CPU cycles: + styleSheets && + // Ensure this render was not canceled + !canceled + ) { + const desiredHrefs: Set = new Set(styleSheets.map((s) => s.href)) + const currentStyleTags: HTMLStyleElement[] = + looseToArray( + document.querySelectorAll('style[data-n-href]') + ) + const currentHrefs: string[] = currentStyleTags.map( + (tag) => tag.getAttribute('data-n-href')! + ) + + // Toggle `\");\n currentlyRenderingBoundaryHasStylesToHoist = !0;\n rules.length = 0;\n hrefs.length = 0;\n }\n}\nfunction hasStylesToHoist(stylesheet) {\n return 2 !== stylesheet.state\n ? (currentlyRenderingBoundaryHasStylesToHoist = !0)\n : !1;\n}\nfunction writeHoistablesForBoundary(destination, hoistableState, renderState) {\n currentlyRenderingBoundaryHasStylesToHoist = !1;\n destinationHasCapacity = !0;\n hoistableState.styles.forEach(flushStyleTagsLateForBoundary, destination);\n hoistableState.stylesheets.forEach(hasStylesToHoist);\n currentlyRenderingBoundaryHasStylesToHoist &&\n (renderState.stylesToHoist = !0);\n return destinationHasCapacity;\n}\nfunction flushResource(resource) {\n for (var i = 0; i < resource.length; i++) this.push(resource[i]);\n resource.length = 0;\n}\nvar stylesheetFlushingQueue = [];\nfunction flushStyleInPreamble(stylesheet) {\n pushLinkImpl(stylesheetFlushingQueue, stylesheet.props);\n for (var i = 0; i < stylesheetFlushingQueue.length; i++)\n this.push(stylesheetFlushingQueue[i]);\n stylesheetFlushingQueue.length = 0;\n stylesheet.state = 2;\n}\nfunction flushStylesInPreamble(styleQueue) {\n var hasStylesheets = 0 < styleQueue.sheets.size;\n styleQueue.sheets.forEach(flushStyleInPreamble, this);\n styleQueue.sheets.clear();\n var rules = styleQueue.rules,\n hrefs = styleQueue.hrefs;\n if (!hasStylesheets || hrefs.length) {\n this.push('\");\n rules.length = 0;\n hrefs.length = 0;\n }\n}\nfunction preloadLateStyle(stylesheet) {\n if (0 === stylesheet.state) {\n stylesheet.state = 1;\n var props = stylesheet.props;\n pushLinkImpl(stylesheetFlushingQueue, {\n rel: \"preload\",\n as: \"style\",\n href: stylesheet.props.href,\n crossOrigin: props.crossOrigin,\n fetchPriority: props.fetchPriority,\n integrity: props.integrity,\n media: props.media,\n hrefLang: props.hrefLang,\n referrerPolicy: props.referrerPolicy\n });\n for (\n stylesheet = 0;\n stylesheet < stylesheetFlushingQueue.length;\n stylesheet++\n )\n this.push(stylesheetFlushingQueue[stylesheet]);\n stylesheetFlushingQueue.length = 0;\n }\n}\nfunction preloadLateStyles(styleQueue) {\n styleQueue.sheets.forEach(preloadLateStyle, this);\n styleQueue.sheets.clear();\n}\nfunction writeStyleResourceDependenciesInJS(destination, hoistableState) {\n destination.push(\"[\");\n var nextArrayOpenBrackChunk = \"[\";\n hoistableState.stylesheets.forEach(function (resource) {\n if (2 !== resource.state)\n if (3 === resource.state)\n destination.push(nextArrayOpenBrackChunk),\n (resource = escapeJSObjectForInstructionScripts(\n \"\" + resource.props.href\n )),\n destination.push(resource),\n destination.push(\"]\"),\n (nextArrayOpenBrackChunk = \",[\");\n else {\n destination.push(nextArrayOpenBrackChunk);\n var precedence = resource.props[\"data-precedence\"],\n props = resource.props,\n coercedHref = sanitizeURL(\"\" + resource.props.href);\n coercedHref = escapeJSObjectForInstructionScripts(coercedHref);\n destination.push(coercedHref);\n precedence = \"\" + precedence;\n destination.push(\",\");\n precedence = escapeJSObjectForInstructionScripts(precedence);\n destination.push(precedence);\n for (var propKey in props)\n if (\n hasOwnProperty.call(props, propKey) &&\n ((precedence = props[propKey]), null != precedence)\n )\n switch (propKey) {\n case \"href\":\n case \"rel\":\n case \"precedence\":\n case \"data-precedence\":\n break;\n case \"children\":\n case \"dangerouslySetInnerHTML\":\n throw Error(formatProdErrorMessage(399, \"link\"));\n default:\n writeStyleResourceAttributeInJS(\n destination,\n propKey,\n precedence\n );\n }\n destination.push(\"]\");\n nextArrayOpenBrackChunk = \",[\";\n resource.state = 3;\n }\n });\n destination.push(\"]\");\n}\nfunction writeStyleResourceAttributeInJS(destination, name, value) {\n var attributeName = name.toLowerCase();\n switch (typeof value) {\n case \"function\":\n case \"symbol\":\n return;\n }\n switch (name) {\n case \"innerHTML\":\n case \"dangerouslySetInnerHTML\":\n case \"suppressContentEditableWarning\":\n case \"suppressHydrationWarning\":\n case \"style\":\n case \"ref\":\n return;\n case \"className\":\n attributeName = \"class\";\n name = \"\" + value;\n break;\n case \"hidden\":\n if (!1 === value) return;\n name = \"\";\n break;\n case \"src\":\n case \"href\":\n value = sanitizeURL(value);\n name = \"\" + value;\n break;\n default:\n if (\n (2 < name.length &&\n (\"o\" === name[0] || \"O\" === name[0]) &&\n (\"n\" === name[1] || \"N\" === name[1])) ||\n !isAttributeNameSafe(name)\n )\n return;\n name = \"\" + value;\n }\n destination.push(\",\");\n attributeName = escapeJSObjectForInstructionScripts(attributeName);\n destination.push(attributeName);\n destination.push(\",\");\n attributeName = escapeJSObjectForInstructionScripts(name);\n destination.push(attributeName);\n}\nfunction createHoistableState() {\n return { styles: new Set(), stylesheets: new Set() };\n}\nfunction prefetchDNS(href) {\n var request = currentRequest ? currentRequest : null;\n if (request) {\n var resumableState = request.resumableState,\n renderState = request.renderState;\n if (\"string\" === typeof href && href) {\n if (!resumableState.dnsResources.hasOwnProperty(href)) {\n resumableState.dnsResources[href] = null;\n resumableState = renderState.headers;\n var header, JSCompiler_temp;\n if (\n (JSCompiler_temp =\n resumableState && 0 < resumableState.remainingCapacity)\n )\n JSCompiler_temp =\n ((header =\n \"<\" +\n (\"\" + href).replace(\n regexForHrefInLinkHeaderURLContext,\n escapeHrefForLinkHeaderURLContextReplacer\n ) +\n \">; rel=dns-prefetch\"),\n 0 <= (resumableState.remainingCapacity -= header.length + 2));\n JSCompiler_temp\n ? ((renderState.resets.dns[href] = null),\n resumableState.preconnects && (resumableState.preconnects += \", \"),\n (resumableState.preconnects += header))\n : ((header = []),\n pushLinkImpl(header, { href: href, rel: \"dns-prefetch\" }),\n renderState.preconnects.add(header));\n }\n enqueueFlush(request);\n }\n } else previousDispatcher.D(href);\n}\nfunction preconnect(href, crossOrigin) {\n var request = currentRequest ? currentRequest : null;\n if (request) {\n var resumableState = request.resumableState,\n renderState = request.renderState;\n if (\"string\" === typeof href && href) {\n var bucket =\n \"use-credentials\" === crossOrigin\n ? \"credentials\"\n : \"string\" === typeof crossOrigin\n ? \"anonymous\"\n : \"default\";\n if (!resumableState.connectResources[bucket].hasOwnProperty(href)) {\n resumableState.connectResources[bucket][href] = null;\n resumableState = renderState.headers;\n var header, JSCompiler_temp;\n if (\n (JSCompiler_temp =\n resumableState && 0 < resumableState.remainingCapacity)\n ) {\n JSCompiler_temp =\n \"<\" +\n (\"\" + href).replace(\n regexForHrefInLinkHeaderURLContext,\n escapeHrefForLinkHeaderURLContextReplacer\n ) +\n \">; rel=preconnect\";\n if (\"string\" === typeof crossOrigin) {\n var escapedCrossOrigin = (\"\" + crossOrigin).replace(\n regexForLinkHeaderQuotedParamValueContext,\n escapeStringForLinkHeaderQuotedParamValueContextReplacer\n );\n JSCompiler_temp += '; crossorigin=\"' + escapedCrossOrigin + '\"';\n }\n JSCompiler_temp =\n ((header = JSCompiler_temp),\n 0 <= (resumableState.remainingCapacity -= header.length + 2));\n }\n JSCompiler_temp\n ? ((renderState.resets.connect[bucket][href] = null),\n resumableState.preconnects && (resumableState.preconnects += \", \"),\n (resumableState.preconnects += header))\n : ((bucket = []),\n pushLinkImpl(bucket, {\n rel: \"preconnect\",\n href: href,\n crossOrigin: crossOrigin\n }),\n renderState.preconnects.add(bucket));\n }\n enqueueFlush(request);\n }\n } else previousDispatcher.C(href, crossOrigin);\n}\nfunction preload(href, as, options) {\n var request = currentRequest ? currentRequest : null;\n if (request) {\n var resumableState = request.resumableState,\n renderState = request.renderState;\n if (as && href) {\n switch (as) {\n case \"image\":\n if (options) {\n var imageSrcSet = options.imageSrcSet;\n var imageSizes = options.imageSizes;\n var fetchPriority = options.fetchPriority;\n }\n var key = imageSrcSet\n ? imageSrcSet + \"\\n\" + (imageSizes || \"\")\n : href;\n if (resumableState.imageResources.hasOwnProperty(key)) return;\n resumableState.imageResources[key] = PRELOAD_NO_CREDS;\n resumableState = renderState.headers;\n var header;\n resumableState &&\n 0 < resumableState.remainingCapacity &&\n \"string\" !== typeof imageSrcSet &&\n \"high\" === fetchPriority &&\n ((header = getPreloadAsHeader(href, as, options)),\n 0 <= (resumableState.remainingCapacity -= header.length + 2))\n ? ((renderState.resets.image[key] = PRELOAD_NO_CREDS),\n resumableState.highImagePreloads &&\n (resumableState.highImagePreloads += \", \"),\n (resumableState.highImagePreloads += header))\n : ((resumableState = []),\n pushLinkImpl(\n resumableState,\n assign(\n { rel: \"preload\", href: imageSrcSet ? void 0 : href, as: as },\n options\n )\n ),\n \"high\" === fetchPriority\n ? renderState.highImagePreloads.add(resumableState)\n : (renderState.bulkPreloads.add(resumableState),\n renderState.preloads.images.set(key, resumableState)));\n break;\n case \"style\":\n if (resumableState.styleResources.hasOwnProperty(href)) return;\n imageSrcSet = [];\n pushLinkImpl(\n imageSrcSet,\n assign({ rel: \"preload\", href: href, as: as }, options)\n );\n resumableState.styleResources[href] =\n !options ||\n (\"string\" !== typeof options.crossOrigin &&\n \"string\" !== typeof options.integrity)\n ? PRELOAD_NO_CREDS\n : [options.crossOrigin, options.integrity];\n renderState.preloads.stylesheets.set(href, imageSrcSet);\n renderState.bulkPreloads.add(imageSrcSet);\n break;\n case \"script\":\n if (resumableState.scriptResources.hasOwnProperty(href)) return;\n imageSrcSet = [];\n renderState.preloads.scripts.set(href, imageSrcSet);\n renderState.bulkPreloads.add(imageSrcSet);\n pushLinkImpl(\n imageSrcSet,\n assign({ rel: \"preload\", href: href, as: as }, options)\n );\n resumableState.scriptResources[href] =\n !options ||\n (\"string\" !== typeof options.crossOrigin &&\n \"string\" !== typeof options.integrity)\n ? PRELOAD_NO_CREDS\n : [options.crossOrigin, options.integrity];\n break;\n default:\n if (resumableState.unknownResources.hasOwnProperty(as)) {\n if (\n ((imageSrcSet = resumableState.unknownResources[as]),\n imageSrcSet.hasOwnProperty(href))\n )\n return;\n } else\n (imageSrcSet = {}),\n (resumableState.unknownResources[as] = imageSrcSet);\n imageSrcSet[href] = PRELOAD_NO_CREDS;\n if (\n (resumableState = renderState.headers) &&\n 0 < resumableState.remainingCapacity &&\n \"font\" === as &&\n ((key = getPreloadAsHeader(href, as, options)),\n 0 <= (resumableState.remainingCapacity -= key.length + 2))\n )\n (renderState.resets.font[href] = PRELOAD_NO_CREDS),\n resumableState.fontPreloads &&\n (resumableState.fontPreloads += \", \"),\n (resumableState.fontPreloads += key);\n else\n switch (\n ((resumableState = []),\n (href = assign({ rel: \"preload\", href: href, as: as }, options)),\n pushLinkImpl(resumableState, href),\n as)\n ) {\n case \"font\":\n renderState.fontPreloads.add(resumableState);\n break;\n default:\n renderState.bulkPreloads.add(resumableState);\n }\n }\n enqueueFlush(request);\n }\n } else previousDispatcher.L(href, as, options);\n}\nfunction preloadModule(href, options) {\n var request = currentRequest ? currentRequest : null;\n if (request) {\n var resumableState = request.resumableState,\n renderState = request.renderState;\n if (href) {\n var as =\n options && \"string\" === typeof options.as ? options.as : \"script\";\n switch (as) {\n case \"script\":\n if (resumableState.moduleScriptResources.hasOwnProperty(href)) return;\n as = [];\n resumableState.moduleScriptResources[href] =\n !options ||\n (\"string\" !== typeof options.crossOrigin &&\n \"string\" !== typeof options.integrity)\n ? PRELOAD_NO_CREDS\n : [options.crossOrigin, options.integrity];\n renderState.preloads.moduleScripts.set(href, as);\n break;\n default:\n if (resumableState.moduleUnknownResources.hasOwnProperty(as)) {\n var resources = resumableState.unknownResources[as];\n if (resources.hasOwnProperty(href)) return;\n } else\n (resources = {}),\n (resumableState.moduleUnknownResources[as] = resources);\n as = [];\n resources[href] = PRELOAD_NO_CREDS;\n }\n pushLinkImpl(as, assign({ rel: \"modulepreload\", href: href }, options));\n renderState.bulkPreloads.add(as);\n enqueueFlush(request);\n }\n } else previousDispatcher.m(href, options);\n}\nfunction preinitStyle(href, precedence, options) {\n var request = currentRequest ? currentRequest : null;\n if (request) {\n var resumableState = request.resumableState,\n renderState = request.renderState;\n if (href) {\n precedence = precedence || \"default\";\n var styleQueue = renderState.styles.get(precedence),\n resourceState = resumableState.styleResources.hasOwnProperty(href)\n ? resumableState.styleResources[href]\n : void 0;\n null !== resourceState &&\n ((resumableState.styleResources[href] = null),\n styleQueue ||\n ((styleQueue = {\n precedence: escapeTextForBrowser(precedence),\n rules: [],\n hrefs: [],\n sheets: new Map()\n }),\n renderState.styles.set(precedence, styleQueue)),\n (precedence = {\n state: 0,\n props: assign(\n { rel: \"stylesheet\", href: href, \"data-precedence\": precedence },\n options\n )\n }),\n resourceState &&\n (2 === resourceState.length &&\n adoptPreloadCredentials(precedence.props, resourceState),\n (renderState = renderState.preloads.stylesheets.get(href)) &&\n 0 < renderState.length\n ? (renderState.length = 0)\n : (precedence.state = 1)),\n styleQueue.sheets.set(href, precedence),\n enqueueFlush(request));\n }\n } else previousDispatcher.S(href, precedence, options);\n}\nfunction preinitScript(src, options) {\n var request = currentRequest ? currentRequest : null;\n if (request) {\n var resumableState = request.resumableState,\n renderState = request.renderState;\n if (src) {\n var resourceState = resumableState.scriptResources.hasOwnProperty(src)\n ? resumableState.scriptResources[src]\n : void 0;\n null !== resourceState &&\n ((resumableState.scriptResources[src] = null),\n (options = assign({ src: src, async: !0 }, options)),\n resourceState &&\n (2 === resourceState.length &&\n adoptPreloadCredentials(options, resourceState),\n (src = renderState.preloads.scripts.get(src))) &&\n (src.length = 0),\n (src = []),\n renderState.scripts.add(src),\n pushScriptImpl(src, options),\n enqueueFlush(request));\n }\n } else previousDispatcher.X(src, options);\n}\nfunction preinitModuleScript(src, options) {\n var request = currentRequest ? currentRequest : null;\n if (request) {\n var resumableState = request.resumableState,\n renderState = request.renderState;\n if (src) {\n var resourceState = resumableState.moduleScriptResources.hasOwnProperty(\n src\n )\n ? resumableState.moduleScriptResources[src]\n : void 0;\n null !== resourceState &&\n ((resumableState.moduleScriptResources[src] = null),\n (options = assign({ src: src, type: \"module\", async: !0 }, options)),\n resourceState &&\n (2 === resourceState.length &&\n adoptPreloadCredentials(options, resourceState),\n (src = renderState.preloads.moduleScripts.get(src))) &&\n (src.length = 0),\n (src = []),\n renderState.scripts.add(src),\n pushScriptImpl(src, options),\n enqueueFlush(request));\n }\n } else previousDispatcher.M(src, options);\n}\nfunction adoptPreloadCredentials(target, preloadState) {\n null == target.crossOrigin && (target.crossOrigin = preloadState[0]);\n null == target.integrity && (target.integrity = preloadState[1]);\n}\nfunction getPreloadAsHeader(href, as, params) {\n href = (\"\" + href).replace(\n regexForHrefInLinkHeaderURLContext,\n escapeHrefForLinkHeaderURLContextReplacer\n );\n as = (\"\" + as).replace(\n regexForLinkHeaderQuotedParamValueContext,\n escapeStringForLinkHeaderQuotedParamValueContextReplacer\n );\n as = \"<\" + href + '>; rel=preload; as=\"' + as + '\"';\n for (var paramName in params)\n hasOwnProperty.call(params, paramName) &&\n ((href = params[paramName]),\n \"string\" === typeof href &&\n (as +=\n \"; \" +\n paramName.toLowerCase() +\n '=\"' +\n (\"\" + href).replace(\n regexForLinkHeaderQuotedParamValueContext,\n escapeStringForLinkHeaderQuotedParamValueContextReplacer\n ) +\n '\"'));\n return as;\n}\nvar regexForHrefInLinkHeaderURLContext = /[<>\\r\\n]/g;\nfunction escapeHrefForLinkHeaderURLContextReplacer(match) {\n switch (match) {\n case \"<\":\n return \"%3C\";\n case \">\":\n return \"%3E\";\n case \"\\n\":\n return \"%0A\";\n case \"\\r\":\n return \"%0D\";\n default:\n throw Error(\n \"escapeLinkHrefForHeaderContextReplacer encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React\"\n );\n }\n}\nvar regexForLinkHeaderQuotedParamValueContext = /[\"';,\\r\\n]/g;\nfunction escapeStringForLinkHeaderQuotedParamValueContextReplacer(match) {\n switch (match) {\n case '\"':\n return \"%22\";\n case \"'\":\n return \"%27\";\n case \";\":\n return \"%3B\";\n case \",\":\n return \"%2C\";\n case \"\\n\":\n return \"%0A\";\n case \"\\r\":\n return \"%0D\";\n default:\n throw Error(\n \"escapeStringForLinkHeaderQuotedParamValueContextReplacer encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React\"\n );\n }\n}\nfunction hoistStyleQueueDependency(styleQueue) {\n this.styles.add(styleQueue);\n}\nfunction hoistStylesheetDependency(stylesheet) {\n this.stylesheets.add(stylesheet);\n}\nfunction createRenderState(resumableState, generateStaticMarkup) {\n var idPrefix = resumableState.idPrefix,\n bootstrapChunks = [],\n bootstrapScriptContent = resumableState.bootstrapScriptContent,\n bootstrapScripts = resumableState.bootstrapScripts,\n bootstrapModules = resumableState.bootstrapModules;\n void 0 !== bootstrapScriptContent &&\n bootstrapChunks.push(\n \"