[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-framework-course/course/template/styles/configs/style.json from the store-framework-course VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "typeScale": [
    3,
    2.25,
    1.5,
    1.25,
    1,
    0.875,
    0.75
  ],
  "spacing": [
    0.125,
    0.25,
    0.5,
    0.75,
    1,
    1.5,
    2,
    3,
    4,
    8,
    16
  ],
  "customMedia": [
    {
      "s": 20
    },
    {
      "ns": {
        "value": 40,
        "minWidth": true
      }
    },
    {
      "m": {
        "value": 40,
        "minWidth": true
      }
    },
    {
      "l": {
        "value": 64,
        "minWidth": true
      }
    },
    {
      "xl": {
        "value": 80,
        "minWidth": true
      }
    }
  ],
  "colors": {
    "black-90": "rgba(0,0,0,.9)",
    "black-80": "rgba(0,0,0,.8)",
    "black-70": "rgba(0,0,0,.7)",
    "black-60": "rgba(0,0,0,.6)",
    "black-50": "rgba(0,0,0,.5)",
    "black-40": "rgba(0,0,0,.4)",
    "black-30": "rgba(0,0,0,.3)",
    "black-20": "rgba(0,0,0,.2)",
    "black-10": "rgba(0,0,0,.1)",
    "black-05": "rgba(0,0,0,.05)",
    "black-025": "rgba(0,0,0,.025)",
    "black-0125": "rgba(0,0,0,.0125)",
    "white-90": "rgba(255,255,255,.9)",
    "white-80": "rgba(255,255,255,.8)",
    "white-70": "rgba(255,255,255,.7)",
    "white-60": "rgba(255,255,255,.6)",
    "white-50": "rgba(255,255,255,.5)",
    "white-40": "rgba(255,255,255,.4)",
    "white-30": "rgba(255,255,255,.3)",
    "white-20": "rgba(255,255,255,.2)",
    "white-10": "rgba(255,255,255,.1)",
    "white-05": "rgba(255,255,255,.05)",
    "white-025": "rgba(255,255,255,.025)",
    "white-0125": "rgba(255,255,255,.0125)"
  },
  "semanticColors": {
    "background": {
      "base": "#ffffff",
      "base--inverted": "#03044e",
      "action-primary": "#0F3E99",
      "action-secondary": "#eef3f7",
      "emphasis": "#f71963",
      "disabled": "#f2f4f5",
      "success": "#8bc34a",
      "success--faded": "#eafce3",
      "danger": "#ff4c4c",
      "danger--faded": "#ffe6e6",
      "warning": "#ffb100",
      "warning--faded": "#fff6e0",
      "muted-1": "#727273",
      "muted-2": "#979899",
      "muted-3": "#cacbcc",
      "muted-4": "#e3e4e6",
      "muted-5": "#f2f4f5"
    },
    "hover-background": {
      "action-primary": "#072c75",
      "action-secondary": "#dbe9fd",
      "emphasis": "#dd1659",
      "success": "#8bc34a",
      "success--faded": "#eafce3",
      "danger": "#e13232",
      "danger--faded": "#ffe6e6",
      "warning": "#ffb100",
      "warning--faded": "#fff6e0",
      "muted-1": "#727273",
      "muted-2": "#979899",
      "muted-3": "#cacbcc",
      "muted-4": "#e3e4e6",
      "muted-5": "#f2f4f5"
    },
    "active-background": {
      "action-primary": "#0c389f",
      "action-secondary": "#dbe9fd",
      "emphasis": "#dd1659",
      "success": "#8bc34a",
      "success--faded": "#eafce3",
      "danger": "#ff4c4c",
      "danger--faded": "#ffe6e6",
      "warning": "#ffb100",
      "warning--faded": "#fff6e0",
      "muted-1": "#727273",
      "muted-2": "#979899",
      "muted-3": "#cacbcc",
      "muted-4": "#e3e4e6",
      "muted-5": "#f2f4f5"
    },
    "text": {
      "action-primary": "#0F3E99",
      "action-secondary": "#eef3f7",
      "link": "#0F3E99",
      "emphasis": "#f71963",
      "disabled": "#979899",
      "success": "#8bc34a",
      "success--faded": "#eafce3",
      "danger": "#ff4c4c",
      "danger--faded": "#ffe6e6",
      "warning": "#ffb100",
      "warning--faded": "#fff6e0",
      "muted-1": "#727273",
      "muted-2": "#979899",
      "muted-3": "#cacbcc",
      "muted-4": "#e3e4e6",
      "muted-5": "#f2f4f5"
    },
    "visited-text": {
      "link": "#0c389f"
    },
    "hover-text": {
      "action-primary": "#072c75",
      "action-secondary": "#dbe9fd",
      "link": "#0c389f",
      "emphasis": "#dd1659",
      "success": "#8bc34a",
      "success--faded": "#eafce3",
      "danger": "#e13232",
      "danger--faded": "#ffe6e6",
      "warning": "#ffb100",
      "warning--faded": "#fff6e0"
    },
    "active-text": {
      "link": "#0c389f",
      "emphasis": "#dd1659",
      "success": "#8bc34a",
      "success--faded": "#eafce3",
      "danger": "#ff4c4c",
      "danger--faded": "#ffe6e6",
      "warning": "#ffb100",
      "warning--faded": "#fff6e0"
    },
    "border": {
      "action-primary": "#0F3E99",
      "action-secondary": "#eef3f7",
      "emphasis": "#f71963",
      "disabled": "#e3e4e6",
      "success": "#8bc34a",
      "success--faded": "#eafce3",
      "danger": "#ff4c4c",
      "danger--faded": "#ffe6e6",
      "warning": "#ffb100",
      "warning--faded": "#fff6e0",
      "muted-1": "#727273",
      "muted-2": "#979899",
      "muted-3": "#cacbcc",
      "muted-4": "#e3e4e6",
      "muted-5": "#f2f4f5"
    },
    "hover-border": {
      "action-primary": "#072c75",
      "action-secondary": "#dbe9fd",
      "emphasis": "#dd1659",
      "success": "#8bc34a",
      "success--faded": "#eafce3",
      "danger": "#e13232",
      "danger--faded": "#ffe6e6",
      "warning": "#ffb100",
      "warning--faded": "#fff6e0",
      "muted-1": "#727273",
      "muted-2": "#979899",
      "muted-3": "#cacbcc",
      "muted-4": "#e3e4e6",
      "muted-5": "#f2f4f5"
    },
    "active-border": {
      "action-primary": "#0c389f",
      "action-secondary": "#dbe9fd",
      "emphasis": "#dd1659",
      "success": "#8bc34a",
      "success--faded": "#eafce3",
      "danger": "#ff4c4c",
      "danger--faded": "#ffe6e6",
      "warning": "#ffb100",
      "warning--faded": "#fff6e0",
      "muted-1": "#727273",
      "muted-2": "#979899",
      "muted-3": "#cacbcc",
      "muted-4": "#e3e4e6",
      "muted-5": "#f2f4f5"
    },
    "on": {
      "base": "#3f3f40",
      "base--inverted": "#ffffff",
      "action-primary": "#ffffff",
      "action-secondary": "#0F3E99",
      "emphasis": "#ffffff",
      "disabled": "#979899",
      "success": "#ffffff",
      "success--faded": "#3f3f40",
      "danger": "#ffffff",
      "danger--faded": "#3f3f40",
      "warning": "#ffffff",
      "warning--faded": "#1a1a1a",
      "muted-1": "#ffffff",
      "muted-2": "#ffffff",
      "muted-3": "#3f3f40",
      "muted-4": "#3f3f40",
      "muted-5": "#3f3f40"
    },
    "hover-on": {
      "action-primary": "#ffffff",
      "action-secondary": "#0F3E99",
      "emphasis": "#ffffff",
      "success": "#ffffff",
      "success--faded": "#3f3f40",
      "danger": "#ffffff",
      "danger--faded": "#3f3f40",
      "warning": "#ffffff",
      "warning--faded": "#1a1a1a"
    },
    "active-on": {
      "action-primary": "#ffffff",
      "action-secondary": "#0F3E99",
      "emphasis": "#ffffff",
      "success": "#ffffff",
      "success--faded": "#3f3f40",
      "danger": "#ffffff",
      "danger--faded": "#3f3f40",
      "warning": "#ffffff",
      "warning--faded": "#1a1a1a"
    }
  },
  "borderWidths": [
    0,
    0.125,
    0.25,
    0.5,
    1,
    2
  ],
  "borderRadius": [
    0,
    0.125,
    0.25,
    0.5,
    1
  ],
  "widths": [
    1,
    2,
    4,
    8,
    16
  ],
  "maxWidths": [
    1,
    2,
    4,
    8,
    16,
    32,
    48,
    64,
    96
  ],
  "heights": [
    1,
    2,
    4,
    8,
    16
  ],
  "sizes": [
    {
      "name": "small",
      "value": 2
    },
    {
      "name": "regular",
      "value": 2.5
    },
    {
      "name": "large",
      "value": 3
    }
  ],
  "typography": {
    "measure": [
      30,
      34,
      20
    ],
    "styles": {
      "heading-1": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "700",
        "fontSize": "3rem",
        "textTransform": "initial",
        "letterSpacing": "0"
      },
      "heading-2": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "700",
        "fontSize": "2.25rem",
        "textTransform": "initial",
        "letterSpacing": "0"
      },
      "heading-3": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "700",
        "fontSize": "1.75rem",
        "textTransform": "initial",
        "letterSpacing": "0"
      },
      "heading-4": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "normal",
        "fontSize": "1.5rem",
        "textTransform": "initial",
        "letterSpacing": "0"
      },
      "heading-5": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "normal",
        "fontSize": "1.25rem",
        "textTransform": "initial",
        "letterSpacing": "0"
      },
      "heading-6": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "normal",
        "fontSize": "1.25rem",
        "textTransform": "initial",
        "letterSpacing": "0"
      },
      "body": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "normal",
        "fontSize": "1rem",
        "textTransform": "initial",
        "letterSpacing": "0"
      },
      "small": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "normal",
        "fontSize": "0.875rem",
        "textTransform": "initial",
        "letterSpacing": "0"
      },
      "mini": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "normal",
        "fontSize": "0.75rem",
        "textTransform": "initial",
        "letterSpacing": "0"
      },
      "action": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "500",
        "fontSize": "1rem",
        "textTransform": "uppercase",
        "letterSpacing": "0"
      },
      "action--small": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "500",
        "fontSize": "0.875rem",
        "textTransform": "uppercase",
        "letterSpacing": "0"
      },
      "action--large": {
        "fontFamily": "Fabriga, -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif",
        "fontWeight": "500",
        "fontSize": "1.25rem",
        "textTransform": "uppercase",
        "letterSpacing": "0"
      },
      "code": {
        "fontFamily": "Consolas, monaco, monospace",
        "fontWeight": "normal",
        "fontSize": "1rem",
        "textTransform": "initial",
        "letterSpacing": "0"
      }
    }
  },
  "opacity": [
    1,
    0.9,
    0.8,
    0.7,
    0.6,
    0.5,
    0.4,
    0.3,
    0.2,
    0.1,
    0.05,
    0.025,
    0
  ]
}

