[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/routes.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { stringify } from 'query-string'
import { isEmpty } from 'ramda'

import navigationPageQuery from '../queries/navigationPage.graphql'
import routePreviews from '../queries/routePreviews.graphql'
import routeDataQuery from '../queries/routeData.graphql'
import { generateExtensions } from './blocks'
import { fetchWithRetry } from './fetch'
import { parseMessages } from './messages'
import { isEnabled } from './flags'
import { DeviceInfo } from './withDevice'
import {
  ApolloClientType,
  PageQueryResponse,
  FetchNavigationDataInput,
  ParsedPageQueryResponse,
  GraphQLResult,
  FetchDefaultPages,
  DefaultPagesQueryResponse,
  ParsedDefaultPagesQueryResponse,
  DefaultPagesQueryResult,
  KeyedString,
  PrefetchRouteData,
  ContentResponse,
  ServerPageResponse,
  ParsedServerPageResponse,
} from '../typings/global'
import { RenderRuntime, Pages } from '../typings/runtime'

const parsePageQueryResponse = (
  page: PageQueryResponse
): ParsedPageQueryResponse => {
  const {
    appsEtag,
    appsSettingsJSON,
    blocksJSON,
    blocksTreeJSON,
    contentMapJSON,
    cacheHintsJSON,
    componentsJSON,
    extensionsJSON,
    messages,
    pagesJSON,
    page: {
      blockId,
      canonicalPath,
      metaTags,
      pageContext: { id, type },
      routeId,
      title,
    },
  } = page

  const [
    blocks,
    blocksTree,
    contentMap,
    cacheHints,
    components,
    pages,
    settings,
  ] = [
    blocksJSON,
    blocksTreeJSON,
    contentMapJSON,
    cacheHintsJSON,
    componentsJSON,
    pagesJSON,
    appsSettingsJSON,
  ].map((json) => JSON.parse(json))

  const extensions = isEmpty(blocksTree)
    ? JSON.parse(extensionsJSON)
    : generateExtensions(blocksTree, blocks, contentMap, pages[routeId])

  return {
    appsEtag,
    cacheHints,
    blocks,
    blocksTree,
    contentMap,
    components,
    extensions,
    matchingPage: {
      blockId,
      canonicalPath,
      metaTags,
      pageContext: { id, type },
      routeId,
      title,
    },
    messages: parseMessages(messages),
    pages,
    settings,
  }
}

const parseDefaultPagesQueryResponse = (
  defaultPages: DefaultPagesQueryResponse
): ParsedDefaultPagesQueryResponse => {
  const { componentsJSON } = defaultPages
  const components = JSON.parse(componentsJSON)
  return {
    components,
  }
}

function prependIOProxy(path: string) {
  if (!path) {
    return '/api/io'
  }

  const joiner = path.startsWith('/') ? '' : '/'

  return `/api/io${joiner}${path}`
}

const runtimeFields = [
  'appsEtag',
  'blocks',
  'blocksTree',
  'components',
  'contentMap',
  'extensions',
  'messages',
  'page',
  'pages',
  'query',
  'queryData',
  'route',
  'runtimeMeta',
  'settings',
].join(',')

function getRelativeURLWithQuery({
  path,
  query,
}: {
  path: string
  query: Record<string, any>
}) {
  // we don't need to have the rootPath here because only
  // the relative path matters
  const baseUrl = window.location.origin
  const urlObj = new URL(path, baseUrl)

  urlObj.search = ''

  Object.entries(query).forEach(([key, value]) => {
    if (typeof value === 'undefined') {
      return
    }
    urlObj.searchParams.set(key, value)
  })

  return urlObj.href.slice(baseUrl.length)
}

export const fetchServerPage = async ({
  fetcher,
  path: rawPath,
  query: rawQuery,
  workspace,
  deviceInfo,
  isJanusProxied,
}: {
  path: string
  query?: Record<string, string>
  fetcher: GlobalFetch['fetch']
  workspace?: string
  deviceInfo?: DeviceInfo
  isJanusProxied?: boolean
}): Promise<ParsedServerPageResponse> => {
  const path = isJanusProxied ? prependIOProxy(rawPath) : rawPath
  const url = getRelativeURLWithQuery({
    path,
    query: {
      ...rawQuery,
      ...(workspace ? { workspace } : {}),
      __pickRuntime: runtimeFields,
      ...(deviceInfo && {
        __device: deviceInfo.type,
      }),
    },
  })
  const page: ServerPageResponse = await fetchWithRetry(
    url,
    {
      credentials: 'same-origin',
      headers: {
        accept: 'application/json',
      },
    },
    fetcher
  ).then(({ response }) => response.json())
  const {
    blocksTree,
    blocks,
    contentMap,
    extensions: pageExtensions,
    pages,
    route,
    route: { routeId },
    queryData,
  } = page
  if (routeId === 'redirect') {
    window.location.href = isJanusProxied
      ? prependIOProxy(route.path)
      : route.path
  }

  const queryString = stringify(rawQuery || {})
  const routePath = `${path}${queryString ? '?' + queryString : queryString}`

  const extensions =
    !isEmpty(blocksTree) && blocksTree && blocks && contentMap
      ? generateExtensions(blocksTree, blocks, contentMap, pages[routeId])
      : pageExtensions

  return {
    ...page,
    extensions,
    matchingPage: {
      ...route,
      path: routePath,
    },
    queryData,
  }
}

interface PrefetchPageResponse {
  page: string
  queryData: RenderRuntime['queryData']
  contentResponse: ContentResponse | null
  route: RenderRuntime['route']
}

const prefetchCounters = {
  pages: 0,
  render: 0,
}

const promiseWithCounterWrapper = <T = any>(
  promise: Promise<T>,
  counterName: 'pages' | 'render'
) => {
  return promise
    .then((data) => {
      prefetchCounters[counterName] = 0
      return data
    })
    .catch((error) => {
      prefetchCounters[counterName]++
      throw error
    })
}

export const isPrefetchEnabled = (
  storeSettings: Record<string, any> | null | undefined
) => Boolean(isEnabled('PREFETCH') && storeSettings?.enablePrefetch)

export const isPrefetchActive = (
  storeSettings: Record<string, any> | null | undefined
) =>
  prefetchCounters.pages < 4 &&
  prefetchCounters.render < 4 &&
  isPrefetchEnabled(storeSettings)

export const getPrefetchForPath = async ({
  fetcher,
  path,
  query: rawQuery,
  workspace,
}: {
  path: string
  query?: Record<string, string>
  fetcher: GlobalFetch['fetch']
  workspace?: string
}): Promise<PrefetchPageResponse | null> => {
  const url = getRelativeURLWithQuery({
    path,
    query: {
      ...rawQuery,
      ...(workspace ? { workspace } : {}),
      __pickRuntime: 'page,queryData,contentResponse,route',
    },
  })

  const pageResponsePromise = fetchWithRetry(
    url,
    {
      credentials: 'same-origin',
      headers: {
        accept: 'application/json',
      },
    },
    fetcher,
    0
  ).then(({ response }) => response.json())
  const pageResponse: PrefetchPageResponse | null = await promiseWithCounterWrapper(
    pageResponsePromise,
    'render'
  ).catch(() => null)

  return pageResponse
}

const parseFecthRouteData = (data: PrefetchBlocks): PrefetchRouteData => {
  return {
    extensions: JSON.parse(data.extensionsJSON),
    components: JSON.parse(data.componentsJSON),
    messages: parseMessages(data.messages),
  }
}

const getDeviceFromHint = (hints: RenderRuntime['hints']) => {
  if (hints.desktop) {
    return 'desktop'
  }
  if (hints.phone) {
    return 'phone'
  }
  if (hints.tablet) {
    return 'tablet'
  }
  return 'unknown'
}

interface FetchRouteDataArgs {
  apolloClient: ApolloClientType
  routeId: string
  declarer: string | null
  query?: string
  hints: RenderRuntime['hints']
  renderMajor: number
}

interface PrefetchBlocksQueryVars {
  routeId: string
  declarer: string | null
  query?: string
  device: 'desktop' | 'phone' | 'tablet' | 'unknown'
  renderMajor: number
}

interface PrefetchBlocksQueryResult {
  prefetchBlocks: PrefetchBlocks
}

interface PrefetchBlocks {
  extensionsJSON: string
  componentsJSON: string
  messages: KeyedString[]
}

export const fetchRouteData = ({
  apolloClient,
  routeId,
  declarer,
  query,
  hints,
  renderMajor,
}: FetchRouteDataArgs) => {
  const promise = apolloClient
    .query<PrefetchBlocksQueryResult, PrefetchBlocksQueryVars>({
      fetchPolicy: 'no-cache',
      query: routeDataQuery,
      variables: {
        declarer,
        query,
        routeId,
        device: getDeviceFromHint(hints),
        renderMajor,
      },
    })
    .then(({ data: { prefetchBlocks }, errors }) =>
      errors ? Promise.reject(errors) : parseFecthRouteData(prefetchBlocks)
    )
  return promiseWithCounterWrapper(promise, 'pages')
}

export const fetchNavigationPage = ({
  apolloClient,
  routeId,
  declarer,
  production,
  paramsJSON,
  renderMajor,
  skipCache,
  query,
}: FetchNavigationDataInput) =>
  apolloClient
    .query<{ navigationPage: PageQueryResponse }>({
      fetchPolicy: production && !skipCache ? 'cache-first' : 'network-only',
      query: navigationPageQuery,
      variables: {
        declarer,
        params: paramsJSON,
        production,
        query,
        renderMajor,
        routeId,
      },
    })
    .then<ParsedPageQueryResponse>(
      ({
        data: { navigationPage: pageData },
        errors,
      }: GraphQLResult<'navigationPage', PageQueryResponse>) =>
        errors ? Promise.reject(errors) : parsePageQueryResponse(pageData)
    )

const getRoutesParam = (routeIds: string[], pages: Pages) => {
  return routeIds
    .filter((routeId) => routeId in pages)
    .map((routeId) => {
      const page = pages[routeId]
      return {
        declarer: page.declarer,
        routeId,
      }
    })
}

export const fetchDefaultPages = ({
  apolloClient,
  pages,
  routeIds,
  renderMajor,
}: FetchDefaultPages) => {
  return apolloClient
    .query<{ defaultPages: DefaultPagesQueryResponse }>({
      query: routePreviews,
      variables: { renderMajor, routes: getRoutesParam(routeIds, pages) },
    })
    .then<ParsedDefaultPagesQueryResponse>(
      ({ data: { defaultPages }, errors }: DefaultPagesQueryResult) => {
        return errors
          ? Promise.reject(errors)
          : parseDefaultPagesQueryResponse(defaultPages)
      }
    )
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/apolloCache.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { parse } from 'graphql'
import { promised } from './promise'
import { canUseDOM } from 'exenv'
import { ApolloClientType } from '../typings/global'

export interface QueryData {
  data: string
  query: string
  variables: Record<string, any>
}

export const createHydrationFn = (
  client: ApolloClientType,
  warningMessage = "Error writing query from render-server in Apollo's cache"
) => {
  return async (queryData: QueryData[] | undefined) => {
    if (!queryData || queryData.length === 0) {
      return
    }

    const hydrationFn = canUseDOM ? hydrateApolloCacheAsync : hydrateApolloCache
    await hydrationFn(queryData, client, warningMessage)
  }
}

const hydrateApolloCache = (
  queryData: QueryData[],
  client: ApolloClientType,
  warningMessage?: string
) => {
  return queryData.map(({ data, query, variables }) => {
    try {
      client.writeQuery({
        query: parse(query),
        data: JSON.parse(data),
        variables,
      })
    } catch (error) {
      if (warningMessage) {
        console.warn(warningMessage, error)
      }
    }
  })
}

const hydrateApolloCacheAsync = (
  queryData: QueryData[],
  client: ApolloClientType,
  warningMessage?: string
) => {
  return Promise.all(
    queryData.map(({ data, query, variables }) => {
      return promised<void>((resolve) => {
        try {
          client.writeQuery({
            query: parse(query),
            data: JSON.parse(data),
            variables,
          })
        } catch (error) {
          if (warningMessage) {
            console.warn(warningMessage, error)
          }
        }
        resolve()
      })
    })
  )
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/registerComponent.tsx from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { path } from 'ramda'
import { ComponentType } from 'react'

import maybeWrapWithHMR from './withHMR'

const loadedComponents: Record<string, { implementer: any }> = {}

export const isComponentType = (Arg: any): Arg is ComponentType => {
  const isFunction = typeof Arg === 'function'

  /** If Arg is a function, assumes an UpperCamelCase naming convention
   * for components, and lowerCamelCase for functions.
   * (See https://reactjs.org/docs/jsx-in-depth.html#user-defined-components-must-be-capitalized)
   * If the function name is unable to be determined, defaults
   * to true (i.e. assumes the function is probably a component)
   *
   * This is needed so that functions exported on IO (e.g. HOC) don't
   * get treated as components, so they can be callable.
   */
  if (isFunction) {
    const name: string | undefined = path(
      ['prototype', 'constructor', 'name'],
      Arg
    )
    if (!name) {
      return true
    }

    const firstChar = name.charAt(0)

    if (!firstChar || firstChar === '') {
      return true
    }

    const isFirstCharUpperCase = firstChar.toUpperCase() === firstChar

    return isFirstCharUpperCase
  }

  return !!(Arg && Arg.prototype && Arg.prototype.render)
}

const idToAppAtMajor = (appId: string) => {
  const [name, version] = appId.split('@')
  const [major] = version.split('.')
  return `${name}@${major}.x`
}

export const getLoadedComponent = (componentLocator: string) => {
  return (
    loadedComponents[componentLocator] &&
    loadedComponents[componentLocator].implementer
  )
}

export default (
  module: Module,
  InitialImplementer: any,
  app: string,
  name: string,
  lazy = false
) => {
  const componentLocators = [`${app}/${name}`, `${idToAppAtMajor(app)}/${name}`]

  if (module.hot || !lazy) {
    const implementer = lazy ? InitialImplementer() : InitialImplementer
    const wrappedComponent = maybeWrapWithHMR(module, implementer)
    componentLocators.forEach((locator) => {
      window.__RENDER_8_COMPONENTS__[locator] = wrappedComponent
      loadedComponents[locator] = { implementer: wrappedComponent }
    })

    return wrappedComponent
  }

  componentLocators.forEach((locator) => {
    Object.defineProperty(window.__RENDER_8_COMPONENTS__, locator, {
      get: () => {
        if (loadedComponents[locator]) {
          return loadedComponents[locator].implementer
        }

        const implementer = InitialImplementer()
        componentLocators.forEach((eachLocator) => {
          loadedComponents[eachLocator] = { implementer }
        })

        return implementer
      },
    })
  })

  return InitialImplementer
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/blocks.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { RenderRuntime, Extension, Page, Extensions } from '../typings/runtime'

interface ExtensionResult {
  treePath: string
  extension: Extension
}

export const withStar = (treePath: string) => {
  const parts = treePath.split('/')
  parts[0] = '*'
  return parts.join('/')
}

const createExtensions = (
  blockTreePath: string,
  extensionPath: string,
  bindingPath: string,
  outerNested: boolean,
  blocksTree: NonNullable<RenderRuntime['blocksTree']>,
  blocksMap: NonNullable<RenderRuntime['blocks']>,
  contentMap: NonNullable<RenderRuntime['contentMap']>
): ExtensionResult[] => {
  const result: ExtensionResult[] = []
  if (!blocksTree[blockTreePath]) {
    return result
  }

  const { blockIdMap, contentIdMap } = blocksTree[blockTreePath]
  const mappedBlockId =
    blockIdMap[extensionPath] ||
    blockIdMap[withStar(extensionPath)] ||
    blockIdMap['*']
  const block = blocksMap[mappedBlockId]
  const { blockId, after = [], around = [], before = [], blocks = [] } = block
  const blockContentMapId =
    contentIdMap[bindingPath] || contentIdMap[withStar(bindingPath)] || blockId
  const blockContentId = `${blockContentMapId}+${blockId}`

  const maybeContent = contentMap[blockContentId] ||
    contentMap[blockContentMapId] || { content: {}, contentIds: [] }
  const content = maybeContent.content || maybeContent
  const contentIds = maybeContent.contentIds || []

  const self = [
    {
      treePath: extensionPath,
      extension: {
        after: after.map((insertion) => insertion.extensionPointId),
        around: around.map((insertion) => insertion.extensionPointId),
        before: before.map((insertion) => insertion.extensionPointId),
        blockId: block.originalBlockId || blockId,
        blocks: blocks,
        component: block.component,
        composition: block.composition,
        content,
        contentIds,
        context: block.context,
        hasContentSchema: block.hasContentSchema,
        preview: block.preview,
        props: block.props,
        render: block.render,
        title: block.title,
        track: block.track,
      },
    },
  ]

  const [afterExtensions, aroundExtensions, beforeExtensions] = [
    after,
    around,
    before,
  ].map((outer) => {
    return outer.reduce((acc, { extensionPointId }) => {
      const outerExtensionPath = `${extensionPath}/${extensionPointId}`
      const outerResults = createExtensions(
        extensionPointId,
        outerExtensionPath,
        bindingPath,
        true,
        blocksTree,
        blocksMap,
        contentMap
      )
      acc.push(...outerResults)
      return acc
    }, [] as ExtensionResult[])
  })

  const innerExtensions = blocks.reduce((acc, { extensionPointId }) => {
    const innerBlockTreePath = `${blockTreePath}/${extensionPointId}`
    const innerExtensionPath = `${extensionPath}/${extensionPointId}`
    const innerBindingPath = outerNested
      ? bindingPath
      : `${bindingPath}/${extensionPointId}`
    const innerResults = createExtensions(
      innerBlockTreePath,
      innerExtensionPath,
      innerBindingPath,
      outerNested,
      blocksTree,
      blocksMap,
      contentMap
    )
    acc.push(...innerResults)
    return acc
  }, [] as ExtensionResult[])

  return result.concat(
    self,
    afterExtensions,
    aroundExtensions,
    beforeExtensions,
    innerExtensions
  )
}

export const generateExtensions = (
  blocksTree: NonNullable<RenderRuntime['blocksTree']>,
  blocks: NonNullable<RenderRuntime['blocks']>,
  contentMap: NonNullable<RenderRuntime['contentMap']>,
  page: Page
): Extensions => {
  const extensions: Extensions = {}
  const routeId = page.routeId
  const routeResult = createExtensions(
    routeId,
    routeId,
    routeId,
    false,
    blocksTree,
    blocks,
    contentMap
  )
  routeResult.forEach((eachResult) => {
    extensions[eachResult.treePath] = eachResult.extension
  })

  return extensions
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/isAdmin.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { canUseDOM } from 'exenv'

/**
 * This function checks if anything is running in the Admin environment,
 * without relying on Render's own features.
 */
export function isAdmin() {
  if (!canUseDOM) {
    return false
  }

  const { host = '' } = window.location

  const isMyvtexDomain = host.includes('myvtex.com')

  if (!isMyvtexDomain) {
    return false
  }

  const adminPathnames = [
    '/admin',
    '/_v/segment/admin-login/v1/login', // Let's consider the Admin Login page as an Admin App even though it's technically not.
  ]

  const { pathname = '' } = window.location

  return adminPathnames.some((path) => pathname.startsWith(path))
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/components.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { uniqWith } from 'ramda'
import { hasComponentImplementation, fetchAssets } from './assets'
import { AssetEntry } from '../typings/global'
import { RenderRuntime, Extensions, BlockInsertion } from '../typings/runtime'

const FILE_PATH_REX = /([^/]+?)(?:$|\?)/ // https://regex101.com/r/joJ2p7/1
const FILE_EXT_REX = /(\.min)?(\.js|\.css)/ // https://regex101.com/r/8vmjes/1

const uniqAsset = uniqWith<AssetEntry, AssetEntry>((a, b) => a.path === b.path)

const getAppAndVersion = (locator: string) => {
  const [appAtVersion] = locator.split('/')
  const [app, version] = appAtVersion.split('@')
  return { app, version }
}

export const isConflictingLoadedComponents = (
  navigatedComponents: RenderRuntime['components'],
  loadedComponents: RenderRuntime['components']
) => {
  const loadedVersionsByApp = Object.keys(loadedComponents).reduce(
    (acc, locator) => {
      const { app, version } = getAppAndVersion(locator)
      const [major] = version.split('.')
      if (!acc[app]) {
        acc[app] = { [major]: version }
      } else {
        acc[app][major] = version
      }
      return acc
    },
    {} as Record<string, Record<string, string>>
  )

  return Object.keys(navigatedComponents).some((locator) => {
    const { app, version } = getAppAndVersion(locator)
    const [major] = version.split('.')
    const loadedVersion = loadedVersionsByApp[app]?.[major]

    return loadedVersion && loadedVersion !== version
  })
}

export const fetchComponents = async (
  components: RenderRuntime['components'],
  runtime: RenderRuntime,
  extensions?: Extensions
) => {
  // In order for only fetching `components`, we create corresponding extensions
  // for them if they weren't passed
  if (!extensions) {
    const componentsNames = Object.keys(components)
    extensions = componentsNames.reduce((acc, component) => {
      acc[component] = { component }
      return acc
    }, {} as RenderRuntime['extensions'])
  }
  const componentsToDownload = Object.values(extensions).reduce<string[]>(
    (acc, extension) => {
      if (!extension) {
        return acc
      }
      if (extension.render === 'lazy') {
        return acc
      }
      if (!hasComponentImplementation(extension.component)) {
        acc.push(extension.component)
      }
      const context = extension?.context?.component
      if (context && !hasComponentImplementation(context)) {
        acc.push(context)
      }
      return acc
    },
    []
  )

  if (componentsToDownload.length === 0) {
    return
  }

  const allAssets = traverseListOfComponents(components, componentsToDownload)
  await fetchAssets(runtime, allAssets)
}

export const traverseListOfComponents = (
  componentsData: RenderRuntime['components'] | Record<string, string[]>,
  componentsToTraverse: string[]
) => {
  const allAssets = componentsToTraverse.reduce((acc, component) => {
    const assets = traverseComponent(componentsData, component, false)
    acc.push(...assets)
    return acc
  }, [] as AssetEntry[])

  return uniqAsset(allAssets)
}

export const traverseComponent = (
  components: RenderRuntime['components'] | Record<string, string[]>,
  component: string,
  isRoot = true
): AssetEntry[] => {
  const entry = components[component]
  const [app] = component.split('/')
  if (Array.isArray(entry)) {
    return entry.map((asset) => {
      return { path: asset, app, name: assetName(asset) }
    })
  }
  const { dependencies, assets } = entry
  const assetsForDeps = dependencies.reduce(
    (acc, dependency) => {
      const depAssets = traverseComponent(components, dependency, false)
      return depAssets.concat(acc)
    },
    assets.map((asset) => {
      return { path: asset, app, name: assetName(asset) }
    })
  )

  if (isRoot) {
    return uniqAsset(assetsForDeps)
  }
  return assetsForDeps
}

export const traverseExtension = (
  extensions: Extensions,
  components: RenderRuntime['components'],
  extensionId: string,
  isRoot: boolean
): AssetEntry[] => {
  const extension = extensions[extensionId]
  if (!extension) {
    return []
  }
  const children: string[] =
    extension.blocks?.map(
      (block: BlockInsertion) => `${extensionId}/${block.extensionPointId}`
    ) ?? []

  const component = extension.component

  const componentAssets = traverseComponent(components, component, isRoot)
  const childrenAssets = children
    .map((child: string) =>
      traverseExtension(extensions, components, child, false)
    )
    .reduce((acc, cur) => {
      acc.push(...cur)
      return acc
    }, [])

  return componentAssets.concat(childrenAssets)
}

const assetName = (asset: string) => {
  const baseNameMatch = FILE_PATH_REX.exec(asset)
  const baseName =
    baseNameMatch && baseNameMatch.length > 0 ? baseNameMatch[1] : ''
  const assetName = baseName.replace(FILE_EXT_REX, '')
  return assetName
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/treePath.tsx from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```tsx
import hoistNonReactStatics from 'hoist-non-react-statics'
import React, { ComponentType, useContext, FC, useMemo } from 'react'
import { Extensions } from '../typings/runtime'

const relative = (parent: string, id: string) => id.replace(`${parent}/`, '')

export const escapeRegex = (s: string) =>
  s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')

export const isDirectChild = (id: string, parent: string) => {
  return (
    id !== parent &&
    new RegExp(`^${escapeRegex(parent)}/[a-zA-Z0-9-_#]+$`).test(id)
  )
}

const parseId = (id: string) => {
  const matches = /^(.*?)(\d+)?$/.exec(id)

  if (matches === null) {
    return []
  }

  const [, text, numericText] = matches
  const numbericValue = numericText ? parseInt(numericText, 10) : 0
  return [text, numbericValue]
}

export const getDirectChildren = (extensions: Extensions, treePath: string) => {
  return Object.entries(extensions)
    .filter(
      ([id, extension]) => extension?.component && isDirectChild(id, treePath)
    )
    .map(([id]) => relative(treePath, id))
    .sort((idA, idB) => {
      const [textA, numberA] = parseId(idA)
      const [textB, numberB] = parseId(idB)
      const [valueA, valueB] =
        textA === textB ? [numberA, numberB] : [textA, textB]

      return valueA < valueB ? -1 : 1
    })
}

export const TreePathContext = React.createContext<TreePathProps>({
  treePath: '',
})
TreePathContext.displayName = 'TreePathContext'

export const TreePathContextProvider: FC<TreePathProps> = ({
  treePath,
  children,
}) => {
  const value = useMemo(() => ({ treePath }), [treePath])
  return (
    <TreePathContext.Provider value={value}>
      {children}
    </TreePathContext.Provider>
  )
}
TreePathContextProvider.displayName = 'TreePathContextProvider'

export const useTreePath = () => {
  return useContext(TreePathContext)
}

export interface TreePathProps {
  treePath: string
}

export function withTreePath<TOriginalProps>(
  Component: ComponentType<TOriginalProps & TreePathProps>
): ComponentType<TOriginalProps> {
  const WithTreePath = (props: TOriginalProps) => {
    const { treePath } = useTreePath()
    return <Component {...props} treePath={treePath} />
  }

  WithTreePath.displayName = `TreePath(${
    Component.displayName || Component.name || 'Component'
  })`
  WithTreePath.WrappedComponent = Component

  return hoistNonReactStatics<TOriginalProps, TreePathProps>(
    WithTreePath,
    Component
  )
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/messages.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { pluck, zipObj } from 'ramda'
import { KeyedString } from '../typings/global'

export const parseMessages = (messages: KeyedString[] | null) => {
  return messages && zipObj(pluck('key', messages), pluck('message', messages))
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/slots.tsx from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC, useMemo } from 'react'

import { getChildExtensions } from '../components/ExtensionPoint/index'
import ComponentLoader from '../components/ExtensionPoint/ComponentLoader'
import { useRuntime } from '../components/RenderContext'
import { Extension } from '../typings/runtime'

interface GenerateSlotArgs {
  treePath: string
  slotName: string
  slotValue: string
  hydration: Extension['hydration']
}

export function generateSlot({
  treePath,
  slotName,
  slotValue,
  hydration,
}: GenerateSlotArgs) {
  const newTreePath = `${treePath}/${slotValue}`

  const SlotComponent: FC<any> = (props) => {
    const runtime = useRuntime()
    const extension = runtime.extensions[newTreePath]

    const slotChildren = getChildExtensions(runtime, newTreePath)
    const componentProps = extension?.props ?? {}
    const extensionContent = extension?.content ?? {}

    const componentLoaderPropsWithContent = useMemo(
      () => ({
        // Props received by the slot being used directly as a component
        ...props,
        // Props received by the block referenced by slotValue in a user's theme
        ...componentProps,
        // Content saved in the CMS for this treePath
        ...extensionContent,
      }),
      [componentProps, extensionContent, props]
    )

    /**
     * The ComponentLoader component below will handle both recursive calls to generateSlots
     * (slots could contain slots another slots in its props) and partial hydration support.
     */
    return (
      <ComponentLoader
        component={extension?.component ?? null}
        props={componentLoaderPropsWithContent}
        treePath={newTreePath}
        runtime={runtime}
        hydration={hydration}
      >
        {Array.isArray(slotChildren) &&
        slotChildren.length === 0 &&
        componentLoaderPropsWithContent.children
          ? componentLoaderPropsWithContent.children
          : slotChildren}
      </ComponentLoader>
    )
  }

  SlotComponent.displayName = `${slotName}Slot`

  return SlotComponent
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/assets.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import queryString from 'query-string'
import { getLoadedComponent } from './registerComponent'
import { isEnabled } from './flags'
import { AssetEntry, RenderComponent } from '../typings/global'
import { RenderRuntime, StyleRef, Extensions } from '../typings/runtime'

const imageHost = isEnabled('VTEX_ASSETS_URL')
  ? 'vtexassets.com'
  : 'vteximg.com.br'

function getExtension(path: string) {
  const adjPath = path.split('?')[0]
  const result = /\.\w+$/.exec(adjPath)
  return result ? result[0] : ''
}

const isRelative = (path: string) => {
  return path && path[0] === '/' && path[1] !== '/'
}

export const getVTEXImgHost = (account: string) => {
  return `https://${account}.${imageHost}`
}

const getAbsoluteURL = (
  account: string,
  url: string,
  production: boolean,
  rootPath: string
) => {
  if (!isRelative(url)) {
    return url
  }

  return production ? `${getVTEXImgHost(account)}${url}` : rootPath + url
}

class ServerSideAssetLoadingError extends Error {
  public constructor() {
    super('Loading assets on server side rendering is not supported')
  }
}

function isAbsolute(path: string) {
  return path && !path.startsWith('/')
}

export function addScriptToPage(src: string): Promise<void> {
  return new Promise((resolve, reject) => {
    if (!document || !document.head) {
      throw new ServerSideAssetLoadingError()
    }
    const script = document.createElement('script')
    script.type = 'text/javascript'
    script.onload = () => resolve()
    script.onerror = () => reject()
    script.async = false
    script.src = src
    if (isAbsolute(src)) {
      script.crossOrigin = 'anonymous'
    }
    document.body.appendChild(script)
  })
}

function addStyleToPage(href: string) {
  if (!document || !document.head) {
    throw new ServerSideAssetLoadingError()
  }
  const link = document.createElement('link')
  link.href = href
  link.type = 'text/css'
  link.rel = 'stylesheet'
  if (isAbsolute(href)) {
    link.crossOrigin = 'anonymous'
  }
  const overrideLink =
    document.getElementById('styles_bundled') ||
    document.getElementById('override_link_0')

  if (overrideLink) {
    document.head.insertBefore(link, overrideLink)
  } else {
    document.head.appendChild(link)
  }
}

const updateHref = (linkElement: HTMLLinkElement) => {
  const href = linkElement && linkElement.getAttribute('href')

  if (href) {
    const now = Date.now()
    const idPrefix = 'hot_reload_css'
    const modifiedHref = href.replace(
      /build(\d)+/,
      `build${Math.round(now / 1000)}`
    )
    const newCssElement = document.createElement('link')
    const nextElementSibling = linkElement.nextElementSibling
    newCssElement.rel = 'stylesheet'
    newCssElement.type = 'text/css'
    newCssElement.href = modifiedHref
    newCssElement.id = idPrefix + now
    newCssElement.onload = function onLoadCb() {
      requestAnimationFrame(() => {
        if (
          nextElementSibling &&
          linkElement.parentNode &&
          nextElementSibling.id.indexOf(idPrefix) > -1
        ) {
          linkElement.parentNode.removeChild(nextElementSibling)
        }

        linkElement.disabled = true
      })
    }

    if (linkElement.parentNode) {
      linkElement.parentNode.insertBefore(newCssElement, nextElementSibling)
    }
  }
}

export const hotReloadOverrides = () => {
  const linkElements: HTMLLinkElement[] = Array.from(
    document.querySelectorAll('.override_link')
  )
  linkElements.forEach(updateHref)
}

export const hotReloadTachyons = () => {
  const linkElements: HTMLLinkElement[] = Array.from(
    document.querySelectorAll('.style_link')
  )
  linkElements.forEach(updateHref)
}

function prefetchStyle(href: string) {
  if (!document || !document.head) {
    throw new ServerSideAssetLoadingError()
  }
  const link = document.createElement('link')
  link.href = href
  link.as = 'style'
  link.rel = 'prefetch'
  if (isAbsolute(href)) {
    link.crossOrigin = 'anonymous'
  }
  document.head.appendChild(link)
}

function prefetchScript(href: string) {
  if (!document || !document.head) {
    throw new ServerSideAssetLoadingError()
  }
  const link = document.createElement('link')
  link.href = href
  link.as = 'script'
  link.rel = 'prefetch'
  if (isAbsolute(href)) {
    link.crossOrigin = 'anonymous'
  }
  document.head.appendChild(link)
}

function getExistingScriptSrcs() {
  const paths: string[] = []
  for (let i = 0; i < document.scripts.length; i++) {
    const script = document.scripts.item(i)
    if (script !== null) {
      paths.push(script.src)
    }
  }
  return paths
}

function getExistingStyleHrefs() {
  return Array.from(
    document.head.querySelectorAll('link[type="text/css"],style')
  ).reduce<string[]>((hrefs, styleSheet) => {
    const href =
      (styleSheet as HTMLLinkElement).href ||
      styleSheet.getAttribute('data-href')

    if (href) {
      hrefs.push(href)
    }
    return hrefs
  }, [])
}

function getExistingPrefetchScriptLinks() {
  return getExistingPrefetchLinks('script')
}
function getExistingPrefetchStyleLinks() {
  return getExistingPrefetchLinks('style')
}

function getExistingPrefetchLinks(prefetchType: string[] | string) {
  const paths: string[] = []
  const links = document.getElementsByTagName('link')
  for (let i = 0; i < links.length; i++) {
    const item = links.item(i)
    if (item && item.rel === 'prefetch' && prefetchType.includes(item.as)) {
      paths.push(item.href)
    }
  }
  return paths
}

function assetOnList(path: string, assets: string[]) {
  return assets.some((asset) => asset.indexOf(path) !== -1)
}

function isScript(path: string) {
  return getExtension(path) === '.js'
}

export function shouldAddScriptToPage(
  path: string,
  scripts: string[] = getExistingScriptSrcs()
) {
  return isScript(path) && !assetOnList(path, scripts)
}

export function getLoadedImplementation<P = {}, S = {}>(component: string) {
  return getLoadedComponent(component) as RenderComponent<P, S>
}

export function getImplementation<P = {}, S = {}>(
  component: string | undefined
) {
  if (!component) return null
  return window.__RENDER_8_COMPONENTS__[component] as RenderComponent<P, S>
}

export function hasComponentImplementation(component: string) {
  return window.__RENDER_8_COMPONENTS__.hasOwnProperty(component)
}

export function getExtensionImplementation<P = {}, S = {}>(
  extensions: Extensions,
  name: string
) {
  const extension = extensions[name]
  return extension && extension.component
    ? getImplementation<P, S>(extension.component)
    : null
}

export function fetchUncriticalStyles(
  refs: StyleRef[]
): Promise<Array<UncriticalStyle>> {
  return Promise.all(
    refs.map(
      (ref) =>
        new Promise<UncriticalStyle>((resolve) => {
          const { path, id, class: className, media = '' } = ref
          fetch(path)
            .then(async (response) => {
              const body = await response.text()
              resolve({ href: path, id, className, media, body })
            })
            .catch((error) => {
              console.error(`Error loading uncritical style.`, error)
              resolve({ href: path, id, className, media, body: '' })
            })
        })
    )
  )
}

export async function fetchAssets(
  runtime: RenderRuntime,
  assets: AssetEntry[]
) {
  const existingScripts = getExistingScriptSrcs()
  const existingStyles = getExistingStyleHrefs()

  const styles = getAssetsToAdd(runtime, assets, '.css', existingStyles)
  styles.forEach(addStyleToPage)

  const scripts = getAssetsToAdd(runtime, assets, '.js', existingScripts)
  if (scripts.length === 0) {
    return
  }

  await Promise.all(scripts.map(addScriptToPage))
}

export function prefetchAssets(runtime: RenderRuntime, assets: AssetEntry[]) {
  const existingScripts = getExistingScriptSrcs()
  const existingStyles = getExistingStyleHrefs()
  const existingScriptsPrefetches = getExistingPrefetchScriptLinks()
  const existingStylesPrefetches = getExistingPrefetchStyleLinks()

  const scripts = getAssetsToAdd(runtime, assets, '.js', [
    ...existingScripts,
    ...existingScriptsPrefetches,
  ])
  const styles = getAssetsToAdd(runtime, assets, '.css', [
    ...existingStyles,
    ...existingStylesPrefetches,
  ])
  scripts.forEach(prefetchScript)
  styles.forEach(prefetchStyle)
}

function getAssetsToAdd(
  runtime: RenderRuntime,
  assets: AssetEntry[],
  assetExtension: string,
  existingAssets: string[]
) {
  const { account, production, rootPath = '' } = runtime

  const existingBundledAssetsByApp = groupAssetsByApp(
    existingAssets,
    assetExtension
  )

  return Array.from(
    assets.reduce((acc, asset) => {
      if (
        shouldAddAssetToPage(
          asset,
          assetExtension,
          existingBundledAssetsByApp,
          existingAssets
        )
      ) {
        const absoluteAsset = getAbsoluteURL(
          account,
          asset.path,
          production,
          rootPath
        )
        acc.add(absoluteAsset)
      }
      return acc
    }, new Set<string>())
  )
}

function shouldAddAssetToPage(
  asset: AssetEntry,
  assetExtension: string,
  existingBundledAssetsByApp: Record<string, string[]>,
  existingAssets: string[]
) {
  return (
    hasExtension(asset.path, assetExtension) &&
    !hasBundledAsset(asset.app, asset.name, existingBundledAssetsByApp) &&
    !assetOnList(asset.path, existingAssets)
  )
}

function hasExtension(path: string, fileExtension: string) {
  return getExtension(path) === fileExtension
}

function parseFilesQueryString(
  filesQueryString: string,
  assetExtension: string
) {
  const [appOrType, ...assets] = filesQueryString.split(',')
  if (assetExtension === '.js') {
    return { app: appOrType, assets }
  }

  if (appOrType.indexOf('react~') === 0) {
    const [, app] = appOrType.split('~')
    return { app, assets }
  }

  return {}
}

function groupAssetsByApp(
  assets: string[],
  assetExtension: string
): Record<string, string[]> {
  return assets.reduce((acc: Record<string, string[]>, asset) => {
    if (!asset) {
      return acc
    }

    const { query: parsedQuery } = queryString.parseUrl(asset)
    if (!parsedQuery || !parsedQuery.files) {
      return acc
    }

    const queryFiles: string[] = Array.isArray(parsedQuery.files)
      ? parsedQuery.files
      : [parsedQuery.files]
    queryFiles.forEach((files: string) => {
      if (!files) {
        return
      }

      const { app, assets } = parseFilesQueryString(files, assetExtension)
      if (!app || !assets || assets.length === 0) {
        return
      }

      if (!acc[app]) {
        acc[app] = assets
      } else {
        acc[app].push(...assets)
      }
    })
    return acc
  }, {})
}

function hasBundledAsset(
  app: string,
  asset: string,
  assetsByApp: Record<string, string[]>
) {
  return !!assetsByApp[app] && assetsByApp[app].indexOf(asset) !== -1
}

export type UncriticalStyle = {
  href: string
  media: string
  className?: string
  id?: string
  body: string
} | null

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/session.tsx from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```tsx
import hoistNonReactStatics from 'hoist-non-react-statics'
import React, { ComponentType } from 'react'

import Session from '../components/Session'

interface Options {
  renderWhileLoading?: boolean
}

export const withSession = ({ renderWhileLoading }: Options = {}) => {
  return function <TOriginalProps>(
    Component: ComponentType<TOriginalProps>
  ): ComponentType<TOriginalProps> {
    class WithSession extends React.Component<TOriginalProps> {
      public static get displayName(): string {
        return `WithSession(${
          Component.displayName || Component.name || 'Component'
        })`
      }

      public static get WrappedComponent() {
        return Component
      }

      public render() {
        return (
          <Session renderWhileLoading={renderWhileLoading}>
            <Component {...this.props} />
          </Session>
        )
      }
    }

    return hoistNonReactStatics<TOriginalProps, {}>(WithSession, Component)
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/pages.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { canUseDOM } from 'exenv'
import { History, LocationDescriptorObject } from 'history'
import queryString from 'query-string'
import { difference, is, isEmpty, keys, startsWith } from 'ramda'
import RouteParser from 'route-parser'

import { isEnabled } from './flags'
import {
  NavigationRoute,
  RelativeScrollToOptions,
  RenderScrollOptions,
} from '../typings/global'
import { Pages } from '../typings/runtime'

const EMPTY_OBJECT = (Object.freeze && Object.freeze({})) || {}

const removeTrailingParenthesis = (path: string) =>
  path.endsWith('(') ? path.substr(0, path.length - 1) : path

export function getComparablePrecedence(path: string): string {
  return path
    .split('/')
    .reduce((acc, pathSegment) => {
      if (pathSegment.startsWith('*')) {
        acc.push(3)
      } else if (pathSegment.startsWith(':')) {
        acc.push(2)
      } else if (pathSegment) {
        acc.push(1)
      }

      return acc
    }, [] as number[])
    .join()
}

function isHost(hostname: string) {
  return (
    hostname === (canUseDOM ? window.location.hostname : window.__hostname__)
  )
}

function trimEndingSlash(token: string) {
  return token.replace(/\/$/, '') || '/'
}

function createLocationDescriptor(
  navigationRoute: NavigationRoute,
  {
    hash,
    query,
    scrollOptions,
    fetchPage,
    preventRemount,
    skipSetPath,
  }: Partial<NavigateOptions>
): LocationDescriptorObject {
  return {
    state: {
      fetchPage,
      navigationRoute,
      preventRemount,
      renderRouting: true,
      scrollOptions,
    },
    ...(skipSetPath ? {} : { pathname: navigationRoute.path }),
    ...(query && { search: query }),
    ...(hash && { hash }),
  }
}

function adjustTemplate(template: string) {
  // make last splat capture optional
  return trimEndingSlash(template).replace(/(\/\*\w+)$/, '($1)')
}

function adjustPath(path: string) {
  const [pathname] = path.split('#')
  return trimEndingSlash(pathname)
}

function getValidTemplate(page: string, pages: Pages) {
  const pageDescriptor = pages[page]

  if (!pageDescriptor) {
    console.error(`Page ${page} was not found`)
    return null
  }

  const { path: template, canonical } = pageDescriptor
  if (!template) {
    console.error(`Page ${page} has no path`)
    return null
  }

  return adjustTemplate(canonical || template)
}

export function pathFromPageName(page: string, pages: Pages, params: any) {
  const validTemplate = getValidTemplate(page, pages)
  if (!validTemplate) {
    return null
  }
  const path = new RouteParser(validTemplate).reverse(params)
  const filters = params
    ? params.rest || (params.terms && `/${params.terms}`) || ''
    : ''
  return path ? `${path}${filters}` : null
}

export function queryStringToMap(query: string): Record<string, any> {
  if (!query) {
    return {}
  }
  return queryString.parse(query)
}

export function mapToQueryString(query: Record<string, any> = {}): string {
  return queryString.stringify(query, { encode: false })
}

export function getPageParams(path: string, routePath: string) {
  const pagePathWithRest =
    routePath && /\*\w+$/.test(routePath)
      ? routePath
      : routePath.replace(/\/?$/, '*_rest')
  return (routePath && getParams(pagePathWithRest, path)) || EMPTY_OBJECT
}

function getParams(template: string, target: string) {
  const properTemplate = adjustTemplate(template)
  const properTarget = adjustPath(target)
  return new RouteParser(properTemplate).match(properTarget)
}

function getPagePath(name: string, pages: Pages) {
  const { path: pagePath, cname } = pages[name]
  return cname && isHost(cname) ? '/' : pagePath
}

function checkValidParams(id: string, pages: Pages, path: string, params: any) {
  const template = getValidTemplate(id, pages) || ''
  const validParams = getParams(template, path) as Record<string, any>
  const invalidParams = difference(keys(params), keys(validParams))

  if (!isEmpty(invalidParams)) {
    console.warn(
      `The following params are invalid: ${invalidParams.join(', ')}`
    )
  }
}

function getRouteFromPageName(
  id: string,
  pages: Pages,
  params: any
): NavigationRoute | null {
  const pathParams = Object.keys(params)
    .filter((key) => !key.startsWith('__'))
    .reduce((acc, key) => {
      acc[key] = params[key]
      return acc
    }, {} as any)

  const path = pathFromPageName(id, pages, pathParams) || ''
  checkValidParams(id, pages, path, pathParams)

  return path ? { id, path, params } : null
}

function getCanonicalPath(
  canonicalPathTemplate: string,
  params: Record<string, string>
): string | false {
  const properPathTemplate = adjustTemplate(canonicalPathTemplate)
  const canonicalPath = new RouteParser(properPathTemplate).reverse(params)
  if (canonicalPath) {
    return canonicalPath
  }

  console.warn(
    `Canonical path template '${canonicalPathTemplate}' could not be created with params: ${params}`
  )
  return false
}

export function getRouteFromPath(
  path: string,
  pages: Pages
): NavigationRoute | null {
  const routeMatch = routeMatchFromPath(path, pages)
  if (!routeMatch) {
    return null
  }

  const params = getPageParams(path, routeMatch.path)
  const navigationPath = routeMatch.canonical
    ? getCanonicalPath(routeMatch.canonical, params) || path
    : path

  return {
    id: routeMatch.id,
    params,
    path: navigationPath,
  }
}

export function getRouteFromPathOld(
  path: string,
  pages: Pages,
  query?: string,
  hash?: string
): NavigationRoute | null {
  const queryMap = query ? queryStringToMap(hash ? query + hash : query) : {}
  const routeMatch = routeIdFromPathAndQueryOld(path, queryMap, pages)
  if (!routeMatch) {
    return null
  }

  const params = getPageParams(path, routeMatch.path)
  const navigationPath = routeMatch.canonical
    ? getCanonicalPath(routeMatch.canonical, params) || path
    : path

  return {
    id: routeMatch.id,
    params,
    path: navigationPath,
  }
}

const mergePersistingQueries = (currentQuery: string, query: string) => {
  const KEYS = ['disableUserLand', '__bindingAddress', '__siteEditor']
  const current = queryStringToMap(currentQuery)
  const next = queryStringToMap(query)
  const has = (value?: string) => !!value || value === null
  const persisting = KEYS.reduce<Record<string, any>>((cur, key) => {
    if (has(current[key]) && current[key] !== 'false') {
      cur[key] = current[key]
    }
    return cur
  }, {})
  return mapToQueryString({ ...persisting, ...next })
}

export const formatPathParameters = (params: Record<string, string>) => {
  const obj: Record<string, string> = {}
  for (const key in params) {
    obj[key] = decodeURI(params[key])
  }

  return obj
}

export function getNavigationRouteToNavigate(
  pages: Pages,
  options: NavigateOptions,
  showLogs = false
) {
  const {
    page,
    params,
    query: inputQuery,
    to: inputTo = '',
    rootPath,
    modifiers,
    modifiersOptions,
  } = options

  if (!page && !inputTo) {
    showLogs &&
      console.error(
        `Invalid navigation options. You should use 'page' or 'to' parameters`
      )
    return null
  }

  if (inputTo && inputQuery) {
    showLogs &&
      console.warn(
        `You shouldn't pass 'query' in a separate prop when using 'to'`
      )
  }

  // If the prop `to` is something like `to="#header"`
  // just change the hash using location, avoid doing a history navigation
  if (inputTo.indexOf('#') === 0 && inputTo.indexOf('?') === -1) {
    return { hash: inputTo }
  }

  const [to, extractedQuery] = (is(String, inputTo) ? inputTo : '').split('?')
  const [realQuery, hash] = (is(String, extractedQuery)
    ? extractedQuery
    : ''
  ).split('#')
  const realHash = is(String, hash) ? `#${hash}` : ''
  let query = inputQuery || realQuery

  const decodedParams = formatPathParameters(params)

  let navigationRoute: any = {}

  if (isEnabled('RENDER_NAVIGATION')) {
    const fallbackPage = { path: to, params: {}, id: '' }
    const routeFromPage =
      page && getRouteFromPageName(page, pages, decodedParams)
    const routeFromPath = getRouteFromPath(to, pages)
    navigationRoute = routeFromPage || routeFromPath || fallbackPage
  } else {
    navigationRoute = page
      ? getRouteFromPageName(page, pages, decodedParams)
      : getRouteFromPathOld(to, pages, query, realHash)
  }

  if (!navigationRoute) {
    showLogs &&
      console.warn(
        `Unable to find route for ${
          page ? `page '${page}' and the passed parameters` : `path '${to}'`
        }`
      )
    return null
  }

  navigationRoute.path = navigationRootPath(navigationRoute.path, rootPath)
  if (modifiers) {
    for (const modifier of modifiers) {
      const { path, query: fixedQuery } = modifier({
        path: navigationRoute.path,
        query,
        options: modifiersOptions,
      })
      navigationRoute.path = path || navigationRoute.path
      query = fixedQuery || query
    }
  }
  navigationRoute.realHash = realHash
  navigationRoute.query = query
  return navigationRoute
}

export function navigate(
  history: History | null,
  pages: Pages,
  options: NavigateOptions
) {
  const {
    scrollOptions,
    fallbackToWindowLocation = true,
    replace,
    fetchPage = true,
    preventRemount,
    skipSetPath = false,
  } = options

  const navigationRoute = getNavigationRouteToNavigate(pages, options, true)
  if (navigationRoute.hash) {
    window.location.hash = navigationRoute.hash
    return true
  }

  if (navigationRoute == null) {
    return false
  }

  if (history) {
    const nextQuery = mergePersistingQueries(
      history.location.search,
      navigationRoute.query
    )
    const location = createLocationDescriptor(navigationRoute, {
      fetchPage,
      preventRemount,
      query: nextQuery,
      scrollOptions,
      hash: navigationRoute.realHash,
      skipSetPath,
    })
    const method = replace ? 'replace' : 'push'
    window.setTimeout(() => history[method](location), 0)
    return true
  }

  if (fallbackToWindowLocation) {
    window.location.href = `${navigationRoute.path}${navigationRoute.query}`
    return true
  }

  return false
}

function navigationRootPath(path: string, rootPath?: string) {
  // Prefix any non-absolute paths (e.g. http:// or https://) with runtime.rootPath
  if (
    rootPath &&
    !path.startsWith('http') &&
    !path.startsWith(`${rootPath}/`)
  ) {
    return rootPath + path
  }

  return path
}

export function goBack(history: History | null) {
  if (history) {
    window.setTimeout(() => history.goBack(), 0)
    return true
  }

  console.warn('Unable to go to previous page')
  return false
}

export function scrollTo(options: RelativeScrollToOptions) {
  const { baseElementId = null } = options || {}
  const scrollAnchor =
    baseElementId && document.querySelector(`#${baseElementId}`)

  if (!scrollAnchor) {
    return polyfillScrollTo(options)
  }

  const { top, left } = scrollAnchor.getBoundingClientRect()
  polyfillScrollTo({
    left: left + window.scrollX + (options.left || 0),
    top: top + window.scrollY + (options.top || 0),
  })
}

function polyfillScrollTo(options: ScrollToOptions) {
  try {
    window.scrollTo(options)
  } catch (e) {
    const x = options.left == null ? window.scrollX : options.left
    const y = options.top == null ? window.scrollY : options.top
    window.scrollTo(x, y)
  }
}

function routeMatchForMappedURL(
  mappedSegments: string[],
  routes: Pages
): RouteMatch | null {
  let id: string | undefined
  let score: number
  let highScore: number = Number.NEGATIVE_INFINITY

  for (const name in routes) {
    const { map = [], path: routePath } = routes[name]
    if (!routePath || map.length === 0 || !startsWith(map, mappedSegments)) {
      continue
    }

    score = map.length
    if (highScore > score) {
      continue
    }

    highScore = score
    id = name
  }

  if (!id) {
    return null
  }

  const { path } = routes[id]
  const pathSegments = path.split('/')
  const slicedPathSegments = pathSegments.slice(0, highScore + 1)
  const newPath = slicedPathSegments.join('/')

  return {
    id,
    path: removeTrailingParenthesis(newPath),
  }
}

function routeMatchFromPath(path: string, routes: Pages): RouteMatch | null {
  let id: string | undefined
  let pathPrecedence: string
  let chosenPathPrecedence: string | null = null

  for (const name in routes) {
    const pagePath = getPagePath(name, routes)
    if (!pagePath) {
      continue
    }

    const matches = !!getParams(pagePath, path)
    if (!matches) {
      continue
    }

    pathPrecedence = getComparablePrecedence(pagePath)
    if (
      chosenPathPrecedence !== null &&
      chosenPathPrecedence < pathPrecedence
    ) {
      continue
    }

    chosenPathPrecedence = pathPrecedence
    id = name
  }

  if (!id) {
    return null
  }

  return {
    canonical: routes[id].canonical,
    id,
    path: getPagePath(id, routes),
  }
}

function routeIdFromPathAndQueryOld(
  path: string,
  query: Record<string, string>,
  routes: Pages
) {
  const mappedSegments = query.map ? query.map.split(',') : []
  let routeMatch: RouteMatch | null = null

  // Don't use map segments to match a route when Render
  // navigation is enabled
  if (mappedSegments.length > 0 && !isEnabled('RENDER_NAVIGATION')) {
    routeMatch = routeMatchForMappedURL(mappedSegments, routes)
  }

  if (!routeMatch) {
    routeMatch = routeMatchFromPath(path, routes)
  }

  return routeMatch
}

interface RouteMatch {
  canonical?: string
  id: string
  path: string
}

export interface NavigateOptions {
  hash?: string
  page?: string
  params?: any
  query?: any
  to?: string
  scrollOptions?: RenderScrollOptions
  fallbackToWindowLocation?: boolean
  replace?: boolean
  fetchPage?: boolean
  preventRemount?: boolean
  rootPath?: string
  modifiers?: Set<NavigationRouteModifier>
  modifiersOptions?: Record<string, any>
  skipSetPath?: boolean
}

export interface NavigationRouteChange {
  path: string
  query?: string
  options?: Record<string, NavigationRouteChange>
}

export type NavigationRouteModifier = (
  navigationRoute: NavigationRouteChange
) => NavigationRouteChange

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/location.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import queryString from 'query-string'
import { pickBy } from 'ramda'

export const appendLocationSearch = (
  search: string,
  params: Record<string, string>
) => {
  if (!search) {
    return `?${queryString.stringify(params, { encode: false })}`
  }

  const included = queryString.parse(search)
  const filteredParams = pickBy(
    (_, paramName) => !(paramName in included),
    params
  ) as Record<string, string>
  const additionalSearch = queryString.stringify(filteredParams, {
    encode: false,
  })
  if (!additionalSearch) {
    return search
  }

  return `${search}&${additionalSearch}`
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/cookie.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { canUseDOM } from 'exenv'

const ONE_YEAR_S = 60 * 60 * 24 * 365

export const setCookie = (
  key: string,
  value: string,
  path = '/',
  maxAge: number = ONE_YEAR_S
) => {
  if (!canUseDOM) {
    return
  }

  window.document.cookie = `${key}=${value};path=${path};max-age=${maxAge}`
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/reactApollo.tsx from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { PureComponent } from 'react'
import { Query, Mutation } from 'react-apollo'
import { compose } from 'ramda'

const noop = () => {}

class RenderRuntimeQuery extends PureComponent<{
  children: (result: any) => JSX.Element | null
}> {
  public render() {
    const { children, ...rest } = this.props

    return (
      <Query {...(rest as any)}>
        {(result: any) => {
          if (result.networkStatus === 1 && result.data === undefined) {
            result.data = {} as any
            result.refetch = noop as any
          }
          return children(result)
        }}
      </Query>
    )
  }
}

class RenderRuntimeMutation extends PureComponent<{
  children: (mutateFunction: any, result: any) => JSX.Element | null
}> {
  public render() {
    const { children, ...rest } = this.props

    return (
      <Mutation {...(rest as any)}>
        {(mutateFunction: any, result: any) => {
          return children(mutateFunction, result)
        }}
      </Mutation>
    )
  }
}

export const createCustomReactApollo = () => {
  // eslint-disable-next-line no-unused-vars
  const { Query, Mutation, ...rest } = window.ReactApollo
  return {
    ...rest,
    Query: RenderRuntimeQuery,
    Mutation: RenderRuntimeMutation,
    compose,
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/vteximg.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { isEnabled } from './flags'

const SECURE_PROTOCOL = 'https://'
const PROTOCOL_RELATIVE_PREFIX = '//'
const ARQUIVOS_RELATIVE_PREFIX = '/arquivos'
const ASSETS_RELATIVE_PREFIX = '/assets'
const FILE_MANAGER_PATH_REGEX = /url\("?(.*\/)assets\/vtex\.file-manager-graphql/

const toVtexAssets = (url: string) =>
  isEnabled('VTEX_ASSETS_URL')
    ? url.replace('vteximg.com.br', 'vtexassets.com')
    : url

export function optimizeSrcForVtexImg(vtexImgHost: string, src?: any) {
  try {
    if (src && src.indexOf(PROTOCOL_RELATIVE_PREFIX) === 0) {
      return toVtexAssets(
        src.replace(PROTOCOL_RELATIVE_PREFIX, SECURE_PROTOCOL)
      )
    }

    if (
      src &&
      (src.indexOf(ARQUIVOS_RELATIVE_PREFIX) === 0 ||
        src.indexOf(ASSETS_RELATIVE_PREFIX) === 0)
    ) {
      return vtexImgHost + src
    }

    return toVtexAssets(src)
  } catch (e) {
    console.warn('Failed to optimize image source.')
    return src
  }
}

export function optimizeStyleForVtexImg(vtexImgHost: string, style?: any) {
  try {
    if (style && style.backgroundImage) {
      const match = FILE_MANAGER_PATH_REGEX.exec(style.backgroundImage)
      if (match && match[1]) {
        style.backgroundImage = style.backgroundImage.replace(
          match[1],
          vtexImgHost + '/'
        )
      }
    }

    return style
  } catch (e) {
    console.warn('Failed to optimize style.')
    return style
  }
}

export function isStyleWritable(props: any): boolean {
  const propertyDescriptor = Object.getOwnPropertyDescriptor(props, 'style')
  return (propertyDescriptor && propertyDescriptor.writable) || false
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/pages.test.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { formatPathParameters, getComparablePrecedence } from './pages'
import RouteParser from 'route-parser'

describe('#formatPathParameters', () => {
  it('should remove any encoding', () => {
    const params = [
      {
        term: 'a%2520b%2520c',
      },
      {
        term: 'a%20b%20c',
      },
    ]

    const results = params.map((param) => formatPathParameters(param))

    expect(results).toEqual([{ term: 'a%20b%20c' }, { term: 'a b c' }])
  })
})

describe('#routeParser', () => {
  it('should not add double enconding', () => {
    const params = [
      {
        term: 'a',
      },
      {
        term: 'b',
      },
      {
        term: 'a%20b%20c',
      },
    ]

    const validTemplate = ['/:term', '/:term', '/:term']

    const results = params.map((param, index) =>
      new RouteParser(validTemplate[index]).reverse(param)
    )

    expect(results).toEqual(['/a', '/b', '/a%20b%20c'])
  })
})

describe('#getPrecedence', () => {
  it('should set precedence as expected', () => {
    const routes = [
      '/a/b/c',
      '/a/:b/c',
      '/:a/b/c',
      '/a/b/:c',
      '/a/:b/:c',
      '/:a/b/:c',
      '/a/b/*c',
      '/a/:b/*c',
      '/:a/b/*c',
      '/a/*b',
      '/:a/*b',
      '/*a',
    ]

    const result = routes.sort((a, b) =>
      getComparablePrecedence(b) > getComparablePrecedence(a) ? -1 : 1
    )

    expect(result).toEqual([
      '/a/b/c',
      '/a/b/:c',
      '/a/b/*c',
      '/a/:b/c',
      '/a/:b/:c',
      '/a/:b/*c',
      '/a/*b',
      '/:a/b/c',
      '/:a/b/:c',
      '/:a/b/*c',
      '/:a/*b',
      '/*a',
    ])
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/events.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import EventEmitter from 'eventemitter3'
import { canUseDOM } from 'exenv'
import { RenderRuntime } from '../typings/runtime'

interface IOEvent {
  key: string
  body: {
    code: string
    type: string
    hash: string
    locales: any
    subject: string
    updated: string[]
  }
}

interface EventEmitterSource extends Array<EventEmitter> {
  eventSource?: EventSource
}

interface EmittersRegistry {
  [key: string]: EventEmitterSource
}

const CONNECTION_CLOSED = 2

const emittersByWorkspace: EmittersRegistry = {}

const initSSE = (
  account: string,
  workspace: string,
  baseURI: string,
  isProductionWorkspace: boolean
) => {
  if (isProductionWorkspace) {
    return undefined
  }

  require('eventsource-polyfill')
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  const myvtexSSE = require('myvtex-sse')
  const path = `vtex.builder-hub:*:react2,pages0,build.status,pages1,styles?workspace=${workspace}`
  const linkInterruptedPath = `colossus:*:link_interrupted?workspace=${workspace}`
  const source: EventSource = myvtexSSE(account, workspace, path, {
    verbose: false,
    host: baseURI,
  })
  const linkInterruptedSource: EventSource = myvtexSSE(
    account,
    workspace,
    linkInterruptedPath,
    {
      verbose: false,
      host: baseURI,
    }
  )

  const handler = ({ data }: MessageEvent) => {
    const event = JSON.parse(data) as IOEvent
    const {
      key,
      body: { code, type, hash, locales, subject, updated },
    } = event

    if (key === 'build.status') {
      switch (code) {
        case 'start':
          console.log(`[build] Build started. app=${subject}`)
          emittersByWorkspace[`${account}/${workspace}`].forEach((e) =>
            e.emit('build.status', code)
          )
          break
        case 'success':
          console.log(`[build] Build success. app=${subject}`)
          emittersByWorkspace[`${account}/${workspace}`].forEach((e) =>
            e.emit('build.status', code)
          )
          break
        case 'fail':
          console.log(`[build] Build failed. app=${subject}`)
          emittersByWorkspace[`${account}/${workspace}`].forEach((e) =>
            e.emit('build.status', code)
          )
          break
      }
      return
    }

    if (key === 'link_interrupted') {
      console.log(`[colossus] Link interrupted.`)
      emittersByWorkspace[`${account}/${workspace}`].forEach((e) =>
        e.emit('link_interrupted', code)
      )
    }

    switch (type) {
      case 'hmr':
        console.log(`[react2] Received update. app=${subject} hash=${hash}`)
        if (window.__RENDER_8_HOT__[subject]) {
          window.__RENDER_8_HOT__[subject].emit('webpackHotUpdate', hash)
        }
        break
      case 'reload':
        console.log(`[react2] Received reload. app=${subject}`)
        emittersByWorkspace[`${account}/${workspace}`].forEach((e) =>
          e.emit('build.status', 'reload')
        )
        location.reload(true)
        break
      case 'locales':
        console.log(
          `[react2] Received locale update. appId=${subject} locales=${locales}`
        )
        emittersByWorkspace[`${account}/${workspace}`].forEach((e) =>
          e.emit('localesUpdated', locales)
        )
        break
      case 'changed':
        console.log('[pages0] Extensions changed.')
        emittersByWorkspace[`${account}/${workspace}`].forEach((e) =>
          e.emit('extensionsUpdated')
        )
        break
      case 'blocks':
        console.log('[pages1] Blocks changed.')
        emittersByWorkspace[`${account}/${workspace}`].forEach((e) =>
          e.emit('blocksUpdated')
        )
        break
      case 'styles':
        console.log('[styles] Styles changed.')
        if (updated.some((filePath) => /style\.json/gi.test(filePath))) {
          emittersByWorkspace[`${account}/${workspace}`].forEach((e) =>
            e.emit('styleTachyonsUpdate')
          )
        }
        if (updated.some((filePath) => /overrides\.css/gi.test(filePath))) {
          emittersByWorkspace[`${account}/${workspace}`].forEach((e) =>
            e.emit('styleOverrides')
          )
        }

        break
    }
  }

  source.onmessage = handler
  linkInterruptedSource.onmessage = handler
  source.onopen = () =>
    console.log('[render] Connected to event server successfully')
  source.onerror = () =>
    console.log('[render] Connection to event server failed')
  linkInterruptedSource.onopen = () =>
    console.log('[colossus] Connected to event server successfully')
  linkInterruptedSource.onerror = () =>
    console.log('[colossus] Connection to event server failed')

  return source
}

export const registerEmitter = (runtime: RenderRuntime, baseURI: string) => {
  if (!canUseDOM) {
    return null
  }

  const { account, production, workspace } = runtime

  // Share SSE connections for same account and workspace
  if (!emittersByWorkspace[`${account}/${workspace}`]) {
    emittersByWorkspace[`${account}/${workspace}`] = []
    emittersByWorkspace[`${account}/${workspace}`].eventSource = initSSE(
      account,
      workspace,
      baseURI,
      production
    )

    if (!production) {
      document.addEventListener('visibilitychange', () => {
        const es = emittersByWorkspace[`${account}/${workspace}`].eventSource
        // Ensure SSE server connection
        if (!document.hidden && es && es.readyState === CONNECTION_CLOSED) {
          emittersByWorkspace[`${account}/${workspace}`].eventSource = initSSE(
            account,
            workspace,
            baseURI,
            production
          )
        }
      })
    }
  }

  if (!runtime.emitter) {
    runtime.emitter = new EventEmitter()
    emittersByWorkspace[`${account}/${workspace}`].push(runtime.emitter)
  }

  return runtime.emitter
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/flags.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
const flags = {
  RENDER_NAVIGATION: true,
  VTEX_ASSETS_URL: true,
  PREFETCH: false,
}

window.flags = flags

export const isEnabled = (flag: keyof typeof flags) => window.flags[flag]

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/host.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { canUseDOM } from 'exenv'

import { RenderRuntime } from '../typings/runtime'

function isRenderServedPage() {
  const generatorMetaTag = document.querySelector(`meta[name='generator']`)
  const generator = generatorMetaTag && generatorMetaTag.getAttribute('content')
  return generator && generator.startsWith('vtex.render-server')
}

export function getBaseURI(runtime: RenderRuntime) {
  const {
    account,
    workspace,
    publicEndpoint,
    isJanusProxied,
    rootPath = '',
  } = runtime

  if (!canUseDOM) {
    return `${workspace}--${account}.${publicEndpoint}`
  }

  const {
    location: { hostname },
  } = window

  if (isJanusProxied) {
    return `${hostname}/api/io`
  }

  return hostname.endsWith(`.${publicEndpoint}`) || isRenderServedPage()
    ? hostname + rootPath
    : `${hostname}/api/io`
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/treePath.test.tsx from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { escapeRegex, isDirectChild } from './treePath'

describe('#escapeRegex', () => {
  const specialRegexCharacters = [
    '[',
    ']',
    '\\',
    '^',
    '$',
    '.',
    '|',
    '?',
    '*',
    '+',
    '(',
    ')',
  ]

  it(`should prefix regex special characters: [ '${specialRegexCharacters.join(
    `', '`
  )}' ] with \\`, () => {
    specialRegexCharacters.forEach((specialRegexCharacter) => {
      expect(escapeRegex(specialRegexCharacter)).toEqual(
        `\\${specialRegexCharacter}`
      )
    })
  })

  it('should return own string if there are no special characters', () => {
    expect('testString').toBe('testString')
  })
})

describe('#isDirectChild', () => {
  it('should return true for one level of nesting', () => {
    expect(isDirectChild('store/home/shelf', 'store/home')).toBe(true)
  })

  it('should allow "_" on names', () => {
    expect(isDirectChild('store/home/shelf_0', 'store/home')).toBe(true)
  })

  it('should allow regex special characters on names', () => {
    expect(
      isDirectChild('store/home/$before_0/shelf_0', 'store/home/$before_0')
    ).toBe(true)
  })

  it('should return false for 2 levels nesting', () => {
    expect(isDirectChild('store/home/shelf/image', 'store/home')).toBe(false)
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/withDevice.tsx from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC, ComponentType } from 'react'
import { useSSR } from '../components/NoSSR'
import { useMediaLayout } from 'use-media'
import { RenderRuntime } from '../typings/runtime'

export interface Props {
  runtime: RenderRuntime
}

export interface WithDeviceProps {
  deviceInfo: DeviceInfo
}

export enum Device {
  phone = 'phone',
  tablet = 'tablet',
  desktop = 'desktop',
  unknown = 'unknown',
}

export interface DeviceInfo {
  type: Device
  isMobile: boolean
}

const useDevice = (hints: RenderRuntime['hints']) => {
  const isSSR = useSSR()

  /** These screensizes are hardcoded, based on the default
   * Tachyons breakpoints. They should probably be the ones
   * configured via the style.json file, if available. */

  const isScreenMedium = useMediaLayout({ minWidth: '40rem' })
  const isScreenLarge = useMediaLayout({ minWidth: '64.1rem' })

  const serverDevice = {
    type: hints.phone
      ? Device.phone
      : hints.tablet
      ? Device.tablet
      : Device.desktop,
    isMobile: hints.mobile,
  }

  const clientDevice = {
    type: isScreenLarge
      ? Device.desktop
      : isScreenMedium
      ? Device.tablet
      : Device.phone,
    isMobile: !isScreenLarge,
  }

  return isSSR ? serverDevice : clientDevice
}

export const withDevice = <P extends Props>(
  Component: ComponentType<P & WithDeviceProps>
): FC<P> => {
  const MemoizedWithDevice = React.memo(
    ({ type, isMobile, ...props }: DeviceInfo & Props) => (
      <Component deviceInfo={{ type, isMobile }} {...(props as P)} />
    )
  )

  MemoizedWithDevice.displayName = 'MemoizedWithDevice'

  const WithDevice = ({ ...props }: P) => {
    const hints = props.runtime.hints
    const deviceInfo = useDevice(hints)

    return (
      <MemoizedWithDevice
        type={deviceInfo.type}
        isMobile={deviceInfo.isMobile}
        {...props}
      />
    )
  }

  return WithDevice
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/withHMR.tsx from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```tsx
import hoistNonReactStatics from 'hoist-non-react-statics'
import PropTypes from 'prop-types'
import React, { Component, ComponentType } from 'react'

import { withEmitter } from '../components/RenderContext'
import { isComponentType } from './registerComponent'
import { withTreePath } from './treePath'

export default (module: Module, InitialImplementer: any) => {
  if (!isComponentType(InitialImplementer) || !module.hot) {
    return InitialImplementer
  }

  /**
   * We should move this registering code to registerComponent.tsx. We currently
   * can not do this since old apps are still using this function to register
   * thenselves with the old withHMR function
   */
  const instances: HMRComponent[] = []
  const registerInstance = (instance: HMRComponent) => {
    instances.push(instance)
  }

  const unregisterInstance = (instance: HMRComponent) => {
    const index = instances.findIndex((registered) => registered === instance)
    if (index !== -1) {
      instances.splice(index, 1)
    }
  }

  class HMRComponent extends Component<any, { lastUpdate?: number }> {
    public static propTypes = {
      __clearError: PropTypes.func,
      __errorInstance: PropTypes.node,
    }

    public static get displayName(): string {
      return (
        HMRComponent.Implementer.displayName ||
        HMRComponent.Implementer.name ||
        'Component'
      )
    }

    public static hotReload = (NewImplementer: ComponentType) => {
      HMRComponent.Implementer = NewImplementer
      hoistNonReactStatics(HMRComponent, NewImplementer)
      instances.forEach((instance) => instance.updateComponent())
    }

    private static Implementer = InitialImplementer as ComponentType

    public updateComponent = () => {
      const { __emitter, treePath, __clearError, __errorInstance } = this.props
      __emitter.emit('build.status', 'hmr:success')

      if (__clearError && __errorInstance) {
        __clearError()
      } else {
        this.setState({ lastUpdate: Date.now() })
      }

      console.log(
        `[render] Component updated via HMR. treePath=${treePath} updated=${HMRComponent.displayName}`
      )
    }

    public componentDidMount() {
      registerInstance(this)
    }

    public componentWillUnmount() {
      unregisterInstance(this)
    }

    public render() {
      // eslint-disable-next-line
      const { __emitter, __clearError, __errorInstance, treePath, ...props } = this.props
      return (
        this.props.__errorInstance || <HMRComponent.Implementer {...props} />
      )
    }
  }

  return hoistNonReactStatics(
    withEmitter(withTreePath(HMRComponent)),
    InitialImplementer
  )
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/graphQLErrorsStore.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { GraphQLError } from 'graphql'

export interface ExtendedGraphQLError extends GraphQLError {
  operationId: string
  extensions: {
    code: string
    exception: {
      stacktrace: string
      name?: string
    }
  }
}

export function isExtendedGraphQLError(
  error: GraphQLError
): error is ExtendedGraphQLError {
  return 'operationId' in error
}

const ignoredErrorTypes = [
  'UserInputError',
  'AuthenticationError',
  'ForbiddenError',
]

class GraphQLErrorsStore {
  private operationIds: string[] = []

  public addOperationIds(errors: readonly GraphQLError[]) {
    const operationIds = errors.reduce<string[]>((acc, error) => {
      if (
        isExtendedGraphQLError(error) &&
        !ignoredErrorTypes.includes(
          (error.extensions.exception && error.extensions.exception.name) || ''
        )
      ) {
        return acc.concat(error.operationId)
      }
      return acc
    }, [])
    this.operationIds = this.operationIds.concat(operationIds)
  }

  public getOperationIds() {
    const operationIds = this.operationIds.slice()
    this.operationIds = []
    return operationIds
  }
}

const Store = new GraphQLErrorsStore()

export default Store

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/cacheControl.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
const MINIMUM_MAX_AGE_S = 5
const DEFAULT_MAX_AGE_S = 10 * 60
const STATIC_PAGE_MAX_AGE_S = 7 * 24 * 60 * 60

const parseCacheControl = (cacheControl: string) => {
  const cacheDirectives = cacheControl.split(',').map((d) => d.trim())
  const maxAgeDirective = cacheDirectives.find((d) => d.startsWith('max-age'))
  const maxAgeStr = maxAgeDirective ? maxAgeDirective.split('=')[1] : null
  const maxAge = maxAgeStr ? parseInt(maxAgeStr, 10) : 0

  return {
    maxAge,
    noCache: cacheDirectives.includes('no-cache'),
    noStore: cacheDirectives.includes('no-store'),
  }
}

const getMaxAge = (response: Response) => {
  const cacheControlHeader = response.headers.get('cache-control')
  if (!cacheControlHeader) {
    return DEFAULT_MAX_AGE_S
  }

  const { noCache, noStore, maxAge } = parseCacheControl(cacheControlHeader)
  if (noCache || noStore) {
    return MINIMUM_MAX_AGE_S
  }

  return Math.max(MINIMUM_MAX_AGE_S, maxAge)
}

export default class PageCacheControl {
  private maxAges: number[] = []

  public get maxAge(): number {
    return this.maxAges.length > 0
      ? Math.min(...this.maxAges)
      : STATIC_PAGE_MAX_AGE_S
  }

  public evaluate(innerResponse: Response) {
    const responseMaxAge = getMaxAge(innerResponse)
    this.maxAges.push(responseMaxAge)
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/reactIntl.tsx from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```tsx
import PropTypes from 'prop-types'
import { FormattedRelativeTime, injectIntl } from 'react-intl'
import React, { FC } from 'react'
import hoistNonReactStatics from 'hoist-non-react-statics'

function getDisplayName(Component: React.ComponentType<any>) {
  return Component.displayName || Component.name || 'Component'
}

// We do our best to maintain compatibility with the prvious API but this is not perfect
const getCorrectUnit = (timeDiff: number) => {
  const years = timeDiff / (millisecondsInDay * 365)
  if (isUnitValid(years)) {
    return {
      value: Math.round(years),
      unit: 'year',
    }
  }
  const months = timeDiff / (millisecondsInDay * 30)
  if (isUnitValid(months)) {
    return {
      value: Math.round(months),
      unit: 'month',
    }
  }

  const days = timeDiff / millisecondsInDay
  if (isUnitValid(days)) {
    return {
      value: Math.round(days),
      unit: 'day',
    }
  }
  const hours = timeDiff / millisecondsInHour
  if (isUnitValid(hours)) {
    return {
      value: Math.round(hours),
      unit: 'hour',
    }
  }
  const minutes = (timeDiff / 60) * 1000
  if (isUnitValid(minutes)) {
    return {
      value: Math.round(minutes),
      unit: 'minute',
    }
  }
  return {
    value: Math.round(timeDiff / 1000),
    unit: 'second',
  }
}

const FormattedRelative: FC<any> = (props) => {
  console.warn(
    'FormattedRelative was removed in react-intl@3.x, you are using an approximation. Plase start using FormattedRelativeTime.'
  )
  const { value } = props
  let timeDiff
  if (value instanceof Date) {
    timeDiff = value.getTime() - Date.now()
  }
  if (typeof value === 'string') {
    timeDiff = new Date(value).getTime() - Date.now()
  }
  if (!timeDiff) {
    return null
  }
  const { value: newValue, unit } = getCorrectUnit(timeDiff)
  return <FormattedRelativeTime value={newValue} unit={unit as any} />
}

const addLocaleData = () => {
  console.warn(
    'addLocaleData was removed in react-intl@3.x and is not needed anymore. Please read: https://github.com/formatjs/react-intl/blob/master/docs/Upgrade-Guide.md#formattedrelativetime'
  )
}

const millisecondsInHour = 60 * 60 * 1000
const millisecondsInDay = 24 * millisecondsInHour

const isUnitValid = (value: number) => Math.abs(value) >= 1

const renderFormatRelative = (intl: any) => (value: any) => {
  console.warn(
    'formatRelative was removed in react-intl@3.x, please start using formatRelativeTime.'
  )
  let timeDiff
  if (value instanceof Date) {
    timeDiff = value.getTime() - Date.now()
  }
  if (typeof value === 'string') {
    timeDiff = new Date(value).getTime() - Date.now()
  }
  if (!timeDiff) {
    return null
  }

  const { value: newValue, unit } = getCorrectUnit(timeDiff)

  return intl.formatRelativeTime(newValue, unit)
}

const renderRuntimeInjectIntl: FC<any> = (
  WrappedComponent: any,
  options?: any
) => {
  const intlPropName = (options && options.intlPropName) || 'intl'
  const forwardRef = (options && options.forwardRef) || false
  const WithRenderRuntimeIntl = injectIntl((props) => {
    const intlObj = props[intlPropName] as any
    if (intlObj) {
      intlObj.formatRelative = renderFormatRelative(intlObj)
    }
    return (
      <WrappedComponent
        {...props}
        ref={forwardRef ? props.forwardedRef : null}
      />
    )
  }, options)

  WithRenderRuntimeIntl.displayName = `renderRuntimeInjectIntl(${getDisplayName(
    WrappedComponent
  )}`
  WithRenderRuntimeIntl.WrappedComponent = WrappedComponent

  if (forwardRef) {
    return hoistNonReactStatics(
      // eslint-disable-next-line react/display-name
      React.forwardRef((props, ref) => (
        <WithRenderRuntimeIntl {...(props as any)} forwardedRef={ref} />
      )),
      WrappedComponent
    ) as any
  }
  return hoistNonReactStatics(WithRenderRuntimeIntl, WrappedComponent) as any
}

export const createReactIntl = () => {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const { injectIntl, ...rest } = window.ReactIntl
  if (!window.ReactIntlLocaleData) {
    window.ReactIntlLocaleData = {}
  }

  return {
    ...rest,
    injectIntl: renderRuntimeInjectIntl,
    intlShape: PropTypes.any,
    FormattedRelative,
    addLocaleData,
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/promise.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { canUseDOM } from 'exenv'

type Resolver<T> = T extends void
  ? (resolve: () => void) => void
  : (resolve: (value: T) => void) => void

export const promised = <T = void>(resolver: Resolver<T>) => {
  return new Promise<T>((resolve) => {
    if (!canUseDOM) {
      resolver(resolve)
    } else {
      setTimeout(() => resolver(resolve), 1)
    }
  })
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/fetch.ts from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```typescript
const delay = (ms: number): Promise<void> => {
  return new Promise((resolve) => setTimeout(resolve, ms))
}

const RETRY_STATUSES = [
  0,
  408,
  425,
  429,
  500,
  501,
  502,
  503,
  504,
  505,
  506,
  507,
  508,
  510,
  511,
]

const canRetry = (status: number) => RETRY_STATUSES.includes(status)

const ok = (status: number) => 200 <= status && status < 300
const isNotFound = (status: number) => status === 404

export const fetchWithRetry = (
  url: string,
  init: RequestInit,
  fetcher: GlobalFetch['fetch'],
  maxRetries = 3
) => {
  let status = 500
  const callFetch = (
    attempt = 0
  ): Promise<{ response: Response; error: any }> =>
    fetcher(url, init)
      .then((response) => {
        status = response.status
        return ok(status) || isNotFound(status)
          ? { response, error: null }
          : response
              .json()
              .then((error) => ({ response, error }))
              .catch(() => ({
                response,
                error: { message: 'Unable to parse JSON' },
              }))
      })
      .then(({ response, error }) => {
        if (error) {
          throw new Error(error.message || 'Unknown error')
        }
        return { response, error: null }
      })
      .catch((error) => {
        console.error(error)

        if (attempt >= maxRetries || !canRetry(status)) {
          throw error
        }

        const ms = 2 ** attempt * 500
        return delay(ms).then(() => callFetch(++attempt))
      })

  return callFetch()
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/utils/dom.tsx from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { canUseDOM } from 'exenv'
import React, { Fragment, ReactElement, ReactPortal } from 'react'
import { createPortal as reactCreatePortal } from 'react-dom'
import { NamedMarkup } from '../typings/global'

const portalPattern = /START_SERVER_PORTAL_([^!]+)!((\n|.)+)END_SERVER_PORTAL_\1!/g

// Map `placeholder/with/slashes` to `render-placeholder-with-slashes`.
const hyphenate = (name: string) => name.replace(/[^\w]/g, '-')

const portalWrapperId = (name: string) => `render-portal-ssr-${hyphenate(name)}`

export const RENDER_CONTAINER_CLASS = 'render-container'

export const ROUTE_CLASS_PREFIX = 'render-route-'

export const routeClass = (name: string) =>
  `${ROUTE_CLASS_PREFIX}${hyphenate(name)}`

const renderContainer = (name: string, markup: string) =>
  `<div class="${RENDER_CONTAINER_CLASS} ${routeClass(name)}">${markup}</div>`

const portalWrapper = (name: string, markup: string) =>
  `<span id="${portalWrapperId(name)}">${markup}</span>`

export const createPortal = (
  children: ReactElement<any>,
  name: string,
  hydrate: boolean
) => {
  window.__hasPortals__ = true

  if (!hydrate) {
    return canUseDOM ? null : (
      <Fragment>
        {`START_SERVER_PORTAL_${name}!`}
        {children}
        {`END_SERVER_PORTAL_${name}!`}
      </Fragment>
    )
  }

  const ssrPortalContainer = document.getElementById(portalWrapperId(name))
  if (ssrPortalContainer) {
    ssrPortalContainer.remove()
  }

  const container = document.getElementsByClassName(routeClass(name))[0]
  if (!container) {
    console.warn(`Missing React Portal container div.${routeClass(name)}`)
    return null
  }

  return reactCreatePortal(children, container) as ReactPortal
}

export const getMarkups = (
  pageName: string,
  pageMarkup: string
): NamedMarkup[] => {
  const markups: NamedMarkup[] = []

  let matches = window.__hasPortals__ && portalPattern.exec(pageMarkup)
  let strippedMarkup = pageMarkup
  while (matches) {
    const [matched, name, markup] = matches
    strippedMarkup = strippedMarkup.replace(matched, '')
    markups.push({
      markup: renderContainer(name, portalWrapper(name, markup)),
      name,
    })
    matches = portalPattern.exec(pageMarkup)
  }

  markups.unshift({
    markup: renderContainer(pageName, strippedMarkup),
    name: pageName,
  })

  return markups
}

const checkIsSiteEditorIframe = () => {
  try {
    return (
      canUseDOM &&
      window.top !== window.self &&
      window.top.__provideRuntime &&
      !!window.top.__provideRuntime &&
      window.__RUNTIME__.route.domain === 'store'
    )
  } catch {
    return false
  }
}

export const isSiteEditorIframe = checkIsSiteEditorIframe()

export const getOrCreateContainer = () => {
  if (!canUseDOM) {
    throw new Error('Should not try to get container on server side')
  }

  const existingContainer = document.getElementsByClassName(
    RENDER_CONTAINER_CLASS
  )[0]

  if (existingContainer) {
    return { container: existingContainer, created: false }
  }

  const containerDiv = document.createElement('div')
  containerDiv.className = `render-container ${routeClass(name)}`
  containerDiv.style.display = 'none'
  document.body.appendChild(containerDiv)
  return { container: containerDiv, created: true }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/components/LoadingBar.css from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```css
@keyframes loading-bar-animation {
  0% {
    transform: scale(0, 1);
  }
  10% {
    transform: scale(0.5, 1);
  }
  20% {
    transform: scale(0.75, 1);
  }
  100% {
    transform: scale(0.95, 1);
  }
}

.loadingBarAnimation {
  transform-origin: 0 0;
  animation: loading-bar-animation 6s ease-out;
  transform: scale(0.95, 1);
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of render-runtime/react/components/ChildBlock.tsx from the render-runtime VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import useChildBlock, { Block, ChildBlockType } from './useChildBlock'

interface ChildBlockProps extends ChildBlockType {
  children(block: Block | null): React.ReactNode
}

function ChildBlock({ id, children }: ChildBlockProps) {
  const block = useChildBlock({ id })

  return children(block)
}

export default ChildBlock

```