{"_id":"@salutejs/canvas-example","_rev":"77-e6a380b6c8690085843d320d86ff19b5","name":"@salutejs/canvas-example","dist-tags":{"canary":"1.0.24--canary.56.8721800856.0","latest":"1.0.24"},"versions":{"1.0.0":{"name":"@salutejs/canvas-example","version":"1.0.0","_id":"@salutejs/canvas-example@1.0.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"3846c69fa21c69a7dabe76fb90f5e4973c01234c","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.0.tgz","fileCount":68,"integrity":"sha512-sJRUK2hrsOVm9Bn7O+IwQPqOmBcCr+8U2em2HHTwZVLAqpyOhgOrH9/rrdUbj3sNn60mVyPsLobLzfuELCjyRA==","signatures":[{"sig":"MEUCICXgPjya4B4Ga+VPOLDpGx7bYJfCudQ+OMFLvglKFSrOAiEA531B1Gw/YwVm+A9DGZ/jEhTTK834esMvaFe1lfjJgEQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":156850,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJihjKHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpDXhAAo3wiUYUSvwTQmOgNZSgWaeGJLTqHnTSwTcuzqCVcNlvlp08G\r\ngTb9G9vVayiqrdY8Dk3AphFitcVaeR23fQ0E6qI0X00WbSPjHm7Y7g8JxjgW\r\namSD/knx3CNYVu2OwR2cVm31MJka3VTWSCLeD0FgXD5GzhJpJXcweLT3Pwzl\r\nrKjmN722TkThzEEgly5Fatbwu0+WmzUIIp95S4YQo90Tt7c/uSDmDWc+q3tL\r\n6a0xqiACf8kiljDDC2UhoWU8fG/zPhKdz3ajPtbxSH84JUcBqRTzLrnv7WvS\r\nMqmSZ8sfjNLZNcuIckQmHMMznneaHM+doLSRqJ4YIzuutDUsdRggzJwNFGT7\r\nBdMj82jpZLzc7/y5gPnB80JhHg/4YcmTKbjHtVf/Up7HKW0oOhJ91I38jQRs\r\ngk0nfB1hNkxIIbBTGouLxaQ5E4gPTE2jtjTVBVtC7grV9xzcs0M5zx75eVmR\r\nJY0REeqJiryphsUJy9LNM/bA/qHjj4aAJgHUKQnzkdwbX+LnK634Mifb4ZL5\r\nnOV/iseFxhTA2NgKhpds3BvooVtOeE7jDL9Ta2dFr2Hb6kJC+R0tIQ3zmje0\r\ntA2vaKn1Zgcx1FFSppeM7nlDgZW4ktULzRdPEE4v7dtIPUB7S9VIO6FpEeoc\r\nL08qQXWOMDvwnYrIBqQAmenjn1Q9n63SYr8=\r\n=735L\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"1147ca3bfd9ef71210251383a461f68c6bfd386b","scripts":{"dev":"NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","prepare":"husky install","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.13","description":"## Быстрый старт","directories":{},"_nodeVersion":"12.22.3","dependencies":{"next":"12.1.5","react":"17.0.2","react-dom":"17.0.2","react-query":"3.34.19","@salutejs/client":"1.0.1--canary.1.128858c45774b1b1b2b8b8523d8aca3e90efb055.0","styled-components":"^5.3.0","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.103.1","@salutejs/plasma-core":"1.58.0","@salutejs/plasma-tokens":"1.18.1","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"husky":"7.0.4","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"17.0.13","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","@salutejs/plasma-icons":"1.83.0","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.11","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.0_1652961926864_0.9964543302564355","host":"s3://npm-registry-packages"}},"1.0.1--canary.2.2429090622.0":{"name":"@salutejs/canvas-example","version":"1.0.1--canary.2.2429090622.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.1--canary.2.2429090622.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"4c2602eb54b37e0be772ee1721c7346dda614f1c","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.1--canary.2.2429090622.0.tgz","fileCount":62,"integrity":"sha512-d9vo9giuGs68iMk0tnIjwUS7+jUc2NwW6wAet0L8pngZAyJCzohY2/DR7AW7Pi1V5KkfaOLuY6AcaM1OoSEZlg==","signatures":[{"sig":"MEQCIBdQv6opTjQJheH4qtB0Kc8cQIATFUqn+wcon8gSGcTHAiAM8d5MCxA5FmEcE0aBdd9qbEWb0zvEq2acVHciaK/AAQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":163310,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJimNF+ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqiZw//TRcwc3fdBm/7HlL6dLO/aV1X/dMIpGag1+2RZVbcZx1ti0Ul\r\nw/b2xlVQJF+3UtJvPl+/FTTagzy9xcRcX9HHXTuimrp2AkIDjwSOVw+uOptx\r\nDbX762d9acZ0Y7vCSoNNxGH2mWRYytgSRq5c2MdH8QqMe86KAvFp8PndnO26\r\nOho4rdOw/Oukyl0VSs8BYK4E67hF4jvBLaNLOSfZhtz+wpZ6b5baqKaSxlWx\r\nNZTP3qX7fiPgCee+kEYPANVUvNu0s+7Hoen8kpbp27nyCtOtTo5o01e7UZGf\r\nRArUYId09ed05uRs3KEb1aDdwHpftv/jHgSDN7CvQU0RJh1xMA2nJU617iup\r\nirYMVnAE4syyU6EuWEh0xKS0t8Uawn1MHOp7QF+gsWPCvuD26VtqmT04mvRX\r\nMieMGolcecw5CjV94voXwLLcSwRacHBsU/QJNa1rqcWJOgjlYVYfNFbX/SKJ\r\nNZm7e3u8bLHuJf+4FP1scBCyjTh0XEc3xPPwcqkoQvFkXoPP5aHJiNlSyHMW\r\nkp7smJ7uKaKLaOwRIbEUc+8PZ2rRw5BQsy0hS6ziTcm6qoaRsM/fsxZyrhmy\r\nm5YJ96eePBTDijS8B0Qe2rrqDQCISHJfyEl5o8V9q3jZDRlKJpj/XLDAuhUM\r\ncuUGJpWL2dC9QZYBr6jprH2K7xh1mO5Li7Q=\r\n=EEXI\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"1fa28f5dc33b8cfbbf2948ac2e15064ea311f2f0","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.1.5","react":"17.0.2","react-dom":"17.0.2","react-query":"3.34.19","@salutejs/client":"1.0.1--canary.1.128858c45774b1b1b2b8b8523d8aca3e90efb055.0","styled-components":"^5.3.0","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.103.1","@salutejs/plasma-core":"1.58.0","@salutejs/plasma-tokens":"1.18.1","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"17.0.13","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","@salutejs/plasma-icons":"1.83.0","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.11","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.1--canary.2.2429090622.0_1654182270660_0.5838502008142716","host":"s3://npm-registry-packages"}},"1.0.1":{"name":"@salutejs/canvas-example","version":"1.0.1","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.1","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"794e6ef0e9433bd0f32572ab8986611ad4b52c87","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.1.tgz","fileCount":63,"integrity":"sha512-zy0X6d1KiPlRilMVgywYMHyztVeZ4lgoANSPq5jO+F/1gvyzgVOs4hdboX3t0oQrIWo9ZaVtXvzp81eKvVIXuw==","signatures":[{"sig":"MEQCIEdoWLr4TUDXeTPfrCippFX7t0PqGnPFhf04XozqTHL2AiBrXXsfZZj4kQrxTTPXorFkFZ6XPlurP5zOVbGfc0yk0w==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":163697,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJimc3jACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrRcRAAoVEsEwNwfdu/PAO3lvptEch0NXIGw6bsGY+pGlWtTkjUoehm\r\nS24i0II4i4RjREY6Ul5o6vYf9qYng3dJMxM7FQ2IcobVPLXix12zCl7Sjn/m\r\n6f6QWX5Q+RGWTF6BtZpVGzX2LkJKhjeYzZntq9585rLiBKTecE6CQqfVZnGl\r\nSz1JcSVsns5NKEoShwB3STtmPttZJZpqnakCjXg7HCzPoa42Z25UKqkCydUP\r\ntsL6gS4Q1Q2kILU89GAFRpD9sQp4Wav+nP9qpzPTi0d0oaZwhia+jvYYevTE\r\nKp8w1xqDXXtbMpXBmUZNHg4SvQXg6A4GYKF6cS3glyvIHICQjyOh8DuMxlBk\r\n7yoRhn6x518mzXGNLASXR7PGE91okmBFkEOKiaVq8caqB/Veg3aAYFS3/JAp\r\ntSSWXVpV/ZYCuu07+wI7s1f3cDCy3lTuhX1CxvBo5JTHvejR5HWHungWlBqI\r\nyVsLeJBT4jBLhiDz6SWnzzFx8T4h1KOt8i71RGVHTv79+/8P44YUweTZ2vQn\r\njZSvn+z6aWZCX5aWIOm+jbRvrY1C1ik2I8w1GBJqvl/uJUSEQ/U7Bcm+XMi5\r\n26p+vjOpKWo9+WRG1nKeMZJTOVZnmmPXrV4FYUhwPcX3TMXdYTFUMVLHQIEf\r\nv35Gz6owX56uIpeX0ktwuP/RQ8g9n1LX7QI=\r\n=C0oV\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"c357c62f0edb41a2c55582732de83650d664e82f","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.1.5","react":"17.0.2","react-dom":"17.0.2","react-query":"3.34.19","@salutejs/client":"1.0.1--canary.1.128858c45774b1b1b2b8b8523d8aca3e90efb055.0","styled-components":"^5.3.0","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.103.1","@salutejs/plasma-core":"1.58.0","@salutejs/plasma-tokens":"1.18.1","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"17.0.13","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","@salutejs/plasma-icons":"1.83.0","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.11","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.1_1654246883276_0.4521651076114235","host":"s3://npm-registry-packages"}},"1.0.2":{"name":"@salutejs/canvas-example","version":"1.0.2","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.2","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"351ca4629d01b8e1d658a76db5a1c1047d2711e2","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.2.tgz","fileCount":64,"integrity":"sha512-Z9X7BKl1ogjZwMxorYqh/6iPdYPAcswALGvsrRfPF/oDuwKOJDYbMdlGV1LZT8ghXTfeZ8Qgfvpr801IKv7HoA==","signatures":[{"sig":"MEYCIQDYmVqvJwOWWtk0VxUQKH2o9MKgLARSgAJBf3EKg3VXzQIhAMmbSIAWGzyr7AilESessaXy38i23NyiMQZt+PQCgH4J","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":164718,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiuXbQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoUSg//TPI/D6TAnq+2W0t5z7y8MXwC/j8USseWZTXNt8dt9WWOg0WN\r\nwpsy2M5QLfmh/4+RFlMKVMlX2BJEQk+BRwtb2Txeo8CUskpVI+xowGfKZjdC\r\nUzgFjOEtYw7Zjohq0Swio1Jnj8EpzRvG4Ahf1K+dPDNg7FIKQ6kNqw5c6eUJ\r\nFhyXRM3RB3DjzQOihYz6hZoRojZRMvH1WqCeTD3+X2FhBq5wo2ZWPxximsN3\r\nZNk84gcwOGEK4QA3UcKHORQvJndANY7bzTUxVVj7Vg+V3vFUDjztmwy95ORP\r\nNoSyDWfGfVO+v7lSzfdWkyf6SB89eZdkXXn0a22WV/e3F8w+kt17XHT+c633\r\ngxHYY+NppAhlrEWniQ/26khhf32kYtK11a/+KgMuar0ufJtnafHROVtC7U88\r\n5QQS4m78Vw+mXWGneq2dVqJgVbMxzmRntEYJrRXwgXIS4oDU1xtSeuZatIXp\r\n9n2qrtidZtgGJDcZRb5vcjO3wXIZKXxCP+nYDhxnhJNmSRvWFDtatDn+/lI1\r\nfOE9U2abzTOEaGDLjYgCK5eWeyRCyfPkTxJsU8aVmvqLjXE3kVZztUtALftM\r\nK5CLTZTksPVmrzo2jYmPg52PcH4tNBdm+sK/ZN/Eo/kpGF1u7wuUV6XU5hO+\r\nCjdCtFyVYb56CJh2wTvJ8Ih3oJx/j0yDoTM=\r\n=2yKn\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"f27a93b85765802d588a6156b712d3c1546a69e4","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.1.5","react":"17.0.2","react-dom":"17.0.2","react-query":"3.34.19","@salutejs/client":"1.0.1--canary.1.128858c45774b1b1b2b8b8523d8aca3e90efb055.0","styled-components":"^5.3.0","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.103.1","@salutejs/plasma-core":"1.58.0","@salutejs/plasma-tokens":"1.18.1","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"17.0.13","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","@salutejs/plasma-icons":"1.83.0","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.11","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.2_1656321744239_0.24387293711624625","host":"s3://npm-registry-packages"}},"1.0.3--canary.4.2995012104.0":{"name":"@salutejs/canvas-example","version":"1.0.3--canary.4.2995012104.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.3--canary.4.2995012104.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"0132dc86d414d1d6dac82f2b7916a786e003a374","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.3--canary.4.2995012104.0.tgz","fileCount":64,"integrity":"sha512-Im6P29JhrlCM8uXFi4Xdfk1rn275nCQ0ASfXd7eUrf6bUaK+XF7IebCrhznCL12yl2DIqyIllHNYrCT9Sjs2iA==","signatures":[{"sig":"MEYCIQCEpi14LSJjXqJ+r/mcutlj/PjtTyxu5uS2kjM8B9wgLgIhAM4tkuDQZICWaGeYfizG+60HDvOX+xI6APjeDv+zy+UG","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":164161,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFi1nACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo4mw/7BvJDd3LPRVrpZRhToXK1OUfzMcDgRscn6nW5LtqOytIpDbCe\r\n/7OUD7z5pBhrGInb8zf51RRapSE3CIGmUv3PYjMsgw9XWdPMcXFtHFIimCB/\r\nniNNHASlS11nHNL7gAifaAoNn77WIjraF0TCm55Spimcf4iDDIiYhMnV8ASi\r\niDbxcKHX0m1Bx626v90n73cLYEP8xmXJNb95ApX01Qx8X47VLtTjkGXaFcVc\r\n9AsBh8kUXIayNhJBlyWFoMJHO/op1jLwQ1SZYNWd32qg3th/NN5M7kgWuF41\r\n4BHqvGGvrwZvYtq7iSDlN4GWFsQ2dG67Rc7CycAUmUZTTq3vHQ9LRId/YSNL\r\nq+0WlvJaUTxp4p/zw9XaDzEZsssmYvSA6ywZAaMrLiI5jmnOHo/WbuuvzvoY\r\noLhOkaVVR46hW2LyZvM0zUApeZQx/ZmBCrDQwAc+VpsYfk4jJSKppQSKvElE\r\nUezgXg1E6oTHPc31//xlljeKThqYwVDVfynVl3QF1bsLbPfDhHYGNOJsdtGg\r\nSbGDBkWNk5dUqZ9P12cwVBFuqkOFXMUCq9KFzJX4b3AuKOnjVKzGHZJqf8dg\r\ngv3WehYr2K/ObukgrB+emsvNpmLOFZKWCeVjCOxLF/6MLQyqwJJ5a7p0sjAm\r\nQU+quhVDurpu0hPCLzRGst35aXHq8+9JQDw=\r\n=txuk\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"63aff563401e8e181d0440ee7681ba318b276609","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.0.1--canary.1.128858c45774b1b1b2b8b8523d8aca3e90efb055.0","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.3--canary.4.2995012104.0_1662397799797_0.5309146922333934","host":"s3://npm-registry-packages"}},"1.0.3--canary.4.2995048186.0":{"name":"@salutejs/canvas-example","version":"1.0.3--canary.4.2995048186.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.3--canary.4.2995048186.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"9609542beec75f75f95914149dc771d0f5954ca8","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.3--canary.4.2995048186.0.tgz","fileCount":65,"integrity":"sha512-v6VFxnDYykxivgQbHe3W2eeq+6S5/e9iyb/GgGedVYz1j8/hqwRWo8HZTY6dHzy0/rWJmLlBOxPAO3nIWc7sfg==","signatures":[{"sig":"MEQCIFmUqlZJuTdJgGPiu1BLrxEVEWNjZDT+c7wV3GtKpCQEAiAgR4qXrhdFfmJEoJw7+81u/Y98pZ+UGEB1ITJMCbB2rQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":164231,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFi8CACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoiGw//frPLvwuT9F5uiaD/zk5hiE785jVwXRVvDtBp17PGKRydOo8O\r\nPZNg3z6GjYazf/ORW5EJPBzKAKuEp7UCEjOa3dkcTN7BBmcPKriDAUem1Nzo\r\n9H+TRuDOSeHXZ0+zFwhHyWrKkMGgDygiBYuA41opS1V6Cz8uY0xCOVnJWZxL\r\n7zZiFID6nB7ReKl+4K8qt/FZmzm5uQ1cSHhqMiAiN1lmsRhCvJ3Z5nLHUuac\r\nnB11r0hTbmv8VF+RxcyRHHHz0CjGmE1bb+gxiWvTMvTA/Q7I2F8KuCEPgdyy\r\nXDUJlHWHlgV07fA8qK6owshcKFndwxSqM5i/2uBKvbyhG5ZAKt4qQmjo7g0g\r\nItQqkVvGujjGuJie9I67joiVHcX+u4gIorCiZG2HeX+UtWLvLhON6x6At1Ik\r\nQkPVmIxhBYCDjPSsgmwCrzdXycM/YYLJJjuUgpOG1owIKJXdT2BBLD8SCSIu\r\ndCCKogVL+Lei4pEKn5Q+RYWbYyTQHOplmcimi9A6HwLy4l3Q41M6pUsfRd2f\r\nMb1eydgDge9lccwnZz746ljtQuTdMh7KvH+LavR1dX8wpO5axY0chdTOPY0F\r\npkNO29Hl6R9E7+iMQYBX1G5lP+V3YOlLMSpqESW14mmEgc70e8njeiEwYlcL\r\np7k6+jYUYqCmBrknPR9thfLqJynPo9nYefY=\r\n=rTPN\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"9b5b05df319eca8bc7535a862bf6b678fbe5a19e","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.0.1--canary.1.128858c45774b1b1b2b8b8523d8aca3e90efb055.0","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.3--canary.4.2995048186.0_1662398209736_0.9526610906518127","host":"s3://npm-registry-packages"}},"1.0.3--canary.4.3001415527.0":{"name":"@salutejs/canvas-example","version":"1.0.3--canary.4.3001415527.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.3--canary.4.3001415527.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"0ecc8100c19b1fbfc0bc2a89fdede02f1d7e509d","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.3--canary.4.3001415527.0.tgz","fileCount":65,"integrity":"sha512-8wpdGGSgL2FGXW7pdV6oq/CHqm3dRoeWKMxRsTngMoYWnnhh2fRfJ2ICfPlscdi7oiXLii6++Dvv4bBu2jHriQ==","signatures":[{"sig":"MEUCIHoiOapa0t1uLmbmcBOr2f2zTyNNZ8leQ41KLKZlr7BbAiEAj0fudYDHspYjMUhiVG5vTw/3kMp4YQc96ry9nfBf8Vc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":164530,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjF2cdACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrSlA/6AveXSB7IF+mP4yt9dZ2TlBLnr5qCA5ACgqv5lR6b5LUh1fzd\r\nFg+Q2cjrGeDlLuVdNTW8bo4EDdZMf6rxbCYx0U7gjDsC66LELDmjl9kAm/EJ\r\n1XTFdBKLEIBuS4W8cTZwV3kIqDq9iJe9L/aEiJR5fsupuM0Fgc9n9Vkh4kOX\r\nzzugpKWniNTzxBJOVytZELGt+9v3YVHLyMFRaeLskGwFINTGIKuOGfLZVM8U\r\nWp7F6XkRfVLCW6xfYW/tKttQgjCTPY+I1B7r9WGHAncLjyNLmIk/wR1eL9ZA\r\nfHEXqxByZ7uuFVwa0zA7uHU3jKLKrUWilMCkDDjb/SSjEAtKLJpD1oLgl8Es\r\nyxksMVOu1JiK2aLAU9gjF9xMfqL4Y1p+44RQxh3NJxM4cq5J6aXQVPsVGD+p\r\nQrq5ZPsfspm/t2rWUwsptZQzV8nsnaPbRWiZ26ZEYO+pKBLHZp0uaRmFQAjC\r\ndCOxHtIBHneoH2JppGBQy2Jc1JVh/EHdP0KvaZCrwbmwh9o9UILKbrPDC7c0\r\nBYRKh4eu8hM2GrCQ67On4hdCkKCJ2cnKYJSemabVkyZIOD8HV2J8VRpo0Ofw\r\nBlbRhPAegs24LEz2nduCCHjI7OHPqSxyUJuhFmeidcH60RJBDyTw3xiP9wmi\r\nWN2AUSQ/PQlpuvK3nl5FYaE1Akx+a05o6/E=\r\n=bk9t\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"8efdbccbe59175f0990036149779ebf640045f97","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.0.1--canary.1.128858c45774b1b1b2b8b8523d8aca3e90efb055.0","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.3--canary.4.3001415527.0_1662478109735_0.8893841992360894","host":"s3://npm-registry-packages"}},"1.0.3--canary.4.3006378868.0":{"name":"@salutejs/canvas-example","version":"1.0.3--canary.4.3006378868.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.3--canary.4.3006378868.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"2fa7c5183570ec06abe265db6f65c7d7aebddfa8","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.3--canary.4.3006378868.0.tgz","fileCount":65,"integrity":"sha512-9bJ5aKkP+tQfNrDxHBO+2XHVHsuZvCijg4HX7zPjSfq3/NQZW5ke1HuWLteBNgGDCs3mbkfiFG+ASMhNG/rfig==","signatures":[{"sig":"MEQCIC322zdAElmZ9FmHc+/nQyiCfgcDxRhlNUwJxj2v5n2RAiBMLtX/wbYxqvtd53B3S0KXvFeSVKtdl73jOM94Z/BQ6g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":164734,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjGF8iACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo77A//Q0JSr5r9kwCH+hfwj7czC1ZQkTmGhTuh8KfdQXMgp8UaqNRe\r\ndiDyBgvj+e835u+4xOqDNfqvboGTP/5lYaX5YjcaJfESY2n/65bas5izpWHr\r\nAQ8SnVc6+hbcBuKq63rmGPTIweFXlUPjs45+pYd0ldjoMu3tMR5N3J/kHXQM\r\nfq7xaSbRd9w+nMr6oZyS/2lODHaGkX1Yd/ZJRuvRBPk37FpKryZUTesaudlz\r\nFdE9jVdWqullFxSgFE5hjnueHKK+OFDZxWakWKKCrDv6jAXmuVUBxfvoRjJJ\r\nEWzvDk/qesKfi/jrAvTkwPAg0WEG8lpKRF+utwOJzS80qdaqbVnd0ciY/yxO\r\nXJusWI0wt6y8jNP96Kv7+bUBTOorCcKfOtS5+ZpkKw4UgyyFwD4lldrXPelz\r\n+IW6Z5WVfQhEgVceBdH7r7T5syenAww4BDhoHnSyK+cH2J3a5VMlaFHa0CRp\r\nUG9miSiWuygCsvqsUy6UEpwZzgkH4ATwWUgRWSY925ZDWtr3/lVpNhpHCwoR\r\n54JJW/INbBSuDiB6LFZN/tcQsFToylp6jawF54W7akSFDNtooZ9k5jtAtu6X\r\nxo29XZQzTzPFnNt/+7GP5EBY5URbDTneAC/FAxov3BIHlnFiPr62zoiu0WFS\r\nKQSvQq4tRE221bJPMVvmXXr9RiCZSdhslJY=\r\n=XJ3p\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"f1f2a57a5de37fd449a99fe66224e95e3ff1c7fc","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.0.1--canary.1.128858c45774b1b1b2b8b8523d8aca3e90efb055.0","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.3--canary.4.3006378868.0_1662541602027_0.514298097239654","host":"s3://npm-registry-packages"}},"1.0.3--canary.4.3006931681.0":{"name":"@salutejs/canvas-example","version":"1.0.3--canary.4.3006931681.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.3--canary.4.3006931681.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"35dafe90a543beb82b2308f2890208eb0287a11e","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.3--canary.4.3006931681.0.tgz","fileCount":65,"integrity":"sha512-OaToZfOzy1UMVV4WBerKsownPchqQJeB7MqXNXivVRymMFjP2GcNaXHeGe1WLWcrRtCs5FShQmqpqaj50BXzlg==","signatures":[{"sig":"MEQCICpGOG1wS0G77zAks8137j9IRUCprKfKYQU13mc5BTlNAiB5iACpgy4O6mceE0O7wLi7tb5JdF0d5LT/MM1vShmzCQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":165649,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjGHQEACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmop2Q//dho7CvMWPAR+8YLLzHfCuHR7XC1DDOzYOL8UcM6lZGe+1cpD\r\niYcjkM+upRKD6vUp0v1kp3G6ApsfVMOyO0MZx0s19tsWm0BvoERip+b3BS70\r\n+JmDclaB9w9yeo46QJmBY9Ugb6VQg3YUb4vhfVnbd4kVc7tn6DOu0n+9N6T2\r\nwzzLmOMXKc2BGGVOXAoGzamsPcm1S55eUkEECEmXOIDAnhs0U8Wq+VncE6m4\r\nFcdNdNUvPLgtUcDe/ZaToPf293VW2sU8UT9nZ7xxZuvOB+eOjA25kMg7SDpP\r\n40LHyhc7nGCAJ1n/4NqdACvvE1aUzWwJqYUXBzWgBEMZMzDoGBPn8n46ltfU\r\nQaAc+9OAXY9CHI+bjnQJyAQnXyZbaI9feQ5qAiNsCH4Pdf5408+D5t4net/j\r\nPoEk8C8oYjbSy2SpV7FF5C1i9ODNPbij8d1/4bwxeFiwEWW98kKuN0+AR03n\r\ndLagFOcKbjEOnm9gkvWDDKp7lEEs9295z/wxmx6/uKUdUdIr/Lpz9+n8IJkB\r\nmFX7HGNFuMCkAc/QY75TJ4rmA0FDReEOlT/M4AL3B9zji1lOSz2YS0/hDktY\r\nlUDTIt/kiRnMkHU0tDuZjT7p7CKGNEh4S2uUZVVbiCMKOwRF0RNU3Aw/Bzpw\r\nduq22TBJRju6LBWPhr5mxHBn36tyMI3AH9w=\r\n=tl2/\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"aae9a674cff3a34e95d79e5efc113e83f39a395a","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.0.1--canary.1.128858c45774b1b1b2b8b8523d8aca3e90efb055.0","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.3--canary.4.3006931681.0_1662546948803_0.5990266743901766","host":"s3://npm-registry-packages"}},"1.0.3--canary.4.3006948602.0":{"name":"@salutejs/canvas-example","version":"1.0.3--canary.4.3006948602.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.3--canary.4.3006948602.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"51f058755c91192c2501d7e7dbbf89d27de37619","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.3--canary.4.3006948602.0.tgz","fileCount":65,"integrity":"sha512-/BKniBnhDBkZf/Qvx3yyi1IegQZ/rz8PTBogrTC8v0dwMF9b9qDnKXATzv9RbdHvKCl/2bgkOW4voKmtHQC/lQ==","signatures":[{"sig":"MEUCIQDT+H9PB+uPTWq4UkmT0ZHZQruXFGd0mtaJtJUOn3vHawIgccdVIbXoBEhoPWLqdoeX5sAej3w4qnH3BEH/RAmEF9g=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":166048,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjGHSvACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmppXg/+O8v0Aq1ml3bh0+ZquxNhXxILNmbErmcOeV5RCQTHK5J56YXI\r\nBVXYpBNwSmYFblBUbnevAKkhSk3oXSVB6J/UqhBslEokLwms/kDL8gECrmRg\r\nfrvffLb12VGCVDRevE5qF7Geg2VK3S13lwqo6o6JWPhLCZ7KXPemQshZdgXi\r\n1gWxcmXI+1Iu09U4u5scNQkSvc0OzwYKlkITNJzVSavFjkFDhEFYiVldPZhZ\r\nGUEmYGe8apvsgV0ymbhGQ8Vywb63n2J3/DAAZdVUXzrMMZBa3dvNx8uw5umF\r\nTglM32/ZsVlQGPh1SeE1FBBvHrk5fycfspSiubcPuVf07GcDRbffRzvGPmvD\r\nbr66sddNFRd49zPiEQKxttjWjkyV/SjE2Z3DRgsUBE0RRw30XGyzDJkvTnHg\r\nmbXzgWwFEOYOge9hC3+MXhaMTdOa7TabV8/FyC2QFklOEk8W35Kl97N9K45S\r\nB8fbqSm0dYvQ4BvMz4ccIEuzfCJTAsUp+YIJLy3itjpzjxYgnamr/lSlvyTG\r\nldOUjadNSIWa6sXkIeArXX8/MX+mgRCR3FwdqvnROtOjtERLQz9JPYN4DhdO\r\nBewgJlmI/kgSMubqSvcqDkokFSeSBhp51EMIQEAq8D4/0YLmnTnkmJIx/3eF\r\nV/YfovDjjGcI6koYbk2CwfRSjWhKCtxFh40=\r\n=l2Gg\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"810f8b866a5def9a692025eb2dca5a428bb2f31a","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.0.1--canary.1.128858c45774b1b1b2b8b8523d8aca3e90efb055.0","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.3--canary.4.3006948602.0_1662547118755_0.573739741271835","host":"s3://npm-registry-packages"}},"1.0.3":{"name":"@salutejs/canvas-example","version":"1.0.3","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.3","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"ad06b17a124218f64395cabf2eacef630f5f8161","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.3.tgz","fileCount":65,"integrity":"sha512-kVgI9i8q8QEqwTN3k1rXGmItAtQAvOj9LxVenNAL7RIP55oUkWvBwAEQQnvZ1Iwcg7x3WOI5uRFFxoQ+cIaubw==","signatures":[{"sig":"MEUCICczt0aqgTo+42LaADwBANHJy9h3UY5ATYz1bQdIwawEAiEA5jv2cQasnL+73aF6UXuQkOs6pUdZHdcJLbNbXlGyE3E=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":166281,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjGHfhACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp5exAAo9mSO14bXeBbXb3hgtVsy0qBoK/iqR9zhIgUJrmWO3IcUYcF\r\nlvARdqhFM6tKVE1zftcMtuOD73ifnsUgVpZGeXDXO9BSQT6rAlRD3sa6vnQh\r\nFeG2N+dnOshqxI3lzb8cG6C7r9jASL0h5j0qPtpddaCO0r+e4tHAv3Ld31bw\r\nEp9zij1+hwbICsPPrTL03/C3yVwKjG2e7hztHAfE1w0Joza6gCLmKHff1QkT\r\nL4HRWYBoScrd7EgroSe1ZzpMrPI+cidSPH71RkLf12ryOAVjAbd1E4C8f03S\r\nmeKT0+Caa3oeEa0wONoWUSL4Y+a2unZByBb81i9QsU8LKRAo8ndmxFYwdUag\r\n4Ak7jdPIFO2lfxRwFBxHS3QMgJJrbwi4weeW4yZNRYq1ZI7dMWZRo0F9o+FW\r\nAQ3hXNiS0Bt9i7afAMHDJTWUaR1aH8wsYoQeLqe60M+FHKzJXuHFFxzsdzvc\r\nbiU2Je9UtIZVQNo7+cm2UHoX0uPU7W5O63GL9EDhzqMKf1zTy06DQ7CqfaXJ\r\n+rogxdiilZoPRSUKjeOQRRdPe4QKkY3OVZd3HEK7buVOOb49IfzWbA2XXfn4\r\n/mxlTajVAYnvMF4TBIcFJgbP6PjyDBJ11/Lwxd54VANsEfkWZQQ3+dx9MF6F\r\nbwYteCImJeD+SJlCWczX2iPoWiunvttdyVE=\r\n=Orkf\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"e931327c9c5342e732258d4b90ecb58681af9790","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.0.1--canary.1.128858c45774b1b1b2b8b8523d8aca3e90efb055.0","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.3_1662547937000_0.3433616866558893","host":"s3://npm-registry-packages"}},"1.0.4--canary.5.3073280476.0":{"name":"@salutejs/canvas-example","version":"1.0.4--canary.5.3073280476.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.4--canary.5.3073280476.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"2bb45dfe39792fbf2976559f88cf00fd93bf5906","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.4--canary.5.3073280476.0.tgz","fileCount":65,"integrity":"sha512-nHiWBKhZLdMVVCZ4DJwaQf9S4N7akMs5eDmHHq6O48nLIZoTOEtQSfwp98SxloMM9DW4yBU2ywI+tLc43BhiSg==","signatures":[{"sig":"MEQCIC+zTdQWr+bdMJNvFQI3tJe2lws/kiUjD+eUjRZMmRZyAiAB9jj4sc3qJ7HIc36+NbcKR+tI8xSF93QrWlGZR1wjcQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":166254,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjJbgiACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpg1hAAizE1AgUzmdEjNcJqOBSYKU2f534QXNeYQi0HjlmaDklfMCd4\r\nId6REmSD7kYpePcGmLr5l1+/52axT2XePoWU0lpelk7kEPDvGPWRXF30nHLJ\r\nMCfJtIEwuqzbYL2irlHlLcdRBHPHlLRX2XvT4THLnvuwmm7vdPzs6N/E+LJT\r\n+4+ub5o8rXNhVyxZO+RhN8qC/gKKk8Zk4Y3nVXkMwyKyUDinrOLAg+eeEMSF\r\n7/hBBSLSfqtnok5a/PEwDVb2jje6LaiNeszTp11TvIGewmIT7tTYaXBEKIv6\r\nm7JcU1DUx7OfR6xlJwQl2wXGhhOQMEWwq5z7aGMjuh6xShJ5ASq3t9aXjZ07\r\nrza6KM0hfCPc+uJOnRrkUcPukCm+1NPahS3t0R/yhHgMpKrke9owEQxI37a2\r\ngzA/+VsaF6KvfInvi1GLyfCYOyc5jnK+PTlt1Vcy6Cu/LVFBNQT0JtEY6Q/9\r\nlS6jPTPRezt/R61JmK+s3cDc0Gr3a4Q9l8c7RFWsACBe1FNgUDivKv+LTQWj\r\nec6M/l1Fb5HvGFrvbcKqB8lZvoqmUmrRt1WJcXCRTQvoMtK639ZGDC1aJwl7\r\nLmrkwwzNDaeZsEVB47W7zOEBC67tj/29rwHqQNoH34082bqdV2LetuO627Zr\r\niPLdT8rij0tiuYdeNIz5TckpRAulg4o6w9s=\r\n=Mlpi\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"26f7072df179a77bebc69dabc4a8c008d8750d10","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.4--canary.5.3073280476.0_1663416354070_0.7425929929483319","host":"s3://npm-registry-packages"}},"1.0.4":{"name":"@salutejs/canvas-example","version":"1.0.4","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.4","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"0ce2cf2ae362fc6fb9420e2ee278c6256be2bc7e","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.4.tgz","fileCount":65,"integrity":"sha512-e4Qlf1p0bu3Pm4dJvdXeM7ritvEHgx6F1Zqb440rq1YirNedxzgtELLpQ/xIO2FouQ3mquxGUyld5m46SWbpBQ==","signatures":[{"sig":"MEUCIQDY72pEiRsDvx03YhgiRbgL52QlwsNYn9e1EKEenOuj8wIgJVwFJVlFFkC9WjxlUtpaSih+fRn8Xf2QhirV0kjx2V4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":166531,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjKHy0ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqncQ/9HrkiGQ6qLnd9VFOU/nr6tkzsGtkxjhU67MybFdHKZgc2Vae0\r\nz8Z6eL0p+jklbUlSaueurev44KEUVr+rfBS30M7oH0ybN2dAF7FJidX3BR6p\r\nVzg0BAmwkzrrF/T8rY/jLY6UgsjOeR7MEnNjeF48qfS/VKqNVx4MdCLhKwsY\r\nJ0hX1xBbrzVAZ6kcSqdp/glI+jK2tEK77VwRNeW+JrVcNoE5GYiNjZd0d5ew\r\nAbXvV6slZ6yItuAN7dSLJhDp4OTPqvR0QWJ3ETnfd5VPJLLfStJoqf4OulAE\r\nuWwY95pW68ckZSpXc9AQfItQ+S1hq0ceEYLEfaq+KMUwPWThoHHiCqIDRl2l\r\nZ45yHtqvuJ85BuQmwfpFui/cOx9o1Uvha8s6fbF5729MXlZKH/HABb4VKicM\r\nn0GzhmRvtQYMO9ct+9FP+X8VZpvE1pmGayb9aZiFX+2UzzMli8P/KK9WOqiT\r\n7Q8AzGS1nR9Wc7U7NlkzEPGPUPWwxQuRIq/pC9uXpUHf0c9Fw3lMQEFheN9W\r\nCxZKRPGIosbuJjWgIgRvgS6NyYgrS4V/Ff/dA9IRkXNXA/Quq6wbt5KW4Yu/\r\nQtUS3sSL6HKN8rWVuxkD0oW5vmUYuMr+hpUI12FJDgyPd3kR47ZEdJysEy+4\r\nDSNhcULaEhAhbK5qjKSM1vZRVXWtaFTMGy4=\r\n=l6I3\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"cceb2bac0a342512c51393ab97b0957f086f2750","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"6.14.16","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"12.22.12","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.4.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.4_1663597748779_0.3149270242353621","host":"s3://npm-registry-packages"}},"1.0.5--canary.6.3182535913.0":{"name":"@salutejs/canvas-example","version":"1.0.5--canary.6.3182535913.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.5--canary.6.3182535913.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"3b5187ea7922df1fd5de363328316f0698d8d5c5","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.5--canary.6.3182535913.0.tgz","fileCount":65,"integrity":"sha512-97A/o7zdYZuGRwSUOxQnxKl6MuMLiw2NuuPbYvoLh2EuQy4g6Y8mlimImkC8VuvTTABCpgmkGYFqvGnSJPwMGQ==","signatures":[{"sig":"MEQCICmSwYsdc0gkC3Y9Cf0upn/BijyXKj65+vteStXo0Z9WAiAu2qNytEORNva/vCesRUn88PE7GJEYlszGHzlXuj7PtA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":166562,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjPDX9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqvzQ//WI6CKqUBRvs/EkpcoBpTlXFojrOUu6DTqdTvsARsYsJ47PFJ\r\n0jK8kpxsfaq2Uuz6pXZsHUkFL7Ul9EpAITJF7OuUewwV5kGwxaIl5aPd81b8\r\nGQnABJK1JIROZRMAwdxsrj9oNiCf7GDjJ6QXtwifZYd2G5tgcy9GnO20cfvi\r\nkGXb5a+H5sEqwDqod9YNlG282xvhrOddar0zokNpstm9KCa1PJOm62S9aPB0\r\nu+CFzIsikKMnaOXVnzu+UE7czgC+EXyaMIhtOqYRSpehE9WltXque39nRHFY\r\n+Hfj7TZwgqOC7oko8CKv/tyFVD/IWMvK6ywCroiYOBfM/ORiR+3HmsIrbZgC\r\neTHN/J16CdrJtm96sLHxHdV8S08A4MF51hJb4z+NXXnOfpNtklnHC90w3B/6\r\nTQuwNRqJDe4/1sHZjkEI65DheNZFiuMzA/WxHH/BQ8Z4PGXrrrJ5JXSHxs4q\r\nMkEqNpn8OXMpDlETDxD/Lx6Ct1q7YZRPGU8aykouX17XceZZJh6xwrhqnPwu\r\n5hY8ABff1qEyQZ6qutZqNdIDzr7vCkkCHQB2CkOdPs+jh2Wtc3T/24DfDa6w\r\nZyyMhfOCZq2eaQu57l20GA0Z3oNLE3zG1lAgB5F/Komr42ZLpIREJHz482Vn\r\nRmRVNg9Gf+cGGWkg41bhct6jh/pZjL+X7gQ=\r\n=IEJ3\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"33da065bb17a9e76008a14e19e3e8f924d4ac12a","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.15.0","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.17.0","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.5--canary.6.3182535913.0_1664890365031_0.43790934712703766","host":"s3://npm-registry-packages"}},"1.0.5":{"name":"@salutejs/canvas-example","version":"1.0.5","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.5","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"35211c8fd47e723f9807c54e9695500cadaba355","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.5.tgz","fileCount":65,"integrity":"sha512-9Rj6vLSI0NQY15aEn1wCQuiIk19GO7evTTXBg8CkNwUH9MPllMeDl7U7zwD1IumN6cbvxXWOrgfwGYpGHmChCg==","signatures":[{"sig":"MEQCIDMs/UXcDkTL+CTV4XkSxSx4mgu/r3qwo37eYCHbC2n0AiAWFK+K7dGbx7kVc7COL88WOyrC/Di2y8/EpSDyQcOkwA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":166798,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjQCrhACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrPnBAAlikE6klPKNQE0UtcyFhXRd3oYXcWscQasKXKdd0nA0lDU51F\r\nNdgU2OuB2iz8sjca+U824s6cpXooasYCQdKNrpZomNAc9lJOHyRDXWK/Am+t\r\nwOFoaQ/OVEIQ7gy/kVHyt2tCvk0595HkvBaKn1XqWemAkWokoP1gDLfFio+D\r\nWh9zoWJfN4luqhQonY66ue/i9vOCH2jEEmXccdydpRfLHxxT5l8KkuCvySvn\r\nMoyH9h3RxwzgN0wOpOkk3EDyMGGjKc7ffkec8puloSQpEdtdbHyKTQockQ1E\r\nU6trxb0jRN8SAf4EWXuD88JGfdYui5AIMh3e2C9/duKzUsYCgWCiBX53vAYl\r\nW5KRe28uWMLomJ+LAMC6wpFNE0vZPJUWb2RlHmxqljZNvMg94BiPxqbtVbJ4\r\nw/g0H2C2v3hzCuJJebZElCv7ksMN2gPplK9t/qdWDw+izSytSIaXlBp1WF2D\r\nVlDBPWJY0ZExujkb27dRCRjGdGwZQ7Mqeofv1RnwpadPDC5gpMAHwnoDirBG\r\nl3Rc4kb/uREiXxTbctP6xfRyVMD4WkBJ7CxxODZIjyzGOGn8CvJY7AEt2znf\r\ncVAKIUS/fDVEeIu+4Zc82bg6jEiAskAQEULk2DnPoKh0KQv9Hv3oGPpBDX8H\r\nw04J+99oHMrb8LdRRqO99gNu4Ayw2HqirFw=\r\n=RxUG\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"5fac5581ec60d8ae2f550dcab2482486039edd2b","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.15.0","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.17.1","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.5_1665149665163_0.664143367297144","host":"s3://npm-registry-packages"}},"1.0.6--canary.7.3594121365.0":{"name":"@salutejs/canvas-example","version":"1.0.6--canary.7.3594121365.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.6--canary.7.3594121365.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"a1d5662213bf31dd0b0fbbe1c5ea78c1f1da70ed","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.6--canary.7.3594121365.0.tgz","fileCount":66,"integrity":"sha512-rVsBB7/ukdK+oltziKkOYvR035lZLM3o0hB945bWTEzMtpF95RZ6cpN5edIBelrA/oD0fIxY+BlKvUqJblTASw==","signatures":[{"sig":"MEYCIQDI/Azi6xJtaIUKcYuhGDGoCgG/Jm/DwxTEYNm8zkXMnwIhAO/OBJzlr4+dDaEID/tw+njoM/yZjY9JLWpe78Lfs19K","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":166883,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjiMrdACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqGNA/9G1a72xECLakGljJB7c0oQR/uUmC/mw33nMxn4tGx19cgCS1Y\r\n/TM+QGjHCREd0Xr7kWLK0KX6XIPvV0ukiokacTba7DHeCcvuG5wPqpdq+bl4\r\n33QpWrcZw7nbnfmb0L/gu/tBX2XeykKJqM4W+uyeXiZKO/sUuj7EURcHoHsc\r\nKliR4r9+FfIHHdG13MABOy0s6O+pKnEUvfo4lnEONTM3kcWYu2IA1zuy9FDm\r\nKt6TIJr4rY+3/EqLEOEatUb1G7q+I8sCe1DNbDfCg6X28Fc1oYBcJDkFlJsz\r\nSRqye2dnUdNkcx8JFuB9qhJD0iztb2z/cpPedKiL8MdZgMnDlkaqE3pzGIds\r\nww/qt00bk1YWg1gu+COB98SGokrGP6X5T8ctOgDtK2z94i9xdD39ibt6gjqb\r\nl6nLiZiH8nq+4oGIcYNDUCn+w/3zCI30T8jerePpAJ2dJZ8/ZB/QvgMcDEHW\r\nAeWebvDEXT6yTmKUWMHJPX/WfSwcFFvEsrhSzpqqMe5/gYtLATI+TDn0WwUn\r\nP4cdJVxRk5kREXzx5RpJyPcGTsLy+/HyZcou+age/nMHJAdU4peIhQnwcanb\r\ndyOWP2GiCvKcBGHYOB/vEab31LMTyCIshY0hoO+GRe5B+PQlyUhKR04qBLGH\r\nryHipd4cfIkoXfUaxaoDPrVkaDfj+nQUeZI=\r\n=OVel\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"eeac300a06df22ea06f96186087ee8698a370a0e","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.18.1","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.6--canary.7.3594121365.0_1669909212921_0.5496532968104793","host":"s3://npm-registry-packages"}},"1.0.6--canary.9.3649382211.0":{"name":"@salutejs/canvas-example","version":"1.0.6--canary.9.3649382211.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.6--canary.9.3649382211.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"5c6db043884ab1bf641a601bc70bceb5261099dd","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.6--canary.9.3649382211.0.tgz","fileCount":67,"integrity":"sha512-roDSHyCMTkaJdwUijQDZp82AtdEZH8t57UjrcKgQ5HRtWsfHxfSJi3qpLPFMecZ3JDCeT7LPnvvGa0cEHRpoTA==","signatures":[{"sig":"MEYCIQD2sNuVeP3bvYsytS2Q5VvaHdVFs6Ugv5f9cCL+3vOlBAIhAImxtB27UK6k3M4E4Ea0smOuk1gbihuyrnhOCoI2BlmK","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":170272,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjkfpLACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpHPxAAk8X2i+Xxl4EPDuOvYUrHXP6AyhbMmC3YNiNQOTmf6WZfMlEU\r\nCCl1muyOQvpcPugOa+ZtfbpXsNrlWfDEMOcs4f2s1cNe9PTgNeX/OLKwWROm\r\n2cKWjU18S6I/wsqJ2jli4ws7uSGEBrN9rpft9f1HUTSaywginOkoJoWr1dNU\r\nomwhRdFU2cfY4WCT0L1NYSvIIFdMTaY4PJKofhnFy21JFxz9W8xNhSh2nL0T\r\n1NtCNalSeskbJdvxLHjP75tTMcd3zMbywUtJgZtoZODVTLylcHBDf47xUN6y\r\n/U46ZgbaROKtvf6G2md/5WQxhTdWJFYHb/+Qt6dLQ8mGXXTm3pqi6REzyLPz\r\nMIxYPDjyZN1o3MGFG68iylVmkAJb6/uivKsmqut5q9k0Mk34dELSKGCjiGkO\r\nXi7NBYW2lZnAoRtPGSsOM6uaCZqvZZFVCe9GihX5dNCAWiiTxllVxoEfQ4QD\r\n7ZPo4WAVsr2AftwTo7kGCDOxffxnX7QYBFVy1uaGawQ+2WL5YC9cBgT7MEKv\r\nhhLx01zCZPthBqR8h/yVRoGYLFBPC7Dej44rI92T3s1Snt4OB5OTkNErQBUF\r\nXe6B2edHHZdb1yHGPPuq6hWOXNWKRpmDGpO6hzbp9VJYJBmTbq6+GEnbDbVW\r\n2e47N+qfXE6EWIOHXjNYw7VNGB/OWLT77NQ=\r\n=iaMR\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Персонаж определяется из команды `character` списка `appInitialData` или полученной  `run-time` команды и может менятся динамически, заменяя css-переменные.","gitHead":"60f5634dda92877cba05b0df8860c4adaabfc03d","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.18.1","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","styled-components":"5.3.5","@salutejs/scenario":"0.25.1-canary.2.3441b90f364b71470a1d73b21ac4c244fffeca14.0","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.6--canary.9.3649382211.0_1670511179111_0.052142918704320085","host":"s3://npm-registry-packages"}},"1.0.6":{"name":"@salutejs/canvas-example","version":"1.0.6","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.6","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"2b437875547334688ba8679e8d6f9cbf2bf05987","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.6.tgz","fileCount":66,"integrity":"sha512-uYTxunxoLimYZobPMMO1sDuf0db7HU9iDH/PGW5eDkL+U9VWFdPhXJOW8ztmCoNTgLsPx49E163Z0hrruMr92w==","signatures":[{"sig":"MEYCIQDSOJXc3s6FkSGLLBll2To354vZyx8ndyWxyQyOEXUu8gIhAK7qK5Tg8xAUsLs+ZMEaR5tIfS/BXgnb33fQXbReGpJI","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":167145,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjluYOACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpaPBAAiHkWBZ2FDyZdVYKgjuiMjx/IhEWVLFbxGyDRC4a2ZJzh0ngn\r\ncZ0HboA/fB8Vz9fr5O0NL+luvaNpdfCgiZFZMfw56YPe19Cb8g9KgwJ/aHW/\r\nguTFqTmmqlkCVKW6W5oo+vg7pk4I4gqBvMtd1U7pSdkY+S6Ns0+SuiwdEzN2\r\nJqftGs3OTeLLM5m194oV+yzw5OU3+gtv1DB5sTxHVhLy2Qdl6iVU/VfW3KCB\r\n7DIv4VQOM4uPqWnpnqdybMJn5fztTN77nXdjcZLzlU6Hh+vHiw2vW3y3h0Vm\r\nwEJgNnbMTJVMmX1s6Okei7iofGOOaLlxEoab6XgQAKJbUWJ/jm6eMchZ1Qzo\r\nTNbpUiGh8mhqcFExtdgBtccpeWFJG8dIhLw4xPudW7xAyZruXcCV9p7utfHj\r\n1XgQRPRlZxZHkDXfQBK33ohtSKx+fWWBd7VRHJvaV52rIk59zJSI3FBm4yn/\r\nVN+lFZ1gX2GhGZiqJJ3QONo8XByhWjZdbj0FGvEWl/JrvAgipggY3PcGHk80\r\nyMSRUjr1S9w27DMObe9Wen+3lV7GuWfuOBAkjP7qvAuMzOSuNfFF9uvaE4/h\r\nqCIlqH3VcpL3kxkq2TDzEXMpySwJoHKxKJ6Q/UugyrSy7k0bOURMSIg0MoWx\r\nAMGpsc9g4UWBDJ1UYW2skbhjPO4MuRxK5r4=\r\n=rW0/\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"5fbccf95adddef71c4d9b2be44c73eeb45e5288d","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.18.1","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.6_1670833678115_0.4321776687888583","host":"s3://npm-registry-packages"}},"1.0.7--canary.9.3674148218.0":{"name":"@salutejs/canvas-example","version":"1.0.7--canary.9.3674148218.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.7--canary.9.3674148218.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"8e8a2d7af1108e8718759aecaa0e770b956c946e","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.7--canary.9.3674148218.0.tgz","fileCount":68,"integrity":"sha512-4YiXsg6j9mN9T3jPSzL+IzqjDqT4MBcnxxr/3HIQfsZarIO3hkUMqe+IhukLlwfccYDtJOAGqbIhHJikb+qBeQ==","signatures":[{"sig":"MEUCIQCP7NZSF++VN5bota3jrVefRPx8YVtZTOaGMf+fODT2yQIgL/xXDpMwUJFOtUSUh8SW6v4e5H6SGNIGRe/ehKWnoSU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":170289,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjlux0ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqB4BAAo/8pJSGlgsWxqKa8Z9lYTBPs7/KYMOmKBCZ5K0TMN00+h1rL\r\nvn+QFMzBQ8TieGtY5mM3FK8nmaxaNqPZw6uEjZPEEJu1lNIEI1kL7jGTnen4\r\nWxSSR3RWQqbLINmkWW+C0bCGzSe9pC2mKfL4FUASmcUPqA8nCNRFctGOA/hc\r\nzcWk4DCXLE6fkGuhPXxl2epUYfXh4zeEAJG7uydgxIgTBV6cG3ns3l53bdbq\r\n3X2rjtUN/sj6aoX7P5jXAEUQXJRRMn4bCVT64/w6xVXzj6MmauDjJoz0e/nf\r\nHBh1cv200kRDlo5PTIYfMkLPnDxDjmIIbnDgxK7ru0SvSKLy9ufDkU4Dn2O6\r\nbwR6vHBUh7pnnL3jZcqaFmpoPZuHo2uk0r6yZay9QeFgK9BPre110A20S58k\r\nNAFgqRpsqAaFhS5JwggL01KsmhnZIVx2VQmigxywXspiNDoheN3CEdVSsF7J\r\nBTdOIMipXXjxkZ1eoBAKig3sXv0ZPdFkQzYjNOEII28fqU6FvzEkPZ2okv61\r\nroQyRJot3Ni6HbdN+GMU812g8jAi8c0rOV8A7WCTKv26DsIkagEbY2Ib+k1M\r\nW0HRomZCYMrzqKNln6A9UjLqKj5+Z3LzsadiUcqL8xJTGOgUr+1X5NG85YiS\r\nLsk0Ik1ilGMX+Naz4DmGMB0ajeEPF+BN55M=\r\n=XeT4\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Персонаж определяется из команды `character` списка `appInitialData` или полученной  `run-time` команды и может менятся динамически, заменяя css-переменные.","gitHead":"67607e5e21535910d00f5cefb5f08cbd2c4aa936","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.18.1","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.7--canary.9.3674148218.0_1670835316249_0.39876973409370176","host":"s3://npm-registry-packages"}},"1.0.7--canary.9.3674155940.0":{"name":"@salutejs/canvas-example","version":"1.0.7--canary.9.3674155940.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.7--canary.9.3674155940.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"5d48dc8cd6be3a8961eedc16da50bfaa81b5cf44","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.7--canary.9.3674155940.0.tgz","fileCount":68,"integrity":"sha512-XLvWVjhDc1v6YbJgQF5vaWoL4xYOXrrwpjaCsFIMLLI7ASR3xZhzTZZJtEa5wu/eYRU3Vps7lX7/pqVQAlbDJA==","signatures":[{"sig":"MEYCIQCtQLj/UYUYZzt9xR4ulYPHcFMLzvmdxWptkkvAAIq11gIhAKGIE+V4wwwyDIPnIYGs5+uLYk2X3X/jEdR8pVkSefjJ","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":170387,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjluywACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrY/w/+NrFJn/rug5UjhuA8b8o1E6haOhdGG8ny4g3cMBlGP6d1qDvA\r\nBoU2SSbI3nnw306W/djRLZI4clsTShTLwWrYQzPULrJ/upV1ETI89yGW3Ow1\r\nc4GEf3Y7zZfhdcWbr6mWBNPr9cjt0WQpnvYupM99Sh1AMwNTK+W4oJs7Pr0H\r\nM1kgfql9iAm2AkFRRqHxRzkWZH0uSkyLY2RKDVytRfcydmq2iLilcTAz9ske\r\naDkGjHzjnHLKrD1RI/XcVD1e12jeSLZthNdMMpfQWve1XN/hzvEXa5jpJ7Ap\r\nk2GCIXIweJTpYwvwHb+bSrQ+glGCYC6VfQVumI1GAUkWWy9Ie0QB2fEJ+01t\r\na33Oso62qhtrq3TJ/kro9tDGp3CBNnancbCtegZievRt2s63mu3nwLCIe7RL\r\nDXZ8W9cEEYJQWzBvIcZvVY8MqFMbSvrpB609EjQ0d6IERd1DRZE6/5ROflm/\r\nOnCleOg6eGcPXBIjIjKId0QZUR67qO7apDO+zO0hI/w3AtjZHvVsgM6ZtFBn\r\nEEqYcGTvoBHh9IUYurVQ5OjM+pT4ffHjXaYhdX9vwZ9wQmtNquCarJNeFNFd\r\ngQlW190G19OHvV2Df1yu5BP3RyxlOYW7GDkVDXWZWnAGywd/B7O3KZaw4QbE\r\nM8BJ2X8JNyT/WuZqNxUAdUkqTiGEG21WyRM=\r\n=n0ZS\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Персонаж определяется из команды `character` списка `appInitialData` или [полученной  команды от Ассистента](https://github.com/salute-developers/salutejs-client#assistantcharactercommand) и может меняться динамически, заменяя css-переменные.","gitHead":"4a8bf68700d02a07b790e313b6686dfe27244b79","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.18.1","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.7--canary.9.3674155940.0_1670835376089_0.9449936072703526","host":"s3://npm-registry-packages"}},"1.0.7--canary.9.3686633577.0":{"name":"@salutejs/canvas-example","version":"1.0.7--canary.9.3686633577.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.7--canary.9.3686633577.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"63c388198f6aaf253f8c2d396a4196dfdd6e26cc","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.7--canary.9.3686633577.0.tgz","fileCount":68,"integrity":"sha512-a39lbCgpyigpiWjgtmzV+1hQ3sz1n96gn0KdStO8L8vMxzSiPvXl2pYP9QYdgGMZzOAXtmfD2bXFuA+jcHjMbg==","signatures":[{"sig":"MEQCICRT9saJtxn6RniMnEWTSW9zZwkN0D4tAAT+Ozl/j0efAiAP4MBs2UpiEyWUIcwkKxrZQAqSytHYu81Wg3EIXAD90A==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":169099,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjmJStACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp5sg//Q0k/6kGxCSFMAOO/9X7DfF/OSuYtlTyG2zeaUzNGqjYjw1v9\r\nQz1BbVXHqBcdwdZnPKARIe34Bh4o4T7il17KWwln3Xc0YZ7CUvn6Kj5Tbdoj\r\nOb9CSL20bqKu8pXStzzrDpPftj4EZc5NonwdkzMBPOB7OZAMbaGH7xUEPKLA\r\nvId35Q1l844CwPdMyXDAKkpNvIahREOVDZWGT6aJzM5/fCCFCZlSz6C3TGoZ\r\nX06uDsnU4QMuomHV6/rRa9OlGK9gNsHozRDOaN1sDZTkjLqcfmWv1GoFL77x\r\ndgzuJRIMAmltcsyuxhEb/fQwWc1obwYq+9PJsn584C+o18AYC/ODkQokWI2y\r\nOJKb1aUSLjOHs3POM85kkK6+0cpZZ2pwh+3wGH+UDEa6AF1Bmmeot5Z3U4mY\r\nLPq6tGqJ+tZJs7lc4snA+0uMYn8ouZ6xhD7JzkQH1UG5IqC/00z8Fx9W3nW+\r\nDu3UvtGh9YLGshwl9rlHTM7q/GFc5oe4hec7lRr6uOJuvUs8rm52TlXZgs3J\r\nwRQmLkAEg5gYIWoek0JjsOS2lFDtV7eWtsomEng61GEKQ2He7yg0BhYx2PUw\r\n1PH87ljask5yBDnXByjEg1ky5Ce8HXE/uwELVxSQCenbfGhysWiUisro04hP\r\nb2ykkmrLLsTwEm0CUZ8lW0+7bhuHGMMqCRo=\r\n=78mF\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Персонаж определяется из команды `character` списка `appInitialData` или [полученной  команды от Ассистента](https://github.com/salute-developers/salutejs-client#assistantcharactercommand) и может меняться динамически, заменяя css-переменные.","gitHead":"9ea628787c4c3fd0a76c122f06289caf9a5edd52","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.18.1","dependencies":{"next":"12.2.5","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.125.0","@salutejs/plasma-core":"1.68.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.7--canary.9.3686633577.0_1670943917479_0.0981987516474303","host":"s3://npm-registry-packages"}},"1.0.7--canary.10.3924672884.0":{"name":"@salutejs/canvas-example","version":"1.0.7--canary.10.3924672884.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.7--canary.10.3924672884.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"de93bee0c97bc5435509f4732061d44f870f6328","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.7--canary.10.3924672884.0.tgz","fileCount":68,"integrity":"sha512-zrL8EaDb5gV8tLJSi2/x9JAyYIuqQeF39GuRRs8hK6uvHuITu/Hjf4/xJl1uyUZeHhZPpixHKs5z3s8fGsslxg==","signatures":[{"sig":"MEYCIQD42WP06GVZVZMZj5oNwtAhOJ3tyTbKaqU10XX81G97dAIhAJmKy4Hn93H4Kub+zVOEalJ4vUUpe2KHkifZLepnLUo/","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":168216,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjxEFpACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp8SRAAoBEWlZFBenjoO3b7uLD7LEaE7WRdDB+uzA2tA9gJrf3gcvu4\r\nbpzpGf1zh+B30++kyxtSwlnWWc0CRmymHmVI3gxT11lfsAxadtu2Cp9cmHhm\r\n0H7hqEmd3FvnB7+9dsfVwEJSW/drajjBt938c9TGbOppWCYg0R0hYRKtPGKX\r\nOAuG2T8jqxJ2VmkaTBOXl6+0dKEILElBszF6Ho/D2xn2OLSJI9K0HHpryGo7\r\nfvyc/7bhF5ztHaVNywflrZdT/gXb4e/O6DoGpdoqbKmrOjaEguJLzELgtQ5U\r\nlJl7cwLp4UkzdhPUl4pzuCESbUjguXxQCLYD8UEqKuyD1PK1FwLM1AcPwRMg\r\nCp+qXq+k+Dc/+wycw+YFP8fgSjs5T6G+zGB26xGWDP8/mT/2YPmTlBUooN29\r\nj28qdmuoFa+10v+w0SZEkKZ9LAenZ3LLnS4z7iTw42W2Sqxbx/8gIjVlx4gC\r\nkbqpOx1OvfYIL7wPctl3AZvUNCf1+CcsrkFWwBXFuG7ExIR7Y69XyNNlHcgD\r\nasMqdTUPCFES4bB/r/Ba21AkHMWPj/6oGTudhUWaFKedr9O2dbHSnri9LK2E\r\neNlEn+fG7nKBuLQxW+62g8xlXJ+mJUtxDe03jthOQ13+kCWxYkw3CWSQkQVr\r\nBQlYvSsQYcKXNTUXNoRHZbvXJGR7itMROEc=\r\n=cYNh\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"0c5b87578767063239938a8a7349051085470cdf","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.3","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.19.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.155.0","@salutejs/plasma-core":"1.88.0","@salutejs/plasma-icons":"1.94.0","@salutejs/plasma-tokens":"1.21.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.7--canary.10.3924672884.0_1673806185543_0.8271273542025555","host":"s3://npm-registry-packages"}},"1.0.7--canary.10.4057069803.0":{"name":"@salutejs/canvas-example","version":"1.0.7--canary.10.4057069803.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.7--canary.10.4057069803.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"c29ead601ed31d20a2371970edfbdd9542bf6803","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.7--canary.10.4057069803.0.tgz","fileCount":68,"integrity":"sha512-Y/cd5+iU0r6KJzdGlb4c4k8SJ865d50xqlckBAbXAnjuJYQxIav6mvQ/12iqHneSa8SKvSuJEoDN2AcI4o9U6A==","signatures":[{"sig":"MEUCIQCKVQKX41m27U+Rl6ZdH1RW20DG4R0Ie4OhiTXw+i2yuQIgaVzVUCl3pgh5ClluLdw0DNjSo/dWHdLBVYmkOkLa53c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":168153,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj2VrvACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq1aA/+Iwu6AZKsP2F+jYJIqrdM4R2zw8aoygZ+oDCf9weLY3JHH601\r\nt7lYeoJQ0N1zKHodEsxDf0mXYNEJ/rFoz5zmECjegEeOKxvPO5EgOlytM8vL\r\njDAgA9BZalYLzlelPCzc3FGPbAYJy2u4AkXUsgau40TFF6JqYieLL/s6quo0\r\ndpzftSCPRnouuNk8q8ztsnpRAmnoRiV4/UpuM6FSmLb7vUYjdeiOY5H8I70O\r\nhZqgBQvWIaKpYf3Uy4ypahB/JgPj957wPC4NDP/s5MvbUWvCXgI9CrKgEMy5\r\n934kzJLHaaabH/AxEuPx4VCqwnHhK4bCSYI+Fskl8zDunKSRiYSbA41qmkL+\r\nLQlrzmQmcj0MJ3dZohPVLilY4L2+ecsweoJdhwnJeKAyDU9wcIDW+OSnpaBB\r\nIcux5MTYJWyYjLQHdeIkn8XCcgE5CGfOe8Dk6uKwU4e47m013aVSlsvtWzN4\r\n1eoUyw2tU5aNEzAm4tuQrgiKMPKCBfBasyxTwuzQ5YRsQBji8tYdrbcpC8HV\r\nybE3w25/Q58yj7JM+F9V22WzjdyWSTkwI+9i2cRgsoXDuSMmRBtPmBGrDR3t\r\nXnLU4b51d7wWHoZbIC3jnt1uqA6NgbO4bmduZvCdkk5JVA2TX061sBL/Xjk5\r\nG4JtX9v2blbXtIyd4O7XyLY8lfZyj8aCMO0=\r\n=cN6L\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"b8e5f8f5f6337241333ee56d4d2023586a81a7b4","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.3","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.19.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-core":"1.90.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.7--canary.10.4057069803.0_1675188974823_0.48295147957941564","host":"s3://npm-registry-packages"}},"1.0.7--canary.10.4064642529.0":{"name":"@salutejs/canvas-example","version":"1.0.7--canary.10.4064642529.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.7--canary.10.4064642529.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"6ddf85f5f842bb0899162544592d827718016c58","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.7--canary.10.4064642529.0.tgz","fileCount":68,"integrity":"sha512-XL5UcpW2dPd/uBVHyefvpIMC7pfuTuDaxNTOTby24f+wzmSWU2hN/9v1S9wZg0h+b+ugaLa6PMtv7ziCPXYThg==","signatures":[{"sig":"MEUCIGDWyPMxBk6A0/P6t14N1GAjWWUWQizxHOoEOjLQPTK6AiEAxRSZ94B+KZfGN5iSmq1yYKlpseg7g1wsp91CzO7aLl8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":167981,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj2mOnACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqwzA//RUXhOkaX4+ElRxdQtPfbZGpAKrBMOzncaynW0T45+K5WUC/8\r\nstTd/4R2g9ZoARZ3H3VvT7mI/KZ6NA9a37oNq+XTDYDnDwSBJm54jiuJdEvd\r\nt1sSRiq0w1nCKIVUxJuczrLx+nUUuKw1sT43uIDKoZLaWPOiGzj4th8YGxIK\r\nLDaXwN9ETGiJedc2GLtzhaHOHN8xUObq1xRhUusY8YIvGwL/W444SHd11xgJ\r\np+pdjppW1J13KwjD58sbMO2i4uPJQ1zhYawRbhwTKkagLxLXT+cZdudU4emC\r\ndA+S/RV6k4U7iKkD9eaoCyKd7kwJYZWdu8+Et9qwWVl48O6Az/i6Eihp6tuR\r\nZMM6X6avpE5H/MiV7CcjRoep+uW8oA6r9HrdYbEaUytfbOo3n2xF827loYrH\r\nnTeSbYEiWloHcVHTwiGVX3ZmF0ISpZnaWR+1N4qqwEtSyDSFuvdjd7IxipGM\r\nOmkQakqNEcamfPeWRTxlXe+0TD/Di2OiWNemn/2eI8P1VHoL+s/FJqN6YAyX\r\n8wylDWrUgfBqJbJnRJhjOixRgOFyUn5NYL7mAiXsl6uGgs4r3N6mVkqbd8rA\r\nhxd1e7/DKPH0KsC3vf8ta7IJNPTMjtTQkbdMnwkDGyau9DbAYhy6vGfUhAYe\r\nRa70z7OEM+iozujMmddqiOWtqqfUU6i68LA=\r\n=3qPd\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"84ef6b4585656725af43674052b350bf7039b9be","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.3","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.19.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-core":"1.90.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.7--canary.10.4064642529.0_1675256743442_0.8328415644686584","host":"s3://npm-registry-packages"}},"1.0.7--canary.10.4072275496.0":{"name":"@salutejs/canvas-example","version":"1.0.7--canary.10.4072275496.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.7--canary.10.4072275496.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"08814a24adb7b8bf582d9c005f08b1cc7c029911","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.7--canary.10.4072275496.0.tgz","fileCount":68,"integrity":"sha512-c38bfvLtWMOtLk1GDlvP5aDiLF1G49d56L0e5Lj/PvkqbDpPTs88ndSS1aE9Q0UjcGXmRusDOJ1Om3dKU6ht3Q==","signatures":[{"sig":"MEYCIQDTvovrH10lJSPHH+7ITtlA2uE5ykvVrdSmn/ytTi3LGQIhALQPTwMkaH78fGOVEs+jK8LB+BabtdoKQRCCdLrImR5V","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":167840,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj22j9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqFPhAAlEBVeKzuhm9CrPRjqqDRAMDLnmlM2rNp7OGlTuzqo6aALeEY\r\nPQCt75pmAT/jbKmimV4d98SP06MizUkUlFewLKMg4+Ek34Mf90n25aTbf1oz\r\nKJUfCY9dxxLBoDvS2afbXQ77Bae75nXwORXCf9lm93szXqXzvJQa8KjseaFy\r\nCPPitoJhew2ZsfY5mg8j+ri4OV4XszTB70vQkh9pakreORAKN1WskhZaqX9h\r\nKDU/VCM4CpzvZWC0NBmMe42Yrsv7v+kGPHWtVg1O/Li1W1OXNSwWN01Eg8gq\r\nn3CWzDw7TUzksXC9ahnG5Uonjsj1aGCHCgqpDNtC3wIeQb0oWuO0uQt+URwE\r\n2aU4yZkbsgAomP7ZJzM/zK+19K4VLTTYFHqXlESUHMmqBSQTZ/VgFiyiKKi/\r\n3ztboJnVUUl5fhN7ET+dmFJO/+NzwGz1tldn1YxKAFLsqnqU728PiYRQVf8E\r\nvkrI2V1SFMjzsfg2fDL3tJ+rQBdUt3DvC2m6lwJhQAJ4g+qI8/ap8TYHFhCb\r\nKGw1a6/Oo78RXWHOybgz1/pXCWyTAXkqvui/rB5rVDt11LGdBjjPbgNYQMVa\r\noNYF3aTxTlK/YXW4k9KCHTnTliIU+qiXNaXMZvLwGdZjYVKUdgze8qTJx/Js\r\ny83KPycGUirAgBb2x8RL+YUS4YZcZtdVS5w=\r\n=4jZM\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"4f740424cce1e9212937a354b94539ab71b6c0d9","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.3","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.19.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-core":"1.90.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.7--canary.10.4072275496.0_1675323645075_0.9317462401569168","host":"s3://npm-registry-packages"}},"1.0.7--canary.10.4072805898.0":{"name":"@salutejs/canvas-example","version":"1.0.7--canary.10.4072805898.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.7--canary.10.4072805898.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"b93a3953f0895790fbc2e2053c6bf82f198aba1d","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.7--canary.10.4072805898.0.tgz","fileCount":68,"integrity":"sha512-lS1OsnQawyM8h876VSWJAZ4KxKXnTwD3yHgIzhakOg7KHvhBkbKganTwe8/12wTLNqq7Ybr7v162ihvc3WGVEw==","signatures":[{"sig":"MEUCIE5cPoSIN3+bPGW1M6R/re+1MR7/0anntpaTuEQ3CENvAiEAo4KFLYaJJ6+Ax4Me/PDqORwuXy1yP3IorocK4peaSng=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":167877,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj23r/ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo1rQ/9HmEV6Xf8+wNrViDl0Skugs31/VfZTRruCHOH3ZyspVfuNyj/\r\ncx6HLfcybo+cxckOnZfHAS6EEoJoecwl6PIEjbPdoDEw07kF8HwhawTpq1xV\r\nqm5iwOcGwzmx6W16Uuv3wSuUjNPXmm0FqWG/ub4ok5H0Zwe25g+N/hUohdYq\r\nzvcGMpxcGYfMSNK3PpKiCReGdH7w0sVK53vNJQKGeEh1D3ol4uuWyowAT87Y\r\nvmsSY9QMeti6CluFErSFpTUjckOgx0Co43kPTH7RjKSOKChZCB1xws3vU4OI\r\nvGQdGwPL6eNqDT4//U5Zn8DbBJ0BbgwYpS89rh+Q83aI0iKOgy1n6gjgarmo\r\nAjSkM1OOCos4fhjWEUCGEre4XPgSAu4d3uPvaXLfhqc+Sqm89WJ/doEsvZgr\r\n2UmwpG9ItR5y85WSU1SXSQUM6LVJ6v05go5P/801G6f3qAReQkf1zsCokM3X\r\nGBC2BXzbNun4X1B/UYXyrrN+mtanktFTwWMjotVNw+Xa/EJcQiDCsWVOtoJZ\r\nxw0S9kEo3BcLfngPZ/b7GBDlx4f1zOodQhQGJslDspC79PNulW37/2fglu0+\r\nNId04urxU9cK5/RwmaAICbPryaWEa5usshNWQA/u3SIYPUxjfh4W5XIaSmVl\r\nFUpme/nxXeTQPn7dBP/VClilgSa1QUbAoZ4=\r\n=P+6D\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"8abd6a563c334d2a753869e5036c5d28ff2109f5","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.3","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.19.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-core":"1.90.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.7--canary.10.4072805898.0_1675328255555_0.5218885606865651","host":"s3://npm-registry-packages"}},"1.0.7--canary.10.4083650739.0":{"name":"@salutejs/canvas-example","version":"1.0.7--canary.10.4083650739.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.7--canary.10.4083650739.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"f7d7fbb07b8d2ca143c6fcd4c3e1584a08a82d6e","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.7--canary.10.4083650739.0.tgz","fileCount":67,"integrity":"sha512-mUvmVHEC3YUagVjDKh73Z2rZFdWddMYM3g4fGcVLmRiSWitAx92rOEeqmoFol4GpaR4QodCw4Y5A9WiaAysn8A==","signatures":[{"sig":"MEQCICMWri8o6jXLsSiWCYhbwnylFFZFcXVOcMsU0zWNqr9JAiBTOFEjzgKAjZY0wpJnHB+YBYMSPVzuAFyBM7UHAnyZVQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":167715,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj3O3MACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpGTw/+OOBWFLX9gJqquNpOAcNIyDrxVzAy0C9MM7cDn70gYhIm8uKo\r\n1Wnai8+glI2WQ0VNplLl2UYmzxElCL1lPFfKZzUG2CNcknfHnNzZhlm3+GZM\r\nLSIhwMwVtMQq5Etk+MBgytt/YhDQts8s9hY2qh34QjYWYEl2I/HZBaehpmhQ\r\nF7RnPSYcM/uQ5AQPzBIPuxgI+OcQWEBZs1gTSDEj148+9GghGKCyXhZ5e8Ps\r\nwt4gFcG9OubelFX6AHv8Ho2HlWdcQevdmBGHB5vNGFQ6cJIbrP6pWv6/Bk4B\r\n+7yu8REqjvUATbv/AYV//Jbjkfs4E1dYiq8+NUHwlhuiyU6St0ZzfIIr69Fa\r\nHYHfeW7Un1OmINPbeiPTCS26NPQHRkH8O9eW/tr0ipbq2gLcwWIozty4Q6Da\r\ng424NgmBwX/zQA7yu5pXjOV7+d970JaZcg87IKfTM99iSK48qgM2EoD+cPmr\r\ntCzTXTSg22qx1JpL5bMDOqOPbvk8WkEjXgBWAxVIoRxbeERU5Awr4T3Ilcm3\r\nAfNtd5WfBxEDDbMKhz9TYPJkDdjJj7A2UpXLcFLQwf+LcgAV0LvD6AHxexVG\r\nQUq62VQvpLpUnKf4BOEIzWEqACflpSxEtUcMgFsPO1B9R1RsE9JmA2GmnkBs\r\nrfwCd7RGpx03RUB9DA2AiLQxGkE0aqANgWE=\r\n=5TTW\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"a06503067022bd322253a588b5d606a915376ef3","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.3","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.19.0","browserslist":[">1%","not IE 11","not op_mini all"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-core":"1.90.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.7--canary.10.4083650739.0_1675423179898_0.40832754298318275","host":"s3://npm-registry-packages"}},"1.0.7":{"name":"@salutejs/canvas-example","version":"1.0.7","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.7","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"9c4611970d0f8c5a37d4c0c7bd3a19dc2e74c009","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.7.tgz","fileCount":67,"integrity":"sha512-eWQ7UBPgv457UoTqA6Npk+OE/cduVKwujJ+5D50F2QjPENGgs2cnUH673l5MLr0zfMsPYquPVnC8ymzUJtiltg==","signatures":[{"sig":"MEYCIQDoRKT4C3sLjV4gI4deesTXOw9Ms4Q/LA2riaDcGMf8SAIhAOu2oyDbhmbFT37uNmH5LJWYpJVDBG82YWcHmuJf8acE","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":167973,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4hRtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoVYA//UZunRNeAymRVdNe5K0j1LMqqGnGBUGcCSkmImGNuQfiSuMUV\r\nABjsd5c1AJFWVG7jbfcAElmGWxScp2N9yyC0/R13+vYqmFnCt4KXPhtxAG0I\r\nqIe43sDWWr+iyUU+PSm6tW56kzzpwjRnJ8+Rgik7i2ct+verMkn4TYr9UlPF\r\nn9dO541VQOBTRdXXxaCKLU8V8CA0POiayURHQ4787FqXM7TVLv+R3S2K9SSK\r\nyPhwCAsD8XENim8IHUoLeuYtexPf3ntojwfX7MIRi1wc87252bvJgdyJU5r1\r\npBawsAz6AttvOPAYgVlGU+ZvsivpCf3ltOaYyFrdxTwvUs88nVTKBw2yEvSG\r\nodwIwOlPqvTy2iIIf/MYIuvWysc2hdW+vxJHfTRlveabaT2nsuOFe2jabPxv\r\nqmCIfA/uEyZQunFNKcSbti1WZdxIH7uWPUYpH80+jfmoJo8utjxF5qNmECt1\r\nqe8zJvAXzxuIdmawoA41VW8ytczLWEvdjxK5DKRJx05ExFYNKejhYe6M5pAc\r\nA864lj5PWzzKSM5VC0dQSj56KaNo94KmzhlFHd30qIVvoqyFcQHHj2n7lmRz\r\nBpHx9yaW96WcNl314BO69s2FiZJDeUdomwosDgQEmmHeFPjBxcPIf/XstS/b\r\nmic93/gHSf/bB6Y/WQg9SN4f1e5Gx+IOs7M=\r\n=EWND\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"5c567cf5a6497f878f80f865079a5935e667e068","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.json","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.json","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.json","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.json","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.json","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.json","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.3","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.19.0","browserslist":[">1%","not IE 11","not op_mini all"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-core":"1.90.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.36.5","eslint":"7.32.0","cypress":"6.4.0","prettier":"2.0.5","cross-env":"^7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"6.2.2","cypress-wait-until":"1.7.1","eslint-plugin-jest":"23.8.2","eslint-plugin-react":"7.19.0","eslint-config-airbnb":"18.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-cypress":"2.11.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","cypress-image-snapshot":"4.0.1","eslint-config-prettier":"6.11.0","eslint-plugin-flowtype":"4.7.0","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","@salutejs/eslint-config":"0.6.0","eslint-config-react-app":"5.2.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.0","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"4.33.0","eslint-plugin-react-hooks":"3.0.0","@salutejs/stylelint-config":"0.3.0","eslint-plugin-no-only-tests":"2.4.0","stylelint-config-recommended":"3.0.0","@types/cypress-image-snapshot":"3.1.6","@typescript-eslint/eslint-plugin":"4.33.0","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.7_1675760749087_0.815053117132275","host":"s3://npm-registry-packages"}},"1.0.8--canary.12.4223427467.0":{"name":"@salutejs/canvas-example","version":"1.0.8--canary.12.4223427467.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.8--canary.12.4223427467.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"1e78b98beffe9e9eb9dd8a78d65f34a3f2a0aec1","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.8--canary.12.4223427467.0.tgz","fileCount":65,"integrity":"sha512-OorjGlzgW49VokyYpNMqY/LMKwz2wE8evG7iSSGKPiE+9pfpvmWABbncvcxXy6Vg/2MUpGhy7QtRunH1i/Lgow==","signatures":[{"sig":"MEQCIGMHN39PU4Fg/B/crf4a+g0Rt8gIjBTvzLciD0DiCa0iAiBQ7z4RHgfhib4n9Fe2H5eAPzNg8PxvuZYat6l24qSQyA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":89434,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj82loACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrjmw/9GmuUFL9IqP3BTP4y1CVr4kZH5YzryCkyhwfNrKD73tH36w+l\r\n7lZ0+hbNP5mGlv9QThUWJX44L8aMg/anrol5IqCEgVBCax0IkdLM2inGolni\r\nYsp1UNzFYpml2Au5M3EoPGOo2s55ROadgl5TZtcWGsn8bnqQE/8LjJ2q+3+L\r\nF+C8GEoT2T2mj4sTm6hTFCjIDx+FwB+eLqG783HV5mEuQcw57zjvrnhsvVBa\r\n7zKiJNx9XC1Hqf2BRKjaAft+4BJ7ykHBtUL3Eu0W6wk+6PKQPhZHX6ouMzpT\r\nFFUg4eny6oam9Zpjx5FhXzQ9KDwxnB/XSwAJJJuoGVToIbTmhOKa2PEQG7Zu\r\n2u2HoQpMky5Ibmhzef1KP3vXidq7N+5nzf9TRcm6FHKb637hsopuScznQ/Za\r\nTabJaUTxPf82qMoBPWehgYnXKEFJVUU3ZeWwL7Did0wMGMSU+3rup6hJh4+q\r\n2UxxiqbmkdKRtNj8CIkabDxEftfuTYSBhOHX11snh37SgxKFQEqBZshd4nuN\r\n4hHbiCpzQxnKsETysc71wbU0bqzsnMvSe23r810sDkWp2Uon/YGPw2fD9ZZO\r\nPTpR40i9if7+GMLQcKrLGQh3pocRXwxqob0m8EXIlD6SZ2/idAc9/Wytb+wg\r\neuJIaXjJvfSjpcy0mGIFatnkSN6zhdvZo3Y=\r\n=JYXc\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"a9cd15b22b4956584a5c00f488b385b183a9694a","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.3","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.19.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-core":"1.90.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.8--canary.12.4223427467.0_1676896616521_0.7056194053445088","host":"s3://npm-registry-packages"}},"1.0.8":{"name":"@salutejs/canvas-example","version":"1.0.8","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.8","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"56c5acc2a1dc525f7bc45a2441209ab63ab8dad7","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.8.tgz","fileCount":65,"integrity":"sha512-yh+zE3Sy3adqJ+X9KhQmLWtFEM1FI2ecuZfiF1JieTUUQgyJv5DCIFqSlpayDOOkP6OmpxYa3L94ipn6IXFW/Q==","signatures":[{"sig":"MEUCIQCGTU3MboJ57KSt3FjReBugWcn+zsacGZaSXZ4z5eSBiwIgHwe6mPmXBFstQIAYfMvl6EP9QHwnnOx9LSx16iId7Zg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":89712,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj82yrACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqOrA/+IX8pu2SNfJUqK1W9DDSB078RBQ19f4uXy8BCPwr7KAbiQaRb\r\nPL1J0jA1lASlMAIX9sNFcuiQInfVpv7aTKjI6VUZCN31OqW7ZWMsEEp5sm9i\r\nYlb1GmSh41/HNGS4lf1gIvMFrHB2Z5+/2KfqrcdqWSzuRP/eAkDkYC04Ym/o\r\nnvTPGJLQ36X4Gt1Rg4yns/GeYwNgEsxv92cc77fnKrgkz514ZB18zF36GRB8\r\nqe/O+MYF/aRMoIH2tXCrC4NeM+tksdyxTroiJudUobLEJcGA9t03L5MJsI/s\r\n/K1tDOCxerjwyFcjKiqJcHaefDs8gfwhssEt2YCt5/6YWql0gir5V/ksKRw8\r\nvYhhw/B1xVnJk4KaArzbptgbzcS0pcSJdGhFe0fvAKHnwpRIvq6ULvnbO5Qx\r\nqtR7s4r5nHlN/fYCW9jiBasmPnOQyv3cYbCUbmQYTyHFc6l+knExNuMOEdku\r\n8tTl2MbWaYTN0m/fUijVnqu4xu6dIyNeFehFdIGG9cK5jJF5V6XhjzEX/SxL\r\nePZ/IKXnW2aSpB5PeQ94GYQguh7Cdz6vPvm3hcSsDnIFBjlJ7kZz7MPa2sDn\r\nKdyXb0gJlCoQmScTHUSxTQhlCV9ilOD1NKuyzlEjeNE1kdJ7ulgZQ2W7HY0U\r\nzbZzIaAF5tVR7le8qHRJFZl6vOSKWC8Az8c=\r\n=Dqdk\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"642498ced423a6b73e6380a96a5cc8528b46c9bb","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.3","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"16.19.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-core":"1.90.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.8_1676897451215_0.38730651260359994","host":"s3://npm-registry-packages"}},"1.0.9--canary.13.4303573114.0":{"name":"@salutejs/canvas-example","version":"1.0.9--canary.13.4303573114.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.9--canary.13.4303573114.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"0cb1e824cfa80178ae54b9067a8016445e54371e","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.9--canary.13.4303573114.0.tgz","fileCount":66,"integrity":"sha512-U7rstkeCWjNqY1/WHyjO9rke5+fPDmvreAm2MImVQTAzjjGMoGy2YJy1XcRNXZebpOlKaRCZNVz/aABNpcZX3A==","signatures":[{"sig":"MEUCIGaAhLQoDbx7zLXfr1hmyjPVll9rLoTp+VbEADw+NhXVAiEA9c0NFkNMFnvKwaTNUog8wvk0QdTrzstemgsX0ssclZc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":89809,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj/0boACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoJ0Q//cRppyJWiRrG9+uGedocuRV9S6to5R7i/jPkXXgq5sXPdbv4y\r\nM3XaEp5BCvF0HEauCxBZiUE36mtSQlTRr0n77mb16inmJqSuvneLCX+kQuc9\r\nQcF64ArmHGq7dT3lArVK1dmYPQt/p+Uw/l3FsXmapofAkEkSLHfoLA915y4I\r\n4CWTflyTjEySLmv3+At8lZnpcDqjP37muW4otJ2QXkDibOPVSRYfzU2qN4In\r\ngeIhCvH5qCKniTWqGE6Cu0HL9Y8QQolfv5WSqR9B951SaufMSf5vvTF9NmFJ\r\nDPs5RWTfnsa5qbmrOqmOCMr4NJi8R9X8hp5SFvx5PLJgUPRWG6ZTgqt1vlvV\r\nK4WZwFpfgwk6iV9D4JC7NHyFOHJSkGSSX5ZDlLeTK8VudWM3nknDj3juxv0o\r\nSmcv4NLjiKN0W3Sw3wRz+cWCMLYQ+ivR+BX53XmKEFg7ImG24rZtEhj3mDk4\r\nJEgMYDd7lb2u+l1iobRZrlm3L+68Qi3ysyPkmK1PIG/0Qo/MdaFA1Q4w5sT1\r\nHBYQrQ2dq8fb/V7CfNSEM70rahuoMlb8/ewaQGF6IT1J2NK43qL/1cqfuXtA\r\nB+AjSxOrk0X1a46KAG6HPpTOZ0gx8m3kSAhhndTUatBoPXEK2uPZCJGst21W\r\nzifcY/9//Qti9WMGfD2tkn7vWo4dylrOq4M=\r\n=4LM2\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"ee50bbb09e841ad4985017c60e596cdbf17d0f42","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-core":"1.90.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.9--canary.13.4303573114.0_1677674215876_0.0719533388068958","host":"s3://npm-registry-packages"}},"1.0.9":{"name":"@salutejs/canvas-example","version":"1.0.9","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.9","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"5efaf44ff9540279b3141ebabd87b90fa4a9d931","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.9.tgz","fileCount":66,"integrity":"sha512-gTPy14R2yI7It1tsedKFOlg2JRaKcoq3ww5AnGvjrk39n6JzGHi61202WtBkLwS1FptCtYg/JoiBDw5EdcjPeA==","signatures":[{"sig":"MEUCICgLJkOw5ZvkLvz++AfMLJAiVEsmzshBUaypB7lueYQsAiEA5BNjlXj0q09XzmaJi5DUh9l/YoVJFku21kmiepqEVGY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90041,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj/1nEACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqhLA/+KaOkg10OPDMhPSkQxwuJrY0E3l6nN+/MCbK//zpLfk+/N/bn\r\n9/DZEy/zGfRUqA9sKOa3DQINGXKqNbZGAH8S4tn2skxSBTbErn1FbWtcNT/B\r\n+rfyjqzm7UNMoXyZcrCRKBVsVBXHuRUhCw9W8HSC+7At8zHCcSSz5+qVN6CY\r\n3PsiKkj0mOOg2MTiJD8Kd3UNhZaecYu5d9m2ohWxBsmI49AZHIkZGEqcroGl\r\n2KdRhpQEY4HWJmrdhB77EvUil5faiHNsNMX7TAplmWJ8QAzdAHjtyOvqEfM2\r\nxoL8N3o2fw6HN3Y8qhcJ1knU3EJkExcUT6yqD2D8AjlZ3UEKTk8shmmYYxtV\r\nQvc5aQNuQoRjLV80rNM6tid7JKX5WWQXNFrm/tss2XYLzQqShiS6XjyScwNt\r\nmeA+KzS3Ch3mwy8FhabEjLjHFG8TDAyqeZpRsQUSCT+5lNLstBPLzwfH6xN2\r\ntlEfPY0uRibTNmdtDxFL9aMagB4JPKxm1q1mXkzvoHP8aCK0HvW5pUIm0yuK\r\nFEBb3CoMDVB8QiqhTymL7TjsMR4drmfAAOYM/gTpzY+ZXvQLjBMLBWsCEl3j\r\nFOoQAuN6Z/IAfWU6I6JvpwkwRf5kdAeBgFibnc7MJb+yLR9jEP8w6hd9puY9\r\naeYXGvuxjeYwSCLfFUW5IjvJEIzSusXzh2s=\r\n=t80w\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"e031fb2bc564d7b1c28fdf69da6e672d12afb572","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-core":"1.90.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.9_1677679044326_0.11201526008703677","host":"s3://npm-registry-packages"}},"1.0.10--canary.14.4464396293.0":{"name":"@salutejs/canvas-example","version":"1.0.10--canary.14.4464396293.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.10--canary.14.4464396293.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"843b1452518ec46cef96bf37714a690da1583db0","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.10--canary.14.4464396293.0.tgz","fileCount":66,"integrity":"sha512-xII6D9bIfPLzw8iwkr5ITaME6eijJlEV2tdMIX6/3y+ppWOnqQqpiLIAHE74i3alB4Hmu4x8oZBeHR4/FKvPOg==","signatures":[{"sig":"MEQCIEDo2ISFdLVEYMFX+xHlAEq6nrwojMSd+HRw2ac9g/vdAiAXRfw1aA73t5sjW7qAvIYQ4exm/tl3S8+4Zug9ZcYE9Q==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90029,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkF9IaACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr6wg//X/h+ZlJqucoNjXYbGcN2/wytfiGt0Zn1b+sQQDF9coa3VKrm\r\noY4F/uT3/3plgim6sXT0GqQkzMh1duu6DX4/8F0girygVuOHheUExcfj79Cn\r\nmUfuGonmm9AYm0KhWLQpDLqcOBf7TWeqtcuWMzUNhtFuwZuGQlwIpEPJuQP1\r\nWknog/X0hZUsC5JDRyvm8lw0Mmoj2iAmsWBn/T2eGY7I3RXL9oJUHD+ADkqh\r\nwR+2NoDCzQFT5Y2vSenXaRUbo9XxFj/VY0Joe4zXem1eSK5zcOv/f6NRK98b\r\nhqcjPSJzCZBjmh4FBsiHBJAKnzDETs/Jj3Zi1JZe6UewFZuzM8/CLqwGhQhG\r\nMoKhLkK/HDkJVukZIHWxYdRiudcrTbLER9A9jfZDlm8w+xNYYGiyWD1YDKEZ\r\nF3YrbAEX4q+nq8ZMTUB0xAqXY7t1ppZcN1nnXDz3aDuM70Q5L8cTTBzNzQLh\r\n+1yGmz/iuy2fkGnSlRHCvZCUGyE9e5z3SDKnndtYu+IaBtfXvAnJ/18h5KeO\r\nRJA5qLhHnfQjwjHDwcZ/Kq0ykK1QpPedCk2hAmFn+froWwAdvyw6H/g2iwvV\r\nw5F7LwjWlZSTCqf+bSPm/GreEnuCfWbhxLR4NmXtt6PyiQqgsQBDbFVP5DQb\r\nzFjir8L1JjCGMrHs2k7I4K3dIWEEJ00k8+c=\r\n=ROuu\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"fcada1fd417ce1d406f20f99cdcb0f03ce8fbe6a","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.10--canary.14.4464396293.0_1679282714722_0.7817731865895017","host":"s3://npm-registry-packages"}},"1.0.10":{"name":"@salutejs/canvas-example","version":"1.0.10","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.10","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"6ef0cb15294019954e3245f410a816ff95d899ec","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.10.tgz","fileCount":66,"integrity":"sha512-CPiTAyd1jbbxEZAZ0GJXuJ00ZFL6qZsEYAGoA4P40qleoq+KDIwBrz8JW+atTbbnpa0vanbQW+ZsqblwtJY47w==","signatures":[{"sig":"MEUCICYYqakqweCZp+yQLxaFs0aC5CHMZ5nA/l3EmZ0EExW5AiEA54aacJ0GnccoXUnjVDe//UX55l37eyzEs0DQqTyt+pM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90266,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkGCUIACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqppg//Qy56wcOpABBEfMAdDlf7JBr4BOm8CB2hE51O23fUr/Li44Sl\r\nSaDUhT/CZydtLh1n9XwKiHjegl5io1oZatAGLR+X7CricZ2jY+UbQMfrEyL+\r\n+ELY74YJPgDXMnl+8FOumOjugnHRWih5gKoIcgjTEb+YUpKu/LjpId6oaNyQ\r\nHeS9yIaW47BTX39uf5MNTWhX8Zs0mT3eBjxpDRRP6Q793WP62oBId8ZZ91/L\r\nIsLT/SmLf+uZzwyrNjCkZHY2kCCinxXp0zyXWR9sF7WbKGySYz3KJk3Kh7J+\r\nR+Rq/sRRRLMxZdzhcKGetgS1q2PIGx1XUlBj22St0lETfOygWQlLxTayHyrk\r\nkNCxpPjFWRq0tCks8t7LB2Pjg75WtF3mIKu8R8MuZHEAZ7Y0DFyI0gGzeTXm\r\nJ0vsX0ZTpKauGjtIao+iKwv0v7Iurk/3b8cBlHxivm0k5h/Q6VSM5dHb9hll\r\nT2XbANUSdMgDQkhT4N0PsbA0y2qI5m9eGmfeh+XFYU9rsJgQLvsJ1mnNpVFQ\r\nAGH0z5h928F88B0WrZSNJuaTzw6LSo+vR5laKjrJvtOn69Cvg8HBzc32TJ+6\r\nDgKqG5d/Xt2jawrgueJrJHh5bGiNPl2jn3W172QAABRIC3hBJg0ZmikrXuaz\r\nQEiiwIdV+Gsz3dTFCCjBykaE9G7WvPXflJ4=\r\n=64BC\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"352b404a417ac1587cdd6719310dcdc43104bbdf","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.10_1679303944264_0.31078178841027926","host":"s3://npm-registry-packages"}},"1.0.11--canary.15.4566450655.0":{"name":"@salutejs/canvas-example","version":"1.0.11--canary.15.4566450655.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.11--canary.15.4566450655.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"2855cd0a1e5594cc3720ec0adbf09fd059d62591","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.11--canary.15.4566450655.0.tgz","fileCount":66,"integrity":"sha512-aIna1sf0xp8E4HSFNxYiosqRkzSNokPRoWU1xvSfnVthr0EHykMQC4wToRIJSfDNcrQ6jzwI/MqKfHsXX4xiIg==","signatures":[{"sig":"MEUCIQC2u+ZLqm4qjWhYMHPgCQcC6EQKnn2d/d7gOKCRea+HvgIgVAjwMPX65PD2wldIUL3IOVMo55JnRlE3neSG0Relg8s=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90036,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkJbFiACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrzeA//U3xE2mwO0E9gd7Y2S7G511yz6Mti5jUiNdNLSTwxK392GZQ3\r\nG492tx1pwBpkynB52BnIjg+41cK6SOPoCg0H++TTrcCArZtLwOCb+uBIUSgS\r\nymyLNuiHz2HSPR7FmecV3/5YDYYt653FOj0N3mo9E04e48O4wSfH3bdDMOXt\r\nIoYkwm3KQJRD+sp/UeO8+GHzgCuWDi5EmYZlPsOr02IUxiay1ivZ+Akc9vaA\r\nEAPchDP9yjR46KHtP+16YkZHQXHFNexORBFruTdCOHzT5yUUN3KhMLDxVMTK\r\nUzHjBDRSnKlxsrqArCOk3NgGEZKMOiR+a2rnWowGqkmrcljONz6J1QUFxHIq\r\nr3d5mVvoOt8cy0kNVeCResUOrrlh1BW+zk25QzTzt0k5eKHpXmKka4+T+amS\r\nqDcT+qRKPQpH7BRp28mB+X+iQsRwAxCbj8tR43JQKrafNvwnDV2KHp38Ytcw\r\nqysCxqjFjDx9sVOFW//3Gb6u9hdZoEhl1vMv6ZIJ8+QCcL1wDFLp69OwTOQJ\r\neK6FfaSR6UisA3btf8YAJQ+b7IJugl1T7qncx3PgDqORCZZClgHw9lJrNHY6\r\nTD7Va5xx/HZSIw0mGDxWZp7I92V1PjIgmSHzKrFbHOXVEWq0vTQJcu++eoKM\r\nUxAPPjJnEb4qXSCtRbhwzfa63BY8hD+lq7A=\r\n=dM7X\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"2429908d6ea35436defeb3ce7a047bd57d6aecf4","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.11--canary.15.4566450655.0_1680191842736_0.5508806942313842","host":"s3://npm-registry-packages"}},"1.0.11--canary.15.4573623434.0":{"name":"@salutejs/canvas-example","version":"1.0.11--canary.15.4573623434.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.11--canary.15.4573623434.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"ef7cd6204ba44b91c75fdeb6197d30298009ceaf","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.11--canary.15.4573623434.0.tgz","fileCount":66,"integrity":"sha512-q/V23353s6zyKF7Y2tjReEIwOosj1o/PdXm3RDcMtli6aXF7hi1sPOiet4h1iatNwR5o0R+30/6SOxIthVVNUw==","signatures":[{"sig":"MEUCIQCCU6o/Wo3VPEoS0P5WYVw9oTNCgpO9rZjk4SustqoIZgIgNfsB1V9jR2dv/zXvG4onCGg+BHhanRL+wpBvaqs8vdw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90070,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkJqiQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrJIA/+Pg0s4CaAGa8rZl11Bw9dHMLZBAx+3gJAGSLw8GEPGqODT/iE\r\n99aayAmY7Gy/kP6kefcpE2flnYp5STGD0fpUEucN9mgE+r09IobG+QEUYLaz\r\n1rw9hZ9Cjsup+uLKGuLTPRuHNYgARjXRVCEnyik2tmp9rPosxgnypTlWmE0h\r\n2GC+WcRJbCWdBIkguJmE3IdKgm+WCF/Yd/s9VU/f+ZW0P7z+wn1c6WrnnMNz\r\nFIHxTxlAxiF/g/fSqCbars9wc8BPOi0ZhAxb6bCCcBiyYEvYhswY/qC6QDEl\r\nOuv5mWaS00fGqwbH91XzlxKXGVSS/eNZ7wPj5bN8XFMjAQT9K23f+txKsbig\r\nODEVY3jqpzafyB/pjia3cKUHRJsxnNAaguh+sT6yIP0RsTFkxIDH8qz4JvpY\r\nyhZ7I8hH4V8GD7gH5ZElU6Ksj4koj0NQ+fngxJg3sxg75F8rR6lIuxVosDB0\r\n4UBS8uMcSjO6rvroC8m5b+nozD7wo6DA+jnSiVTSA6J7JapoLJKm0k/DskBY\r\nvkE4mBrgGuoMRtLY7sLl+NaLSU6EMe5G0SEc4I4mom99XstHUBQ2PfNlcD28\r\nJi2BbBWfuHtwvOljYGDQLEe8C1SGui/VxRXSiIqcNtVZxU4ZQ6oGkQfY11wR\r\nOsG7BupbGYpqvv3yldKZ/9M0nAYotVWDbD8=\r\n=8ZRE\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"8cf420eb79f6879d42add43f404575f7bf2c12e5","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.11--canary.15.4573623434.0_1680255119953_0.5992110835309548","host":"s3://npm-registry-packages"}},"1.0.11--canary.15.4597153264.0":{"name":"@salutejs/canvas-example","version":"1.0.11--canary.15.4597153264.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.11--canary.15.4597153264.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"c7d14914a882010c01cadbff329c596fc356f109","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.11--canary.15.4597153264.0.tgz","fileCount":66,"integrity":"sha512-PV3Q4XE4aLH0DSGJQk8Cl93SnSzZy0xhFLubRZq4JxVcLdWrhQblrRsmkc1wBcXtXNVRi2z7gnBILqo/VbS2tQ==","signatures":[{"sig":"MEUCIBdMxdfnFuetuN5tCSJdL43Aw3MZI0f0o/tHkpr5QczvAiEAjJgmN+O2jrqrg9IwYFs/DRbZ4mdMKHyxRICSsKo9Poo=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90067,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkKtIqACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrpcA/7BTdPZrWP467GSLXKSJkFNneoAWSpMLMg479z03Spix7YtY4t\r\n4Ext3m9FgXd1KdmWfPq1hso5cV3L3FmtpW+akI560hotVZiCRRmkCENthNUs\r\n+GQykGaqyIJ2zAUw1mpAhgrIxgGmGf5O8wZ6kSItT9cC48w/xugcsUhX9+tK\r\nAGN70Ra9xslYEeWzsjfyhHQyqPLCfVsUKoch4vRF3rPcgmcOapGTIC98qbMx\r\nc1P5MvtDHrw2clZWgBEv8u6RidLlLPFLcsbsKXTuaqkJ7q+fVtQVqD5uCSXE\r\nYH8nt0BKpGCQwfgABjDl0jwmKZtJZXu4uJORLJhwaLXBQrtdTmMVc/UxaIUH\r\nVPVol/MaeiQgtewsbDhrzahIKMUZNixsfxXxbO3m80P2EVmcR90GJUhne8s+\r\nE/PlmZyxhAi+PcR3xs27oM5lWpVXxuziOhxYbK3TbWONyS2q89+LhtnkHS9d\r\niron9mKJz69H0ZAQikwP15+LMrGkkUK8KdxuHpXypZf9+AgRcR3VSFdNNp/H\r\njebI64ZzYhbOF+1Wmp+KJ1Fs2l682fRCoEg2nVPqDlEpe/KDI4yJtD2IeQ8P\r\nurUx5dImkoN1hwFOCDefylgZMFRL7aFUR0ibCrfUi0eEHLTq7cNfeExKoyiG\r\no9Yljo0l4/+4jULzX1kGKbLaKpR/Ho3KiPM=\r\n=fRg6\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"571d750a620e12a1e9fefe5e268df2d8f052e439","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.11--canary.15.4597153264.0_1680527914690_0.5511090724741883","host":"s3://npm-registry-packages"}},"1.0.11":{"name":"@salutejs/canvas-example","version":"1.0.11","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.11","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"a21f154a44a9a8e5be20f99a5bb75d9f608badf6","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.11.tgz","fileCount":66,"integrity":"sha512-rFysRGFtM6Xe/E+j2NAw2mh0/I9jdik1JuEPMspUNnb0wdhZrE/8AXrVGOX4cXUL38uNzssg2BsfWFoQlJ5bKg==","signatures":[{"sig":"MEYCIQCrGyI2ch/7Rmp05GeebuRe9B6OJzCsu0UsfJfUEMd3OgIhAOK5R+zymBZpd7+qXwmd1FpV7JOAPCeJheQ73TbhdFCz","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90348,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkK8xXACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrROhAAifB/D/FdHGSnjXJzeVmM5GLXFfw8ul24xpaLeCmbdylADSG0\r\nyxYIi/5aXU/O2rLS7K1flamT0xzayS46hv9Ek1xTy6ZpXNQVmnj14Zd5pqgA\r\nloTYhNUIMpJijk3an/u+huyOks9647cT3pfSvBqNxY08Y0SWfjUmT4MVXp4p\r\nUhDEZF38yFg3OoZDZO5WGKUtCKuRf01aNGsNUJ8XKHVgDn1QoZWYRm8TESaA\r\nYSDi74u4IaT+6jSbiaKNNFZ/Q/oUb2NqSYmcumvkdPPrCMOQfysdnVP+B2XU\r\nd1neF4xS5wC9QaWHAj1TLKsILTEY2sqlxNYyMCd5gOxz4VxPpcgWLGFTUvhH\r\nv8QucvG8XwxvP9MQNZ6ySB5dZ6qr/NqRx6dXrHg9ls57E22RVW5sPKjavUu1\r\nzRk4eTQI/B8yS9Yo0etiYcEKrrS5HY3z9GRBNVwdYvxnfuR6HUhPPzgUR/Pa\r\nvBXGG718b1UZIFOLJRUVoVq6I+kVmi/GYlXzsnE/Mv0wK1rMSDYbe1vSowUE\r\nEnGLOvw1ldyHJGszC9OKKoQHXHgLr3RT1ujtLocWSQHGA+uYKXip+GzsfimX\r\n4kdxPUVgW9/XuX5sLSWCikZCWKpxXUFsKH4sgIZKAHbaQZ74+GMeH88IZIev\r\n1LvwD/AR+iHCxt1WDGEV4grdhfZRpXhyzFo=\r\n=3Ei6\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"21e6754f2438673fe8ff188ffa094651ec9ca7fb","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.11_1680591959311_0.16048084838677346","host":"s3://npm-registry-packages"}},"1.0.12--canary.20.4610439466.0":{"name":"@salutejs/canvas-example","version":"1.0.12--canary.20.4610439466.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.12--canary.20.4610439466.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"68245dba61e58c54fa801a07afcd9168bb665b28","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.12--canary.20.4610439466.0.tgz","fileCount":65,"integrity":"sha512-cuZ2uimoZru7tLnnsdQ5DYNB4a3ZsX2a46U5aCIoCXO1611pzQqdje6rTBArcdtJyPmR4tQam26fUYz7zSr1eQ==","signatures":[{"sig":"MEQCIFUDhX4puyWGbCvz7FLlp51LNA9LJzeMnaq4zYARY4GwAiAc2UNo+VqHAal30UuL5Jg4MBkNMYw0jPnD7DkPCGP/8Q==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90277,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkLFolACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmphJg/+P3AKo+TXcJeBhtZa6f/v0DCHKIhrNpT+4HcZBwSsFwWHMjPX\r\nxyds/PWvGMor82iHTTMk8K4f9oKRP5ILG6u/Fv1x7eXqZWdC0uBs3UGQBadf\r\nFdNT36Y57uL9vKWuW32mgxYvKTQxXIfMkoEppe7v+eUgBKpw8ZU0vM1cMv54\r\n0moI5ntZJh+/ovEfLXsi7erMmJ/+/8JYHXJnvUPmFhYJ7Wyl2BGfTpBkVLhF\r\nTi62Cd9CJXA3ak6KLbGy/pxfxse4pAd3mPl0791+czMQm4+ERVQvTevv05LD\r\nDygt5jk/yvGpfy7eWb6si1Ll97d4WC3J3ce1FQ12/t0ELl6LIzQuzkKcxY3b\r\nKGba+Yv69QYCfnJ/8ohpI5eqzdSk7I1rAs11s0shdT/4vVfQ7XfJFsqwxGLE\r\nNFyOUnuUU9CyAcg8ZyVrfMmecLboIs8MShWO4ABIwQfnyBGpfIwfe3t3OM/q\r\n6SdcXuFMfByM1HXTOsGKUnyHEGbof96FdECUBAgHrA9c/gP/71/DnuTCBoN6\r\nSgkBurdvjdWSVCHc1yMM0DL2Wu9Ha/uefz2sQ+MKlXspua1I74wOP8jVvjCr\r\ncAZwWDAk1OtjKrFAnBrM6qGGvfGEv8jO5jXAQNtkCxP83W0f5XSU0qmyCxca\r\nahDNcS/E9ctHDOuoGGqq3DyrtOjjUO8AxVs=\r\n=oIql\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"86a9d1b186549a61d68a263af250310586c00eff","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.12--canary.20.4610439466.0_1680628261654_0.05927670978937383","host":"s3://npm-registry-packages"}},"1.0.12--canary.20.4616910992.0":{"name":"@salutejs/canvas-example","version":"1.0.12--canary.20.4616910992.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.12--canary.20.4616910992.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"48f25fd8e97c0adc9b312b0493c861e442561550","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.12--canary.20.4616910992.0.tgz","fileCount":65,"integrity":"sha512-mmaUdGKGTyljHFo80O14VMsjvDP4ZSHN6kiQJJwsBwyc1jro1xxDBrNE+rD9kC38a+wZ3SyrV1LLRA7iuGlfJQ==","signatures":[{"sig":"MEYCIQCboaB4L/cQB7GnkYBI+G09bN36FxGQ3x7fIAOE/Xf+vAIhAKOABVdcVtqwqwh9dJB+zD+qJkSJcPy1m0f3gIGDT268","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90598,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkLTzYACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmouAg//SYgcFGr02gkcGgW8K3904KUJ8dal/Kym1ahxJpbXOoczTjyu\r\n4hWQlWlwLqBUu9t0R9iYq4jDNX2ZE6jbSZ9qNvxOIYTtdJ81xFiKG5jb9jVx\r\nm6t7Tz311wJWps2JFGAk5UOLtzjBTKhnEl0AFML0H3HtOvLp+HATaDQyrenN\r\nQBtmKcq1PWMcZ5YHwgdJE0Hb4ryowHPWQH+AhyvqmlQYtF6/1DkVkJnzEoPT\r\ncym374ge8/Yr8vu1OMs2u4/++TrHV7e4FMkPs5puqBdRG4bq6p3Fxs2QKhXF\r\no0lnqTy6mvhgEdbDx7OnfzkCN2IdX7xcvMGsFYpUBAE1JG1W5S6Z+k1FPIlg\r\n/RIY2TxbXNOISxra917C5SBIanRlNtiiz2QlUEu2m0WrjznUJUGZShFYuszw\r\n1RAonsjPlkZhhFTQFblF4thPuk3r27SwZ7EhkkhuTz+Xl7sOKnV585rW9O9k\r\nmJAj7My6XtUPNhwcd1yWcaoUc48/XcZZmPHG5JPMKaUzSk03UhVkgAmx1NdP\r\ntSrAagAUZMD1HmLsj3cGWcl626WaQT31tTfFYX8dXjtPwvwrIzvBf3zoT5x5\r\nULKAUMaC2tEm8BRKSNaeAhsJoGe1evj2pOU96rUH2lrmCcVJzVMkaGjOXL+k\r\nbUMLXcTBfeX/p17fPg7VY+VJjyuOgwdgrqY=\r\n=tJvo\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"3885620d3c080268931f8749874123f578312674","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.12--canary.20.4616910992.0_1680686296335_0.9759475379529692","host":"s3://npm-registry-packages"}},"1.0.12--canary.20.4617840951.0":{"name":"@salutejs/canvas-example","version":"1.0.12--canary.20.4617840951.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.12--canary.20.4617840951.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"37b408b4862086498fbed31e07ace929fd916961","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.12--canary.20.4617840951.0.tgz","fileCount":65,"integrity":"sha512-E4xtnILg5BgVaRf+W+yZQldoop9KEBRSPasdXCF7qF4ovzqq7QkOTevMh/owQs/SdFO/DVAY0Wg50WY9FY9n4A==","signatures":[{"sig":"MEUCIQCxUnxLbp2RTntdC47iMR9SuqAJuZkDAgbPlawEkO6xHQIgZAOarDZl96YKuf9u2eoyHq9503OvXDtHYGC2AIZc5CQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90542,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkLVeOACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmriTBAAn1TPxp+Cabrw0uxn283VF4pk2ZNkoddFT+JmPagS1J4Zaiiq\r\nLP8iwkXTeMTKOZgEZFFtyi7FHefV7JrY3Ggt1XjgYFe4YnDjE6ygcH/I04EI\r\n2hrTn19peXWcZAwqv+lyquUxp8KpbgCNMqUs5vw3qQP8Ckg0Gaxc68GGu1W5\r\nEmZ3DBwJc3LqQFT7w1wXCpNkt/S/vLAQto6bgWp0WYKP8VIzSxgGQSYGR6Zv\r\nvKu1y7bne1LOj258gf1euMS9rX+/0cixdNiHEvywcVQHp1bYZsOSlpws7wJq\r\n9ANuHDMpyOViU0BJ4JKmLMVIi6xWkNEtBLaYakmqPCMsbTrjb6ZOC7G6M2qc\r\np3kdUkaB82XSKAnil+1JrK9CpoLBN8UEnNm1S5bcu2tuMtj00ZRIcwrFAvSK\r\n0zfdlxNgKVZxDhe9yAN5g9q88lSUgPTvTYNftOhh/BX7B6cxuZ4bildaC99y\r\nmFVLoNQrr7Ksxh9I4i85gPnM3A89XCWKU/e7mrULhBwtc2t4Cszv8535+H78\r\nQ/IdGkYjKD7+sFhX4FRUNjpUpXd3GEMbtEkhrBECuHIaugV+IBU+y2ivdHco\r\nBz4EQKVPzVJJDjk4vQQ88V1eC3tqrb8EBrZZGkuMipvTv2SDFMbJHQNoMhcQ\r\noYR8rsPSnXZh5rWsVd8m28Nv/zzQu5mZJZ4=\r\n=/60Y\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"12f6a1ff5d39dc41752eb226e3405dc2d47e44e8","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.12--canary.20.4617840951.0_1680693134201_0.8348462427107695","host":"s3://npm-registry-packages"}},"1.0.12--canary.20.4617846024.0":{"name":"@salutejs/canvas-example","version":"1.0.12--canary.20.4617846024.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.12--canary.20.4617846024.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"058de8ea889bd32d300506cd86de09b1b54d9c92","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.12--canary.20.4617846024.0.tgz","fileCount":65,"integrity":"sha512-8uZMqtDjgk6Hga5ZnFXO1/nM2IFUvnGepr1RgnZ0c39G1YRpdRTbqGHQOir317RMXMBtYUUKgwkkNpeDuulAKA==","signatures":[{"sig":"MEQCICcGs/JsfQf5pGrGt8v8fbYTK+GaKO3q2CkyhVcSzGWuAiAFTSd38XftvYgq75L8Jvby88Zm6YpG/HAnP9HRxAFXUw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90490,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkLVezACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpJ/BAAgRpEwAW+WPIiGX5yr0ICsbfmyRrRqwDpoA5d07FQlrfmNFEf\r\nQYXOn+j5VcLmKFHbbB5WcdgZNaQ7HAa/TECr7Gl7l+Sx5NtakMkJ7INa7qfr\r\n+yZgEysjM34yUbkJQcaFUNTjXzCrVfCx8U98rC9MBceX757zaE8mnSfxN9fc\r\nlfA31SQk00hnjR4UXu4gigJXpredbCn24RmjhlIiSM1OOKB3TtORnXhJlw9J\r\n6blp0ClwzetWCKAo84qfIaa6/x7sMMlkrR9xOTipB88p/+Gwuhcyaze2P7zA\r\nMMCk33wLhymlVS/5fxLhym7g8/dnpkrceWrMdXlPD2vOIbKzElKqlXJrfK1l\r\nzGM7OAx1QCWpmushxf5GvOIjzJtrspGS4rxXyBcXt/6DDqnMTULBBMDAX1K+\r\nHrL2+6kjylkKL4Q+s113+QgxsSA/Nyhao6E60U3uopUUHdx8XrF0TO1C5SSI\r\nTtFkeJXm9a/AKJKAqJwmFrWWI4PJtO6gPmXRKIT0k18lOlRKSPll13TeOCmW\r\nbuOoQtVRnVlgj0+MOSfiascmMwCldqrSs2trJx1WqpL3Ah0klbT80M9OJuck\r\njaXV47eXCzlQmOkvLZxD44eUw1h9b3Lq0o8Tyvrl3h91pqkuvXvF7hfiZTKt\r\nJLr0ScMdCU8y5pEAYYw7O2oZqSHumLCHOww=\r\n=Z6yx\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"32d3a69399cc94e7a12643325ae11847a3996d2d","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.12--canary.20.4617846024.0_1680693170834_0.5204043262115985","host":"s3://npm-registry-packages"}},"1.0.12":{"name":"@salutejs/canvas-example","version":"1.0.12","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.12","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"fb05675ecb46680c4a8938a3d970210f44be7f11","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.12.tgz","fileCount":65,"integrity":"sha512-yjOhnLPp50i1Si95a2VqzmfgUDVmNBnn6tingH6bQ3R9jltVM8hsCeAITbsSNuikSlZJBzN2Zte2HMPfRDzSWg==","signatures":[{"sig":"MEUCIAZqUE0++7emDGijqqJTz3Jvd7tTrJc0P393/L8yKsPXAiEAi7mVJOsAfoCSui73EaexFuxpQvMEmqpwMTVfFf6pR80=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90797,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkLVoMACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrPVA/+Jdb9dv4nsu91cnDFml1jxOj0dWNUEmmaEag/JnST41ik+szC\r\nk0kjogpQRqBp3GkYXmXFi6tHEf6ZFKctvFPvLdefc6nL4uBtuyo5Rg7CZ7/L\r\nJ4+YKBg9ijfJKlpqenWN6Yrv4mXOQX4fp7fXzbF0jxxmDdfKWmttXHmj8Ird\r\n3Vx8ZBmzSJdr1kIbDMvsGQrcKXScaZcMUYmp2JgPfLtd42FuM381e1WXJNig\r\nCbFDEj4rif0ty5wbLXx3VC/kvzehQDuGk61MTsfVIMlwE0FR+cf66k+lnWAf\r\nBmw0coItgiWlSfbclSw8C2vXA58Xu8W8tkHbLRlnKdQctFCAqy0oyWSLeHC4\r\nGWU5rF7QjJvKRT4FZlSNzPbr3PzJTebAT2g5wy122rdSNQbEYNvtgLi1llN6\r\nSNM85b/BOJp+3qIrOcboaWPPRUXpTkJ1VUSgoHP0KUVf/p9dGNOccPb2OsXc\r\nMy7xxOoJ8CqHYJN9U/RAturHcMDZJ7g6+MH0lAFbVI4OIeNeJxu4haHYj2r/\r\nIHYhpioMoF3olg984u20WXft090NXqMTdAPvVynEsQoMKSbQJqjh7lmfU9eY\r\nH61z/LYtm057Fw5u/paS+DUWPdVH96FW2VHRnsuyTeYuRZUwu6KEh3dF21Wr\r\nI7TQBJDUqZrPPXRsnSWglkYasAMRdWCgbX8=\r\n=CXXG\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"377c4ea8a5972f2db839aa5ea1e61d50a9f5d84f","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.12_1680693771866_0.9998769051377407","host":"s3://npm-registry-packages"}},"1.0.13--canary.23.4746216504.0":{"name":"@salutejs/canvas-example","version":"1.0.13--canary.23.4746216504.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.13--canary.23.4746216504.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"658e768157a688e1ed757582b59d733ecd6659a2","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.13--canary.23.4746216504.0.tgz","fileCount":65,"integrity":"sha512-Md+svOTnYov2rTF7Y9wQSbkfal0XstOI4Ofy8jotX7PAFHzsxWVIuVr/W5Y//HwCNATkqwnshyu1pIeTZYnhhg==","signatures":[{"sig":"MEUCIQDkVHOvM8VyhzpbvHtp/gq1EczmGLjeNT+GVsDvr/bT7QIgIUlihJbnspE4NKEYDvlSb6Bl3qwz067m5UcQXnrZaaI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90901,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkQCXGACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqPAxAAjr85Pl5itCMp4l0f+u6L7BTqIXJ+fFb/prj/UTJQ9mX5sCHG\r\n0zX/m5O0esLPk7V0C6mdeUQW9eQ8vimc61l2QoAvilsaxzXhvISw1V2kE+nJ\r\nGUpsUD8kRIGpZDVCfParRkw6Orh0C8OAYaD5EzFT+2fAOGZ5hmF9PKKOydNe\r\nSVCUJk8Wl3Weew/dscl86EWrCluSiMP9qCgfM0uqaoomzUCARgaoMuAG71um\r\no98gjkVAiuJw+b9pHPW7gyQpwWZEp1Balemv4HfoPyAnDcs9f4Hrfhtb8jPn\r\nXGATw7pB6EhEds1+njoT2sUX+ZGjAn0XOeGY08VPYPG8g3XllIofIEgdL5SB\r\nbvToiUsant41YnLjADQHot5w+g7fL1NS+eflyppKIGNyJpcexd409u3YG/YT\r\nTxOX3kNxxrzQPFKU+CIXi26zXfcE0Tsw2N+mKvtt63R6sMd3qXuIgHeytBJ8\r\nJ3tJovwXGGbYV0s0kshBCGtjPcE3+Czaqn5UX19Mly2vXg+5GrbHg+b+AVm0\r\nx+p+iAUYy2Byi9ZA7KEOivsZmQEH8GxcS0YChZW+KoBusBkZNbutexhOZhIY\r\n0xcl9pV1L0pPZLYrXAD5OkExA6mR10adf5naYvDIOOmF3puO6tLUzmwN0yDG\r\nKxZ2L5DCJ7CJ2nEeH+6DEkhe69QtICfWmKs=\r\n=H6og\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"af41101e67de2803737068f1a7d66e07d53ddbc5","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.13--canary.23.4746216504.0_1681925574718_0.07818330184580291","host":"s3://npm-registry-packages"}},"1.0.13":{"name":"@salutejs/canvas-example","version":"1.0.13","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.13","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"e7d524294ee8bb3b583fe52934d2abc884123f8e","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.13.tgz","fileCount":65,"integrity":"sha512-3kKHiAI3xePnEsIPK4ur+mCs4WlwUlWiGI4UdOZx5qWgk6hoy8gtBuMJjmA3fTZ8Z2NtXRwkUdFzIJ5iXcA4pg==","signatures":[{"sig":"MEYCIQD0c42eG29HYHvLeqmhf51u15BhcbxoBT8c5me/LUsjsgIhAPA48mak/6R1ZctKUVbMC7TCJ30JRoQTHmL7egAKHNM/","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":91173,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkQCbzACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoosA//cpBx+1d5AlpbgS/pY8RDbLHo+E+omSus3zfATUB6SCWpea/B\r\n/JRBkgXccqMnMjM+2g0gnH30P6x9oMzZsQ9GGd7OPdXoXaZs0qzQvBtBSFI8\r\nzcwghytL+tHVV5yf2q6OF8/iNy8tQ08vuorVOohfh7iB+FkjQoTXPvmcthvY\r\nlJ+d785GuXOsPVZss/fztNHDSC8N5QjfIKhz2aDV0B32h8hF2TxeO3q3k/ho\r\nb98hseivP4vNORNgcWSGBd3+CoIuCOyaJh0+92um+5P6H7J6wpcXyMXgCcW3\r\n/NWzbHujxz06vVFkk7e3nX0LAQSpdCuff6OcOJrQjTqI8bVEWVic2lCA74qn\r\ntiC/RIKib87RPy0zu/ABKzFbInK3wwIGy10iWvobP2+g/QR8b/3fuzsoGVBF\r\nMNlulB+1z2wdrDXx86Ng/HStQOp7qkETq4byeEgc4qgkg5fBgLRim5ID8SY0\r\nF8HsjSguyiE2FNjIKKpXlgVmSIsyUDOhGQGRpCz49tbVWacIj/jlhs44Xpyg\r\nulUvFA1ciJCg/CTGoFP6gtMKYI+SJmDb7yZ7RySQ39al8udtTK5BaJK1KV2C\r\n+uM/mTqEtrqSUqnjBKKfuvecfqERJIN0XmNUkIdXzbSksq/vMmOLPTFxgWf3\r\nnAtJgaYyQUG3yn+u+gSa5vGDayXj1zTvKME=\r\n=kuyu\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"25a24e7413c5017d2a037536161b47e386c4ca52","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.13_1681925875098_0.8615183987908992","host":"s3://npm-registry-packages"}},"1.0.14--canary.24.4746302300.0":{"name":"@salutejs/canvas-example","version":"1.0.14--canary.24.4746302300.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.14--canary.24.4746302300.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"86d8183b2a539eab8ed2aa2038ceedef85688fc6","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.14--canary.24.4746302300.0.tgz","fileCount":65,"integrity":"sha512-F9PljiViYnLncD67Nzy6uOfsDgZlAExRW9hB7l60MxB0qSyVev9xGjDYSt7rd9/St/UqfI77OVliXQHDlnXEFw==","signatures":[{"sig":"MEUCID53/4XrHNMNshewbvLfd4mz34dz38NEIkVjQmWNdFL5AiEAgXFwdRP14orJZ8DXTgVALeQKVk02fFUMeIDp5fmUfNQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":90901,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkQChLACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrsHg/+LSp9TR22wrfz2Qfq+dW+oVixUwW9mpxKb4gFPDxL12l+ka2G\r\n+oHzQoDHGc///lCet+ITHluihDeSSwGsF73ePsI25Y1VFbWP2k9cGE22Dik9\r\nVojNrBwtgISrt/+l8MJwDY1YwGTlfHDoDvrmPwEK5FrFeS1I2fpvrawNb+3F\r\n9Zg4D8BrcnrrUFDUIeGURZR94RS5IAWHx4Cw7uK9FXwg3H7FkVqlLHzkRkp3\r\n4x1QA7HYy3YaNvZISsDcdumegFi5/rdOhwOve/30zzdHCEiaOmIjntmwom41\r\nK+/ipBYKEcIGCccX+D2WbxOqjjuQMk+M76boEAHR/aF8HATgUuGNS+s7KIjd\r\nnmBP1Y9c+WnsiPwPQS7jdvSEy7bnsxQmAd6TJ/RsERByIZ5mrXFuQuMUvhoa\r\nGfaTd2UxLbZUmWt04UDMNsVHoW1hru+WVAMh88xFNPQhgM2tLXVQrcLJPjmO\r\n+pONLvMsRVLl0CQBgaw1+cHnhPK2qUmU6AJSSoLIkTeOC9+N/NwvS8j3thcp\r\n6MOnObTb9sUaBtx5lTr40PpC0EcW12+2FUGw9m8cIWbAZH/b4ua9oYl4rZeY\r\ns2iMnL5iNWvAjK8/x1+eGoi9A0+Yx0uQeCPDbVrrbV2A6OUwJco7WqMwJlv7\r\nPhTltYG+k7d7XivlhDSHvWhu6FeWljK+ITE=\r\n=mevK\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"9a342139e7832843e33c1b3c0a7a415cbb008479","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.14--canary.24.4746302300.0_1681926219275_0.5931475557004187","host":"s3://npm-registry-packages"}},"1.0.14":{"name":"@salutejs/canvas-example","version":"1.0.14","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.14","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"fbb5955e862efece3194523eb171619b9ec68f86","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.14.tgz","fileCount":65,"integrity":"sha512-wTMjwa19Am1cP0L6yHXvQRf+hOPc4IWUXXCdYyB9H+iEAO0obEFfQ/TuDeqLk6qPNoUOAcxxZRWIblIKenascA==","signatures":[{"sig":"MEUCIQDQ05eatMKnijK/Ae5IM5Gd+7oS3Zr5LimZx+JCkrYYlAIgegkD/gJS5fkKR8qfGdAE4M/cV9IDUJry7i7/G3H5nFs=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":91191,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkQChjACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr8ag/9E97vivvCh9N27qt9EE8RYt6hQknvH0D3TekN05o0Ws0hRpvc\r\n3EqjTAPFeyB3vxd2fxDextI3f3iDGkKcXFP8PQLgkRp/+YOTGSD9mXRtH8Pe\r\ni+fNXAx0ob0ke11sOF0+DemLetWyvkJ1NntBqzj5AG5c9NxnS09Sol7yturY\r\nOsrw5KZmDmLgNarsrtpL8Bq0c89k47FoX+PrrHkriyYf68vIKGFrTE6qHJ0/\r\n2ZIq68j2xTfjZ/CzyxNE6zx6wrcikCR+tKwd7niTXurT/fWqm5tV9OrxrZyX\r\nFr8rbQRDA4TIHMghDT7aVcjZeIHDknJz1Ob8lt+QDFkID+wh5ZzSMIjrttQ9\r\nn5FEiOPz5J4y2yLjbpigvDcQSx1hhlFKfJ2j2UunvVQexbQvO+gBHbG/aJot\r\nt264SlL+q19g9zYlf2aa3uWQuPqoPPx3+bzG2IAdzWp7iT0pfWuuFrre1nFj\r\nUBdTyShUMeROcNQNDjeDcd4C5o4JyaPtnI4uMn70f/vDYygu55rpcRc1UWTw\r\n7r80baUeDCkJ+Rzw3eQHl/6C3HDuGuEKJ6zYi5omziNGY8yN2yNHahWl5gh3\r\nF1f8VCvOhC9PFktiOND9SeC7TsbAofCtK0Bwz2YIUBmpfJGmk98+3xiQhC8v\r\nOyUG4JhWHBf1wrLZMbkUNLvnYwtDYujmHm0=\r\n=s3Ph\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"e89244486121c8a6cd249cdc6d91e3f75d783245","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.14_1681926243558_0.8766685907065674","host":"s3://npm-registry-packages"}},"1.0.15--canary.25.4753297681.0":{"name":"@salutejs/canvas-example","version":"1.0.15--canary.25.4753297681.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.15--canary.25.4753297681.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"427f9d3b8925387687ed31b21dba155767d6219a","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.15--canary.25.4753297681.0.tgz","fileCount":67,"integrity":"sha512-8zR+WstD5/hyuUGb6jQ/yZKi+fI7Bpp9pfrTf4VFjOTO/GYkj5FJ2EAVnsYy5HwqCcuL0uhvMDQ6Q8fDW6LJQw==","signatures":[{"sig":"MEYCIQDGSJufYEKiOygBohbh9X+K34GUM4azEgPUJXonu7hTqAIhALjhy4iFikElAT4ns8QmmKriuMrwoXBqBvlr5jPPGvQ3","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92394,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkQRV9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrqUg//aq/8AGl42dkG/tEhn+RRE99DEQHZVSH1tQ+4u7VH7TeYbM9T\r\n9nS2oidDU2B9C4kYzcheN5M9ATr/MMvy/nZkq2Z6tH995SKJKRWan3JdftGU\r\nO/9+G4E1jNwOVhWSUlYcaMh7mrufxokZfezmxloNNL6/2/2MgEiLigdxtIIF\r\nJbco8rgUOMVn2e7noG4nVuLDcJhxu+BRmA/DXSl1b1ugVw9DLW4/I8kZLj3D\r\niY8nqxs36NKxoBSEIPZbHWc0b6APmVUfQZSNIDeg4obC5rqUqOyICRk1ZWO5\r\nncuicv3R559Z0/gb8+MFqHsG76CnEUQQEBnB95ppjMgQXuSF5H8k2Lfmq/4J\r\ncBbJ7RM24rz7aih1ekE/todtMZc0Mhc3LM5utADQxaY6DEYBQSH+3WI3/9oq\r\nOncB9msb/OF/C7D8P4sSOY7U+pzkaPtoGXymLX1H9gp4vmvXo7N5D94clnkw\r\nMs3Kc6oFXNkBcgn7JkiM16DjdRlbCHvvMtAVyxphmNwlg7oEkBJV6ibOrVbf\r\nKrSJCLHhfL19iIsJdCBYXu9ZTKscUkeVIlOSfT8hQQHRijx83lh2irxNpavu\r\n+hxavkZY4fWrcg6AuJDTx0lia25dOpSr0DnHwZM4ak3w9uKxVlKa7UFdH8Rl\r\nVbGyo28fSxim3lgv0u3hrvh08eNwvz+IHNE=\r\n=4mk5\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"3c71ac50cbb4427d873381724d4478db17388257","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-temple":"1.151.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.15--canary.25.4753297681.0_1681986940794_0.7839483343797164","host":"s3://npm-registry-packages"}},"1.0.15--canary.26.4755161044.0":{"name":"@salutejs/canvas-example","version":"1.0.15--canary.26.4755161044.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.15--canary.26.4755161044.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"28e3f1c48b824ce68b6715c6132032352d68dcc4","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.15--canary.26.4755161044.0.tgz","fileCount":65,"integrity":"sha512-wL+RGEbjI2Bg0bf3LhQcv/qEU8nPatnTtPgdGeFSLdSxtDWtjOJcvHmCeeqGam4LywIRr/88gX4sT+HayzCA4g==","signatures":[{"sig":"MEYCIQDq+GRJbdtqF356YiJJt4bzGdIFjmdyib9TylZPu7HgWAIhANU5/+nyQg7rvSGMg4VuP9M+3ssOUNn85+mbMVtPxCiw","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":91110,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkQUVuACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqBOBAApBQZG0QKBExy1tYr+KmJo3SpRKSLbcOblHWsPUagcTyqrXSD\r\nGu8oYUBDOuDe+W2Zr7NW+fFImydCiPQC7bhLSKuqMyCOHmRAopExH6ZXJUsS\r\nDG9ZM29byzqZFW0Z2KdOWw5+aFeSMn0BjwGcDs7QgX037t13+tbyR3voldpC\r\nlXDe86t1UGfefcOm33z9LCKzu2sWT96FCCJstWQSfY0nZG19IXj3YmlUE4XM\r\nkm4Ky42KcCbBU06Ovm2tKGwj27eC8T8AuyRv3Zsa4LaplAZqctUVZ94ZD/TD\r\nK3zryePYeV8tFtIq42IW6S0fkdAFTveaQ0k8a+YUN9RA3A+zUPVFx2zGpGNe\r\nbY22u1N/d7YioBn5W+wtYa2r55v0N+uPsb4kyOSYKy+6+ezqbthiKOSd1rzp\r\nCGWsl5Gz16WUOCEMLbEuSCp1E7rCXQjLm/iJ8yrr6Glyyduxa1PLYOxf6v+Q\r\nc3PXplyoth3wWkmwCFRqD1NWFnD6zS+qjNa4H3PxaQJu7b2O8xHUCSW4JXcS\r\nbxWAtaYlboMHhHWzz2WMXjynkUR30t84/+7EAVJGRfMFrVCIpoMWBtKOUrau\r\n2+PiK0gBJ1SIEwH/PkQm/LxDA+FqFcDtrIBiOmdhfbs1h7cy86CyD9nYjBL4\r\nDwpUxpZJNAkSuhwD45nO1fDuje/bp8/zZ3M=\r\n=11m2\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"db0a60cb9ad76ccc28e48d8a7585ef0908e8ee52","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.15--canary.26.4755161044.0_1681999213899_0.7547900805284145","host":"s3://npm-registry-packages"}},"1.0.15--canary.25.4756580223.0":{"name":"@salutejs/canvas-example","version":"1.0.15--canary.25.4756580223.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.15--canary.25.4756580223.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"d90d052225cf8bb464089f65b9077830a909f29a","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.15--canary.25.4756580223.0.tgz","fileCount":68,"integrity":"sha512-1jK1Ugliuyv3QLzJ0gGsDo0MO56A21xhCijISOWp4rnLb4kh2EvjYbDjB7inGFmvV1biu6UKGTUvqUZ/GeiHZA==","signatures":[{"sig":"MEYCIQCYw01Ie8sMmgApACj83itugOUZLNeilcW2B7rAlyuHEgIhAMiNpswoh9o6cUbwb65KlTYGRjW10fNZ0XfAUyiLRSd6","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":93056,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkQWaHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpANA/+Ohs0veW/+s6AYRYWorDOsNI9HnRJ932/doxmK3ieubMHwjRy\r\nS9wG3wL0CEG4eyEj6fsXp2r42HVbG6iYlnZ2si7qVcbEe7jOBhiBrows0LuS\r\no8iI2HtoGh3ZcgnHwVewnwpBD0gkdFR1lOJfAgl0hGrX8b+Lfczim3LlLa4M\r\nc9Dwn7J10Nn+d/vJzI74rIczTaHZmZ+nk8vrzdS7scpwBdVoQZ0z/SRp21rL\r\n8rry35B2h1VDd082Ut3X1H9Tn0t7k9BM70fFJJbp/ljAYiyLNNY2z/QwZtkP\r\nnU6LYQkAWfX7TVUiXEOHeTv5EDNp9sBxrsLrprB2N+qufOk1wuoCYYew9p1d\r\nc0aDIuXnl8aKhqPNCf0/ve18Ndfq+lGYlVtmrdkar6kv9l/hzofOPhuzV6PC\r\nZ18uCOOPj12dDciwEH850Me1735lme5aXRlhWks4bYnC7M+TI2ehqG8EEZoh\r\nMf9B2iEoPwolN7w/PdUwPzOw1W2++GaM9XMQyzpdbLopF+DHuuQFhSr7jIEu\r\nhDWGR1MMZzgAQH4Wv+9UeWKgupnpf9UArtbL9eRF/l1V/SsHRyeuY8ITamnL\r\ny6PVuPOo/52b/891YD+wiS/oMmQ7yugpn0u3VcvIDLgRUIxne8IbMzp2uspu\r\nGTlSNQtwt7M7pXrOAb2V/UFoXkQ1kYTlBSg=\r\n=zOOS\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"c453c267d54e2ad6f55d044c61b5200f5a4dbff6","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.15--canary.25.4756580223.0_1682007687286_0.8013792931873414","host":"s3://npm-registry-packages"}},"1.0.15--canary.25.4769196067.0":{"name":"@salutejs/canvas-example","version":"1.0.15--canary.25.4769196067.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.15--canary.25.4769196067.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"7e98ac2b3705fe3ef88dccee5a4bbcd1a1e7d222","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.15--canary.25.4769196067.0.tgz","fileCount":68,"integrity":"sha512-G69g70pPJb9jDUIrN0KZhbzi6XcB9ZGWElbx2qWKUz5IUcwy6sfjYl60Ni3V3FPW/mGZo2LDVYqkvz6nJKmvYw==","signatures":[{"sig":"MEUCIHGYxnHlb3yzdOR059zltq7lfu3sFHH0a2U6kgoRIEtfAiEAzwIj6fkcNhZpMg/cr99Fa5qWPRkBsFfC1Ko92OI0UhA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92334,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkQwnaACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr8Iw//RihSIZDXATRr3OvyeAhQGDl1Jzt1fjujRYJyN85WOo3M7Zcr\r\n9ewycJKhDwWcM6AnZp38LZwOyPHlzevOHMi7G+ZkaGeeu1QNRLBrHYz+OsHF\r\nfcYk98MW/WEcXFaKnL2YZM8d/nqAiP+I6/aGVYQ9na1HL9flH1oT/Ihwu5Tn\r\n0SYUDchVQ9k0OrJRQrTAsSv6mVHZPXSHwcdUSzoktDY31nvdJf1fFgvk1raQ\r\nIObm2JQvR2oVzOgTevFiU5+3N8DGaO+MWF9kZ8llWU2CNVhpZ9QEctX+isyg\r\nPQa2xpnEDCgWyVZk/T16UxTAjz/ZZEy8syfg6EQwhQBKfMyA/ryfk6zZTZpX\r\ncJuIRNvfYlWbfMtuYZw7odLMRL+JxXF9MkTSwAwqZg84XRL411MqfW+oRqjo\r\nLid2Ir4EpTKhjrgA1Pm6nR9ENshqRLrf6m6Wz4NZeJwNrOR0cryfnid8LEX8\r\n6nmREO++0hqet8ax7N6qrwzaRFigTOpVxDrPNEjisemP+sckSEC+6iB2L+I3\r\n95cNZpadFOr0rmvCGSX368aNo8+bFWYJfvtxonbor1fLr4fnEY0E3b6inYC8\r\n4+DjAlg3vyaDcWSBsJBUdmL6CSoMBQJFKBO5KD8+zkG5tUn3pReKSK56guaM\r\nauYa4P146TRQvMbqaQMnggQrwA8h03AjnaI=\r\n=XWR8\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"3de3fae9c776c03b1664bcbdc35e845a523066bf","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.15--canary.25.4769196067.0_1682115034392_0.6990747803436788","host":"s3://npm-registry-packages"}},"1.0.15--canary.25.4784787395.0":{"name":"@salutejs/canvas-example","version":"1.0.15--canary.25.4784787395.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.15--canary.25.4784787395.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"beaca9c49b4d773faa30656ad14cbd87e5d1f537","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.15--canary.25.4784787395.0.tgz","fileCount":68,"integrity":"sha512-jV7BNOtytJ0PQBMaQ1Xs9t5tAnL2VJBeQdJ2iSXNGi6zZQBLKn1BoEsZWwmFTg7BcrHGhnaqvQlviot1aoInQg==","signatures":[{"sig":"MEQCICoe31dvcs6xS3pQOi/9z18LEiyaxi6+LPs8ud+w5aXWAiA7i+XB52tFSfnGBZlDD+4jbWwIjN+x9fFQ3ok0kFrDsw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92247,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkRk50ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoEJxAAo3KhVxn/8JPS6O/2QhEJ0zXyEjdAFecWVY2T5jqYYVf2McCc\r\nwE9rxGElIVRqZ97n3V0joPK/ulcSggnXztk05l4+4TuRTQPALohaUmlhJxox\r\nT35uwQ8A0HUhxes0QynfarQIrZqWkJQlSF7+YJ2Xubuh+3InysAv7Qb4T1Qs\r\nIKnn712Aeo9B67ivZFj9044wDtVCUSVJINAMS9zT5ly6qnfy0nU9loHOGVEl\r\nyT8FQ7pz8cciAHd6TQo/fuES6gMI4v3MmYBVxv9Kn5hJUfECaPJ4CincChnt\r\nnQm+vXT7sG4MDdMwjEJ1XSnDIUwLjt4qhKnVfktoGSnHsHJFkh8aB+Effmxf\r\n3xmA79gPP1GX5nUmz5w9pl+xsKXEPGrk3GZ8KDIT+Hp3izuDj14c6Cj4qGQD\r\nxeq+mhWHOsO7Zd7i6oaFmFVj91JL2/pzJ79xdNdAb4EoAnAck7s2NnnZVORW\r\nS/u0WuzX9WYn0GSzonkgaWKFUUx2Bjd/LQi1HGn29LHn/6kfRuinaxNiQqgp\r\nIto+BU2AY4eEgVFemgWT8eMXI4/EBsidZpCqFReOvG3lDEiYFhnqIChVL4ZT\r\nZl9PoDQxK1lbFlHQNKHPNuOqvEjKhVOLblEaUnf6vUvk55j9qLHF/++auj4B\r\nLVgnTrFRfPm3PajOOnesJB3t9B4vw/Dyanw=\r\n=YpHY\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"dfae6ac2944939b8d2145cfb2d1976b3cd98bba0","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.15--canary.25.4784787395.0_1682329204567_0.6471108447692944","host":"s3://npm-registry-packages"}},"1.0.15--canary.25.4785222305.0":{"name":"@salutejs/canvas-example","version":"1.0.15--canary.25.4785222305.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.15--canary.25.4785222305.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"959eeda9cdb8c00376d44c2c638345050d7c14f1","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.15--canary.25.4785222305.0.tgz","fileCount":68,"integrity":"sha512-AGncF72CVhWp2ePn8uMvKpcwInENuk/BXUmVcFFbBtFiOvtp6dnx3QX/tRVOPUah+H4uru71EsKrQBDEKtTfZQ==","signatures":[{"sig":"MEYCIQDsVjkVJpg2GZf+sWAFZ8voEjuKMPXVbkAgoRjffnmdzQIhAMTIQkxLczN+/eT5dFy01c6SUmgmWVC4BBYlHH3nIfIc","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92247,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkRlnjACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqYyg//ehspHSE9oNtABaIa/vBVT6jKo39nUpTtxWiLGlCPKpd6yHrA\r\navNSaqosr6hW/4xY97ehnSGhwUyW2C1kLEVAklcGOWRuiWRJZQwwXnBTKf0V\r\nLmoB6007RyzHvE1AL7aQmjJjulzJrdU7ShMZ/9etTsbKswsX0StCQfLiQtr1\r\naBmKSBZi4H8TEd2W9/wePd/ai03z80wa1kjJPppy+Ya16+caJTPch+CSnE+/\r\nxnoOeWvvtrQ25o8ecVsOqvq9/sVaNeju0bb+ubgmiDD1ENWvcTnIxO8pQwo4\r\nSBcFf6OTGworh8ADALWMkh3mydfkrnntAigqr38RhpPEP9A6L2PuV8qchMPp\r\n7rrxDDLPv19x499O/9us6UXOZmOwj/LKcIS1Zr0MG26QPJV9L5yfv63lONbA\r\nTum7HQXNPkaXbp5U+9DnS8O8bCuMub4dcpEpYBnB/cafUHB9PoGAuoYToNLS\r\n41vkMMp6iwDwP2fUOXOeGmqMK+UCstfgz4vWJfdH/JvqayBSgCFQzxINbyhW\r\nv2SBw5/wrpst5AmKF7WY9aVphNvHqJShn+HA0N0pfJ1cXA+Af+SSk+IgtRvG\r\nl3XwaXMkvVaPQJ4eRa8FI0wMutKBalbTy+Ioj2h0OtQZZgIUZBHVtQrOa4GM\r\ncLrN2oq0/FoI6qE2UwiHxuyc6aaXz8h9AFY=\r\n=DIid\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"9ec10d6e7b392d5ed7d0e368a056d3d0f76ec3da","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","browserslist":[">0.1%","not IE 11","not op_mini all","last 2 Edge versions"],"dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.15--canary.25.4785222305.0_1682332130907_0.21386076944128285","host":"s3://npm-registry-packages"}},"1.0.15":{"name":"@salutejs/canvas-example","version":"1.0.15","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.15","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"4d3bd5c7e62515e3e43124bc8d2c241a5d9f5d66","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.15.tgz","fileCount":65,"integrity":"sha512-FlZExh6XPqZe9KqdPmohftVn6nfs/x0RwyDiJta+wmONqw3pMqEMZ0X5ULADVkqPmVVP9nE2O/1X8k12/E0SCg==","signatures":[{"sig":"MEYCIQD/zUcCJWjLL8Tnwsij7AUEaJY2Z8OAFQILSxdtP9bt4AIhAJzwrhL9EAJzOfuxvfvQccmbmFkjMW4lrrnYrfULkg2j","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":91364,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkR5asACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp2yg/9H1bzq6/+4mw1mswdxa2vkZNds4dQFh/xucRntk01vNRv5lrs\r\npM6ZXPKgRDMbCqgU9jiWUVdTTRq0abThzc0NQ5T46cMSnjzw02W/mvBS/reM\r\nGAcaj9JhOUUwnRD9SJ2LowmhZIqMlbAAgPeK1rGUCxJLtJBCuJK9mSmO7iJT\r\nOwf5XSxZblG1J3uEkYeJuABKBZOa9bXmu/L9bVHHhYZ7afu9tBuGPbusAiMX\r\n902KX7UOUS4u1jJf1O0xJvT6GXiBvyVXmDIE3D0df/+aSC0pUvT62HqXOBXo\r\ngX9/4QCqT2OWZJcQDAxr7teW74wh6zTreZoqw4ay/qgahJ1KeVJ3hrKcQrvV\r\n5+8QxSnn//g0xT6/8PXVM/EmBoDvpBqOQ9tAKPNVs6c8FzbaKNavQRMri9C/\r\nLQIC9rTgtpN5tTauvHYkeUAHlMtAxRBohJ+RaZKq4SFAhMPJpfDWYfs1LIAq\r\nltT54iM+XrrUMa4ESfn+qPoN5nLQ+i4HuIVvyjqNA60fHwDj517tBmWxHCZC\r\nnEAgOGITrWUlFK8NLf4yNHE57Khdkrzby8/XrjQmb2muB8H+lysL6qjNSxC0\r\nbKJDAM9Uls7EO7XxeeLrjgVXZJHLRYj0aS6IVMtp4gS+UbtDIEdVDK1sajMk\r\nK9xNB9OWJApmp68OV9reW8XddbLS4K+dUt0=\r\n=KXqm\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"49e5499784cd31f3e940e6362a617966e77ae175","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.15_1682413228773_0.055660628106990906","host":"s3://npm-registry-packages"}},"1.0.16--canary.25.4798818870.0":{"name":"@salutejs/canvas-example","version":"1.0.16--canary.25.4798818870.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.16--canary.25.4798818870.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"b1a5d50e66bfc6344b79bf4a8cdf3e81614af935","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.16--canary.25.4798818870.0.tgz","fileCount":68,"integrity":"sha512-4OgsuFtcnmejx3JSpjWLDy/SvdhlBtGAhQyw/fWHpsNDJWg26KeOsVGuGL4sUWeMCVoagN2Tp3A37UhyjZqvxg==","signatures":[{"sig":"MEUCIC85lP/LmQYi6DGqlF/BtRtJS+qhThi2ZHCL6PItOLdWAiEA5s/Wjz/wE0beelPpVX/yYeShuz3DL3l/b1Jfm4J3tek=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92420,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkR+ahACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqzsw//WRDLUesd/eeVJoKzXW18YYAiYNvdKvwcbcm+w+xOnoCLkMpF\r\n8mMFOHRey/1cIRRLbxAcRNxRiMzx9lchpRo53leZdv/6qHsw7pFtoQvZxu17\r\nytY2ic3OPt+8nC4g96EkUt8LtVJVLdjR56zIVFjrZQW0gY6gUU4cGMB3A55L\r\nTUJ8E3Iwp4fZzN0YIKjY1XtEXFBg2yooOsNIPY4HhCzUaUWJxB4uBOntaeWP\r\nBG8K63n6O2z/g3HCdmb7m8rXCsSX9nyUMCtrkWhGxMr424dhHokz691OBijF\r\nW2Fs8uauy5szat30ftUykdBbm43ec9Q/PDemyDQNHilD40zwVOTNrMMb5/Ea\r\nxOAcTxpPNiTHErm34IcmDHHWmiLTCMea0h7+epNT2Pv/Lmg8n1+fQaHVhUhu\r\nX2MNXpIKtOccgaWJ9iQeOd+yj9USE13ucJ9MKoY+T+dFSTLO5mZGAMz9DXkm\r\n0akifBko8x4DbwX6mpV9kTmjcjf40ZXiM6HZwLkdsyp/06c/VOYB9UxJkMK9\r\nlPPhHXVw5IzwkJP1iTBYeGV4GBOymaAvUqRHh1jhvfuDVSSUcrNzHpaFeSVa\r\nC0IfvuDnzXLXOGkwxftQlh0rmYTgoZ3Re5VLYh6xxHS69v0p7P32ga7TdSMU\r\nEXa9l1IvxJ9Bfxu5DDJLXxoml6vaS1p9sT4=\r\n=kqlD\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"1a8af4eb19941c09101b69a91c2ee6b60718bab8","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.16--canary.25.4798818870.0_1682433697308_0.07453250895111752","host":"s3://npm-registry-packages"}},"1.0.16":{"name":"@salutejs/canvas-example","version":"1.0.16","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.16","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"515143d79107d95235e425816d08bd6bdb683bbb","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.16.tgz","fileCount":68,"integrity":"sha512-Rkqxd5Q6kEUgKL5m3IsV8hz6pHhdq6jeNKEwvohSnKz0TZcx3ip9AXba2ZW+hJgEg17Gbc9/5zQW+zxYl1SRVg==","signatures":[{"sig":"MEUCIFj0ksyt3et1Q4VZlWM1+lR0m1J6PZ3IRkpc4rIOIVHeAiEA62FE1hBTwwqd0oNn+Scx6kQGvmChL+LtecQXIpViRVg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92747,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkR+kmACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrPVA/8D2vuNrHcT4EjiwYGUarE+Tt1yYabBgJle9kHl0F++JzFpqEf\r\npN7rUL0b3bkq9b6eTjGjZBVcE71AgLWZhAzFAfgh4BktwjkM5h3wBeT1gyWP\r\n864fsuA0zwhd0TJDxj6Qj30+Jgr7Tchd670bA45+RBld9Uo6T/9y/I2IvKSC\r\nq+U44Y7MNMdsn694QhW6PE9cApVijvK7kZ8LkoyqSDAuPhj9skaKeD3LDutE\r\n/10bnElkrO8hKucgjMeLSmN7eIzOmzf7ZSSCTGwGOqVtiox1s1UkQJ3tXpy4\r\nlOoq3COLxFY4nOG3gXE0+TZRJyju9QUDnmmR0cXGyfFOmeoyALIFXKfR5ZTJ\r\n43bFccYo2an0dZeIscfmp0VBjSqpdSjAxFsrhz/SNrawY9v8VQtF4mKuKHdR\r\nTjbuLjWql6SdEHCE0/v/72waOZdxdsK6kTpDk6n/cJ+jdC3g2mWRCro8WROR\r\nwVLPS6Xle1xSub8JVVGIM1eYZ9Oj1PD60ahfzecEKe/HcaVcIFqDZxhsokcK\r\n+VWwsMDSC/qH6OHpcMuFAu1RpfdUnm8GgPfSJIpZbP3bhr6rHJ4Re6HIaUaz\r\n2THMbZ16r52CqqGfGvMQ5YWoZW9KFToQAPR+36fzt/wGIfTnAsv8xtwZyBGN\r\nYco7D+W1FCGM9+6PdsmW8xdzf1ESjBEtY6M=\r\n=KETV\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"fcbf6f92eac6ad5d8c0259028098423eccf33999","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.16_1682434341953_0.519533922675737","host":"s3://npm-registry-packages"}},"1.0.17--canary.29.4799434793.0":{"name":"@salutejs/canvas-example","version":"1.0.17--canary.29.4799434793.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.17--canary.29.4799434793.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"645763bf5f99caa471c811816a3745da242ec93e","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.17--canary.29.4799434793.0.tgz","fileCount":68,"integrity":"sha512-2BrS+mH7J8JyRoZ3iQtZQQ2MsQqlufBgHUSmIzVNAw+6xnHtAyU9y8TkkKCElpuYx3aN0E7nc8nlTakDwTdJjA==","signatures":[{"sig":"MEQCICpG3P2PnECovhlpyNjQe/VQA0G0n6pMazNXlZDYhElHAiBDMQdeta3vYWOrktJ3UKqHDxswcNpVjy4MQVjygM9IDg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92420,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkR/RPACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmriLw/6Ag1ScCsl/ViZWjOZeIZ77wcxSL7h1DEcBGqzmfgWdANSALz7\r\nL/o7rZxipX9g63WjMCCt6VgAv2O7oY9yWWXEZp0ZiBQZxT1V61eMzehGT1qS\r\nTdFUNEFwCcD5fYAopwyYO4js9nmtt+wKUgTWdGXKKleIZYLK9PsrG2IUit0M\r\nEWF0Yk7Aek8akdfCXGHHwgjN1QkUCMuzdl1HbYTVCk3BOxcPB/8ZyJrrcI6d\r\nMkIttlNTpAMiAFonRSliNmqNs1gO1HrAyZxhZkIw6/KvcBR+Yl+MOEJ4609z\r\nDkbffoqITinLLG5he8wj7h+Q6TLgMVfhak64AEHm269UsFKTcvHYiZ7h5eyk\r\nCkj/49IkRnSBb6V5j6ngltiRFLmJad4o1eLtPwnmLQM86z0bqG1ZmMAa4N5Q\r\nEEeFphQGErqj1uBuNtV7V4IvV8sObvE4NuLgla494Yjp6Bi3YV6FlyArTVMn\r\nKxkLoF3JGX6ZnPFBVzi4L5LtX1bbP0lIyZ3enO+QrjvPjoxYAMq+hAZYMrK+\r\nLODweizoWfhZfu1JeT1cT1HfG1yrVZtoBu94R+E9N7jsHtVYfAnWi2AuQK9u\r\nIqb2fxTdUy07eMpvlZc+9hDRQi30zsPfYErxcqmPXVQxlPjWLcSlkIxjWh4n\r\n17Ddi6SAftH0EaXhHnM8CuIjCsQ3SpD90A8=\r\n=rhed\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"eb58fce979ce42b8ac3ae909185debbb92823913","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.17--canary.29.4799434793.0_1682437199151_0.3736424079001941","host":"s3://npm-registry-packages"}},"1.0.17":{"name":"@salutejs/canvas-example","version":"1.0.17","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.17","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"e4acf2f8c34f8b983b261fed98d9a9989384350a","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.17.tgz","fileCount":68,"integrity":"sha512-jxkKK7QtZU+27ySN0kRaW7ybfix0MUcmOp/6c0wREDlso2zL1EqjZLSmwCB01otxNfkpkQDOncwAsSFut7KV1Q==","signatures":[{"sig":"MEYCIQD3khREt4stFG5flUdjvmS/xKJ+e0fzaHbGwY/x0/2AuwIhAIEJNi9jZzCBkrgtTnsjqEZVl2vnYGK4yV8zJ2MRTBuX","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92692,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkR/UpACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoGJA/+Mc1D9yi4/FCNOIwyf/o0v9lzKIW0DjunKcDewkDjVQI94LlX\r\np+xkH3nVkrlfoC5h85B1qIqvnt0ZhcMm8lI9IIYFMxiLgtJLn96VvRWkvd5Q\r\nVnVBpQeVj6FjWJkWmghJvKRjtPrc+S1ZfyQN5ImqD2TIL0L/oFld383ZiM9t\r\nt9nD+ME+3ty8v8KfpL57iKUlrayMPrqjERDsgJjCvyq+/iMM+EkyGxEk804C\r\nocil5zdgVx4OELK8OovIWtcYKUY/R8fosZstkp+Wn6Jb5swXRwWrx+CWDkRu\r\nCoQqkgLXN1DjGu2wdm7GHrEUR6VzsIDDdcggPTVvo0+U5/gJHK2CxBCWOehv\r\nltwBOhGkZY8vbMLApTIBGvSCmsdOYmTR/+sYgaklTvv6s8ixNLdz8ZCPWdeQ\r\nJzM1CzVaybfC1BRjRxEEUESRspSqtRqDewMu7y/2D+8wOh47O+xRlElF9S9E\r\nKFYnYxVz/ZuhjrwRL1ShLUmTP3Uqy9SzX3YAkvO1BIhhaDozVTiMf+26Pv77\r\nJsYXdL3/22U10AQd8SCFSP+Y2dhseVRrmXs8BQU5tREF88gS6HHfaZQKgUyA\r\nnvRTKkTV6U/t7zQgyQJO7g0jOFfsMf1ClnGw9FLX/2FYpKwunVI4XRbC06t9\r\nkghDGQN01f630uNpSfniz+vlZ6tFsYLjuU0=\r\n=0HWQ\r\n-----END PGP SIGNATURE-----\r\n"},"gitHead":"afa902911c3a711fda87f9fc5956f0b3d6c8d571","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.17_1682437417191_0.00027260185446964513","host":"s3://npm-registry-packages"}},"1.0.18--canary.35.4838622031.0":{"name":"@salutejs/canvas-example","version":"1.0.18--canary.35.4838622031.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.18--canary.35.4838622031.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"3c85cf620a7e61441556370f6916e32980724918","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.18--canary.35.4838622031.0.tgz","fileCount":68,"integrity":"sha512-9UQoPbMnyrG16SCwHh8FvQLBHSnSBj76DTZavSbeNB1gXw2febopzQOVRlWwgqx8G0xodFwrbDHcAPCJHTOsmA==","signatures":[{"sig":"MEUCIQDq662ovp9RomS/SX4p4N9UaYMW8XMXECrVhgzrQuTUBwIgNhzbKwjbgNfrr7UuZgh99Xju1F39ChZw8M/RESdXtm4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92713,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkTQd9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmro6w/+NVm1hsM0G1utSX14kFVjJ7cBs/DLeIxoBs8IXK527Nsbjisg\r\n5ZiW/4zQMACs22S42JAa6JdRDgSpOmJjg7KxfhXwVQgQyx0D/CeDwKgiNxMx\r\nIIrtwCDZ4gThmUEclRVvYUqt8XcejLrg3ZHxfmX7kwlAncF9kWBP7DcW1y2v\r\n4DSw9izYhNtP4Y/pz4YxTjYloUNIxYaSJ6tNCZikA9TLoX4rt3Sxx4/7ZTkr\r\nKp6n/c5BSoCJgIk08A4odJswfneWByIWbhmsAjjPe4e51458nW88rmf0YiPf\r\nqETpUn7bGPwJitLOijPnO4OqWCnI4OHpDF2FNds3a3kfiC3uQP9uIncyArXQ\r\n6n2ZNPh0+AqxjOJn1jvnU7dnykr+wglkFN2jbAogp5Jy+Td3t58qmXeZY5vj\r\nM3JSj5yRpR03bIfajgJguXCw5rLVyCXcNm5rY3PIHndPBfliZ25OeXMkct3h\r\nw83mUioeNMSPCGxh/8nnCLobFw0EQYfWTp9C64e6ZfTsWL7fwU1ofRjp3hzC\r\n+s0J6u0eNEpEtAbe+fisH7HsnwaV4ck8s8n02OJqO7QdMBOPcJIyrFaTd/b7\r\nmephv92pZPv1PwDqGYJHbxW7dSipaCrHOJSW4k3rd9ITJaAMvnczkmgkmNvg\r\nlD6/kFAeRp+38fA47J9Y4ILnGHyHNMQ/JiU=\r\n=paXg\r\n-----END PGP SIGNATURE-----\r\n"},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"40ace80239aff49eb4d06d8212b7a1442ff33065","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.18--canary.35.4838622031.0_1682769789103_0.08572390235940852","host":"s3://npm-registry-packages"}},"1.0.18":{"name":"@salutejs/canvas-example","version":"1.0.18","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.18","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"806f70889e220f9ffdeed6f85bc5d6f41a30d4e9","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.18.tgz","fileCount":68,"integrity":"sha512-u57nOjuhX2j1HmBbQ5IV7OVYXOW31r2v6tkxDH24KqkYUcgSY+f3/xO+sgEMflxkqK0vM+DEq+ZVhkEkeE84aw==","signatures":[{"sig":"MEUCIAXvGkiW+f901bn5LGSLxuEEhmmO3Uvp/dYJe4NiSWhBAiEAhu7Wt0lXWj6D2E3C0+G8nOfI3RuJz1kw/7IlMqRO7VY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92985},"gitHead":"60598e4dd1bbbc4048ef3989b144e7bf2c478d68","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.18_1683286526688_0.5337913497649933","host":"s3://npm-registry-packages"}},"1.0.19--canary.36.4894164029.0":{"name":"@salutejs/canvas-example","version":"1.0.19--canary.36.4894164029.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.19--canary.36.4894164029.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"ecd66853667c432f05141caf101e252a20a1c18b","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.19--canary.36.4894164029.0.tgz","fileCount":68,"integrity":"sha512-TwpjQSfugr5gFKcWyVnl5OUo3W1Xl6Suum/o2T/c8eHEtbo5+SRxHVUhUl2+jdOeng85Mpe76IRoG51xc7XUvA==","signatures":[{"sig":"MEUCICncffmIr7zAqVm6kDKe4sTf1Bs07KhaCyaXaG/vlPDQAiEAyD0rY521CTvHVdAGbTDh8gFoNrFmYU8hh7U/cIFjGds=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92961},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"0dcfbfc1e793b1957f162626ae3fabdf897bd737","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.19--canary.36.4894164029.0_1683295022073_0.9373741110876328","host":"s3://npm-registry-packages"}},"1.0.19--canary.37.4935377993.0":{"name":"@salutejs/canvas-example","version":"1.0.19--canary.37.4935377993.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.19--canary.37.4935377993.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"16de08660f32d4fd452bf3f4771676d40a694428","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.19--canary.37.4935377993.0.tgz","fileCount":68,"integrity":"sha512-Z5Hu9vASsN31hhKxztlEXGKFdQzv0Hu4aEe33L8tw7+P6t/tWVlzjwuMVRUaP3URQN4umlBHI9eZ0EBSHzPegw==","signatures":[{"sig":"MEQCIHJk7yQUFrSL02FFAAqkToWr76Ra5u5NE5Ynqqgt30LvAiA32MfB7fJnhoB569ahtW7CT9twcjrLfjrHgPkTyG+lNQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92961},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"997cdd144599640ccc6cbaf1a278ddf691396ac4","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.6.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.19--canary.37.4935377993.0_1683710941606_0.16996052300628328","host":"s3://npm-registry-packages"}},"1.0.19--canary.37.4935522220.0":{"name":"@salutejs/canvas-example","version":"1.0.19--canary.37.4935522220.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.19--canary.37.4935522220.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"a5be7e5f888768eea0913dc1b81df548f5d0865a","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.19--canary.37.4935522220.0.tgz","fileCount":68,"integrity":"sha512-Ca8baH8m9ZJHpWeOHgKTjPVgjcpB7OQ1gQViLZP6UlKykjo6vQLmj3DOOZq6lDXTwwTWkoWlFq/bvbJiLDz+3w==","signatures":[{"sig":"MEQCIC7NwkD/YpDR0n5cR7vxUA3K2Xbhxr0gMtmtTjN+5og5AiB1fMXKXK8EXFv1obiuDv0WF6KXDlYm/138V1eMmqllHg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":93119},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"b9fa3167797a398d9b9bf7e9aded60d4e38c0c8f","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.19--canary.37.4935522220.0_1683711893242_0.9401617166472669","host":"s3://npm-registry-packages"}},"1.0.19--canary.38.4935616622.0":{"name":"@salutejs/canvas-example","version":"1.0.19--canary.38.4935616622.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.19--canary.38.4935616622.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"9ddc6e847bf01162b1b47f78fba2acf4f2d1f4d1","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.19--canary.38.4935616622.0.tgz","fileCount":68,"integrity":"sha512-UKY8QwZhcDNSHx+qG0eTGDiUbbMcIh+Kn27rfWd6IsFk2foqRIwYXmWK9+ttoh7alZQiJVIUOHDgdg2xICphFg==","signatures":[{"sig":"MEYCIQDF4zcDFkLh3MIM4zgg0IW7nunazJV3VjcEVSWTMeKlxQIhANZkMKVbt8PmvD/Obg8jHJmyJ+T8ynXT4fOS52HnSs7M","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92527},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"e8fbd0ee97d7febb9df1e45801ba590f4ece9c2a","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.19--canary.38.4935616622.0_1683712553516_0.3567526843765345","host":"s3://npm-registry-packages"}},"1.0.19":{"name":"@salutejs/canvas-example","version":"1.0.19","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.19","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"a49564e080178b1e87fc9419df725ec5a7691ace","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.19.tgz","fileCount":68,"integrity":"sha512-49wynRQT1QCl8lTAmY7LQFFny3T1N/Cr5AUx8RF0MaX08p+DoVG264jMk5cwNdJerCZ8UAa7/9HiTNFGsEMlmA==","signatures":[{"sig":"MEUCIQDhngSqMyETAA4YJxXTFgsaK3TEVCEys2rcSveS3X9ZpgIgaLnZpYS/C/g7a/TCuMwAxb5oYX+1qurx2j/9P+JkhZU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92796},"gitHead":"92bc91e5edc4fed401793268e2379699a47f9756","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.19_1683713012472_0.3641244413567166","host":"s3://npm-registry-packages"}},"1.0.20--canary.40.5252799851.0":{"name":"@salutejs/canvas-example","version":"1.0.20--canary.40.5252799851.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.20--canary.40.5252799851.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"84833716c6cc4e0be44683b4dfd5a80038c5bb02","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.20--canary.40.5252799851.0.tgz","fileCount":68,"integrity":"sha512-d73c71LT0hkSTMROqqJTl28CHWvvdCNv51eAOzIF6nQFXSxGYK+r2fo7qqxQbCgcPBeyd+3LZq0ZUlL4YUhWMw==","signatures":[{"sig":"MEUCICJ01F+kzB+sLZsgFQcXqCPsaqEcPWqXRTZKgU3FNXYKAiEAmMpArbDtFFT6mJSe6UwrzDW2WAgK6t80d+yWCjhcBhY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":92978},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"4b0f49a540853b407b1a28b799bbf9ca3fe94f54","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.20--canary.40.5252799851.0_1686643262988_0.7673705063821326","host":"s3://npm-registry-packages"}},"1.0.20":{"name":"@salutejs/canvas-example","version":"1.0.20","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.20","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"653c4b8f135787aa22b1d62ad990da1ba5a1103b","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.20.tgz","fileCount":68,"integrity":"sha512-lqcmXeHqtUHjvkoKX7vVHt4gNljX9Dsv8lI6EO0KhQvEd3bzDvTxUAA6pplczhb6gedvvrifQiKBK1Q9ZQGUQQ==","signatures":[{"sig":"MEQCIFlEnlPnlFcrMrOljflbXfOSJ/DRZSA9siYbIM7TDrLwAiA9zBAp1paNK8r1tH8LDmlOHiT/PppWMGG4KhvIsLTC4g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":93249},"gitHead":"457960edaf74823df6889b3c0fd580ff7338cb64","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.6.2","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.20_1686650985401_0.5869262787146279","host":"s3://npm-registry-packages"}},"1.0.21":{"name":"@salutejs/canvas-example","version":"1.0.21","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.21","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"d4f2b9460bb4c7520b3cea6d92451247894a6d78","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.21.tgz","fileCount":68,"integrity":"sha512-rPV92vDk3zr5XL/vZI3KcGTez7ZzhhgJCYujgqu1dyB8xrv3MoCULFyxBr9f3PgFq/ik4w6LeLGKEuW3i5A5wg==","signatures":[{"sig":"MEYCIQCV/1suF26VwmivRBcNESE2Xd/oVEH+f0NCF25Q+bDRIAIhAKRZpAemVbFjAGP4c7nftQGsR0+zHtGzu/bNQACu8p8u","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":93528},"gitHead":"829d84f97341aa24ffa3bb0d61f15926b5105f40","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.24.0","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.21_1695301669263_0.7290365034715283","host":"s3://npm-registry-packages"}},"1.0.22--canary.44.6492655838.0":{"name":"@salutejs/canvas-example","version":"1.0.22--canary.44.6492655838.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.22--canary.44.6492655838.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"4c5788e877aef872a561b1e621be050d9b1055f3","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.22--canary.44.6492655838.0.tgz","fileCount":69,"integrity":"sha512-lFClKXSH5oh2aVqeyVLxLxcDw3GrKOUJbWLAgscJJyr+C/ue6hXXyB7voN6BVVLFCO56vhKRRKUauLd4PqTZ2Q==","signatures":[{"sig":"MEQCIEx5esRVE2NJelgRUWfbIzCOG5iNaYXayTy+yS0xmYLQAiB/Q0UKSFSvwHGrRjPQpJPrtXcftWjHEm6ReTC31+VWeg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":94694},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"e745e95c6bc56692909f4fb42a3b2589b3f0947e","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.24.0","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.22--canary.44.6492655838.0_1697096064993_0.9970973898585698","host":"s3://npm-registry-packages"}},"1.0.22":{"name":"@salutejs/canvas-example","version":"1.0.22","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.22","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"578d3ece2231fb61ff11c83e4ea0d60c5d9f3d0d","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.22.tgz","fileCount":69,"integrity":"sha512-BdEcFuN2Kd575aTaKE36MVR0jEd6dzvbeq68h9rSjFKAlS/VuM5Yl/ATAxWXaUg8cGJqKaXQIbZzA+jgcMHVGQ==","signatures":[{"sig":"MEQCIHiFPFqesh0rIiFY4E/NNUU/c5/e++TUyJUExtVEDr9rAiArGWJan3pF+JMl5nyzmIS/I8QI1Mr2XyAaSmtjYgSqqQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":94963},"gitHead":"13cede28c13c033e72778ad42abb307d81182bb8","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.24.0","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.27.2","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.23.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.22_1697192528047_0.8657308264930386","host":"s3://npm-registry-packages"}},"1.0.23--canary.53.7087619298.0":{"name":"@salutejs/canvas-example","version":"1.0.23--canary.53.7087619298.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.23--canary.53.7087619298.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"b438d51c68f16ab8dd94952adf629bfd0b04abce","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.23--canary.53.7087619298.0.tgz","fileCount":69,"integrity":"sha512-v5h6EwSuN2hOAtbu8g2+shDtbu1+c+NCqbLY9/vCZAF/CQAv0RgwZRWGfjLU+M9y5zWDS4uufX7zUcSx9E2dvw==","signatures":[{"sig":"MEUCIQCJB0lAtuiIUOI1n826rP4390zT15m+2vfLqL+TEQlSPwIgZbmq9/N3QzFPhiSkXgem0qDluH/6E/W1W2GurL7mEK4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":94983},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"34788042dd7f14dd95bda82acc86d2693660a3c5","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.24.0","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.39.1","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.39.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.23--canary.53.7087619298.0_1701698268830_0.07224298836011434","host":"s3://npm-registry-packages"}},"1.0.23":{"name":"@salutejs/canvas-example","version":"1.0.23","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.23","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"d55df3fb8af8e4e23d1d015daf2f5777abc265fe","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.23.tgz","fileCount":69,"integrity":"sha512-0fIi1zAm/WjF4hNJJ7E8CKGSHngq8zYTVvoCT87MsiwE7nQPBTNx54JC0bFzbUwj01o5ahPiJ2ykxsscQRETZg==","signatures":[{"sig":"MEQCICzSsRk1H+0hlssYToaHUgr7vI6YOfiRqHfdOAydVdasAiBl+uju97381Hh4goGus4Nwbu1G1BQ4GJvkpzVUpHiC+Q==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96030},"gitHead":"5170e7b9c06e88f12284f004d5298a1c60d7e9db","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.24.0","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"0.39.1","@salutejs/plasma-ui":"1.159.0","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@salutejs/storage-adapter-memory":"0.39.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.36.5","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.3.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.23_1701763314468_0.5671566912403265","host":"s3://npm-registry-packages"}},"1.0.24--canary.56.8701852161.0":{"name":"@salutejs/canvas-example","version":"1.0.24--canary.56.8701852161.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.24--canary.56.8701852161.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"54a70e4ba7daa03bd660f953cb58fd98189506a7","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.24--canary.56.8701852161.0.tgz","fileCount":69,"integrity":"sha512-wwRS10gsi8P9m7+cd/Hip7Ynl/IGSci98QTO7c9kVam7J0s57WODDghp1mViKY3WQOMFMwINS+yDVAC/vy6bcw==","signatures":[{"sig":"MEQCIFtt2magFJGPXJ2d46r7zneN8qTW9vdc+yrDgNfvLkcRAiBB+NtrOGFGgWtYc7+I6j7kACTfmRa9gYaZUNcbaGd/0Q==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96350},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"3125b71ababfa39cb0cb1844673626ced5cd88d3","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.24.0","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"1.1.0","@salutejs/plasma-ui":"1.159.0","@tanstack/react-query":"5.29.2","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@tanstack/react-query-devtools":"5.29.2","@salutejs/storage-adapter-memory":"0.39.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"11.1.6","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.9.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.24--canary.56.8701852161.0_1713253641646_0.1388452319039024","host":"s3://npm-registry-packages"}},"1.0.24--canary.56.8721800856.0":{"name":"@salutejs/canvas-example","version":"1.0.24--canary.56.8721800856.0","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.24--canary.56.8721800856.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"498cb0f935fc0268dbe353e8d35f5c4f23e42b6b","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.24--canary.56.8721800856.0.tgz","fileCount":69,"integrity":"sha512-yEjSzDIxOGBVQtgaaoK8c38x5NYqPEA/Y72aOi325b30H6gjE+141yJaIsmfNa3SlTjP7h6PWUKFppl+lTCdGw==","signatures":[{"sig":"MEYCIQDd/bqU+y/SeBUsup4Ukn9sOyTD73ZA23nUFdFTKE4R2wIhALUVa3+f+Go5kpNuoRv6rF9YBSHsWoDdEKIzWlQbjPqt","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96302},"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","gitHead":"085a21e62d7c3f0d56652433882e0bb5f5aa0af2","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.24.0","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"1.1.0","@salutejs/plasma-ui":"1.159.0","@tanstack/react-query":"5.29.2","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@tanstack/react-query-devtools":"5.29.2","@salutejs/storage-adapter-memory":"0.39.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"11.1.6","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.9.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.24--canary.56.8721800856.0_1713356665595_0.5420270737322308","host":"s3://npm-registry-packages"}},"1.0.24":{"name":"@salutejs/canvas-example","version":"1.0.24","author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/canvas-example@1.0.24","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"dist":{"shasum":"0a19cfee38056e3b32abc22d2dba422585aafa70","tarball":"https://registry.npmjs.org/@salutejs/canvas-example/-/canvas-example-1.0.24.tgz","fileCount":69,"integrity":"sha512-m4xEt87PrUz9SOkgjYhW84/GMkZNqVVuQpPSxU2TVIbM8vYATaoA6b6nPGlUBmt4/+/yS7Kq6QrwEVzv8I/atQ==","signatures":[{"sig":"MEQCIBHZJriRp+OloiJs1UsJAeOcBiqu/s0l0sJ2ahVh++uNAiAguV/eFwYPY7zSsuVpg6Agq6CARtaezdjmGz1mmpEvhA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96597},"gitHead":"f487734a90910bd8a10a2eb079ce5088e224deb4","scripts":{"dev":"cross-env NODE_OPTIONS='--inspect' next dev","tsc":"tsc","lint":"eslint --ext .js,.ts,.tsx .","build":"next build","start":"next start","cy:run":"cypress run -b chromium","cy:open":"cypress open -b chromium --env failOnSnapshotDiff=false","release":"git diff && auto shipit","cy:clean":"rm -rf cypress/{results,reports} && rm -rf cypress/snapshots/**/*/__diff_output__ && rm -rf coverage && rm -rf .nyc_output","cy:mobile":"start-test ${PORT:-3000} cy:open:mobile","cy:portal":"start-test ${PORT:-3000} cy:open:portal","cy:sberbox":"start-test ${PORT:-3000} cy:open:sberbox","lint:eslint":"eslint","cy:test-build":"start-server-and-test build:serve ${PORT:-3000} cy:test","cy:open:mobile":"npm run cy:open -- --config-file cypress/config/mobile.config.ts","cy:open:portal":"npm run cy:open -- --config-file cypress/config/portal.config.ts","lint:stylelint":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}","test:cy:mobile":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/mobile.config.ts","test:cy:portal":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/portal.config.ts","cy:open:sberbox":"npm run cy:open -- --config-file cypress/config/sberbox.config.ts","lint:eslint:fix":"eslint --fix","test:cy:sberbox":"CYPRESS_BASE_URL=${PUBLIC_URL:-'http://localhost:3000/'} CYPRESS_REPORT_FOLDER=cypress/ REPORT_FOLDER=cypress/ npm run cy:run -- --config-file cypress/config/sberbox.config.ts","lint:stylelint:fix":"stylelint --config ./.stylelintrc.js ./src/{components,hooks,pages,styles}/**/*.{ts,tsx}"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"_npmVersion":"8.19.2","description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","directories":{},"_nodeVersion":"18.12.0","dependencies":{"next":"13.1.2","react":"18.2.0","react-dom":"18.2.0","react-query":"3.39.2","@salutejs/client":"1.24.0","@salutejs/spatial":"3.0.1","styled-components":"5.3.5","@salutejs/scenario":"1.1.0","@salutejs/plasma-ui":"1.159.0","@tanstack/react-query":"5.29.2","@salutejs/plasma-icons":"1.118.0","@salutejs/plasma-tokens":"1.32.0","@tanstack/react-query-devtools":"5.29.2","@salutejs/storage-adapter-memory":"0.39.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"11.1.6","eslint":"8.32.0","cypress":"11.1.0","prettier":"2.0.5","cross-env":"7.0.3","stylelint":"13.13.0","typescript":"4.9.5","@types/react":"18.0.18","babel-eslint":"10.1.0","global-agent":"3.0.0","stylelint-scss":"3.17.1","stylelint-order":"4.0.0","eslint-plugin-vue":"9.9.0","cypress-wait-until":"1.7.1","eslint-plugin-jest":"27.2.1","eslint-plugin-react":"7.32.0","eslint-config-airbnb":"19.0.4","eslint-plugin-import":"2.27.4","eslint-plugin-cypress":"2.12.1","start-server-and-test":"1.13.1","@cypress/code-coverage":"3.9.10","eslint-config-prettier":"8.6.0","eslint-plugin-flowtype":"8.0.3","eslint-plugin-jsx-a11y":"6.7.1","eslint-plugin-prettier":"4.2.1","@salutejs/eslint-config":"1.2.0","eslint-config-react-app":"7.0.1","@types/styled-components":"5.1.26","eslint-plugin-react-perf":"3.3.1","@salutejs/prettier-config":"0.3.0","@typescript-eslint/parser":"5.48.2","eslint-plugin-react-hooks":"4.6.0","@salutejs/stylelint-config":"0.8.0","eslint-plugin-no-only-tests":"3.1.0","stylelint-config-recommended":"3.0.0","@typescript-eslint/eslint-plugin":"5.48.2","stylelint-config-styled-components":"0.1.1","stylelint-processor-styled-components":"1.10.0"},"_npmOperationalInternal":{"tmp":"tmp/canvas-example_1.0.24_1713356977192_0.8706285160764031","host":"s3://npm-registry-packages"}}},"time":{"created":"2022-05-19T12:05:26.763Z","modified":"2026-08-05T13:53:39.958Z","1.0.0":"2022-05-19T12:05:26.996Z","1.0.1--canary.2.2429090622.0":"2022-06-02T15:04:30.895Z","1.0.1":"2022-06-03T09:01:23.475Z","1.0.2":"2022-06-27T09:22:24.394Z","1.0.3--canary.4.2995012104.0":"2022-09-05T17:09:59.944Z","1.0.3--canary.4.2995048186.0":"2022-09-05T17:16:49.982Z","1.0.3--canary.4.3001415527.0":"2022-09-06T15:28:29.939Z","1.0.3--canary.4.3006378868.0":"2022-09-07T09:06:42.153Z","1.0.3--canary.4.3006931681.0":"2022-09-07T10:35:48.946Z","1.0.3--canary.4.3006948602.0":"2022-09-07T10:38:39.406Z","1.0.3":"2022-09-07T10:52:17.174Z","1.0.4--canary.5.3073280476.0":"2022-09-17T12:05:54.340Z","1.0.4":"2022-09-19T14:29:08.929Z","1.0.5--canary.6.3182535913.0":"2022-10-04T13:32:45.290Z","1.0.5":"2022-10-07T13:34:25.527Z","1.0.6--canary.7.3594121365.0":"2022-12-01T15:40:13.095Z","1.0.6--canary.9.3649382211.0":"2022-12-08T14:52:59.327Z","1.0.6":"2022-12-12T08:27:58.360Z","1.0.7--canary.9.3674148218.0":"2022-12-12T08:55:16.434Z","1.0.7--canary.9.3674155940.0":"2022-12-12T08:56:16.217Z","1.0.7--canary.9.3686633577.0":"2022-12-13T15:05:17.680Z","1.0.7--canary.10.3924672884.0":"2023-01-15T18:09:45.729Z","1.0.7--canary.10.4057069803.0":"2023-01-31T18:16:15.030Z","1.0.7--canary.10.4064642529.0":"2023-02-01T13:05:43.659Z","1.0.7--canary.10.4072275496.0":"2023-02-02T07:40:45.246Z","1.0.7--canary.10.4072805898.0":"2023-02-02T08:57:35.706Z","1.0.7--canary.10.4083650739.0":"2023-02-03T11:19:40.074Z","1.0.7":"2023-02-07T09:05:49.318Z","1.0.8--canary.12.4223427467.0":"2023-02-20T12:36:56.662Z","1.0.8":"2023-02-20T12:50:51.491Z","1.0.9--canary.13.4303573114.0":"2023-03-01T12:36:56.017Z","1.0.9":"2023-03-01T13:57:24.507Z","1.0.10--canary.14.4464396293.0":"2023-03-20T03:25:14.896Z","1.0.10":"2023-03-20T09:19:04.450Z","1.0.11--canary.15.4566450655.0":"2023-03-30T15:57:22.964Z","1.0.11--canary.15.4573623434.0":"2023-03-31T09:32:00.152Z","1.0.11--canary.15.4597153264.0":"2023-04-03T13:18:34.893Z","1.0.11":"2023-04-04T07:05:59.496Z","1.0.12--canary.20.4610439466.0":"2023-04-04T17:11:01.867Z","1.0.12--canary.20.4616910992.0":"2023-04-05T09:18:16.537Z","1.0.12--canary.20.4617840951.0":"2023-04-05T11:12:14.360Z","1.0.12--canary.20.4617846024.0":"2023-04-05T11:12:50.973Z","1.0.12":"2023-04-05T11:22:52.093Z","1.0.13--canary.23.4746216504.0":"2023-04-19T17:32:54.897Z","1.0.13":"2023-04-19T17:37:55.276Z","1.0.14--canary.24.4746302300.0":"2023-04-19T17:43:39.439Z","1.0.14":"2023-04-19T17:44:03.766Z","1.0.15--canary.25.4753297681.0":"2023-04-20T10:35:40.981Z","1.0.15--canary.26.4755161044.0":"2023-04-20T14:00:14.059Z","1.0.15--canary.25.4756580223.0":"2023-04-20T16:21:27.449Z","1.0.15--canary.25.4769196067.0":"2023-04-21T22:10:34.609Z","1.0.15--canary.25.4784787395.0":"2023-04-24T09:40:04.713Z","1.0.15--canary.25.4785222305.0":"2023-04-24T10:28:51.048Z","1.0.15":"2023-04-25T09:00:28.945Z","1.0.16--canary.25.4798818870.0":"2023-04-25T14:41:37.473Z","1.0.16":"2023-04-25T14:52:22.132Z","1.0.17--canary.29.4799434793.0":"2023-04-25T15:39:59.345Z","1.0.17":"2023-04-25T15:43:37.354Z","1.0.18--canary.35.4838622031.0":"2023-04-29T12:03:09.264Z","1.0.18":"2023-05-05T11:35:26.875Z","1.0.19--canary.36.4894164029.0":"2023-05-05T13:57:02.257Z","1.0.19--canary.37.4935377993.0":"2023-05-10T09:29:01.799Z","1.0.19--canary.37.4935522220.0":"2023-05-10T09:44:53.461Z","1.0.19--canary.38.4935616622.0":"2023-05-10T09:55:53.737Z","1.0.19":"2023-05-10T10:03:32.646Z","1.0.20--canary.40.5252799851.0":"2023-06-13T08:01:03.219Z","1.0.20":"2023-06-13T10:09:45.571Z","1.0.21":"2023-09-21T13:07:49.576Z","1.0.22--canary.44.6492655838.0":"2023-10-12T07:34:25.397Z","1.0.22":"2023-10-13T10:22:08.296Z","1.0.23--canary.53.7087619298.0":"2023-12-04T13:57:49.067Z","1.0.23":"2023-12-05T08:01:54.687Z","1.0.24--canary.56.8701852161.0":"2024-04-16T07:47:21.888Z","1.0.24--canary.56.8721800856.0":"2024-04-17T12:24:25.819Z","1.0.24":"2024-04-17T12:29:37.357Z"},"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"description":"Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).","maintainers":[{"email":"yeti.or@gmail.com","name":"yeti-or"},{"email":"salute.developers@gmail.com","name":"salute-eva"}],"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -> SmartApp API\n  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`\n  - Хостинг фронтенда -> `<https ссылка туннеля>`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","readmeFilename":"README.md"}