```


[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-framework-course/course/template/store/blocks/drawer.jsonc from the store-framework-course VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "drawer": {
      "children": [
      "vtex.menu@2.x:menu#drawer"
      ]
    },

  "vtex.menu@2.x:menu#drawer": {
      "children": [
        "menu-item#major-appliances-acc",
        "menu-item#small-appliances-acc",
        "menu-item#electronics-acc"
      ],
      "props":{
          "orientation": "vertical"
      }
    },
  "menu-item#major-appliances-acc": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 1,
        "text": "Major Appliances"
      }
    },
    "blocks": [
      "vtex.menu@2.x:submenu.accordion#major"
    ]
  },
  "menu-item#small-appliances-acc": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 2,
        "text": "Small Appliances"
      }
    },
    "blocks": [
      "vtex.menu@2.x:submenu.accordion#small"
    ]
  },
  "menu-item#electronics-acc": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 3,
        "text": "Electronics"
      }
    },
    "blocks": [
      "vtex.menu@2.x:submenu.accordion#electronics"
    ]
  },
  "vtex.menu@2.x:submenu.accordion#major":{
      "children":[
          "vtex.menu@2.x:menu#major"
      ]
    },
  "vtex.menu@2.x:submenu.accordion#small":{
    "children":[
        "vtex.menu@2.x:menu#small"
    ]
  },
  "vtex.menu@2.x:submenu.accordion#electronics":{
    "children":[
        "vtex.menu@2.x:menu#electronics"
    ]
  },
  "vtex.menu@2.x:menu#major":{
    "children": [
        "menu-item#refrigerators",
        "menu-item#ovens",
        "menu-item#washers"
      ],
      "props":{
          "orientation": "vertical"
      }
  },
  "vtex.menu@2.x:menu#small":{
      "children": [
          "menu-item#mixers",
          "menu-item#toasters",
          "menu-item#coffee"
        ],
        "props":{
            "orientation": "vertical"
        }
  },
  "vtex.menu@2.x:menu#electronics":{
      "children": [
          "menu-item#cameras",
          "menu-item#laptops",
          "menu-item#tvs"
        ],
        "props":{
            "orientation": "vertical"
        }
  }
}
```


[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-framework-course/course/template/store/blocks/default.jsonc from the store-framework-course VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store.orderplaced": {
    "blocks": ["order-placed"]
  },

  "store.account": {
    "blocks": ["my-account"],
    "parent": {
      "challenge": "challenge.profile"
    }
  },

  "store.login": {
    "blocks": ["login-content#default"]
  },

  "login-content#default": {
    "props": {
      "isInitialScreenOptionOnly": false,
      "defaultOption": 1,
      "showPasswordVerificationIntoTooltip": true
    }
  },

  "product-summary.shelf": {
    "children": [
      "product-summary-add-to-list-button",
      "product-summary-name",
      "product-rating-inline",
      "product-summary-space",
      "product-summary-price",
      "product-identifier.summary",
      "product-summary-buy-button"
    ]
  }
}

```


[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-framework-course/course/template/store/blocks/menu-items.jsonc from the store-framework-course VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "menu-item#major-appliances": {
      "props": {
      "type": "custom",
      "highlight": true,
      "itemProps": {
        "text": "Major Aplliances"
      }
    }
  },
  "menu-item#small-appliances": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 2,
        "text": "Small Appliances"
      }
    }
  },
  "menu-item#electronics": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 3,
        "text": "Electronics"
      }
    }
  },
  "menu-item#refrigerators": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 4,
        "text": "Refrigerators"
      }
    }
  },
  "menu-item#ovens": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 5,
        "text": "Ranges & Ovens"
      }
    }
  },
  "menu-item#washers": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 6,
        "text": "Washers & Dryers"
      }
    }
  },
  "menu-item#mixers": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 7,
        "text": "Mixers & Blenders"
      }
    }
  },
  "menu-item#toasters": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 8,
        "text": "Toasters & Grills"
      }
    }
  },
  "menu-item#coffee": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 9,
        "text": "Coffee Makers"
      }
    }
  },
  "menu-item#tvs": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 10,
        "text": "TVs"
      }
    }
  },
  "menu-item#laptops": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 11,
        "text": "Laptops"
      }
    }
  },
  "menu-item#cameras": {
      "props":
    {
      "type": "category",
      "highlight": true,
      "itemProps": {
        "categoryId": 12,
        "text": "Cameras"
      }
    }
  }
}
```


[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-framework-course/course/template/store/blocks/home.jsonc from the store-framework-course VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store.home": {
    "blocks": [

    ]
  }
}
```


[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-framework-course/course/template/store/blocks/product.jsonc from the store-framework-course VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store.product": {
    "children": [
    ]
  }
}
```


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

[Assistant]
Here's the implementation:

```markdown
# VTEX Sticky Layout
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->

The Sticky Layout app provides layout structures to help building elements that should be fixed relative to the viewport in certain contexts.

> Sticky positioning is a hybrid of relative and fixed positioning. The element is treated as relative positioned until it crosses a specified threshold, at which point it is treated as fixed positioned.
>
> You can understand more by reading about in the [MDN `position` documentation](https://developer.mozilla.org/en-US/docs/Web/CSS/position#Sticky_positioning).

<!-- @import "[TOC]" {cmd="toc" depthFrom=3 depthTo=6 orderedList=false} -->

<!-- code_chunk_output -->

- [`sticky-layout`](#sticky-layout)
- [`sticky-layout.stack-container`](#sticky-layoutstack-container)

<!-- /code_chunk_output -->

## Blocks

### `sticky-layout`

The `sticky-layout` block is responsible for making its children _stick_ to a certain position on the page when exiting the viewport while scrolling.

**Props:**

| Prop name         | Type           | Description                                                                      | Default value |
| ----------------- | -------------- | -------------------------------------------------------------------------------- | ------------- |
| `blockClass`      | `String`       | Unique class name to be appended to the container class                          | `""`          |
| `position`        | `PositionEnum` | Indicates where the component should stick                                       | `N/A`         |
| `verticalSpacing` | `Number`       | Indicates the distance in pixels from the position chosen in the `position` prop | `0`           |

`PositionEnum` options:

| Enum name | Enum value | Description                                  |
| --------- | ---------- | -------------------------------------------- |
| `TOP`     | `'top'`    | Component will stick to the top of screen    |
| `BOTTOM`  | `'bottom'` | Component will stick to the bottom of screen |

**CSS Handles:**

| Prop name        | Description                                                                                |
| ---------------- | ------------------------------------------------------------------------------------------------------------- |
| `container`      | Sticky layout container                                                                                |
| `wrapper`        | Wrapper element that takes up the space previously used by the stuck element to prevent the page from jumping |
| `wrapper--stuck` | Sticky layout wrapper when stuck to a position on the page                                                    |

**Example usage:**

```jsonc
{
  "store.product": {
    "children": [
      "flex-layout.row#product-breadcrumb",
      "flex-layout.row#product-main",
      "sticky-layout#buy-button"
    ],
    "parent": {
      "challenge": "challenge.address"
    }
  },
  "sticky-layout#buy-button": {
    "props": {
      "position": "bottom"
    },
    "children": ["flex-layout.row#buy-button"]
  },
  "flex-layout.row#buy-button": {
    "props": {
      "marginTop": 4,
      "marginBottom": 7,
      "paddingBottom": 2
    },
    "children": ["buy-button"]
  }
}
```

### `sticky-layout.stack-container`

The `sticky-layout.stack-container` block can be used to orchestrate multiple `sticky-layout`s to have a stack behavior instead of one being on top of the other.

**Props:**

| Prop name  | Type           | Description                                                                                | Default value |
| ---------- | -------------- | --------------------------------------------------------------------------------------------------------- | ------------- |
| `position` | `PositionEnum` | Indicates where the component should stick. _It overrides the `position` of its children `sticky-layout`_ | `N/A`         |

`PositionEnum` options:

| Enum name | Enum value | Description                                  |
| --------- | ---------- | -------------------------------------------- |
| `TOP`     | `'top'`    | Component will stick to the top of screen    |
| `BOTTOM`  | `'bottom'` | Component will stick to the bottom of screen |

**Example usage:**

Imagine three blocks: the first and the last being a `sticky-layout` and the second being any other block. A gap between both `sticky-layout`s will appear the moment the user starts scrolling the page. By defining those blocks inside a `sticky-layout.stack-container`, the second `sticky-layout` block will stick to the first `sticky-layout` instead of respecting the aformetioned gap or being one on top of the other.

```jsonc
{
  "header": {
    "blocks": ["header-layout.desktop"]
  },
  "header.full": {
    "blocks": ["header-layout.desktop"]
  },
  "header-layout.desktop": {
    // define a stack-container
    "children": ["sticky-layout.stack-container#header"]
  },
  "sticky-layout.stack-container#header": {
    "props": {
      "position": "top"
    },
    "children": [
      "sticky-layout#links-menu",
      // this notification.bar is not sticky, it will be scrolled away
      "notification.bar#home",
      "sticky-layout#main-menu"
    ]
  },
  "notification.bar#home": {
    "props": {
      "content": "SELECTED ITEMS ON SALE! CHECK IT OUT!"
    }
  },
  "sticky-layout#links-menu": {
    "children": ["vtex.menu@2.x:menu#websites"]
  },
  "sticky-layout#main-menu": {
    "children": ["vtex.menu@2.x:menu#category-menu"]
  }
}
```

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
```


