[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 minicart/react/legacy/__tests__/hooks/useMarketingSessionParams.test.js from the minicart VTEX app.

[Assistant]
Here's the implementation:

```javascript
import { renderHook, wait } from '@vtex/test-tools/react'

import useMarketingSessionParams from '../../hooks/useMarketingSessionParams'

const fakeSessionWithData = {
  response: {
    namespaces: {
      public: {
        utm_source: {
          value: 'utmSource',
        },
        utm_medium: {
          value: 'utmMedium',
        },
        utm_campaign: {
          value: 'utmCampaign',
        },
        utmi_cp: {
          value: 'utmiCampaign',
        },
        utmi_p: {
          value: 'utmiPage',
        },
        utmi_pc: {
          value: 'utmiPart',
        },
      },
    },
  },
}

const fakeSessionNoData = {
  response: {
    namespaces: {
      public: {},
    },
  },
}

test('if session has correct data, get and format input correctly', async () => {
  window.__RENDER_8_SESSION__ = {}
  window.__RENDER_8_SESSION__.sessionPromise = new Promise(resolve =>
    resolve(fakeSessionWithData)
  )
  const { result } = renderHook(() => useMarketingSessionParams())

  await wait()

  const { utmParams, utmiParams } = result.current
  expect(utmParams).toMatchObject({
    source: 'utmSource',
    medium: 'utmMedium',
    campaign: 'utmCampaign',
  })
  expect(utmiParams).toMatchObject({
    campaign: 'utmiCampaign',
    page: 'utmiPage',
    part: 'utmiPart',
  })
})

test('if session does not have data, leave object as undefined', async () => {
  window.__RENDER_8_SESSION__ = {}
  window.__RENDER_8_SESSION__.sessionPromise = new Promise(resolve =>
    resolve(fakeSessionNoData)
  )
  const { result } = renderHook(() => useMarketingSessionParams())

  await wait()

  const { utmParams, utmiParams } = result.current
  expect(utmParams).toBeUndefined()
  expect(utmiParams).toBeUndefined()
})

test('if session promise rejects, everything still works, response is undefined', async () => {
  window.__RENDER_8_SESSION__ = {}
  window.__RENDER_8_SESSION__.sessionPromise = new Promise((_, reject) =>
    reject()
  )
  const { result } = renderHook(() => useMarketingSessionParams())

  await wait()

  const { utmParams, utmiParams } = result.current
  expect(utmParams).toBeUndefined()
  expect(utmiParams).toBeUndefined()
})

```


[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 instore-stone-fields/messages/pt.json from the instore-stone-fields VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "instore-stone-fields.stone-code": "Stone Code*",
  "instore-stone-fields.mothers-name": "Nome da mãe",
  "instore-stone-fields.business-name": "Nome do negócio"
}

```


[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 instore-stone-fields/messages/en.json from the instore-stone-fields VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "instore-stone-fields.stone-code": "Stone Code*",
  "instore-stone-fields.mothers-name": "Mother's name",
  "instore-stone-fields.business-name": "Business name"
}

```


[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 instore-stone-fields/messages/es.json from the instore-stone-fields VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "instore-stone-fields.stone-code": "Stone Code*",
  "instore-stone-fields.mothers-name": "Nombre de la Madre",
  "instore-stone-fields.business-name": "Nombre del Negocio"
}

```


[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 instore-stone-fields/docs/README.md from the instore-stone-fields VTEX app.

[Assistant]
Here's the implementation:

```markdown
# Instore Stone Fields

Refer to [Instore Form Fields](https://github.com/vtex-apps/instore-form-fields)

Apparently you had to declare another "index" extension, even if it rendered nothing, otherwise locales wouldn't load :(
```


[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 instore-stone-fields/pages/pages.json from the instore-stone-fields VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "extensions": {
    "registration-container/index": {
      "component": "index"
    },
    "registration-container/1": {
      "component": "vtex.instore-form-fields/FormInput",
      "props": {
        "ffor": "customsenha",
        "nameKey": "instore-stone-fields.stone-code",
        "isRequired": true
      }
    },
    "registration-container/2": {
      "component": "vtex.instore-form-fields/FormInput",
      "props": {
        "ffor": "customnomemae",
        "nameKey": "instore-stone-fields.mothers-name",
        "isRequired": false
      }
    },
    "registration-container/3": {
      "component": "vtex.instore-form-fields/FormInput",
      "props": {
        "ffor": "nomefantasiapessoal",
        "nameKey": "instore-stone-fields.business-name",
        "isRequired": 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 instore-stone-fields/react/index.js from the instore-stone-fields VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import FormInput from 'vtex.instore-form-fields/FormInput'

class StoneFields extends React.Component {
  render() {
    return null
  }
}

export default StoneFields

```


[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 external-prices-node-template/docs/README.md from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```markdown
# External Price App

A reference IO app to integrate external price sources with VTEX Pricing Hub.

## Implementation

1. Fork this app.
2. In the `manifest.json` file:
    * Change the `vendor` field to the name of the account you are using.
    * Change the `name` field to one of your choosing.
    * Add your service host (e.g. `myhost.com`) in an `outbound-access` policy.
3. In the `node/env.ts` file, add your service endpoint as follows:

    ```
    const ENV = {
      SERVICE_ENDPOINT: 'http://myservice.com',
    }

    export default ENV
    ```

4. Change the ` node/clients/externalPrice.ts` file to parse data received by the external pricing app and return it in a way that Pricing Hub can understand. See more details on the specification of this format in the [Pricing Hub documentation](https://developers.vtex.com/vtex-rest-api/docs/pricing-hub).

> ⛔ Do not change the `"routes"` in `node/service.json` nor the files in `node/typings/`, since they were created to reflect Pricing Hub behavior.

```


[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 external-prices-node-template/node/service.json from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "memory": 256,
  "ttl": 10,
  "timeout": 2,
  "minReplicas": 2,
  "maxReplicas": 4,
  "workers": 1,
  "routes": {
    "price": {
      "path": "/_v/external-prices/price",
      "public": false,
      "access": "authorized",
      "policies": [
        {
          "effect": "allow",
          "actions": [
            "post"
          ],
          "principals": [
            "vrn:vtex.vtex-id:*:*:*:user/vtexappkey-appvtex"
          ]
        }
      ]
    }
  }
}

```


[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 external-prices-node-template/node/package.json from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "dependencies": {
    "co-body": "^6.0.0",
    "ramda": "^0.25.0"
  },
  "devDependencies": {
    "@types/co-body": "^0.0.3",
    "@types/jest": "^24.0.18",
    "@types/node": "^12.0.0",
    "@types/node-fetch": "^2.5.8",
    "@types/ramda": "types/npm-ramda#dist",
    "@vtex/api": "6.41.0",
    "@vtex/test-tools": "^1.0.0",
    "@vtex/tsconfig": "^0.5.6",
    "typescript": "3.9.7"
  },
  "scripts": {
    "lint": "tsc --noEmit --pretty"
  },
  "version": "0.3.2"
}

```


[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 external-prices-node-template/node/index.ts from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { ClientsConfig } from '@vtex/api'
import { LRUCache, method, Service } from '@vtex/api'

import { Clients } from './clients'
import { validate } from './handlers/validate'
import { fetchPrice } from './handlers/fetchPrice'
import { formatResponse } from './handlers/formatResponse'

const TIMEOUT_MS = 800
const memoryCache = new LRUCache<string, never>({ max: 5000 })

const clients: ClientsConfig<Clients> = {
  implementation: Clients,
  options: {
    default: {
      retries: 2,
      timeout: TIMEOUT_MS,
    },
    status: {
      memoryCache,
    },
  },
}

export default new Service({
  clients,
  routes: {
    price: method({
      POST: [validate, fetchPrice, formatResponse],
    }),
  },
})

```


[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 external-prices-node-template/node/env.ts from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
//App Settings
const ENV = {
  SERVICE_ENDPOINT: 'YOUR_SERVICE_ENDPOINT',
}

export default ENV

```


[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 external-prices-node-template/node/clients/externalPrice.ts from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext } from '@vtex/api'
import { ExternalClient } from '@vtex/api'

import type { InputItem } from '../typings/externalPrice'
import ENV from '../env'

export interface ExternalPriceClient {
  getPrice: (item: InputItem) => Promise<number | undefined>
}

export default class ExternalPrice
  extends ExternalClient
  implements ExternalPriceClient {
  constructor(context: IOContext, options?: InstanceOptions) {
    super(ENV.SERVICE_ENDPOINT, context, options)
  }

  public async getPrice(item: InputItem): Promise<number | undefined> {
    try {
      return await this.http.get(item.skuId, {
        metric: 'get-price',
      })
    } catch (e) {
      if (e.response.status === 404) {
        return undefined
      }

      throw e
    }
  }
}

```


[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 external-prices-node-template/node/clients/externalPriceMock.ts from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { IOClient } from '@vtex/api'

import type { ExternalPriceClient, } from './externalPrice'
import type { InputItem } from '../typings/externalPrice'

export default class ExternalPrice extends IOClient implements ExternalPriceClient {
  public async getPrice(item: InputItem): Promise<number | undefined> {
    return Number(item.skuId) * 10
  }
}

```


[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 external-prices-node-template/node/clients/index.ts from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { IOClients } from '@vtex/api'

import ExternalPrice from './externalPriceMock'

// Extend the default IOClients implementation with our own custom clients.
export class Clients extends IOClients {
  public get externalPrice() {
    return this.getOrSet('externalPrice', ExternalPrice)
  }
}

```


[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 external-prices-node-template/node/typings/externalPrice.d.ts from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Maybe } from '@vtex/api'

export type Quote = {
  skuId: string
  price: number
}

export type InputItem = {
  skuId: string
  quantity: number
  index: number
}

export type ExternalPriceInput = {
  context: Record<string, unknown>
  item: InputItem
}

export type ExternalPriceResponse = {
  message: string
  item: {
    index: number
    skuId: string
    price: number
    costPrice: Maybe<number>
    listPrice: Maybe<number>
    priceValidUntil: Maybe<number>
    priceTables: string
  }
}

```


[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 external-prices-node-template/node/typings/global.d.ts from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { RecorderState, ServiceContext } from '@vtex/api'
import type { Clients } from '../clients'
import type { Quote, ExternalPriceInput } from './externalPrice'

declare global {
  type Context<TState extends State = State> = ServiceContext<Clients, TState>

  interface State extends RecorderState {
    quote?: Quote
    body: ExternalPriceInput
  }

  type Next = () => Promise<void>
}

```


[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 external-prices-node-template/node/__tests__/simple.test.ts from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
// You can import your files here for testing them
//
// More info: https://github.com/sapegin/jest-cheat-sheet

describe('simpleTest', () => {
  it('one test case', async () => {
    expect(0.2 + 0.1).not.toBe(0.3)
  })
})

```


[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 external-prices-node-template/node/handlers/formatResponse.ts from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { ExternalPriceResponse, Quote } from '../typings/externalPrice'

export async function formatResponse(ctx: Context) {
  const { state, body } = ctx

  const quote = state.quote as Quote

  const response: ExternalPriceResponse = {
    message: 'Price quoted successfully.',
    item: {
      costPrice: null,
      listPrice: null,
      priceTables: '',
      priceValidUntil: null,
      index: body.item.index,
      ...quote,
    },
  }

  ctx.body = response
  ctx.status = 200
}

```


[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 external-prices-node-template/node/handlers/validate.ts from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { json } from 'co-body'
import { UserInputError } from '@vtex/api'

export async function validate(ctx: Context, next: Next) {
  ctx.body = await json(ctx.req)

  const { item } = ctx.body

  if (!item?.skuId) throw new UserInputError('Item.SkuId is required')

  await next()
}

```


[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 external-prices-node-template/node/handlers/fetchPrice.ts from the external-prices-node-template VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { NotFoundError } from '@vtex/api'

export async function fetchPrice(
  ctx: Context,
  next: Next
) {
  const { clients, body } = ctx

  const { externalPrice } = clients

  const price = await externalPrice.getPrice(body.item)

  if (!price) throw new NotFoundError('Price not found')

  ctx.state.quote = {
    skuId: body.item.skuId,
    price,
  }

  await next()
}

```


[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 store-sitemap/docs/README.md from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```markdown
# Sitemap

The Sitemap app allows you to generate a `sitemap.xml` file for your store website. After deploying and installing the app on your account, you can create a sitemap to improve store visibility on search engines such as Google.

To learn how to generate a sitemap, follow the instructions below.

## Before you begin

- This app is available for stores using `vtex.edition-store@3.x` or a later version of the [Edition App](https://developers.vtex.com/docs/guides/vtex-io-documentation-edition-app).
   To check which Edition App is installed on your account, run `vtex edition get`. If it's an older edition, please [open a ticket](https://help-tickets.vtex.com/smartlink/sso/login/zendesk) with VTEX Support asking for the installation of the `vtex.edition-store@5.x` Edition App.

- Adjust the products, navigation, and custom routes that will be included in the sitemap. Check the [Advanced Configuration section](#advanced-configuration) for more information.

## Instructions

1. Using your terminal and the [VTEX IO CLI](https://vtex.io/docs/recipes/development/vtex-io-cli-installation-and-command-reference/), log in to your account.

2. Run `vtex use {workspaceName} --production` to use a production workspace or [create a production workspace](https://vtex.io/docs/recipes/development/creating-a-production-workspace/) from scratch.

   >⚠ Remember to replace the values between the curly brackets with the corresponding values of your environment.

3. Run `vtex install vtex.store-sitemap@2.x` to install the Sitemap app.

   > ℹ If you're using `vtex.edition-store@5.x`, skip step 3, as the `vtex.store-sitemap@2.x` app is installed by default with this version. Check our [Edition App documentation](https://developers.vtex.com/docs/guides/vtex-io-documentation-edition-app) to learn more about the different versions.

4. Run `vtex install vtex.admin-graphql-ide@3.x` to install the GraphQL admin IDE.

5. In your browser, access the Admin and go to **Store Setting > Storefront > GraphQL IDE**.

6. From the dropdown list, choose the `vtex.routes-bootstrap@0.x` app.

7. If this isn't the first time you're generating the store sitemap or if no changes have been made to the store routes since the last time you generated the store sitemap, go to step 8. Otherwise, run the following query:

   ```gql
   {
     bootstrap {
       brands
       categories
     }
   }
   ```

   The expected response body is

   ```json
   {
     "data":{
       "bootstrap": {
         "brands": true,
         "categories": true
       }
     }
   }
   ```

8. Now, from the GraphQL IDE dropdown list, select the `vtex.store-sitemap@2.x` app.

9. Run the following query:

  > ℹ If your store is **not** cross-border, the sitemap will be automatically updated daily.
  > In this case, running this GraphQL query is not necessary and will have no effect on the sitemap generation.

   ```gql
   {
     generateSitemap
   }
   ```

   The expected response body is

   ```json
   {
     "data": {
       "generateSitemap": true
     }
   }
   ```

   This means your sitemap will be available in some minutes, after being processed and saved on our database.

   >ℹ The time taken to generate a sitemap is proportional to the number of products. For example, the average time to generate a sitemap for a store with 60k products is 30 minutes. For 5k products, the duration should be about 5 minutes.

   If you attempt to send a new request to the Sitemap API during store sitemap generation, the following message will be displayed:

   ```
   Sitemap generation already in place
   Next generation available: <End-date>
   ```

   To force a restart, add the `force` argument to the query, as in `generateSitemap(force: true)`. This will cancel the previous process.

10. Check the sitemap generated for the current workspace you're working on by accessing `https://{workspace}--{account}.myvtex.com/sitemap.xml` on your browser. Keep in mind that if you  have a cross-border store, you’ll first see an index with a sitemap for each locale.

    >ℹ Different `.xml` files are generated based on their entity type (product, category, subcategory, user routes, brand, and department), and each `.xml` file supports a maximum of 5k routes.

11. If you're happy with the results, run `vtex promote` to promote your workspace and include your sitemap in your master workspace.

    Once you’ve promoted your workspace, no further actions are needed on your part: you’re ready to check  the store sitemap by accessing `https://{account}.myvtex.com/sitemap.xml` on your browser.

### Advanced configuration

#### Managing routes

You can manage whether to include products, navigation, apps, and/or routes containing a specific term in your sitemap. To do that,  follow the instructions below:

1. In your browser, type `{workspace}--{account}.myvtex.com/admin`, replacing the values between curly brackets with the corresponding values of your environment, to access the account's Admin of the production workspace you're working on.

2. Go to **Account settings > Apps > My apps** and search for **Sitemap** app.

3. Enable or disable products, navigation, apps, or routes containing a specific term for your scenario.

   ![sitemap-admin](https://github.com/vtexdocs/dev-portal-content/assets/112641072/649f7dcf-583d-497f-a69c-4cfc3d8a805a)

#### Enabling custom routes

If you have [custom pages](https://developers.vtex.com/vtex-developer-docs/docs/vtex-io-documentation-creating-a-new-custom-page) configured in a `routes.json` file and want them to be included in the store sitemap, add `isSitemapEntry=true` as a prop of the routes you want to include in the sitemap. Example:

   ```json
   {
       "store.custom#about-us": {
         "path": "/about-us",
         "isSitemapEntry": true
     }
   }
   ```

Once everything is set up, go back to **step 4** of the [Instructions](#instructions).

#### Extending the sitemap

To add custom routes created by an app (for example, the ones created by [`store-locator`](https://github.com/vtex-apps/store-locator)) to the store sitemap, the app must respond to an XML file containing a list of the routes created by that app. Lastly, you must include the path to the XML file that your app responds to as an index of the store sitemap.

For implementation details, check the following instructions:

1. Create or modify your app to respond to the following route `/sitemap/{index-name}.xml` and to return an XML file containing the data that you want the search engine (for example, Google) to index. Replace the values between the curly brackets according to your scenario.

   >ℹ We recommend using a pre-generated XML file. Otherwise, the XML file will be built from scratch for every request, consuming more time to complete the task.

2. [Publish](https://developers.vtex.com/vtex-developer-docs/docs/vtex-io-documentation-publishing-an-app) and install your app in a production workspace.

3. To make your index available in the sitemap root file (`/sitemap.xml`), access the account Admin of the workspace you're working on, and select the GraphQL IDE.

4. From the dropdown list, choose the `vtex.store-sitemap@2.x` app and perform the following mutation, adapting it to your scenario:

   ```gql
   mutation {
     saveIndex(index: "{index-name}")
   }
   ```

   >ℹ If you have a [cross-border](https://developers.vtex.com/docs/guides/vtex-io-cross-border-stores) store, the `saveIndex` mutation also accepts the `binding` ID as an argument, which means that by specifying the `binding` ID, you can add your new index to the sitemap of the desired binding. If the `binding` ID is not specified, the mutation will consider the default binding of the store.

5. Check the updated sitemap for the current workspace you're working on by accessing `https://{workspace}--{account}.myvtex.com/sitemap.xml` in your browser.

6. If you're happy with the results, run `vtex promote` to promote your workspace and include the sitemap in your master workspace.

#### Removing a custom route

If you ever want to remove a custom route, you can execute the following mutation, which takes the same arguments as `saveIndex`:

   ```gql
   mutation {
      deleteIndex(index: "{index-name}")
   }
   ```

### Using Sitemap API Endpoints

Beyond generating a complete sitemap, this app exposes specific API endpoints that can be used to get route information for external sitemap generation or other needs.

#### Available Endpoints

The following endpoint provides combined route information in JSON format:

1. **Custom Routes**
   - Endpoint: `/_v/public/sitemap/custom-routes`
   - Description: Returns a combination of routes defined via CMS and internal pages (excluding product, brands and category routes) and routes defined by installed apps in their `build.json` files.
   - **Important**: The generation of custom-routes is capped under a certain limit. If you notice missing routes in your custom-routes file, please contact VTEX Support.
   - Example response:
   ```json
   [
     {
       "name": "apps-routes",
       "routes": ["/store-locator/ny", "/store-locator/ca"]
     },
     {
       "name": "user-routes",
       "routes": ["/about-us", "/contact", "/faq"]
     }
   ]
   ```

#### Using the Endpoint

This endpoint can be accessed via an HTTP GET request to:
- `https://{workspace}--{account}.myvtex.com/_v/public/sitemap/custom-routes`

```


[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 store-sitemap/graphql/schema.graphql from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```graphql
type Query {
  generateSitemap(force: Boolean): Boolean! @requiresAuth
}

type Mutation {
  saveIndex(index: String!, binding: String): Boolean!
  deleteIndex(index: String!, binding: String): Boolean!
}

```


[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 store-sitemap/node/utils.test.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Binding, Events, IOContext, Logger, VBase, VBaseSaveResponse } from '@vtex/api'
import * as TypeMoq from 'typemoq'

import { MultipleSitemapGenerationError } from './errors'
import { CONFIG_BUCKET, GENERATION_CONFIG_FILE, startSitemapGeneration } from './utils'

import { Clients } from './clients'

const eventsTypeMock = TypeMoq.Mock.ofInstance(Events)
const vbaseTypeMock = TypeMoq.Mock.ofInstance(VBase)
const contextMock = TypeMoq.Mock.ofType<Context>()
const ioContext = TypeMoq.Mock.ofType<IOContext>()
const state = TypeMoq.Mock.ofType<State>()
const loggerMock = TypeMoq.Mock.ofType<Logger>()

const oneHourFromNowMS = () => `${new Date(Date.now() + 1 * 60 * 60 * 1000)}`
const minusOneHourFromNowMS = () => `${new Date(Date.now() - 1 * 60 * 60 * 1000)}`

const eventSent = jest.fn()

const DEFAULT_CONFIG = {
  endDate: oneHourFromNowMS(),
  generationId: '10',
}

describe('Test startSitemapGeneration', () => {
  let context: Context

  const vbase = class VBaseMock extends vbaseTypeMock.object {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    private jsonData: Record<string, any> = {}

    constructor() {
      super(ioContext.object)
    }

    public getJSON = async <T>(
      bucket: string,
      file: string,
      nullOrUndefined?: boolean | undefined
    ): Promise<T> => {
      if (!this.jsonData[bucket]?.[file] && nullOrUndefined) {
        return (null as unknown) as T
      }
      return Promise.resolve(this.jsonData[bucket][file] as T)
    }

    public saveJSON = async <T>(
      bucket: string,
      file: string,
      data: T
    ): Promise<VBaseSaveResponse> => {
      if (!this.jsonData[bucket]) {
        this.jsonData[bucket] = {}
      }
      this.jsonData[bucket][file] = data
      return Promise.resolve([{ path: file, hash: 'mocked-hash' }])
    }
  }

  // tslint:disable-next-line:max-classes-per-file
  const events = class EventsMock extends eventsTypeMock.object {
    constructor() {
      super(ioContext.object)
    }

    public sendEvent = async (_: any, __:string, ___: any, ____: any) => {
      eventSent()
    }
  }

    beforeEach(() => {
      // tslint:disable-next-line: max-classes-per-file
      const ClientsImpl = class ClientsMock extends Clients {
        get vbase() {
          return this.getOrSet('vbase', vbase)
        }

        get events() {
          return this.getOrSet('events', events)
        }
      }

      context = {
        ...contextMock.object,
        clients: new ClientsImpl({}, ioContext.object),
        query: {},
        state: {
          ...state.object,
          binding: {
            id: '1',
          } as Binding,
          bucket: 'bucket',
          forwardedHost: 'www.host.com',
          forwardedPath: '/sitemap/file1.xml',
          rootPath: '',
        },
        vtex: {
          ...ioContext.object,
          logger: loggerMock.object,
        },
      }
    })

    it('Should not start a generation if has already started', async () => {
     const { vbase: vbaseClient } = context.clients
     await vbaseClient.saveJSON(CONFIG_BUCKET, GENERATION_CONFIG_FILE, DEFAULT_CONFIG)
      try {
        await startSitemapGeneration(context)
        expect(true).toBe(false)
      } catch(err) {
        expect(err instanceof MultipleSitemapGenerationError).toBe(true)
      }
   })

   it('Should start a generation if date is expired', async () => {
    const { vbase: vbaseClient } = context.clients
    await vbaseClient.saveJSON(CONFIG_BUCKET, GENERATION_CONFIG_FILE, {
      ...DEFAULT_CONFIG,
      endDate: minusOneHourFromNowMS(),
    })
    await startSitemapGeneration(context)
    expect(eventSent).toBeCalled()
   })

   it('Should start a generation if date is invalid', async () => {
    const { vbase: vbaseClient } = context.clients
    await vbaseClient.saveJSON(CONFIG_BUCKET, GENERATION_CONFIG_FILE, {
      ...DEFAULT_CONFIG,
      endDate: 'INVALID',
    })
    await startSitemapGeneration(context)
    expect(eventSent).toBeCalled()
   })

   it('Should start a generation with the force param', async () => {
    const { vbase: vbaseClient } = context.clients
    await vbaseClient.saveJSON(CONFIG_BUCKET, GENERATION_CONFIG_FILE, DEFAULT_CONFIG)
    await startSitemapGeneration(context, true)
    expect(eventSent).toBeCalled()
   })

   it('Should start a generation', async () => {
      await startSitemapGeneration(context)
      expect(eventSent).toBeCalled()
   })
})

```


[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 store-sitemap/node/errors.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
export class MultipleSitemapGenerationError extends Error {
  constructor(endDate: string) {
    super()
    this.message = `Sitemap generation already in place\nNext generation available: ${endDate}`
  }
}

export class MultipleCustomRoutesGenerationError extends Error {
  constructor(endDate: string, account: string) {
    super()
    this.message = `Custom routes generation already in progress for account ${account}\nNext generation available: ${endDate}`
  }
}

```


[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 store-sitemap/node/service.json from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "memory": 512,
  "ttl": 60,
  "timeout": 55,
  "minReplicas": 2,
  "maxReplicas": 10,
  "workers": 4,
  "routes": {
    "sitemap": {
      "path": "/sitemap.xml",
      "public": true
    },
    "sitemapEntry": {
      "path": "/sitemap/:path",
      "public": true
    },
    "generateSitemap": {
      "path": "/generate-sitemap",
      "public": false
    },
    "robots": {
      "path": "/robots.txt",
      "public": true
    },
    "customRoutes": {
      "path": "/_v/public/sitemap/custom-routes",
      "public": true,
      "smartcache": true
    }
  },
  "events": {
    "generateSitemap": {
      "sender": "vtex.store-sitemap",
      "keys": ["sitemap.generate"]
    },
    "generateRewriterRoutes": {
      "sender": "vtex.store-sitemap",
      "keys": ["sitemap.generate:rewriter-routes"]
    },
    "generateProductRoutes": {
      "sender": "vtex.store-sitemap",
      "keys": ["sitemap.generate:product-routes"]
    },
    "generateAppsRoutes": {
      "sender": "vtex.store-sitemap",
      "keys": ["sitemap.generate:apps-routes"]
    },
    "groupEntries": {
      "sender": "vtex.store-sitemap",
      "keys": ["sitemap.generate:group-entries"]
    }
  }
}

```


[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 store-sitemap/node/utils.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Binding, LINKED, TenantClient } from '@vtex/api'
import { any, startsWith } from 'ramda'

import { MultipleSitemapGenerationError } from './errors'
import { GENERATE_SITEMAP_EVENT } from './middlewares/generateMiddlewares/utils'

export const CONFIG_BUCKET = `${LINKED ? 'linked' : ''}configuration`
export const CONFIG_FILE = 'config.json'
export const GENERATION_CONFIG_FILE = 'generation.json'
export const CUSTOM_ROUTES_GENERATION_FILE = 'customRoutesGeneration.json'
export const EXTENDED_INDEX_FILE = 'extendedIndex.json'
export const MAX_CALL_STACK_SIZE =  1000

export const CUSTOM_ROUTES_BUCKET = 'custom-routes'
export const CUSTOM_ROUTES_FILENAME = 'custom-routes.json'
export const CUSTOM_ROUTES_GENERATION_LOCK_FILENAME = 'generation-lock.json'

export const TENANT_CACHE_TTL_S = 60 * 10

export const STORE_PRODUCT = 'vtex-storefront'

const fiveDaysFromNowMS = () => `${new Date(Date.now() + 5 * 24 * 60 * 60 * 1000)}`

const validBinding = (path: string) => (binding: Binding) => {
  const isStoreBinding = binding.targetProduct === STORE_PRODUCT
  const matchesPath = any(startsWith(path), [
    binding.canonicalBaseAddress,
    ...binding.alternateBaseAddresses,
  ])

  return matchesPath && isStoreBinding
}

export const xmlTruncateNodes = ( xml: string[], limit: number = MAX_CALL_STACK_SIZE) =>
  xml.slice(0, limit).join('\n')

export const notFound = <T>(fallback: T) => (error: any): T => {
  if (error.response && error.response.status === 404) {
    return fallback
  }
  throw error
}

export class SitemapNotFound extends Error {}

export const SITEMAP_URL = '/sitemap/:path'

export const getMatchingBindings = async (
  path: string,
  tenant: TenantClient
) => {
  const pathWithoutWorkspace = path.replace(/^(.)+--/, '')
  const tenantInfo = await tenant.info({
    forceMaxAge: TENANT_CACHE_TTL_S,
  })
  // gets bindings that matches path
  return tenantInfo.bindings.filter(validBinding(pathWithoutWorkspace))
}

export const hashString = (str: string) => {
  let hash = 0
  for (let i = 0; i < str.length; i++) {
    hash += (str.charCodeAt(i) * 31) ** (str.length - i)
    // tslint:disable-next-line:no-bitwise
    hash &= hash
  }
  return hash.toString()
}

export const getBucket = (prefix: string, bucketName: string) => `${prefix}_${bucketName}`

export const startSitemapGeneration = async (ctx: Context, force?: boolean) => {
  const { clients: { vbase, events }, vtex: { logger } } = ctx
  const config = await vbase.getJSON<GenerationConfig>(CONFIG_BUCKET, GENERATION_CONFIG_FILE, true)
  if (config && validDate(config.endDate) && !force) {
    throw new MultipleSitemapGenerationError(config.endDate)
  }
  const generationId = (Math.random() * 10000).toString()
  const caller = ctx.request.header['x-vtex-caller']
  logger.info({ message: `New generation started by ${caller}`, generationId })
  await vbase.saveJSON<GenerationConfig>(CONFIG_BUCKET, GENERATION_CONFIG_FILE, {
    endDate: fiveDaysFromNowMS(),
    generationId,
  })
  events.sendEvent('', GENERATE_SITEMAP_EVENT, { generationId })
}

export const validDate = (endDate: string) => {
  const date = new Date(endDate)
  if ((date && date <= new Date()) || date.toString() === 'Invalid Date') {
    return false
  }
  return true
}

export const getStoreBindings = async (tenant: TenantClient) => {
  const { bindings } = await tenant.info({
    forceMaxAge: TENANT_CACHE_TTL_S,
  })
  const storeBindings = bindings.filter(binding => binding.targetProduct === STORE_PRODUCT)
  return storeBindings
}

```


[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 store-sitemap/node/package.json from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "jest": {
    "transform": {
      "^.+\\.tsx?$": "ts-jest"
    },
    "testMatch": [
      "**/__tests__/**/*.(js|ts)?",
      "**/?(*.)+(spec|test).(js|ts)"
    ],
    "moduleFileExtensions": [
      "ts",
      "js"
    ],
    "setupFiles": [
      "<rootDir>/jest/setEnvVars.js"
    ]
  },
  "dependencies": {
    "apollo-datasource-rest": "^0.2.1",
    "apollo-server-caching": "^0.2.1",
    "bluebird": "^3.5.0",
    "cheerio": "^1.0.0-rc.2",
    "co-body": "^6.0.0",
    "graphql": "^14.5.8",
    "graphql-tools": "^4.0.6",
    "query-string": "^6.12.0",
    "ramda": "^0.26.1",
    "route-parser": "^0.0.5"
  },
  "devDependencies": {
    "@types/bluebird": "^3.5.8",
    "@types/cheerio": "^0.22.8",
    "@types/co-body": "^0.0.3",
    "@types/jest": "~25.1.4",
    "@types/koa": "^2.0.48",
    "@types/node": "^12.0.0",
    "@types/ramda": "^0.26.8",
    "@types/route-parser": "^0.1.2",
    "@vtex/api": "6.50.1",
    "gocommerce.sitemap-app": "http://gocommerce.vtexassets.com/_v/public/typings/v1/gocommerce.sitemap-app@1.2.1/public/_types/react",
    "jest": "^25.1.0",
    "ts-jest": "^25.2.1",
    "tslint": "^5.11.0",
    "tslint-config-vtex": "^2.1.0",
    "typemoq": "^2.1.0",
    "typescript": "3.9.7",
    "vtex.catalog-api-proxy": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.catalog-api-proxy@0.10.4/public/_types/react",
    "vtex.catalog-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.catalog-graphql@1.103.1/public/@types/vtex.catalog-graphql",
    "vtex.graphql-server": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.graphql-server@1.69.0/public/_types/react",
    "vtex.messages": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.messages@1.69.0/public/@types/vtex.messages",
    "vtex.rewriter": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.rewriter@1.65.1/public/@types/vtex.rewriter",
    "vtex.routes-bootstrap": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.routes-bootstrap@0.5.1/public/@types/vtex.routes-bootstrap",
    "vtex.store-sitemap": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-sitemap@2.16.6/public/@types/vtex.store-sitemap"
  },
  "resolutions": {
    "**/lodash": "^4.17.11"
  },
  "scripts": {
    "lint": "tsc --noEmit --pretty && tslint -c tslint.json --fix './**/*.ts'",
    "test": "jest"
  },
  "version": "2.18.3"
}

```


[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 store-sitemap/node/globals.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  Binding,
  EventContext as ColossusEventContext,
  RecorderState,
  ServiceContext,
} from '@vtex/api'
import { Settings } from './middlewares/settings'

import { Clients } from './clients'

declare global {
  interface State extends RecorderState {
    settings: Settings
    enabledIndexFiles: string[]
    platform?: string
    binding: Binding
    bucket: string
    forwardedHost: string
    forwardedPath: string
    rootPath: string
    matchingBindings: Binding[]
    bindingAddress?: string
    isCrossBorder: boolean
    nextEvent:  {
      event: string,
      payload: Events
    }
    useLongCacheControl?: boolean
  }

  type Context = ServiceContext<Clients, State>

  type EventContext = ColossusEventContext<Clients, State>

  type Maybe<T> = T | null | undefined

  type Middleware = (ctx: Context) => Promise<void>

  interface CatalogPageTypeResponse {
    pageType: string
  }

  interface Route {
    id: string
    path: string,
    alternates?: AlternateRoute[]
    imagePath?: string
    imageTitle?: string
  }

  interface AlternateRoute {
    bindingId: string
    path: string
  }

  interface Config {
    productionPrefix: string
    generationPrefix: string
  }

  interface GenerationConfig {
    generationId: string
    endDate: string
  }

  type Events = RewriterRoutesGenerationEvent | ProductRoutesGenerationEvent | GroupEntriesEvent

  interface DefaultEvent {
    generationId: string
  }

  interface GroupEntriesEvent extends DefaultEvent {
    indexFile: string
    from: number
  }

  interface RewriterRoutesGenerationEvent extends DefaultEvent  {
    next: Maybe<string>
    report: Record<string, number>
    count: number
    disableRoutesTerm: string
  }

  interface ProductRoutesGenerationEvent extends DefaultEvent   {
    page: number
    processedProducts: number
    invalidProducts: number
  }

  interface CustomRoutesData {
    timestamp: number
    data: Array<{
      name: string
      routes: string[]
    }>
  }
}

```


[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 store-sitemap/node/index.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import './globals'

import {
  Cached,
  ClientsConfig,
  LRUCache,
  method,
  MetricsAccumulator,
  ParamsContext,
  Service,
} from '@vtex/api'

import { Clients } from './clients'
import { Authorization } from './directives/auth'
import { binding } from './middlewares/binding'
import { cache } from './middlewares/cache'
import { errors } from './middlewares/errors'
import { generateAppsRoutes } from './middlewares/generateMiddlewares/generateAppsRoutes'
import { generateProductRoutes } from './middlewares/generateMiddlewares/generateProductRoutes'
import { generateRewriterRoutes } from './middlewares/generateMiddlewares/generateRewriterRoutes'
import {
  generateSitemap,
  generateSitemapFromREST,
} from './middlewares/generateMiddlewares/generateSitemap'
import { groupEntries } from './middlewares/generateMiddlewares/groupEntries'
import { isCrossBorder } from './middlewares/generateMiddlewares/isCrossBorder'
import { prepare as generationPrepare } from './middlewares/generateMiddlewares/prepare'
import { sendNextEvent } from './middlewares/generateMiddlewares/sendNextEvent'
import { methodNotAllowed } from './middlewares/methods'
import { prepare } from './middlewares/prepare'
import { robots } from './middlewares/robots'
import { settings } from './middlewares/settings'
import { sitemap } from './middlewares/sitemap'
import { sitemapEntry } from './middlewares/sitemapEntry'
import { tenant } from './middlewares/tenant'
import { throttle } from './middlewares/throttle'
import { resolvers } from './resolvers'
import { customRoutes } from './middlewares/customRoutes'
import { sitemapErrors } from './middlewares/sitemapErrors'

const THREE_SECONDS_MS = 3 * 1000
const EIGHT_SECOND_MS = 8 * 1000
const TEN_SECONDS_MS = 10 * 1000

const tenantCacheStorage = new LRUCache<string, Cached>({
  max: 3000,
})

const vbaseCacheStorage = new LRUCache<string, Cached>({
  max: 3000,
})

if (!global.metrics) {
  global.metrics = new MetricsAccumulator()
}

metrics.trackCache('tenant', tenantCacheStorage)
metrics.trackCache('vbase', vbaseCacheStorage)

const clients: ClientsConfig<Clients> = {
  implementation: Clients,
  options: {
    catalog: {
      timeout: EIGHT_SECOND_MS,
    },
    catalogGraphQL: {
      concurrency: 10,
    },
    default: {
      concurrency: 5,
      retries: 1,
      timeout: THREE_SECONDS_MS,
    },
    events: {
      timeout: EIGHT_SECOND_MS,
    },
    graphqlServer: {
      concurrency: 10,
      retries: 1,
      timeout: EIGHT_SECOND_MS,
    },
    messages: {
      timeout: EIGHT_SECOND_MS,
    },
    rewriter: {
      timeout: TEN_SECONDS_MS,
    },
    tenant: {
      memoryCache: tenantCacheStorage,
    },
    vbase: {
      memoryCache: vbaseCacheStorage,
      retries: 3,
    },
  },
}

const sitemapPipeline = [
  sitemapErrors,
  settings,
  isCrossBorder,
  prepare,
  sitemap,
]
const sitemapEntryPipeline = [
  sitemapErrors,
  prepare,
  isCrossBorder,
  sitemapEntry,
]

export default new Service<Clients, State, ParamsContext>({
  clients,
  events: {
    /**
     * @deprecated This event is being deprecated. Sitemap generation in this major version will not be triggered by events.
     * Use the `/sitemap/apps-routes` endpoint instead.
     */
    generateAppsRoutes: [
      throttle,
      errors,
      isCrossBorder,
      generationPrepare,
      generateAppsRoutes,
    ],
    /**
     * @deprecated This event is being deprecated. Sitemap generation in this major version will not be triggered by events.
     * Use the REST API endpoints instead.
     */
    generateProductRoutes: [
      throttle,
      errors,
      isCrossBorder,
      generationPrepare,
      tenant,
      generateProductRoutes,
      sendNextEvent,
    ],
    /**
     * @deprecated This event is being deprecated. Sitemap generation in this major version will not be triggered by events.
     * Use the `/sitemap/user-routes` endpoint instead.
     */
    generateRewriterRoutes: [
      throttle,
      errors,
      isCrossBorder,
      generationPrepare,
      generateRewriterRoutes,
      sendNextEvent,
    ],
    /**
     * @deprecated This event is being deprecated. Sitemap generation in this major version will not be triggered by events.
     * Use the REST API endpoints instead.
     */
    generateSitemap: [
      settings,
      isCrossBorder,
      generationPrepare,
      generateSitemap,
    ],
    /**
     * @deprecated This event is being deprecated. Sitemap generation in this major version will not be triggered by events.
     * Use the REST API endpoints instead.
     */
    groupEntries: [
      throttle,
      errors,
      settings,
      isCrossBorder,
      generationPrepare,
      groupEntries,
      sendNextEvent,
    ],
  },
  graphql: {
    resolvers,
    schemaDirectives: {
      requiresAuth: Authorization,
    },
  },
  routes: {
    generateSitemap: generateSitemapFromREST,
    robots: method({
      DEFAULT: methodNotAllowed,
      GET: [cache, binding, robots],
    }),
    sitemap: sitemapPipeline,
    sitemapEntry: sitemapEntryPipeline,
    customRoutes: method({
      GET: [cache, binding, customRoutes],
    }),
  },
})

```


[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 store-sitemap/node/directives/auth.test.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { IOContext, Logger, Sphinx } from '@vtex/api'
import * as TypeMoq from 'typemoq'

import { Clients } from '../clients'
import { VtexID } from '../clients/id'
import { authFromCookie } from './auth'

const vtexIDTypeMock = TypeMoq.Mock.ofInstance(VtexID)
const SphinxTypeMock = TypeMoq.Mock.ofInstance(Sphinx)
const contextMock = TypeMoq.Mock.ofType<Context>()
const ioContext = TypeMoq.Mock.ofType<IOContext>()
const state = TypeMoq.Mock.ofType<State>()
const loggerMock = TypeMoq.Mock.ofType<Logger>()

describe('Test auth directive code', () => {
  let context: Context

  const vtexID = class VtexIDMock extends vtexIDTypeMock.object {
    constructor() {
      super(ioContext.object)
    }

    public getIdUser = async (_: string, token: string) => {
      switch (token) {
        case '1':
          return { user: 'email@test.com' }
        case '2':
          return { user: 'email2@test.com' }
        case '3':
          return { user: 'email@vtex.com' }
        default:
          return {}
      }
    }
  }

  // tslint:disable-next-line:max-classes-per-file
  const sphinx = class SphinxMock extends SphinxTypeMock.object {
    constructor() {
      super(ioContext.object)
    }

    public isAdmin = async (email: string) => {
      switch (email) {
        case 'email@test.com':
          return true
        default:
          return false
      }
    }
  }

  beforeEach(() => {
    // tslint:disable-next-line: max-classes-per-file
    const ClientsImpl = class ClientsMock extends Clients {
      get vtexID() {
        return this.getOrSet('vtexID', vtexID)
      }

      get sphinx() {
        return this.getOrSet('sphinx', sphinx)
      }
    }
    const cookies: Record<string, string> = { 'VtexIdclientAutCookie': '1' }

    context = {
      ...contextMock.object,
      clients: new ClientsImpl({}, ioContext.object),
      cookies: {
        get: (key: string) => cookies[key],
      } as any,
      state: {
        ...state.object,
      },
      vtex: {
        ...ioContext.object,
        authToken: '3',
        logger: loggerMock.object,
      },
    }

  })

  it('Should authorize vtex email', async () => {
    const authrorized = await authFromCookie(context)
    expect(authrorized).toBe(true)
  })

  it('Should throw error when VtexIdclientAutCookie is not found', async () => {
    context.cookies = {
      get: (_: string) => undefined,
    } as any
    const authorized = await authFromCookie(context)
    expect(authorized).toBe('VtexIdclientAutCookie not found.')
  })

  it('Should throw error when user is not found', async () => {
    context.vtex.authToken = '4'
    const authorized = await authFromCookie(context)
    expect(authorized).toBe('Could not find user specified by token.')
  })

  it('Should throw error if user is not admin', async () => {
    context.vtex.authToken = '2'
    const authorized = await authFromCookie(context)
    expect(authorized).toBe('User is not admin and can not access resource.')
  })
  it('Should authorize admins', async () => {
    context.vtex.authToken = '1'
    const authorized = await authFromCookie(context)
    expect(authorized).toBe(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 store-sitemap/node/directives/auth.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { defaultFieldResolver, GraphQLField } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'

const canBypass = (email: string) => {
  return email.includes('@vtex.com')
}

export const authFromCookie = async (ctx: Context) => {
  const {
    clients: { sphinx, vtexID },
    vtex: { authToken },
  } = ctx

  const vtexIdToken = ctx.cookies.get('VtexIdclientAutCookie')
  if (!vtexIdToken) {
    return 'VtexIdclientAutCookie not found.'
  }

  const { user: email } =
    (await vtexID.getIdUser(vtexIdToken, authToken)) ||  { user: '' }
  if (!email) {
    return 'Could not find user specified by token.'
  }

  if (canBypass(email)) {
    return true
  }

  const isAdminUser = await sphinx.isAdmin(email)
  if (!isAdminUser) {
    return 'User is not admin and can not access resource.'
  }

  return true
}

export class Authorization extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field
    field.resolve = async (root, args, ctx, info) => {
      const cookieAllowsAccess = await authFromCookie(ctx)

      if (cookieAllowsAccess !== true) {
        throw new Error(cookieAllowsAccess)
      }

      return resolve(root, args, ctx, info)
    }
  }
}

```


[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 store-sitemap/node/clients/robots.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ExternalClient, InstanceOptions, IOContext } from '@vtex/api'

export class Robots extends ExternalClient {
  constructor (context: IOContext, options?: InstanceOptions) {
    super('http://janus-edge.vtex.com.br', context, options)
  }

  public fromLegacy = (account: string) => this.http.get<string>(`/robots.txt?an=${account}`)
}

```


[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 store-sitemap/node/clients/messages.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { MessagesGraphQL } from '@vtex/api'
import { Query, TranslateArgs } from 'vtex.messages'

interface Response {
  translate: Query['translate']
}

interface Variables {
  args: TranslateArgs
}

export class Messages extends MessagesGraphQL {
  public async translateNoCache(args: TranslateArgs) {
    const response = await this.graphql.query<Response, Variables>(
      {
        query: `query Translate($args: TranslateArgs!) {
          translate(args: $args)
        }`,
        variables: { args },
      },
      {
        headers: {
          'cache-control': 'no-cache',
        },
        metric: 'messages-translate-v2',
      }
    )
    return response.data!.translate as string[]
  }
}

```


[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 store-sitemap/node/clients/graphqlServer.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  AppClient,
  GraphQLClient,
  InstanceOptions,
  IOContext,
} from '@vtex/api'
import { any } from 'ramda'

export class ProductNotFound extends Error {
  public graphQLErrors: any

  constructor(graphQLErrors: any[]) {
    super()
    this.graphQLErrors = graphQLErrors
  }
}

const handleNotFoundErrror = (error: any) => {
  if (error.graphQLErrors && error.graphQLErrors.length === 1) {
    const hasNotFounError = any((err: any )=> err.message.startsWith('No product was found'), error.graphQLErrors)
    if (hasNotFounError) {
      throw new ProductNotFound(error.graphQLErrors)
    }
  }

  throw error
}

// tslint:disable-next-line:max-classes-per-file
export class GraphQLServer extends AppClient {
  protected graphql: GraphQLClient

  constructor(ctx: IOContext, opts?: InstanceOptions) {
    super('vtex.graphql-server@1.x', ctx, opts)
    this.graphql = new GraphQLClient(this.http)
  }

  public query = async (query: string, variables: any, extensions: any) => {
    return this.graphql.query(
      {
        extensions,
        query,
        variables,
      },
      {
        params: {
          locale: this.context.locale,
        },
        url: '/graphql',
      }
    ).catch(err => handleNotFoundErrror(err))
  }
}

```


[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 store-sitemap/node/clients/vbase.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ConflictsResolver, inflightURL, InstanceOptions, IOContext, IOResponse, RequestTracingConfig, VBase } from '@vtex/api'
import { AxiosError } from 'axios'

interface Headers { [key: string]: string | number }
const appId = process.env.VTEX_APP_ID
const [runningAppName] = appId ? appId.split('@') : ['']

const routes = {
  Bucket: (bucket: string) => `/buckets/${runningAppName}/${bucket}`,
  File: (bucket: string, path: string) => `${routes.Bucket(bucket)}/files/${path}`,
}

export class CVBase extends VBase {
  constructor (context: IOContext, options?: InstanceOptions) {
    super(context, options)
  }

  public getRawJSON = <T>(bucket: string, path: string, nullIfNotFound?: boolean, conflictsResolver?: ConflictsResolver<T>, tracingConfig?: RequestTracingConfig) => {
    const headers: Headers = { 'Cache-Control': 'no-cache' }
    if (conflictsResolver) {
      headers['X-Vtex-Detect-Conflicts'] = 'true'
    }
    const inflightKey = inflightURL
    const metric = 'vbase-get-json'
    return this.http.getRaw<T>(routes.File(bucket, path), {
      headers, inflightKey, metric, nullIfNotFound, tracing: {
        requestSpanNameSuffix: metric,
        ...tracingConfig?.tracing,
      },
    })
      .catch(async (error: AxiosError<T>) => {
        const { response } = error
        if (response && response.status === 409 && conflictsResolver) {
          return { ...response, data: await conflictsResolver.resolve() } as IOResponse<T>
        }
        throw error
      })
  }
}

```


[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 store-sitemap/node/clients/rewriter.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { AppGraphQLClient, InstanceOptions, IOContext } from '@vtex/api'
import {
  EntityLocator,
  Internal,
  ListInternalsResponse,
  RoutesByBinding,
} from 'vtex.rewriter'

const MAX_RETRIES = 3
const RETRY_DELAY_MS = 2000

export class Rewriter extends AppGraphQLClient {
  constructor(ctx: IOContext, opts?: InstanceOptions) {
    super('vtex.rewriter@1.x', ctx, opts)
  }

  public listInternals = (
    limit: number,
    next: Maybe<string>
  ): Promise<ListInternalsResponse> =>
    this.graphql
      .query<
        { internal: { listInternals: { next: string; routes: Internal[] } } },
        { next: Maybe<string>; limit: number }
      >(
        {
          query: `
      query ListInternals($limit: Int, $next: String) {
        internal {
          listInternals(limit: $limit, next: $next) {
            routes {
              binding
              from
              type
              endDate
              imagePath
              imageTitle
              id
              disableSitemapEntry
            }
            next
          }
        }
      }
      `,
          variables: { limit, next },
        },
        {
          headers: {
            'cache-control': 'no-cache',
          },
          metric: 'rewriter-get-internals',
        }
      )
      .then(res => res.data?.internal?.listInternals) as Promise<
      ListInternalsResponse
    >

  /**
   * List internals with automatic retry on timeout errors.
   *
   * Wraps listInternals with retry logic that:
   * - Detects timeout-specific error messages and codes
   * - Preserves the pagination cursor across retries
   * - Uses exponential backoff (2s, 4s, 6s)
   * - Logs retry attempts if logger is available
   * - Maximum of 3 retry attempts
   *
   * @param limit - Maximum number of results per request
   * @param cursor - Pagination cursor (preserved across retries)
   * @param retryCount - Internal retry counter (do not set manually)
   * @returns Promise resolving to ListInternalsResponse
   * @throws Original error if not a timeout or max retries exceeded
   */
  public listInternalsWithRetry = async (
    limit: number,
    cursor: Maybe<string>,
    retryCount = 0
  ): Promise<ListInternalsResponse> => {
    try {
      return await this.listInternals(limit, cursor)
    } catch (error) {
      if (retryCount < MAX_RETRIES) {
        const delay = RETRY_DELAY_MS * (retryCount + 1) // Exponential backoff

        // Log warning if logger is available in context
        if (this.context?.logger) {
          this.context.logger.warn({
            message: 'listInternals error, retrying',
            attempt: retryCount + 1,
            maxRetries: MAX_RETRIES,
            delayMs: delay,
            cursor: cursor ?? 'initial',
            error: error?.message,
          })
        }

        // Wait before retrying
        await new Promise(resolve => setTimeout(resolve, delay))

        // Retry with the same cursor
        return this.listInternalsWithRetry(limit, cursor, retryCount + 1)
      }

      // Max retries reached, throw the error
      throw error
    }
  }

  public routesById = (locator: EntityLocator): Promise<RoutesByBinding[]> =>
    this.graphql
      .query<
        { internal: { routes: RoutesByBinding[] } },
        { locator: EntityLocator }
      >(
        {
          query: `
        query Routes($locator: EntityLocator!) {
          internal {
            routes(locator: $locator) {
              route
              binding
            }
          }
        }
        `,
          variables: { locator },
        },
        {
          metric: 'rewriter-get-internals',
        }
      )
      .then(res => res.data?.internal?.routes) as Promise<RoutesByBinding[]>
}

```


[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 store-sitemap/node/clients/index.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { IOClients, Sphinx, VBase } from '@vtex/api'

import { Catalog } from './catalog'
import { GraphQLServer } from './graphqlServer'
import { VtexID } from './id'
import { Messages } from './messages'
import { Rewriter } from './rewriter'
import { Robots } from './robots'
import { RobotsGC } from './robotsGC'
import { CVBase } from './vbase'

export class Clients extends IOClients {
  public get vbase() {
    return this.getOrSet('vbase', CVBase)
  }

  public get vbaseWithCache() {
    return this.getOrSet('vbaseWithCache', VBase)
  }

  public get robots() {
    return this.getOrSet('robots', Robots)
  }

  public get robotsGC() {
    return this.getOrSet('robotsGC', RobotsGC)
  }

  public get rewriter() {
    return this.getOrSet('rewriter', Rewriter)
  }

  get catalog() {
    return this.getOrSet('catalog', Catalog)
  }

  get messages() {
    return this.getOrSet('messages', Messages)
  }

  public get graphqlServer(): GraphQLServer {
    return this.getOrSet('graphqlServer', GraphQLServer)
  }

  public get vtexID() {
    return this.getOrSet('vtexID', VtexID)
  }

  public get sphinx() {
    return this.getOrSet('sphinx', Sphinx)
  }

}

```


[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 store-sitemap/node/clients/robotsGC.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { AppClient, InstanceOptions, IOContext } from '@vtex/api'

export class RobotsGC extends AppClient {
  constructor (context: IOContext, options?: InstanceOptions) {
    super(`gocommerce.sitemap-app`, context, options)
  }

  public fromLegacy = (account: string) => this.http.get<string>(`/robots.txt?an=${account}`)
}

```


[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 store-sitemap/node/clients/id.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ExternalClient, InstanceOptions, IOContext } from '@vtex/api'

const userAgent = process.env.VTEX_APP_ID!

export class VtexID extends ExternalClient {
  public constructor(context: IOContext, options?: InstanceOptions) {
    super('http://vtexid.vtex.com.br/api/vtexid', context, options)
  }

  public getIdUser = (token: string, authToken: string) =>
    this.http.get(`pub/authenticated/user`, {
      headers: {
        Accept: 'application/json',
        Authorization: authToken,
        'Content-Type': 'application/json',
        'User-Agent': userAgent,
        'X-VTEX-Proxy-To': 'https://vtexid.vtex.com.br',
        'X-Vtex-Use-Https': true,
      },
      metric: 'vtexid-authtoken',
      params: {
        authToken: token,
      },
    })
}

```


[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 store-sitemap/node/clients/catalog.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  ExternalClient,
  FORWARDED_HOST_HEADER,
  InstanceOptions,
  IOContext,
} from '@vtex/api'

import { CatalogSitemapError } from '../errors/CatalogSitemapError'

export interface GetProductsAndSkuIdsReponse {
  items: number[]
  paging: {
    total: number
    page: number
    perPage: number
    pages: number
  }
}

const PAGE_SIZE = 100

export class Catalog extends ExternalClient {
  constructor(protected context: IOContext, options?: InstanceOptions) {
    super(`http://${context.account}.vtexcommercestable.com.br`, context, {
      ...(options ?? {}),
      headers: {
        ...(options?.headers ?? {}),
        'Content-Type': 'application/json',
        VtexIdclientAutCookie: context.authToken,
        'X-Vtex-Use-Https': 'true',
      },
    })
  }

  public getProductsIds(
    page: number,
    salesChannels?: string[]
  ): Promise<GetProductsAndSkuIdsReponse> {
    return this.http.get('/api/catalog_system/pvt/products/GetProductsIds', {
      params: {
        ...(salesChannels ? { SalesChannelId: salesChannels.join(',') } : {}),
        Active: true,
        Page: page,
        pageSize: PAGE_SIZE,
      },
    })
  }

  public async getSitemap(host: string, path = 'sitemap.xml') {
    try {
      return await this.http.get(path, {
        headers: {
          [FORWARDED_HOST_HEADER]: host,
        },
      })
    } catch (error) {
      throw new CatalogSitemapError(
        `Failed to fetch catalog sitemap: ${error.message}`,
        error
      )
    }
  }
}

```


[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 store-sitemap/node/resources/bindings.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Binding, PUBLIC_DOMAINS, Tenant } from '@vtex/api'
import { TENANT_CACHE_TTL_S } from '../utils'

export interface BindingDiscoveryResult {
  binding: Binding | null
  tenant: Tenant | null
}

interface BindingDiscoveryOptions {
  tenantCacheTTL?: number
}

const TEN_MINUTES_S = 10 * 60

const defaultOptions: BindingDiscoveryOptions = {
  tenantCacheTTL: TEN_MINUTES_S,
}

const ensureEndingSlash = (address: string) => {
  return address.endsWith('/') ? address : `${address}/`
}

const getStoreBinding = (bindings: Binding[]) => {
  const storeBinding = bindings.find(b => b.targetProduct === 'vtex-storefront')
  if (!storeBinding) {
    throw new Error('No binding found')
  }
  return storeBinding
}

export const getDefaultStoreBinding = async (ctx: Context) => {
  const {
    clients: { tenant },
  } = ctx

  const { bindings = [] } = await tenant.info({
    forceMaxAge: TENANT_CACHE_TTL_S,
  })
  const defaultStoreBinding = getStoreBinding(bindings)
  return defaultStoreBinding.id
}

export class BindingResolver {
  constructor(private options: BindingDiscoveryOptions = defaultOptions) {}

  public async discover(ctx: Context): Promise<Binding> {
    const {
      clients: { tenant: tenantClient },
      vtex: { account },
    } = ctx

    const tenantInfo = await tenantClient
      .info({
        forceMaxAge: this.options.tenantCacheTTL,
        nullIfNotFound: true,
        params: {
          q: account,
        },
      })

    const matchingBinding = this.resolve(ctx, tenantInfo.bindings)

    return matchingBinding
  }

  protected findMatchingBinding(
    account: string,
    hostAndPath: string,
    bindings: Binding[]
  ): Binding | null {
    const matching = bindings.reduce((currentWinner, candidate) => {
      const { canonicalBaseAddress, alternateBaseAddresses } = candidate
      const allCandidateAddresses = [
        canonicalBaseAddress,
        ...alternateBaseAddresses,
        ...PUBLIC_DOMAINS.map(
          domain => `${account}.${domain}/${canonicalBaseAddress}`
        ),
      ].map(ensureEndingSlash)
      const allMatchingAddress = allCandidateAddresses.filter(address =>
        hostAndPath.startsWith(address)
      )
      if (allMatchingAddress.length === 0) {
        return currentWinner
      }

      const [bestMatchingAddress] =
        allMatchingAddress.length === 1
          ? allMatchingAddress
          : allMatchingAddress.sort(
              (addressA, addressB) => addressB.length - addressA.length
            )

      const currentWinnerScore = currentWinner?.matchingAddress.length ?? 0

      return bestMatchingAddress.length > currentWinnerScore
        ? { matchingAddress: bestMatchingAddress, binding: candidate }
        : currentWinner
    }, null as { binding: Binding; matchingAddress: string } | null)

    return matching?.binding ?? null
  }

  protected mountHostAndPath(ctx: Context): string {
    const {
      vtex: { account, workspace },
    } = ctx
    const workspacePrefix = `${workspace}--`
    const rootPath = ctx.get('x-vtex-root-path')
    const forwardedHost = ctx.get('x-forwarded-host')
    const forwardedPath = ctx.get('x-forwarded-path')
    const isInternalPublicDomain = PUBLIC_DOMAINS.some(domain =>
      forwardedHost.endsWith(`${account}.${domain}`)
    )
    const bindingPath = isInternalPublicDomain
      ? this.toBindingPath(forwardedPath)
      : this.toBindingPath(rootPath)
    const resolvedHost =
      isInternalPublicDomain && forwardedHost.startsWith(workspacePrefix)
        ? forwardedHost.replace(`${workspace}--`, '')
        : forwardedHost
    return `${resolvedHost}${bindingPath}`
  }

  protected toBindingPath(path?: string): string {
    let resultPath = path?.split('?')[0] ?? '/'
    if (!resultPath.startsWith('/')) {
      resultPath = `/${resultPath}`
    }

    if (!resultPath.endsWith('/')) {
      resultPath += '/'
    }

    return resultPath
  }

  private resolve(ctx: Context, bindings: Binding[]): Binding {
    const {
      vtex: { account, binding, logger },
      query: { __bindingAddress, __bindingId },
    } = ctx
    const bindingId = binding?.id ?? __bindingId
    if (bindingId) {
      return this.getBindingById(bindingId, bindings)
    }

    if (__bindingAddress) {
      return this.getBindingByAddress(__bindingAddress, bindings)
    }

    const hostAndPath = this.mountHostAndPath(ctx)
    const matching = this.findMatchingBinding(account, hostAndPath, bindings)
    if (matching) {
      return matching
    }

    logger.warn({
      headers: ctx.request.headers,
      message: 'No binding found, falling back to the first store binding',
    })
    return getStoreBinding(bindings)
  }

  private getBindingById(bindingId: string, bindings: Binding[]): Binding {
    const binding = bindings.find(item => item.id === bindingId)
    if (!binding) {
      throw new Error(`Unable to find binding for id ${bindingId}`)
    }

    return binding
  }

  private getBindingByAddress(
    bindindAddress: string,
    bindings: Binding[]
  ): Binding {
    const binding = bindings.find(
      item => item.canonicalBaseAddress === bindindAddress
    )
    if (!binding) {
      throw new Error(`Unable to find binding for address ${bindindAddress}`)
    }

    return binding
  }
}

```


[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 store-sitemap/node/middlewares/robots.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Apps } from '@vtex/api'

import { notFound } from '../utils'

const TEN_MINUTES_S = 10 * 60

interface RobotsFile {
  robots: Record<string, string>
}

const SITEMAP_BUILD_FILE = 'dist/vtex.store-sitemap/build.json'

const getRobotForBinding = async (
  bindingId: string,
  account: string,
  apps: Apps
) => {
  const appId = `${account}.robots@0.x`

  try {
    await apps.getApp(appId)
  } catch (err) {
    notFound(undefined)(err)
  }

  const buildFile = await apps.getAppJSON<Partial<RobotsFile> | null>(
    appId,
    SITEMAP_BUILD_FILE,
    true
  )

  return buildFile?.robots?.[bindingId]
}

export async function robots(ctx: Context) {
  const {
    vtex: { account, production, platform },
    state: { binding },
  } = ctx

  let data: string | undefined

  const { clients } = ctx
  const robotsDataSource =
    platform === 'gocommerce' ? clients.robotsGC : clients.robots

  if (binding?.id) {
    data = await getRobotForBinding(binding.id, account, clients.apps)
  }
  if (!data || !binding?.id) {
    data = await robotsDataSource.fromLegacy(account)
  }

  ctx.set('Content-Type', 'text/plain')
  ctx.body = data
  ctx.status = 200
  ctx.set(
    'cache-control',
    production ? `public, max-age=${TEN_MINUTES_S}` : 'no-cache'
  )
}

```


[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 store-sitemap/node/middlewares/prepare.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { parse } from 'query-string'

import { BindingResolver } from '../resources/bindings'
import { CONFIG_BUCKET, CONFIG_FILE, getBucket, getMatchingBindings, hashString, startSitemapGeneration } from '../utils'
import { DEFAULT_CONFIG } from './generateMiddlewares/utils'

const ONE_DAY_S = 24 * 60 * 60
const FORWARDED_HOST_HEADER = 'x-forwarded-host'
const VTEX_ROOT_PATH_HEADER = 'x-vtex-root-path'

export async function prepare(ctx: Context, next: () => Promise<void>) {
  const {
    vtex: { production },
    clients: { vbase, tenant },
  } = ctx
  const forwardedHost = ctx.get('x-forwarded-host')
  let rootPath = ctx.get('x-vtex-root-path')
  // Defend against malformed root path. It should always start with `/`.
  if (rootPath && !rootPath.startsWith('/')) {
    rootPath = `/${rootPath}`
  }
  if (rootPath === '/') {
    rootPath = ''
  }
  const [forwardedPath, queryString] = ctx.get('x-forwarded-path').split('?')
  const matchingBindings = await getMatchingBindings(forwardedHost, tenant)
  const bindingResolver = new BindingResolver()
  const binding = await bindingResolver.discover(ctx)
  if (!binding) {
    throw new Error(`Binding from context not found`)
  }

  const query = parse(queryString)

  const { productionPrefix } = await vbase.getJSON<Config>(CONFIG_BUCKET, CONFIG_FILE, true) || DEFAULT_CONFIG

  const bucket = getBucket(productionPrefix, hashString(binding.id))

  ctx.state = {
    ...ctx.state,
    binding,
    bindingAddress: query.__bindingAddress as string | undefined,
    bucket,
    forwardedHost,
    forwardedPath,
    matchingBindings,
    rootPath,
  }

  await next()

  ctx.set('Content-Type', 'text/xml')
  ctx.status = 200
  ctx.set(
    'cache-control',
    production ? `public, max-age=${ONE_DAY_S}` : 'no-cache'
  )
  if (production) {
    startSitemapGeneration(ctx)
  }
  ctx.vary(FORWARDED_HOST_HEADER)
  ctx.vary(VTEX_ROOT_PATH_HEADER)

  return
}

```


[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 store-sitemap/node/middlewares/throttle.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { TooManyRequestsError } from '@vtex/api'
import { sleep } from './generateMiddlewares/utils'

const MAX_REQUEST = 4
let COUNTER = 0

export async function throttle(
  _: EventContext,
  next: () => Promise<void>
) {
  COUNTER++
  try {
    if (COUNTER > MAX_REQUEST) {
      const timeToSleep = Math.ceil(Math.random() * 100)
      await sleep(timeToSleep)
      throw new TooManyRequestsError()
    }
    await next()
  } finally {
    COUNTER--
  }
}

```


[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 store-sitemap/node/middlewares/binding.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { BindingResolver } from '../resources/bindings'

export async function binding(ctx: Context, next: () => Promise<any>) {
  const bindingResolver = new BindingResolver()
  ctx.state.binding = await bindingResolver.discover(ctx)
  await next()
}

```


[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 store-sitemap/node/middlewares/sitemapEntry.test.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Binding, IOContext, Logger, VBase } from '@vtex/api'
import * as TypeMoq from 'typemoq'

import { Clients } from '../clients'
import { sitemapEntry, URLEntry } from './sitemapEntry'

const vbaseTypeMock = TypeMoq.Mock.ofInstance(VBase)
const contextMock = TypeMoq.Mock.ofType<Context>()
const ioContext = TypeMoq.Mock.ofType<IOContext>()
const state = TypeMoq.Mock.ofType<State>()
const loggerMock = TypeMoq.Mock.ofType<Logger>()

const removeSpaces = (str: string) => str.replace(/(\r\n|\n|\r|\s)/gm, '')

describe('Test sitemap entry', () => {
  let context: Context

  const vbase = class VBaseMock extends vbaseTypeMock.object {
    constructor() {
      super(ioContext.object)
    }

    public getJSON = async <T>(
      _: string,
      file: string,
      __?: boolean | undefined
    ): Promise<T> => {
      switch (file) {
        case 'file1':
          return {
            lastUpdated: '2019-12-04',
            routes: [
            {
              id: 1,
              path: '/banana',
            },
            {
              id: 1,
              imagePath: 'image',
              imageTitle: 'title',
              path: '/watermelon',
            },
          ],
        } as unknown as T
        default:
          return null as unknown as T
      }
    }
  }

  // tslint:disable-next-line:no-empty
  const next = async (): Promise<void> => {
  }

  const defaultRoute: Route = {
    id: '2',
    path: '/pear',
  }

  const defaultLastUpdated = '2019-12-04'

  beforeEach(() => {
    // tslint:disable-next-line: max-classes-per-file
    const ClientsImpl = class ClientsMock extends Clients {
      get vbase() {
        return this.getOrSet('vbase', vbase)
      }
    }

    context = {
      ...contextMock.object,
      clients: new ClientsImpl({}, ioContext.object),
      state: {
        ...state.object,
        binding: {
          id: '1',
        } as Binding,
        bucket: 'bucket',
        forwardedHost: 'host.com',
        forwardedPath: '/sitemap/file1.xml',
        matchingBindings: [
          {
            canonicalBaseAddress: 'www.host.com',
            defaultLocale: 'en-US',
            id: '1',
          },
          {
            canonicalBaseAddress: 'www.host.com/br',
            defaultLocale: 'pt-BR',
            id: '2',
          },
          {
            canonicalBaseAddress: 'www.host.com/de',
            defaultLocale: 'de-DE',
            id: '3',
          },
        ] as Binding[],
        rootPath: '',
      },
      vtex: {
        ...ioContext.object,
        logger: loggerMock.object,
      },
    }
  })

  it('Should return 404 when entry is not found', async () => {
    const thisContext = {
      ...context,
      state: {
        ...context.state,
        forwardedPath: '/sitemap/not-existent-file.xml',
      },
    }
    await sitemapEntry(thisContext, next)
    expect(thisContext.status).toStrictEqual(404)
  })

  it('Should return 404 when url does not match', async () => {
    const thisContext = {
      ...context,
      state: {
        ...context.state,
        forwardedPath: '/not-sitemap/not-existent-file.xml',
      },
    }
    await sitemapEntry(thisContext, next).catch(_ => null)
    expect(thisContext.status).toStrictEqual(404)
  })

  it('Should create corrects sitemap entries', async () => {
    await sitemapEntry(context, next)
    expect(removeSpaces(context.body)).toStrictEqual(removeSpaces(
      `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
        <url>
          <loc>https://host.com/banana</loc>
          <lastmod>2019-12-04</lastmod>
          </url><url>
          <image:image>
            <image:loc>image</image:loc>
            <image:title>title</image:title>
          </image:image>

          <loc>https://host.com/watermelon</loc>
          <lastmod>2019-12-04</lastmod>
        </url>
      </urlset>`
    ))
  })

  it('Should create a correct sitemap entry with localization', async () => {
    const alternates: AlternateRoute[] = [
      {
        bindingId: '1',
        path: '/pear',
      },
      {
        bindingId: '2',
        path: '/pera',
      },
      {
        bindingId: '3',
        path: '/brine',
      },
    ]
    const route: Route = {
      ...defaultRoute,
      alternates,
    }
    const entry = URLEntry(context, route, defaultLastUpdated)
    expect(removeSpaces(entry)).toStrictEqual(removeSpaces(
    `<url>
      <loc>https://host.com/pear</loc>
      <xhtml:link rel="alternate" hreflang="pt-BR" href="https://www.host.com/br/pera"/>
      <xhtml:link rel="alternate" hreflang="de-DE" href="https://www.host.com/de/brine"/>
      <lastmod>2019-12-04</lastmod>
     </url>`
    ))
  })

  it('Should create a correct sitemap entry with localization with binding address querystring', async () => {
    const alternates: AlternateRoute[] = [
      {
        bindingId: '1',
        path: '/pear',
      },
      {
        bindingId: '2',
        path: '/pera',
      },
      {
        bindingId: '3',
        path: '/brine',
      },
    ]
    const route: Route = {
      ...defaultRoute,
      alternates,
    }
    const thisContext = {
      ...context,
      state: {
        ...context.state,
        bindingAddress: 'www.host.com/es',
      },
    }
    const entry = URLEntry(thisContext, route, defaultLastUpdated)
    expect(removeSpaces(entry)).toStrictEqual(removeSpaces(
    `<url>
      <loc>https://host.com/pear?__bindingAddress=www.host.com/es</loc>
      <xhtml:link rel="alternate" hreflang="pt-BR" href="https://host.com/pera?__bindingAddress=www.host.com/br"/>
      <xhtml:link rel="alternate" hreflang="de-DE" href="https://host.com/brine?__bindingAddress=www.host.com/de"/>
      <lastmod>2019-12-04</lastmod>
     </url>`
    ))
  })

  it('Should create a correct sitemap entry with root path', async () => {
    const thisContext = {
      ...context,
      state: {
        ...context.state,
        rootPath: '/es',
      },
    }
    const entry = URLEntry(thisContext, defaultRoute, defaultLastUpdated)
    expect(removeSpaces(entry)).toStrictEqual(removeSpaces(
    `<url>
      <loc>https://host.com/es/pear</loc>
      <lastmod>2019-12-04</lastmod>
     </url>`
    ))
  })

  it('Should create a correct sitemap entry with binding adress querystring', async () => {
    const thisContext = {
      ...context,
      state: {
        ...context.state,
        bindingAddress: 'www.host.com/es',
      },
    }
    const entry = URLEntry(thisContext, defaultRoute, defaultLastUpdated)
    expect(removeSpaces(entry)).toStrictEqual(removeSpaces(
    `<url>
      <loc>https://host.com/pear?__bindingAddress=www.host.com/es</loc>
      <lastmod>2019-12-04</lastmod>
     </url>`
    ))
  })

  it('Should create a correct sitemap entry with image path and title', async () => {
    const route: Route = {
      ...defaultRoute,
      imagePath: 'image',
      imageTitle: 'title',
    }
    const entry = URLEntry(context, route, defaultLastUpdated)
    expect(removeSpaces(entry)).toStrictEqual(removeSpaces(
    `<url>
      <image:image>
        <image:loc>image</image:loc>
        <image:title>title</image:title>
      </image:image>
      <loc>https://host.com/pear</loc>
      <lastmod>2019-12-04</lastmod>
     </url>`
    ))
  })
})

```


[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 store-sitemap/node/middlewares/errors.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { TooManyRequestsError } from '@vtex/api'
import { any } from 'ramda'

const ERROR_429 = 'E_HTTP_429'

const isTooManyRequestError = (error: any) => {
  // Checks if has one error and it is the TooManyRequestError
  if (error.graphQLErrors && error.graphQLErrors.length === 1) {
    return any((err: any )=> err.extensions?.exception?.code === ERROR_429 , error.graphQLErrors)
  }
  return false
}

export async function errors(_: EventContext, next: () => Promise<void>) {
  try {
    await next()
  } catch (error) {
    if (isTooManyRequestError(error)) {
      throw new TooManyRequestsError()
    }
    throw error
  }
}

```


[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 store-sitemap/node/middlewares/sitemapErrors.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { CatalogSitemapError } from '../errors/CatalogSitemapError'

/**
 * Error handling middleware specifically for sitemap pipelines.
 * Catches errors and returns appropriate HTTP responses with proper content types
 * instead of allowing errors to bubble up and potentially return XML error content.
 */
export async function sitemapErrors(ctx: Context, next: () => Promise<void>) {
  try {
    await next()
  } catch (error) {
    const {
      vtex: { logger },
    } = ctx

    // Log the error for debugging
    logger.error({
      message: 'Sitemap pipeline error',
      payload: {
        error: error.message,
        method: ctx.method,
        path: ctx.path,
        stack: error.stack,
        ...(error instanceof CatalogSitemapError && {
          statusCode: error.statusCode,
          originalError: error.originalError?.message,
        }),
      },
    })

    // Set content type to text/plain to ensure browsers handle it properly
    ctx.type = 'text/plain; charset=utf-8'

    if (error instanceof CatalogSitemapError) {
      // Use the actual HTTP status code from the catalog API response
      ctx.status = error.statusCode
      ctx.body = 'Error fetching sitemap data'
      return
    }

    // Default error handling for unknown errors
    ctx.status = 500
    ctx.body = 'Internal Server Error'
  }
}

```


[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 store-sitemap/node/middlewares/settings.ts from the store-sitemap VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { appIdToAppAtMajor } from '@vtex/api'
import {
  APPS_ROUTES_INDEX,
  PRODUCT_ROUTES_INDEX,
  REWRITER_ROUTES_INDEX,
} from './generateMiddlewares/utils'

export interface Settings {
  enableAppsRoutes: boolean
  enableProductRoutes: boolean
  enableNavigationRoutes: boolean
  ignoreBindings: boolean
  disableRoutesTerm: string
}

const VTEX_APP_ID = process.env.VTEX_APP_ID!
const VTEX_APP_AT_MAJOR = appIdToAppAtMajor(VTEX_APP_ID)

const DEFAULT_SETTINGS = {
  disableRoutesTerm: '',
  enableAppsRoutes: true,
  enableNavigationRoutes: true,
  enableProductRoutes: true,
  ignoreBindings: false,
}

const INDEX_MAP = {
  disableRoutesTerm: '',
  enableAppsRoutes: APPS_ROUTES_INDEX,
  enableNavigationRoutes: REWRITER_ROUTES_INDEX,
  enableProductRoutes: PRODUCT_ROUTES_INDEX,
  ignoreBindings: '',
}

export async function settings(
  ctx: Context | EventContext,
  next: () => Promise<void>
) {
  const {
    clients: { apps },
  } = ctx

  const appSettings: Settings = {
    ...DEFAULT_SETTINGS,
    ...(await apps.getAppSettings(VTEX_APP_AT_MAJOR)),
  }
  const keys = Object.keys(appSettings) as Array<keyof Settings>
  const enabledIndexFiles = keys.reduce((acc, key) => {
    if (appSettings[key] && INDEX_MAP[key]) {
      acc.push(INDEX_MAP[key])
    }
    return acc
  }, [] as string[])

  ctx.state = {
    ...ctx.state,
    enabledIndexFiles,
    settings: appSettings,
  }

  await next()
}

```