[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 sticky-layout/react/StackContainer.tsx from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

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

import useCumulativeHeightState from './modules/useCumulativeHeightState'
import { Positions } from './types'

interface StackContext {
  position?: Positions
  onResize(height: number): void
  stickOffset: number
}

interface Props {
  position: Positions
}

export const StackContext = createContext<StackContext>({
  onResize: () => {},
  stickOffset: 0,
})

/** This component is used to accumulate the height of multiple StickLayout blocks,
 * so that they get stacked instead of appearing in front of each other */
const StackContainer: FC<Props> = ({ children, position = Positions.TOP }) => {
  const { updateRowHeight, getAccumulatedHeight } = useCumulativeHeightState()

  return (
    <React.Fragment>
      {React.Children.map(children, (child, index) => (
        <StackContext.Provider
          value={{
            position,
            onResize: (height: number) => updateRowHeight({ height, index }),
            stickOffset: getAccumulatedHeight(index),
          }}
        >
          {child}
        </StackContext.Provider>
      ))}
    </React.Fragment>
  )
}

export default StackContainer

```


[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 sticky-layout/react/StickyLayout.tsx from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, {
  FC,
  useContext,
  useState,
  useLayoutEffect,
  useCallback,
  useRef,
  CSSProperties,
} from 'react'
import ReactResizeDetector from 'react-resize-detector'
import { useCssHandles, applyModifiers } from 'vtex.css-handles'

import { useStickyScroll } from './modules/useStickyScroll'
import { StackContext } from './StackContainer'
import { Positions } from './types'
import { useWindowListener } from './modules/useWindowListener'

const CSS_HANDLES = ['container', 'wrapper'] as const

interface Props {
  position?: Positions
  verticalSpacing?: number
  zIndex?: number
}

const StickyLayoutComponent: FC<Props> = ({
  children,
  position: positionProp,
  zIndex,
  verticalSpacing = 0,
}) => {
  const handles = useCssHandles(CSS_HANDLES)

  const wrapperRef = useRef<HTMLDivElement>(null)
  const containerRef = useRef<HTMLDivElement>(null)

  // Height of the sticky layout content
  const [contentHeight, setContentHeight] = useState<number | string>('auto')
  // Distance of the sticky layout wrapper to the top of the page
  const [wrapperOffsetTop, setWrapperOffsetTop] = useState<number | undefined>()

  const { stickOffset, onResize, position: positionContext } = useContext(
    StackContext
  )

  // Capturing the `wrapper.offsetTop` is costly, triggering
  // the layer tree update, paint and composite steps.
  // To solve this, we use the DOMContentLoaded together with a setInterval
  // to regularly update this value instead of reading it on EVERY scroll handler.
  const updateWrapperOffset = useCallback(() => {
    const offsetTop = wrapperRef.current?.offsetTop ?? 0
    if (offsetTop !== wrapperOffsetTop) {
      setWrapperOffsetTop(offsetTop)
    }
  }, [wrapperOffsetTop])

  // update the wrapper offset regularly because the page structure may have changed
  useLayoutEffect(() => {
    const intervalID = setInterval(updateWrapperOffset, 3000)
    return () => clearInterval(intervalID)
  }, [updateWrapperOffset])

  // update the wrapper offset when DOM parsing is done
  useWindowListener(['DOMContentLoaded', 'resize'], updateWrapperOffset)

  // Context position prop precedes the StickyLayout position prop
  const position = positionContext ?? positionProp ?? Positions.TOP

  const { isStuck } = useStickyScroll({
    stickOffset,
    position,
    verticalSpacing,
    contentHeight,
    wrapperOffsetTop,
  })

  if (positionContext != null && positionProp != null) {
    console.warn(
      '"StackContainer" overrides the "StickyLayout" "position" prop.'
    )
  }

  const containerClassname = [
    handles.container,
    !zIndex && 'z-999',
    isStuck ? 'fixed' : 'relative',
    'left-0 right-0',
  ]
    .filter(Boolean)
    .join(' ')

  const wrapperClassname = isStuck
    ? applyModifiers(handles.wrapper, ['stuck'])
    : handles.wrapper

  const containerStyle: CSSProperties = {
    [position]: isStuck ? verticalSpacing + stickOffset : 0,
    zIndex,
  }

  const wrapperStyle: CSSProperties = {
    position: 'relative',
    height: contentHeight,
  }

  return (
    <div ref={wrapperRef} className={wrapperClassname} style={wrapperStyle}>
      <div
        ref={containerRef}
        className={containerClassname}
        style={containerStyle}
      >
        {children}
        <ReactResizeDetector
          handleHeight
          onResize={(_, height) => {
            if (isStuck === false) {
              setContentHeight(height)
            }
            onResize(height)
          }}
        />
      </div>
    </div>
  )
}

export default StickyLayoutComponent

```


[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 sticky-layout/react/package.json from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "vtex.sticky-layout",
  "scripts": {
    "lint": "eslint . --ext .ts --ext .tsx",
    "lint:fix": "eslint --fix . --ext .ts --ext .tsx",
    "test": "vtex-test-tools test"
  },
  "dependencies": {
    "react-resize-detector": "^4.2.1",
    "throttleit": "^1.0.0"
  },
  "devDependencies": {
    "@types/jest": "^25.1.2",
    "@types/node": "^13.7.1",
    "@types/ramda": "^0.26.41",
    "@types/react": "^16.9.19",
    "@types/react-resize-detector": "^4.2.0",
    "@vtex/test-tools": "^3.0.3",
    "@vtex/tsconfig": "^0.4.3",
    "apollo-cache-inmemory": "^1.6.5",
    "apollo-client": "^2.6.8",
    "typescript": "3.9.7",
    "vtex.css-handles": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@0.4.1/public/@types/vtex.css-handles",
    "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.90.2/public/@types/vtex.render-runtime"
  },
  "vtexTestTools": {
    "defaultLocale": "pt-BR"
  },
  "version": "0.3.6"
}

```


[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 sticky-layout/store/interfaces.json from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "sticky-layout": {
    "allowed": "*",
    "component": "StickyLayout",
    "composition": "children"
  },
  "sticky-layout.stack-container": {
    "allowed": "*",
    "component": "StackContainer",
    "composition": "children"
  }
}

```


[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 sticky-layout/react/types/index.ts from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

```typescript
export enum Positions {
  BOTTOM = 'bottom',
  TOP = 'top',
}

```


[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 sticky-layout/react/typings/vtex.css-handles.d.ts from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.css-handles' {
  type CssHandlesInput = readonly string[]
  type ValueOf<T extends readonly any[]> = T[number]
  type CssHandles<T extends CssHandlesInput> = Record<ValueOf<T>, string>
  interface CssHandlesOptions {
    migrationFrom?: string | string[]
  }

  export const useCssHandles: <T extends CssHandlesInput>(
    handles: T,
    options: CssHandlesOptions = {}
  ) => CssHandles<T>
  export const applyModifiers: (
    handles: string,
    modifier: string | string[]
  ) => 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 sticky-layout/react/typings/css.d.ts from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.css' {
  const css: any
  export default css
}

```


[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 sticky-layout/react/typings/vtex.styleguide.d.ts from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.styleguide' {
  export const Button
  export const Modal
}

```


[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 sticky-layout/react/modules/useWindowListener.ts from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { useLayoutEffect } from 'react'
import throttle from 'throttleit'

import canUsePassiveEventListeners from './canUsePassiveEventListeners'

const canUsePassiveListener = canUsePassiveEventListeners()
const currentEvents: Record<string, Set<Function>> = {}

const throttledScrollResizeHandler = throttle((e: Event) => {
  const scrollY = window.pageYOffset
  currentEvents[e.type].forEach(fn => fn(scrollY))
}, 50)

const handlers: Record<string, (e: Event) => void> = {
  scroll: throttledScrollResizeHandler,
  resize: throttledScrollResizeHandler,
  default: (e: Event) => {
    currentEvents[e.type].forEach(fn => fn(e))
  },
}

function getHandler(event: string): (e: Event) => void {
  return handlers[event] ?? handlers.default
}

function detachListener(eventList: string[], fn: Function) {
  if (typeof window === 'undefined') {
    return
  }

  eventList.forEach(event => {
    currentEvents[event].delete(fn)
    if (currentEvents[event].size === 0) {
      window.removeEventListener(event, getHandler(event))
    }
  })
}

function attachListener(eventList: string[], fn: Function) {
  if (typeof window === 'undefined') {
    return
  }

  eventList.forEach(event => {
    if (!(event in currentEvents)) currentEvents[event] = new Set()
    if (currentEvents[event].size === 0) {
      window.addEventListener(
        event,
        getHandler(event),
        canUsePassiveListener ? { passive: true } : false
      )
    }
    currentEvents[event].add(fn)
  })

  return () => detachListener(eventList, fn)
}

/**
 * Hook responsible for handling multiple event callbacks
 * by using a single listener per event
 * @export
 * @param {string[]} eventList
 * @param {Function} fn
 */
export function useWindowListener(eventList: string[], fn: Function) {
  useLayoutEffect(() => attachListener(eventList, fn), [eventList, fn])
}

```


[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 sticky-layout/react/modules/useStickyScroll.ts from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { useCallback, useState } from 'react'

import { useWindowListener } from './useWindowListener'
import { Positions } from '../types'

interface StickyProps {
  position?: Positions
  verticalSpacing?: number
  stickOffset?: number
  contentHeight: number | string
  wrapperOffsetTop: number | undefined
}

/*
 * Hook responsible for defining if a sticky element should stuck to the viewport or not
 *
 * @param {StickyProps} {
 *   position,
 *   verticalSpacing = 0,
 *   stickOffset = 0,
 *   contentHeight,
 *   wrapperOffsetTop,
 * }
 * @returns { isStuck }
 */
export const useStickyScroll = ({
  position,
  verticalSpacing = 0,
  stickOffset = 0,
  contentHeight,
  wrapperOffsetTop,
}: StickyProps) => {
  const [isStuck, setStuck] = useState<boolean>(false)

  const handlePosition = useCallback(
    (scrollY: number) => {
      if (!contentHeight || typeof contentHeight !== 'number' || wrapperOffsetTop === undefined) return

      const offset = stickOffset + verticalSpacing
      let currentPosition = scrollY
      let stuckPosition = wrapperOffsetTop

      if (position === Positions.TOP) {
        stuckPosition -= offset
        /*
         * We remove `1` from the current position because stuck elements at the top
         * of the page must be able to be considered "unstuck".
         * In example, a mobile header at the top of the page should be considered unstuck
         * if the user scrolls back to the top of the page.
         */
        if (stuckPosition === 0 && currentPosition === 0) {
          currentPosition -= 1
        }
      } else {
        currentPosition += window.innerHeight
        stuckPosition += offset + contentHeight
      }

      const shouldStick = currentPosition >= stuckPosition

      // same state, do nothing
      if (isStuck === shouldStick) return

      setStuck(shouldStick)
    },
    [
      contentHeight,
      wrapperOffsetTop,
      position,
      isStuck,
      stickOffset,
      verticalSpacing,
    ]
  )

  useWindowListener(['resize', 'scroll'], handlePosition)

  return {
    isStuck,
  }
}

```


[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 sticky-layout/react/modules/useCumulativeHeightState.ts from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { useState } from 'react'

type State = Record<number, number>

/**
 * Accumulate the height of multiple elements.
 *
 * @returns { getAccumulatedHeight, updateRowHeight }
 */
const useCumulativeHeightState = () => {
  const [state, set] = useState<State>({})

  const updateRowHeight = ({
    height,
    index,
  }: {
    height: number
    index: number
  }) => {
    if (state[index] === height) {
      return
    }

    set(prev => ({ ...prev, [index]: height }))
  }

  const getAccumulatedHeight = (index: number) => {
    const sortedIndices = Object.keys(state)
      .map(key => parseInt(key, 10))
      .sort((a, b) => a - b)

    const indices = sortedIndices.slice(0, sortedIndices.indexOf(index))
    return indices.reduce((acc, cur) => (state[cur] || 0) + acc, 0)
  }

  return {
    getAccumulatedHeight,
    updateRowHeight,
  }
}

export default useCumulativeHeightState

```


[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 sticky-layout/react/modules/canUsePassiveEventListeners.ts from the sticky-layout VTEX app.

[Assistant]
Here's the implementation:

```typescript
// Adapted from https://github.com/lencioni/consolidated-events/blob/master/src/canUsePassiveEventListeners.js
function testPassiveEventListeners() {
  if (typeof window === 'undefined') {
    return false
  }

  let supportsPassiveOption = false
  try {
    const opts = Object.defineProperty({}, 'passive', {
      get() {
        supportsPassiveOption = true
      },
    })
    const noop = () => {}
    window.addEventListener('testPassiveEventSupport', noop, opts)
    window.removeEventListener('testPassiveEventSupport', noop, opts)
  } catch (e) {
    // do nothing
  }

  return supportsPassiveOption
}

let memoizedResult: boolean

/**
 * Checks if the browser supports using passive event listeners.
 *
 * @export
 * @returns
 */
export default function canUsePassiveEventListeners() {
  if (memoizedResult === undefined) {
    memoizedResult = testPassiveEventListeners()
  }
  return memoizedResult
}

```


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

[Assistant]
Here's the implementation:

```markdown
# Crowdin Integration

Integration between IO Messages and Crowdin.

```


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

[Assistant]
Here's the implementation:

```json
{
  "memory": 256,
  "ttl": 10,
  "timeout": 15,
  "minReplicas": 2,
  "maxReplicas": 4,
  "events": {
    "updateMessage": {
      "sender": "vtex.messages",
      "keys": ["updateMessage"]
    },
    "receiveExportedMessages": {
      "sender": "vtex.messages",
      "keys": ["sendExportedMessages"]
    }
  },
  "routes" : {
    "listenToUpdates": {
      "path": "/_v/translations/update",
      "public": true,
      "smartcache": 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 crowdin-integration/node/package.json from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "dependencies": {
    "bluebird": "^3.5.5",
    "co-body": "^6.0.0",
    "crypto": "^1.0.1",
    "form-data": "^2.5.1",
    "koa-compose": "^4.0.0",
    "ramda": "^0.25.0",
    "@vtex/api": "3.65.0"
  },
  "devDependencies": {
    "@types/bluebird": "^3.5.27",
    "@types/form-data": "^2.2.1",
    "@types/co-body": "^0.x",
    "@types/koa-compose": "^3.2.0",
    "@types/node": "8.x",
    "@types/ramda": "types/npm-ramda#dist",
    "@vtex/api": "^3.65.0",
    "tslint": "^5.14.0",
    "tslint-config-vtex": "^2.1.0",
    "typescript": "3.5.2"
  },
  "scripts": {
    "lint": "tsc --noEmit --pretty && tslint -c tslint.json --fix './**/*.ts'"
  },
  "version": "0.7.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 crowdin-integration/node/index.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

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

import { Clients } from './clients'
import { unwrap } from './events/crowdinAPI/unwrap'
import { updateCrowdinProject } from './events/crowdinAPI/update'
import { saveInCrowdin } from './events/export'
import { getSettings } from './events/settings'
import { getInfosFromCrowdin } from './middlewares/listenToUpdates'
import { saveIOMessage } from './middlewares/saveUpdateInMessages'

const TIMEOUT_MS = 3000
const TRANSLATION_CONCURRENCY = 5
const TRANSLATION_RETRIES = 3
const SHORT_TIMEOUT_MS = 1 * 1000
const CONCURRENCY = 10

const appsCacheStorage = new LRUCache<string, any>({
  max: 10,
})

metrics.trackCache('apps', appsCacheStorage)

const clients: ClientsConfig<Clients> = {
  implementation: Clients,
  options: {
    apps: {
      concurrency: CONCURRENCY,
      memoryCache: appsCacheStorage,
      retries: 1,
      timeout: SHORT_TIMEOUT_MS,
    },
    default: {
      retries: 2,
      timeout: TIMEOUT_MS,
    },
    messagesGraphQL: {
      concurrency: TRANSLATION_CONCURRENCY,
      retries: TRANSLATION_RETRIES,
      timeout: TIMEOUT_MS,
    },
  },
}

export default new Service<Clients, State>({
  clients,
  events: {
    receiveExportedMessages: [getSettings, saveInCrowdin],
    updateMessage: [getSettings, unwrap, updateCrowdinProject],
  },
  routes: {
    listenToUpdates: method({
      POST: [getSettings, getInfosFromCrowdin, saveIOMessage],
    }),
  },
})

```


[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 crowdin-integration/node/clients/Crowdin.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

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

import {
  CrowdinGetProjectResponse,
  CrowdinGetStringResponse,
  CrowdinGetTranslationResponse,
  CrowdinListProjects,
  StringToCrowdin,
  TranslationToCrowdin
} from '../typings/Crowdin'
import { crowdinSettings } from '../utils/crowdin'

export class Crowdin extends ExternalClient {
  constructor(context: IOContext, options?: InstanceOptions){
    super('http://api.crowdin.com', context, {
      ...options,
      headers: {
        Authorization: `Bearer ${crowdinSettings.tokenApiV2}`,
        'content-type': 'application/json',
        'x-vtex-use-https': 'true',
      },
    })
  }

  public async addString(data: StringToCrowdin, projectId: string) {
    const dataToCrowdin = {
      context: data.groupContext,
      identifier: data.key,
      text: data.message,
    }

    let errMsg
    const res = await this.http.post<any>(`/api/v2/projects/${projectId}/strings`,
      dataToCrowdin,
      {
        metric: 'crowdin-add-string',
      })
      .catch((err) => {errMsg = err.response}
    )

    return {
      err: errMsg,
      res,
    }
  }

  public async addTranslation(data: TranslationToCrowdin, projectId: string) {
    const dataToCrowdin = {
      languageId: data.to,
      stringId: data.stringId,
      text: data.translation,
    }
    let errMsg

    const res = await this.http.post<any>(`/api/v2/projects/${projectId}/translations`,
      dataToCrowdin,
      {
        metric: 'crowdin-add-translation',
      })
      .catch((err) => {errMsg = err.response}
    )

    return {
      err: errMsg,
      res,
    }
  }

  public async getString(stringId: string, projectId: string) {
    let errMsg
    const res = await this.http.get<CrowdinGetStringResponse>(`/api/v2/projects/${projectId}/strings/${stringId}`,
      {
        metric: 'crowdin-get-string',
      }
    )
    .catch((err) => {errMsg = err.response})

    return {
      err: errMsg,
      res,
    }
  }

  public async getTranslation(translationId: string, projectId: string) {
    let errMsg
    const res = await this.http.get<CrowdinGetTranslationResponse>(`/api/v2/projects/${projectId}/translations/${translationId}`,
      {
        metric: 'crowdin-get-translation',
      }
    )
    .catch((err) => {errMsg = err.response})

    return {
      err: errMsg,
      res,
    }
  }

  public async getProject(projectId: string) {
    let errMsg
    const res = await this.http.get<CrowdinGetProjectResponse>(`/api/v2/projects/${projectId}`,
      {
        metric: 'crowdin-get-project',
      }
    )
    .catch((err) => {errMsg = err.response})

    return {
      err: errMsg,
      res,
    }
  }

  public async listProjects() {
    let errMsg
    const res = await this.http.get<CrowdinListProjects>(`/api/v2/projects`,
      {
        metric: 'crowdin-list-projects',
      }
    )
    .catch((err) => {errMsg = err.response})

    return {
      err: errMsg,
      res,
    }
  }
}

```


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

[Assistant]
Here's the implementation:

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

import { Crowdin } from './Crowdin'

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

```


[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 crowdin-integration/node/typings/Colossus.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

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

import { Clients } from '../clients'
import { MessagesCrowdinByGroupContextAndSrcLang } from './Messages'

export interface ColossusEventContext extends EventContext<Clients, State> {
  status: number
  key: string
  sender: string
  body: any
  clients: Clients
  state: State
}

export interface State {
  to: string
  messagesCrowdinByGroupContextAndSrcLang: MessagesCrowdinByGroupContextAndSrcLang
}

```


[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 crowdin-integration/node/typings/Messages.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
export interface MessagesCrowdin {
  [key: string] : {
    message: string,
    srcMessage?: string,
    description?: string
  }
}

export interface MessagesCrowdinBySrcLang {
  [from: string]: MessagesCrowdin
}

export interface MessagesCrowdinByGroupContextAndSrcLang {
  [groupContext: string]: MessagesCrowdinBySrcLang
}

export interface UpdateMessageToCrowdinArg {
  to?: string,
  from: string,
  groupContext: string,
  messages: MessagesCrowdin
}

export interface Translations {
  lang: string
  translation: string
}

export interface ExportedMessage {
  srcLang: string
  groupContext?: string
  context: string
  translations: Translations[]
}

export interface Range {
  from: number
  to: number
}

export interface ExportedMessageObject {
  finished: boolean,
  range: Range
  messages: ExportedMessage[]
}

```


[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 crowdin-integration/node/typings/Crowdin.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
export interface StringToCrowdin {
  message: string,
  key: string,
  groupContext: string
}

export interface TranslationToCrowdin {
  translation: string,
  to: string,
  stringId: number
}

export interface CrowdinListProjects {
  data: CrowdinGetProjectResponse[]
}

export interface CrowdinGetProjectResponse {
  data: CrowdinGetProjectResponseData
}

interface CrowdinGetProjectResponseData {
  id: string,
  groupId: string,
  userId: string,
  sourceLanguageId: string,
  targetLanguageIds: string[],
  joinPolicy: string,
  languageAccessPolicy: string,
  name: string,
  cname: string,
  identifier: string,
  description: string,
  visibility: string,
  logo: string,
  background: string,
  isExternal: boolean,
  externalType: string,
  advancedWorkflowId: number,
  hasCrowdsourcing: boolean,
  createdAt: string,
  updatedAt: string
}

export interface CrowdinGetStringResponse {
  data: CrowdinGetStringResponseData
}

interface CrowdinGetStringResponseData {
  id: number,
  projectId: number,
  fileId: number,
  identifier: string,
  text: string,
  type: string,
  context: string,
  maxLength: number,
  isHidden: boolean,
  revision: number,
  isIc: boolean,
  createdAt: string,
  updatedAt: string
}

export interface CrowdinGetTranslationResponse {
  data: CrowdinGetTranslationResponseData
}

interface CrowdinGetTranslationResponseData {
  id: number,
  text: string,
  pluralCategoryName: string,
  user: CrowdinUser,
  rating: number
}

interface CrowdinUser {
  id: number,
  login: string
}

export interface CrowdinNewTranslationApprovedEvent {
  event: string,
  project: string,
  project_id: string,
  language: string,
  source_string_id: string,
  translation_id: string,
  user: string,
  user_id: string,
  file_id: string,
  file: 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 crowdin-integration/node/typings/typings.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

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

import { Clients } from '../clients'

declare global {
  interface State {
    srcMessage: string,
    targetMessage: string,
    srcLang: string,
    targetLang: string,
    groupContext: string
  }

  type Context = ServiceContext<Clients, State, 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 crowdin-integration/node/middlewares/saveUpdateInMessages.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
export async function saveIOMessage(ctx: Context, next:() => Promise<any>) {
  const {
    clients: {messagesGraphQL},
    state: {
      groupContext,
      srcLang,
      srcMessage,
      targetLang,
      targetMessage,
    },
  } = ctx

  const messages = [{
    groupContext,
    srcLang,
    srcMessage,
    targetMessage,
  }]

  await messagesGraphQL.saveV2({
    fireEvent: false,
    messages,
    to: targetLang,
  })

  ctx.status = 200
  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 crowdin-integration/node/middlewares/listenToUpdates.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import parseBody = require('co-body')
import { isEmpty, toPairs } from 'ramda'

import {
  CrowdinGetProjectResponse,
  CrowdinGetStringResponse,
  CrowdinGetTranslationResponse,
  CrowdinNewTranslationApprovedEvent
} from '../typings/Crowdin'
import { CROWDIN_BUCKET } from '../utils/constants'
import { crowdinProjectsIds } from '../utils/crowdin'

export async function getInfosFromCrowdin(ctx: Context, next: () => Promise<void>) {
  const { crowdin, vbase } = ctx.clients
  const { logger } = ctx.vtex
  const body = await parseBody(ctx.req) as CrowdinNewTranslationApprovedEvent

  const vbaseFileName = body.source_string_id
  const stringCrowdinId = await vbase.getJSON<any>(CROWDIN_BUCKET, vbaseFileName, true)
  const stringId = stringCrowdinId.crowdinId
  const srcMessageInfoFromCrowdin = await crowdin.getString(stringId, body.project_id)
  if(srcMessageInfoFromCrowdin.err) {
    logger.error(srcMessageInfoFromCrowdin.err)
    return
  }
  if(!(srcMessageInfoFromCrowdin.res as CrowdinGetStringResponse).data) {
    logger.error('Unexpected data format returned from Crowdin')
    return
  }
  const srcMessage = (srcMessageInfoFromCrowdin.res as CrowdinGetStringResponse).data.text
  const groupContext = (srcMessageInfoFromCrowdin.res as CrowdinGetStringResponse).data.context

  const targetMessageCrowdinId = body.translation_id
  const targetMessageInfoFromCrowdin = await crowdin.getTranslation(targetMessageCrowdinId, body.project_id)
  if(targetMessageInfoFromCrowdin.err) {
    logger.error(targetMessageInfoFromCrowdin.err)
    return
  }
  if(!(targetMessageInfoFromCrowdin.res as CrowdinGetTranslationResponse).data) {
    logger.error('Unexpected data format returned from Crowdin')
    return
  }
  const targetMessage = (targetMessageInfoFromCrowdin.res as CrowdinGetTranslationResponse).data.text

  const crowdinProjectsIdsArray = toPairs(crowdinProjectsIds)
  const maybeProject = crowdinProjectsIdsArray.filter(([_, projectId]) => body.project_id === projectId)
  let srcLang
  if(isEmpty(maybeProject)) {
    const projectInfoFromCrowdin = await crowdin.getProject(body.project_id)
    if(projectInfoFromCrowdin.err) {
      logger.error(projectInfoFromCrowdin.err)
      return
    }
    srcLang = (projectInfoFromCrowdin.res as CrowdinGetProjectResponse).data.sourceLanguageId
  } else {
    srcLang = maybeProject[0][0]
  }

  const targetLang = body.language

  ctx.state = {
    groupContext,
    srcLang,
    srcMessage,
    targetLang,
    targetMessage,
  }
  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 crowdin-integration/node/utils/crowdin.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { objToHash } from '.'
import { Crowdin } from '../clients/Crowdin'
import { CrowdinListProjects } from '../typings/Crowdin'

export const toCrowdinFilePath = (groupContext: string) => {
  let dirPath = ''
  if (groupContext.toLowerCase().startsWith('product')){
    dirPath = 'Catalog/Products/'
  }
  if (groupContext.toLowerCase().startsWith('sku')){
    dirPath = 'Catalog/SKUs/'
  }
  if (groupContext.toLowerCase().startsWith('brand')){
    dirPath = 'Catalog/Brands/'
  }
  if (groupContext.toLowerCase().startsWith('category')){
    dirPath = 'Catalog/Categories/'
  }
  if (groupContext.toLowerCase().startsWith('specification')){
    dirPath = 'Catalog/'
  }

  return {
    dirPath,
    fileName: `${groupContext}.json`,
  }
}

export const toVbaseSourceCrowdinFileName = (dirPath: string, fileName: string) => {
  const filePath = `${dirPath}${fileName}`
  return `${objToHash<string>(filePath)}.json`
}

export function languageLocaleToCrowdinLanguageId(lang: string): string {
  switch (lang) {
    case 'en-DV':
      return 'en'
    case 'ja-JP':
      return 'ja'
    default:
      return lang
  }
}

export async function languageLocaleToCrowdinProjectId(crowdin: Crowdin, languageLocale: string) {
  const crowdinLangId = languageLocaleToCrowdinLanguageId(languageLocale)

  if(crowdinProjectsIds[crowdinLangId]) {
    return {value: crowdinProjectsIds[crowdinLangId], err: null}
  } else {
    const projectsInfoObj = await crowdin.listProjects()
    if(projectsInfoObj.err) {
      return {value: null, err: 'Error getting projects list for account'}
    }
    const projectsInfo = (projectsInfoObj.res as CrowdinListProjects).data
    const projectBySrcLang = projectsInfo.filter((projectInfo) => projectInfo.data.sourceLanguageId === crowdinLangId)
    if(projectBySrcLang.length === 0) {
      return {value: null, err: `There is not a project with source language ${crowdinLangId} in Crowdin. You need to create it before saving strings in that language.`}
    }
    projectsInfo.map((project) => crowdinProjectsIds[project.data.sourceLanguageId] = project.data.id)

    return {value: projectBySrcLang[0].data.id, err: null}
  }
}

export const crowdinSettings = { tokenApiV2: '' }
export const crowdinProjectsIds = {} as Record<string, 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 crowdin-integration/node/utils/constants.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const CROWDIN_BUCKET = 'crowdin'
export const FILE_NOT_FOUND = 8
export const DIRECTORY_ALREADY_EXISTS = 50
export const STRING_ALREADY_EXPORTED_MESSAGE = 'Invalid identifier given. Identifier must be unique'
export const APP_ID = process.env.VTEX_APP_ID!

```


[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 crowdin-integration/node/utils/index.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import * as crypto from 'crypto'

export const objToHash = <T>(obj: T) =>
  crypto
    .createHash('md5')
    .update(JSON.stringify(obj))
    .digest('hex')

```


[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 crowdin-integration/node/events/settings.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ColossusEventContext } from '../typings/Colossus'
import { APP_ID } from '../utils/constants'
import { crowdinSettings } from './../utils/crowdin'

export async function getSettings(ctx: Context | ColossusEventContext, next: () => Promise<any>) {
  const {clients: { apps }} = ctx

  if(!crowdinSettings.tokenApiV2) {
    const appSettings = await apps.getAppSettings(`${APP_ID}`)
    crowdinSettings.tokenApiV2 = appSettings.CrowdinAPIToken
  }

  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 crowdin-integration/node/events/export.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

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

import { ColossusEventContext } from '../typings/Colossus'
import { ExportedMessageObject } from '../typings/Messages'
import { objToHash } from '../utils'
import { CROWDIN_BUCKET, STRING_ALREADY_EXPORTED_MESSAGE } from '../utils/constants'
import { languageLocaleToCrowdinLanguageId, languageLocaleToCrowdinProjectId } from '../utils/crowdin'

interface ExportErrors {
  type: 'string' | 'translation'
  from: string
  to: string
  string: string
  status: string
  projectId?: string
  originalStringId?: number
}

export async function saveInCrowdin(ctx: ColossusEventContext, next: () => Promise<any>) {
  const { crowdin, vbase } = ctx.clients
  const { logger } = ctx.vtex
  const { finished, messages } = ctx.body as ExportedMessageObject
  const errors = [] as ExportErrors[]

  if(!finished) {
    await Promise.all(messages.map(async ({srcLang, context, groupContext, translations}) => {
      const crowdinGroupContext = groupContext ? groupContext : ''
      const crowdinContext = context ? context : ''
      const projectId = await languageLocaleToCrowdinProjectId(crowdin, srcLang)
      let stringCrowdinId = 0
      let shouldLogError = true

      await Promise.all(translations.map(async ({ lang, translation }) => {
        shouldLogError = true
        if(srcLang === lang) {
          if(projectId.err) {
            errors.push({type: 'string', string: translation, from: lang, to: lang, status: `Error getting projectId. Returned error: ${projectId.err}`})
          } else if (projectId.value) {
            const key = objToHash<string>(translation + crowdinContext + srcLang)
            const saveMessageInCrowdin = await crowdin.addString({message: translation, key, groupContext: crowdinGroupContext}, projectId.value)
            if(saveMessageInCrowdin.err) {
              const statusCode = (saveMessageInCrowdin.err as any).status
              const errorMessage = (saveMessageInCrowdin.err as any).data.errors[0].error.errors[0].message
              if(errorMessage === STRING_ALREADY_EXPORTED_MESSAGE) {
                shouldLogError = false
              } else {
                errors.push({type: 'string', string: translation, from: lang, to: lang, status: errorMessage, projectId: projectId.value})
              }
              if (statusCode === 429) {
                throw new TooManyRequestsError()
              }
            }
            if(saveMessageInCrowdin.res) {
              stringCrowdinId = saveMessageInCrowdin.res.data.id
              const vbaseFileName = key
              await vbase.saveJSON(CROWDIN_BUCKET, vbaseFileName, {crowdinId: stringCrowdinId})
            }
          }
        }
      }))

      await Promise.all(translations.map(async ({lang, translation}) => {
        if(srcLang !== lang) {
          const to = languageLocaleToCrowdinLanguageId(lang)
          if(projectId.err) {
            errors.push({type: 'translation', string: translation, from: srcLang, to, status: `Error getting projectId. Returned error: ${projectId.err}`})
          } else if (projectId.value) {
            if(stringCrowdinId) {
              const saveTranslationsInCrowdin = await crowdin.addTranslation({translation, to, stringId: stringCrowdinId}, projectId.value)
              if(saveTranslationsInCrowdin.err) {
                const statusCode = (saveTranslationsInCrowdin.err as any).status
                const errorMessage = (saveTranslationsInCrowdin.err as any).data.errors[0].error.errors[0].message
                errors.push({type: 'translation', string: translation, from: srcLang, to, status: errorMessage, originalStringId: stringCrowdinId, projectId: projectId.value})
                if (statusCode === 429) {
                  throw new TooManyRequestsError()
                }
              }
            } else if(shouldLogError){
              errors.push({type: 'translation', string: translation, from: srcLang, to, status: 'Cannot get the Crowdin id of original string', projectId: projectId.value})
            }
          }
        }
      }))

    }))
  }

  logger.error(errors)

  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 crowdin-integration/node/events/crowdinAPI/unwrap.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { SaveArgsV2 as MessagesObject } from '@vtex/api'
import { reduce } from 'ramda'

import { ColossusEventContext } from '../../typings/Colossus'
import { MessagesCrowdinByGroupContextAndSrcLang } from '../../typings/Messages'
import { objToHash } from '../../utils'
import { languageLocaleToCrowdinLanguageId } from '../../utils/crowdin'

export async function unwrap(ctx: ColossusEventContext, next: () => Promise<any>){
  const messagesObject = ctx.body as MessagesObject
  const to = languageLocaleToCrowdinLanguageId(messagesObject.to)
  const messages = messagesObject.messages.map((message) => {
    message.srcLang = languageLocaleToCrowdinLanguageId(message.srcLang)
    return message
  })
  if(!messages || ! to) {
    return
  }

  const messagesCrowdinByGroupContextAndSrcLang = reduce(
    (acc, {srcLang, srcMessage, targetMessage, groupContext = 'Sorted', context}) => {
      const messageHash = objToHash<string>(srcMessage + (context || '') + srcLang)
      if(!acc[groupContext]) {
        acc[groupContext] = {}
      }
      acc[groupContext][srcLang] = {
        ...acc[groupContext][srcLang],
        [messageHash] : {
          message: targetMessage,
          srcMessage,
          ...context && {description: context},
        },
      }
      return acc
    },
    {} as MessagesCrowdinByGroupContextAndSrcLang,
    messages
  )

  ctx.state.messagesCrowdinByGroupContextAndSrcLang = messagesCrowdinByGroupContextAndSrcLang
  ctx.state.to = to

  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 crowdin-integration/node/events/crowdinAPI/update.ts from the crowdin-integration VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { TooManyRequestsError } from '@vtex/api'
import { map as mapP } from 'bluebird'
import { map, toPairs } from 'ramda'

import { ColossusEventContext } from '../../typings/Colossus'
import { UpdateMessageToCrowdinArg } from '../../typings/Messages'
import { objToHash } from '../../utils'
import { CROWDIN_BUCKET, STRING_ALREADY_EXPORTED_MESSAGE } from '../../utils/constants'
import { languageLocaleToCrowdinProjectId } from '../../utils/crowdin'

const updateStringBasedCrowdinString = async (args: UpdateMessageToCrowdinArg, {clients: {crowdin, vbase}, vtex: {logger}}: ColossusEventContext, srcLang: string) => {
  const messagesInPairs = toPairs(args.messages)
  const groupContext = args.groupContext ? args.groupContext : ''
  map(async ([key, {message}]) => {
    const projectId = await languageLocaleToCrowdinProjectId(crowdin, srcLang)
    if (projectId.err) {
      logger.error(projectId.err)
    } else if (projectId.value) {
      const saveMessageInCrowdin = await crowdin.addString({message, key, groupContext}, projectId.value)
      if(saveMessageInCrowdin.err) {
        const statusCode = (saveMessageInCrowdin.err as any).status
        const errorMessage = (saveMessageInCrowdin.err as any).data.errors ? (saveMessageInCrowdin.err as any).data.errors[0].error.errors[0].message : ''
        if(errorMessage !== STRING_ALREADY_EXPORTED_MESSAGE) {
          logger.error(saveMessageInCrowdin.err)
        }
        if (statusCode === 429) {
          throw new TooManyRequestsError()
        }
      } else {
        const stringCrowdinId = saveMessageInCrowdin.res.data.id
        if(stringCrowdinId) {
          const vbaseFileName = key
          await vbase.saveJSON<any>(CROWDIN_BUCKET, vbaseFileName, {crowdinId: stringCrowdinId})
        }
      }
    }
  }, messagesInPairs)
}

const updateStringBasedCrowdinTranslation = async (args: UpdateMessageToCrowdinArg, {clients: {crowdin, vbase}, vtex: {logger}}: ColossusEventContext, srcLang: string) => {
  const messagesInPairs = toPairs(args.messages)
  map(async ([_, {message, srcMessage, description}]) => {
    const context = description ? description : ''
    const vbaseFileName = objToHash<string>(srcMessage + (context || '') + args.from)
    const stringCrowdinId = await vbase.getJSON<any>(CROWDIN_BUCKET, vbaseFileName, true)
    const projectId = await languageLocaleToCrowdinProjectId(crowdin, srcLang)
    if (projectId.err) {
      logger.error(projectId.err)
    } else if (projectId.value) {
      const saveTranslationsInCrowdin = await crowdin.addTranslation({translation: message, to: args.to!, stringId: stringCrowdinId.crowdinId}, projectId.value)
      if(saveTranslationsInCrowdin.err) {
        const statusCode = (saveTranslationsInCrowdin.err as any).status
        logger.error(saveTranslationsInCrowdin.err)
        if (statusCode === 429) {
          throw new TooManyRequestsError()
        }
      }
    }
  }, messagesInPairs)
}

export async function updateCrowdinProject(ctx: ColossusEventContext, next: () => Promise<any>){
  const {to, messagesCrowdinByGroupContextAndSrcLang} = ctx.state
  const messagesCrowdinByGroupContextPairs = toPairs(messagesCrowdinByGroupContextAndSrcLang)

  await mapP(
    messagesCrowdinByGroupContextPairs,
    ([groupContext, messagesBySrcLang]) => {
      const messagesBySrcLangPairs = toPairs(messagesBySrcLang)
      map(
        ([srcLang, messages]) => {
          (srcLang === to) ?
            updateStringBasedCrowdinString({messages, groupContext, from: srcLang}, ctx, srcLang) :
            updateStringBasedCrowdinTranslation({messages, groupContext, to, from: srcLang}, ctx, srcLang)
        },
        messagesBySrcLangPairs
      )
    }
  )

  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 google-analytics/docs/README.md from the google-analytics VTEX app.

[Assistant]
Here's the implementation:

```markdown
# DEPRECATED

Please use Google Tag Manager pixel instead.
```


[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 google-analytics/react/index.tsx from the google-analytics VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { orderPlaced } from './events/commonEvents'
import {
  addToCart,
  productDetail,
  productClick,
  productImpression,
  purchase,
  removeFromCart,
  Impression,
} from './events/enhancedCommerce'

let currentUrl = ''

interface EventData {
  event: string
  eventName: string
  currency: string
}

interface PageViewData extends EventData {
  pageTitle: string
  pageUrl: string
}

// Common pageview function
const pageView = (origin: string, data: PageViewData) => {
  if (!data.pageUrl || data.pageUrl === currentUrl) {
    return
  }

  currentUrl = data.pageUrl
  ga('set', {
    location: currentUrl,
    page: currentUrl.replace(origin, ''),
    ...(data.pageTitle && {
      title: data.pageTitle,
    }),
  })
  ga('send', 'pageview')
}

function listener(e: MessageEvent) {
  // Event listener for productDetail
  if (e.data.event === 'productView') {
    productDetail(e.data.product)
    return
  }

  // Event listener for productClick
  if (e.data.event === 'productClick') {
    const prodData = e.data.product
    const skuId = prodData && prodData.sku && prodData.sku.itemId
    const selectedProduct = {
      ...e.data.product,
      selectedSku: skuId,
    }
    productClick(selectedProduct)
    return
  }

  // Event listener for productImpression
  if (e.data.event === 'productImpression') {
    const {
      product: oldFormatProduct,
      position: oldFormatPosition,
      impressions,
    } = e.data
    let parsedImpressions: Impression[] = []
    if (oldFormatProduct != null && oldFormatPosition !== null) {
      const skuId =
        oldFormatProduct && oldFormatProduct.sku && oldFormatProduct.sku.itemId
      const newProduct = { ...oldFormatProduct, selectedSku: skuId }
      parsedImpressions = [{ product: newProduct, position: oldFormatPosition }]
    }

    if (impressions != null) {
      parsedImpressions = impressions.map(({ product }: Impression) => {
        const skuId = product && product.sku && product.sku.itemId
        return { ...product, selectedSku: skuId }
      })
    }

    productImpression(parsedImpressions, e.data.list)
    return
  }

  // Event listener for orderPlaced
  if (e.data.event === 'orderPlaced') {
    orderPlaced(e.data)
    purchase(e.data)
    return
  }

  if (e.data.event === 'pageView') {
    pageView(e.origin, e.data)
    return
  }

  if (e.data.event === 'addToCart') {
    e.data.items.forEach((product: any) => {
      product.productName = product.name
      addToCart(product, product.price, product.quantity)
    })
    return
  }

  if (e.data.event === 'removeFromCart') {
    e.data.items.forEach((product: any) => {
      product.productId = product.id
      product.productName = product.name
      removeFromCart(product, product.sellingPrice)
    })
    return
  }
}

// Event listener for pageview
window.addEventListener('message', listener)

```


[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 google-analytics/react/tests-setup.js from the google-analytics VTEX app.

[Assistant]
Here's the implementation:

```javascript
/* eslint-disable */
window.matchMedia = window.matchMedia || function() {
  return {
    matches: false,
    addListener: function() {},
    removeListener: function() {},
  }
}

```


[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 google-analytics/react/package.json from the google-analytics VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "vtex.google-analytics",
  "description": "Google Analytics Pixel App",
  "scripts": {
    "test": "jest"
  },
  "author": "VTEX",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^10.12.18",
    "eslint": "^5.15.1",
    "eslint-config-vtex": "^10.1.0",
    "identity-obj-proxy": "^3.0.0",
    "tslint": "^5.11.0",
    "tslint-config-vtex": "^2.0.0",
    "tslint-eslint-rules": "^5.4.0",
    "typescript": "3.5.2"
  },
  "jest": {
    "verbose": true,
    "transform": {
      "^.+\\.(js|jsx|mjs)$": "<rootDir>/node_modules/babel-jest"
    },
    "transformIgnorePatterns": [
      "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$"
    ],
    "setupFiles": [
      "./tests-setup.js"
    ],
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__test__/fileMock.js",
      "\\.(css|less|scss)$": "identity-obj-proxy"
    }
  },
  "dependencies": {
    "@types/google.analytics": "0.0.39"
  },
  "version": "1.1.0"
}

```


[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 google-analytics/react/utils.tsx from the google-analytics VTEX app.

[Assistant]
Here's the implementation:

```tsx
export const getCategory = (rawCategories: string[]) => {
  if (!rawCategories || !rawCategories.length) return

  const _filterEmpty = (string: string) => {
    if (string.length > 0) return string
  }

  const categories = rawCategories.map(function (categoryPath: string) {
    let splitedPath = categoryPath.split('/').filter(_filterEmpty)
    return splitedPath[0]
  })

  return categories ? categories[0] : categories
}

```


[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 google-analytics/react/typings/global.d.ts from the google-analytics VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface Window extends Window {
  ga: Function
}

```


[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 google-analytics/react/events/commonEvents.ts from the google-analytics VTEX app.

[Assistant]
Here's the implementation:

```typescript
// Common OrderPlaced function
export const orderPlaced = (order: any) => {
  ga('send', 'event', {
    eventAction: 'view',
    eventCategory: 'orderPlaced',
    ...order,
  })
}

```


[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 google-analytics/react/events/enhancedCommerce.ts from the google-analytics VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { getCategory } from './../utils'

interface Item {
  itemId: string
  name: string
}

export interface Product {
  brand: string
  categoryId: string
  categories: string[]
  productId: string
  productName: string
  selectedSku: string
  items: Item[]
  variant: string
  sku: any
}

export interface Impression {
  product: Product
  position: number
}

const getSkuName = (selectedSku: string, items: Item[]) =>
  (items.find((item: Item) => selectedSku === item.itemId) || ({} as Item)).name

/** Product viewed event
 * https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-actvities
 */
export const productDetail = (product: Product) => {
  if (!product) return

  const category = getCategory(product.categories)

  ga('ec:addProduct', {
    brand: product.brand,
    category: category,
    id: product.productId,
    name: product.productName,
    variant: getSkuName(product.selectedSku, product.items),
  })
  ga('ec:setAction', 'detail')
  ga('send', 'event', {
    eventAction: 'Detail',
    eventCategory: 'Ecommerce',
    nonInteraction: 1,
  })
}

/** Product clicked event
 * https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#product-click
 */
export const productClick = (product: Product) => {
  if (!product) return

  const category = getCategory(product.categories)

  ga('ec:addProduct', {
    id: product.productId,
    name: product.productName,
    category: category,
    brand: product.brand,
    variant: getSkuName(product.selectedSku, product.items),
  })
  ga('ec:setAction', 'click')
  ga('send', 'event', {
    eventAction: 'Click',
    eventCategory: 'Product',
  })
}

/** Product Impression event
 * https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#product-impression
 */
export const productImpression = (impressions: Impression[], list: string) => {
  impressions.forEach(({ product, position }) => {
    if (!product) return

    const category = getCategory(product.categories)

    ga('ec:addImpression', {
      id: product.productId,
      name: product.productName,
      category: category,
      brand: product.brand,
      variant: getSkuName(product.selectedSku, product.items),
      list: list,
      position: position,
    })
    ga('send', 'event', {
      eventAction: 'Impression',
      eventCategory: 'Product',
      nonInteraction: 1,
    })
  })
}

/** Addition to cart events
 * https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#add-remove-cart
 */
export const addToCart = (
  product: Product,
  price: number,
  quantity: number
) => {
  if (!product) return

  ga('ec:addProduct', {
    name: product.productName,
    brand: product.brand,
    variant: product.variant,
    price,
    quantity,
  })
  ga('ec:setAction', 'add')
  ga('send', 'event', {
    eventAction: 'Click',
    eventCategory: 'Add to cart',
  })
}

/** Removal from cart events
 * https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#add-remove-cart
 */
export const removeFromCart = (product: Product, price: number) => {
  if (!product) return

  ga('ec:addProduct', {
    id: product.productId,
    name: product.productName,
    price,
  })
  ga('ec:setAction', 'remove')
  ga('send', 'event', {
    eventAction: 'Click',
    eventCategory: 'Remove from cart',
  })
}

/** Purchase event
 * https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-transaction
 */
export const purchase = (order: any) => {
  order.transactionProducts.map((product: any) => {
    ga('ec:addProduct', {
      brand: product.brand,
      category: product.category,
      id: product.id,
      name: product.name,
      price: product.sellingPrice,
      quantity: product.quantity,
      variant: product.skuName,
    })
  })

  ga('ec:setAction', 'purchase', {
    affiliation: order.transactionAffiliation,
    coupon: order.coupon,
    id: order.orderGroup,
    revenue: order.transactionTotal,
    shipping: order.transactionShipping,
    tax: order.transactionTax,
  })

  ga('send', 'event', {
    eventAction: 'Purchase',
    eventCategory: 'Ecommerce',
    nonInteraction: 1,
  })
}

```


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

[User] Show me the implementation of orders-tracking-example/docs/README.md from the orders-tracking-example VTEX app.

[Assistant]
Here's the implementation:

```markdown
# Orders Tracking Example

A boilerplate app implementing an event handler receiving invoice/tracking notifications from OMS.

## How to Use

This app handles events sent by the app `vtex.sno`, as you can see by looking at `node/service.json`.

```json
{
  "memory": 256,
  "ttl": 10,
  "timeout": 2,
  "minReplicas": 2,
  "maxReplicas": 4,
  "workers": 1,
  "events": {
    "snoHandler": {
      "sender": "vtex.sno",
      "topics": ["invoice-notification"]
    }
  }
}
```

Normally `vtex.sno` sends events only in `master` workspace. If you want to use it in a developer workspace, you have to do the following:

1. Create your development workspace by running `vtex use {workspaceName}`
2. Go to `https://{accountName}.myvtex.com/admin/apps/vtex.sno/setup`
3. Change the `Target Workspace` variable to the name of the workspace you have created previously.
4. Now you can link this app (`vtex.sno`) in your desired workspace and receive order status updates.

Here is an example body that you can expect to receive:

```json
{
  "recorder": {
    "_record": {
      "x-vtex-meta": {},
      "x-vtex-meta-bucket": {}
    }
  },
  "invoiceNumber": "691",
  "trackingNumber": null,
  "trackingUrl": null,
  "invoiceKey": null,
  "courier": null,
  "orderId": "00-v70368842atmc-01",
  "type": "Output",
  "eventType": "InvoiceNotification",
  "eventId": "01ac8957-f5b1-48b8-b865-33811d154791_00-v70368842atmc-01_691_InvoiceNotification_0"
}```

If you want to understand further how invoices work, check out [this documentation](https://developers.vtex.com/reference/tracking#updatetrackingstatus).

```


[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 orders-tracking-example/node/service.json from the orders-tracking-example VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "memory": 256,
  "ttl": 10,
  "timeout": 2,
  "minReplicas": 2,
  "maxReplicas": 4,
  "workers": 1,
  "events": {
    "snoHandler": {
      "sender": "vtex.sno",
      "topics": ["invoice-notification"]
    }
  }
}

```


[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 orders-tracking-example/node/package.json from the orders-tracking-example 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/ramda": "types/npm-ramda#dist",
    "@vtex/api": "6.35.0",
    "@vtex/test-tools": "^1.0.0",
    "tslint": "^5.14.0",
    "tslint-config-vtex": "^2.1.0",
    "typescript": "3.8.3"
  },
  "scripts": {
    "lint": "tsc --noEmit --pretty && tslint -c tslint.json --fix './**/*.ts'"
  },
  "version": "0.2.0"
}

```


[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 orders-tracking-example/node/index.ts from the orders-tracking-example VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  ClientsConfig,
  LRUCache,
  Service,
  ServiceContext,
  RecorderState,
  EventContext,
} from '@vtex/api'

import { Clients } from './clients'
import { snoHandler } from './middlewares/snoHandler'
import { EventType } from './typings/notification'

const TIMEOUT_MS = 800

// Create a LRU memory cache for the Status client.
// The @vtex/api HttpClient respects Cache-Control headers and uses the provided cache.
const memoryCache = new LRUCache<string, any>({ max: 5000 })
metrics.trackCache('status', memoryCache)

// This is the configuration for clients available in `ctx.clients`.
const clients: ClientsConfig<Clients> = {
  // We pass our custom implementation of the clients bag, containing the Status client.
  implementation: Clients,
  options: {
    // All IO Clients will be initialized with these options, unless otherwise specified.
    default: {
      retries: 2,
      timeout: TIMEOUT_MS,
    },
    // This key will be merged with the default options and add this cache to our Status client.
    status: {
      memoryCache,
    },
  },
}

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

  interface NotificationContext extends EventContext<Clients> {
    body: {
      type: string,
      invoiceNumber: string,
      trackingNumber: string,
      trackingUrl: string,
      courier: string,
      orderId: string,
      eventType: EventType,
      eventId: string,
      invoiceKey: string
    }
  }
  // The shape of our State object found in `ctx.state`. This is used as state bag to communicate between middlewares.
  interface State extends RecorderState {}
}

// Export a service that defines route handlers and client options.
export default new Service({
  clients,
  events: {
    snoHandler
  },
})

```


[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 orders-tracking-example/node/clients/index.ts from the orders-tracking-example VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { IOClients } from "@vtex/api";

// Extend the default IOClients implementation with our own custom clients.
export class Clients extends IOClients {}

```


[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 orders-tracking-example/node/typings/notification.ts from the orders-tracking-example VTEX app.

[Assistant]
Here's the implementation:

```typescript
export enum EventType {
  SHIPPING = 'ShippingNotification',
  INVOICE = 'InvoiceNotification',
  INVOICE_STATUS_POOLING = 'InvoiceStatusPooling',
  SHIPPING_STATUS_CHANGED = 'ShippingStatusChanged'
}

```


[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 orders-tracking-example/node/middlewares/snoHandler.ts from the orders-tracking-example VTEX app.

[Assistant]
Here's the implementation:

```typescript
export async function snoHandler(
  ctx: NotificationContext,
  next: () => Promise<any>
) {
  console.log(ctx.body)
  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 freshchat/docs/README.md from the freshchat VTEX app.

[Assistant]
Here's the implementation:

```markdown
# FreshChat

Open the VTEX APP Store and install the app on your store.

or

Run the following command:

```sh
vtex install vtex.freshchat-pixel@1.x
```

Next, open the app settings on your admin and fill the form with Site ID.

```


[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 freshchat/react/index.tsx from the freshchat VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { canUseDOM } from 'vtex.render-runtime'
import { PixelMessage } from './typings/events'

export function handleEvents(e: PixelMessage) {
  switch (e.data.eventName) {
    case 'vtex:pageView': {

    }
    default: {
      return
    }
  }
}

if (canUseDOM) {
  window.addEventListener('message', handleEvents)
}

```


[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 freshchat/react/package.json from the freshchat VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "scripts": {
    "test": "vtex-test-tools test"
  },
  "dependencies": {
    "ramda": "^0.26.1"
  },
  "devDependencies": {
    "@types/graphql": "^14.5.0",
    "@types/jest": "^24.0.11",
    "@types/node": "^11.13.0",
    "@types/ramda": "^0.26.5",
    "@vtex/test-tools": "^1.1.0",
    "eslint": "^5.16.0",
    "eslint-config-vtex-react": "^4.1.0",
    "graphql": "^14.5.4",
    "prettier": "^1.18.2",
    "tslint-eslint-rules": "^5.4.0",
    "typescript": "3.5.2"
  },
  "version": "1.0.1"
}

```


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

[User] Show me the implementation of freshchat/store/interfaces.json from the freshchat VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "pixel.freshchat-pixel": {
    "component": "index"
  }
}

```


[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 freshchat/react/typings/events.d.ts from the freshchat VTEX app.

[Assistant]
Here's the implementation:

```typescript
export interface PixelMessage extends MessageEvent {
  data:
    | ProductViewData
    | ProductClickData
    | OrderPlacedData
    | PageViewData
    | ProductImpressionData
    | AddToCartData
    | RemoveToCartData
}

export interface EventData {
  event: string
  eventName: string
  currency: string
}

export interface PageViewData extends EventData {
  event: 'pageView'
  eventName: 'vtex:pageView'
  pageTitle: string
  pageUrl: string
  referrer: string
}

export interface AddToCartData extends EventData {
  event: 'addToCart'
  eventName: 'vtex:addToCart'
  items: CartItem[]
}

export interface RemoveToCartData extends EventData {
  event: 'removeFromCart'
  eventName: 'vtex:removeFromCart'
  items: CartItem[]
}

export interface OrderPlacedData extends Order, EventData {
  event: 'orderPlaced'
  eventName: 'vtex:orderPlaced'
}

export interface ProductViewData extends EventData {
  event: 'productView'
  eventName: 'vtex:productView'
  product: Product
}

export interface ProductClickData extends EventData {
  event: 'productClick'
  eventName: 'vtex:productClick'
  product: Product
}

export interface ProductImpressionData extends EventData {
  event: 'productImpression'
  eventName: 'vtex:productImpression'
  impressions: Impression[]
  product?: Product // deprecated, use impressions list!
  position?: number // deprecated, use impressions list!
  list: string
}

interface CartItem {
  skuId: string
  variant: string
  price: number
  name: string
  quantity: number
  productRefId: string
  brand: string
  category: string
}

export interface Order {
  currency: string
  accountName: string
  orderGroup: string
  salesChannel: string
  coupon: string
  visitorType: string
  visitorContactInfo: string[]
  visitorAddressState: string
  visitorAddressCountry: string
  visitorAddressPostalCode: string
  transactionId: string
  transactionDate: string
  transactionAffiliation: string
  transactionTotal: number
  transactionShipping: number
  transactionSubtotal: number
  transactionDiscounts: number
  transactionTax: number
  transactionCurrency: string
  transactionPaymentType: PaymentType[]
  transactionShippingMethod: ShippingMethod[]
  transactionProducts: ProductOrder[]
  transactionPayment: {
    id: string
  }
}

export interface Impression {
  product: Product
  position: number
}

interface PaymentType {
  group: string
  paymentSystemName: string
  installments: number
  value: number
}

interface ShippingMethod {
  itemId: string
  selectedSla: string
}

interface ProductOrder {
  id: string
  name: string
  sku: string
  skuRefId: string
  skuName: string
  productRefId: string
  ean: string
  slug: string
  brand: string
  brandId: string
  seller: string
  sellerId: string
  category: string
  categoryId: string
  categoryTree: string[]
  categoryIdTree: string[]
  priceTags: PriceTag[]
  originalPrice: number
  price: number
  sellingPrice: number
  tax: number
  quantity: number
  components: any[]
  measurementUnit: string
  unitMultiplier: number
}

interface PriceTag {
  identifier: string
  isPercentual: boolean
  value: number
}

interface Product {
  brand: string
  categoryId?: string // inconsistency
  categories: string[]
  productId: string
  productName: string
  selectedSku?: string // inconsistency
  items: Item[]
  sku: Item
  [key: string]: any
}

interface Item {
  itemId: string
  name: string
  seller?: Seller
  [key: string]: any
}

interface Seller {
  commertialOffer: CommertialOffer
  [key: string]: any
}

interface CommertialOffer {
  Price: number
  [key: string]: any
}

```


[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 freshchat/react/typings/vtex.render-runtime.d.ts from the freshchat VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.render-runtime' {
  export const canUseDOM: boolean
}

```